blob: 88f908e8c0081a3342154bc00d3ab6898ee7d2b5 [file] [log] [blame]
/*
* Copyright (c) 2015-2025 The Khronos Group Inc.
* Copyright (c) 2015-2025 Valve Corporation
* Copyright (c) 2015-2025 LunarG, Inc.
* Copyright (c) 2015-2025 Google, Inc.
*
* 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
*/
#include "../framework/layer_validation_tests.h"
#include "../framework/ray_tracing_objects.h"
#include "../framework/shader_helper.h"
#include "../framework/feature_requirements.h"
#include "../layers/utils/vk_layer_utils.h"
#include "../framework/descriptor_helper.h"
#include "../framework/pipeline_helper.h"
#include <iterator>
void RayTracingTest::InitFrameworkForRayTracingTest(VkValidationFeaturesEXT* enabled_features /*= nullptr*/) {
AddRequiredExtensions(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_RAY_QUERY_EXTENSION_NAME);
AddRequiredExtensions(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_SPIRV_1_4_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_PIPELINE_LIBRARY_EXTENSION_NAME);
RETURN_IF_SKIP(InitFramework(enabled_features));
}
class PositiveRayTracing : public RayTracingTest {};
TEST_F(PositiveRayTracing, GetAccelerationStructureBuildSizes) {
TEST_DESCRIPTION("Test enabled features for GetAccelerationStructureBuildSizes");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredExtensions(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::accelerationStructure);
RETURN_IF_SKIP(InitFrameworkForRayTracingTest());
RETURN_IF_SKIP(InitState());
VkAccelerationStructureBuildGeometryInfoKHR build_info = vku::InitStructHelper();
build_info.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR;
uint32_t max_primitives_count = 0;
VkAccelerationStructureBuildSizesInfoKHR build_sizes_info = vku::InitStructHelper();
vk::GetAccelerationStructureBuildSizesKHR(device(), VK_ACCELERATION_STRUCTURE_BUILD_TYPE_HOST_OR_DEVICE_KHR, &build_info,
&max_primitives_count, &build_sizes_info);
}
TEST_F(PositiveRayTracing, AccelerationStructureReference) {
TEST_DESCRIPTION("Test device side accelerationStructureReference");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredExtensions(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_RAY_QUERY_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
AddRequiredFeature(vkt::Feature::accelerationStructure);
AddRequiredFeature(vkt::Feature::rayQuery);
RETURN_IF_SKIP(Init());
m_command_buffer.Begin();
// Build Bottom Level Acceleration Structure
auto blas =
std::make_shared<vkt::as::BuildGeometryInfoKHR>(vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device));
blas->BuildCmdBuffer(m_command_buffer.handle());
m_command_buffer.End();
m_default_queue->Submit(m_command_buffer);
m_device->Wait();
m_command_buffer.Begin();
// Build Top Level Acceleration Structure
// ---
vkt::as::BuildGeometryInfoKHR tlas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceTopLevel(*m_device, blas);
tlas.BuildCmdBuffer(m_command_buffer.handle());
m_command_buffer.End();
m_default_queue->Submit(m_command_buffer);
m_device->Wait();
}
TEST_F(PositiveRayTracing, HostAccelerationStructureReference) {
TEST_DESCRIPTION("Test host side accelerationStructureReference");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredExtensions(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::accelerationStructure);
AddRequiredFeature(vkt::Feature::accelerationStructureHostCommands);
RETURN_IF_SKIP(Init());
// Build Bottom Level Acceleration Structure
auto blas =
std::make_shared<vkt::as::BuildGeometryInfoKHR>(vkt::as::blueprint::BuildGeometryInfoSimpleOnHostBottomLevel(*m_device));
blas->BuildHost();
// Build Top Level Acceleration Structure
vkt::as::BuildGeometryInfoKHR tlas = vkt::as::blueprint::BuildGeometryInfoSimpleOnHostTopLevel(*m_device, blas);
tlas.BuildHost();
}
TEST_F(PositiveRayTracing, CreateAccelerationStructureKHR) {
TEST_DESCRIPTION("Validate acceleration structure creation.");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredFeature(vkt::Feature::accelerationStructure);
RETURN_IF_SKIP(InitFrameworkForRayTracingTest());
RETURN_IF_SKIP(InitState());
vkt::Buffer buffer(*m_device, 4096, VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR);
VkAccelerationStructureKHR as;
VkAccelerationStructureCreateInfoKHR as_create_info = vku::InitStructHelper();
as_create_info.buffer = buffer.handle();
as_create_info.size = 4096;
as_create_info.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR;
vk::CreateAccelerationStructureKHR(device(), &as_create_info, nullptr, &as);
vk::DestroyAccelerationStructureKHR(device(), as, nullptr);
}
TEST_F(PositiveRayTracing, StridedDeviceAddressRegion) {
TEST_DESCRIPTION("Test different valid VkStridedDeviceAddressRegionKHR");
SetTargetApiVersion(VK_API_VERSION_1_2);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
AddRequiredFeature(vkt::Feature::accelerationStructure);
AddRequiredFeature(vkt::Feature::rayTracingPipeline);
AddRequiredFeature(vkt::Feature::rayQuery);
RETURN_IF_SKIP(InitFrameworkForRayTracingTest());
RETURN_IF_SKIP(InitState());
// Create ray tracing pipeline
VkPipeline raytracing_pipeline = VK_NULL_HANDLE;
{
VkShaderObj rgen_shader(this, kRayTracingMinimalGlsl, VK_SHADER_STAGE_RAYGEN_BIT_KHR, SPV_ENV_VULKAN_1_2);
VkShaderObj chit_shader(this, kRayTracingMinimalGlsl, VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, SPV_ENV_VULKAN_1_2);
const vkt::PipelineLayout pipeline_layout(*m_device, {});
std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages;
shader_stages[0] = vku::InitStructHelper();
shader_stages[0].stage = VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR;
shader_stages[0].module = chit_shader.handle();
shader_stages[0].pName = "main";
shader_stages[1] = vku::InitStructHelper();
shader_stages[1].stage = VK_SHADER_STAGE_RAYGEN_BIT_KHR;
shader_stages[1].module = rgen_shader.handle();
shader_stages[1].pName = "main";
std::array<VkRayTracingShaderGroupCreateInfoKHR, 1> shader_groups;
shader_groups[0] = vku::InitStructHelper();
shader_groups[0].type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR;
shader_groups[0].generalShader = 1;
shader_groups[0].closestHitShader = VK_SHADER_UNUSED_KHR;
shader_groups[0].anyHitShader = VK_SHADER_UNUSED_KHR;
shader_groups[0].intersectionShader = VK_SHADER_UNUSED_KHR;
VkRayTracingPipelineCreateInfoKHR raytracing_pipeline_ci = vku::InitStructHelper();
raytracing_pipeline_ci.flags = 0;
raytracing_pipeline_ci.stageCount = static_cast<uint32_t>(shader_stages.size());
raytracing_pipeline_ci.pStages = shader_stages.data();
raytracing_pipeline_ci.pGroups = shader_groups.data();
raytracing_pipeline_ci.groupCount = shader_groups.size();
raytracing_pipeline_ci.layout = pipeline_layout.handle();
const VkResult result = vk::CreateRayTracingPipelinesKHR(m_device->handle(), VK_NULL_HANDLE, VK_NULL_HANDLE, 1,
&raytracing_pipeline_ci, nullptr, &raytracing_pipeline);
ASSERT_EQ(VK_SUCCESS, result);
}
VkBufferCreateInfo buffer_ci = vku::InitStructHelper();
buffer_ci.usage =
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR;
buffer_ci.size = 4096;
vkt::Buffer buffer(*m_device, buffer_ci, vkt::no_mem);
VkMemoryRequirements mem_reqs;
vk::GetBufferMemoryRequirements(device(), buffer.handle(), &mem_reqs);
VkMemoryAllocateFlagsInfo alloc_flags = vku::InitStructHelper();
alloc_flags.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT;
VkMemoryAllocateInfo alloc_info = vku::InitStructHelper(&alloc_flags);
alloc_info.allocationSize = 4096;
vkt::DeviceMemory mem(*m_device, alloc_info);
vk::BindBufferMemory(device(), buffer.handle(), mem.handle(), 0);
VkPhysicalDeviceRayTracingPipelinePropertiesKHR ray_tracing_properties = vku::InitStructHelper();
GetPhysicalDeviceProperties2(ray_tracing_properties);
const VkDeviceAddress device_address = buffer.Address();
VkStridedDeviceAddressRegionKHR stridebufregion = {};
stridebufregion.deviceAddress = device_address;
stridebufregion.stride = ray_tracing_properties.shaderGroupHandleAlignment;
stridebufregion.size = stridebufregion.stride;
m_command_buffer.Begin();
vk::CmdBindPipeline(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, raytracing_pipeline);
vk::CmdTraceRaysKHR(m_command_buffer.handle(), &stridebufregion, &stridebufregion, &stridebufregion, &stridebufregion, 100, 100,
1);
// pRayGenShaderBindingTable->deviceAddress == 0
{
VkStridedDeviceAddressRegionKHR valid_region = stridebufregion;
valid_region.deviceAddress = 0;
vk::CmdTraceRaysKHR(m_command_buffer.handle(), &stridebufregion, &valid_region, &stridebufregion, &stridebufregion, 100,
100, 1);
}
// pRayGenShaderBindingTable->size == 0, deviceAddress is invalid => region is considered unused so no error
{
VkStridedDeviceAddressRegionKHR empty_region = stridebufregion;
empty_region.deviceAddress += buffer.CreateInfo().size + 128;
empty_region.size = 0;
empty_region.stride = 0;
vk::CmdTraceRaysKHR(m_command_buffer.handle(), &stridebufregion, &empty_region, &stridebufregion, &stridebufregion, 100,
100, 1);
}
m_command_buffer.End();
m_default_queue->Submit(m_command_buffer);
m_device->Wait();
vk::DestroyPipeline(device(), raytracing_pipeline, nullptr);
}
TEST_F(PositiveRayTracing, BarrierAccessMaskAccelerationStructureRayQueryEnabledRTXDisabled) {
TEST_DESCRIPTION(
"Test barrier with access ACCELERATION_STRUCTURE bit."
"Ray query extension is enabled, as well as feature."
"RTX extensions are disabled.");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredExtensions(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_RAY_QUERY_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::synchronization2);
AddRequiredFeature(vkt::Feature::rayQuery);
RETURN_IF_SKIP(Init());
VkMemoryBarrier2 mem_barrier = vku::InitStructHelper();
mem_barrier.dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT;
mem_barrier.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT;
vkt::Buffer buffer(*m_device, 32, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
VkBufferMemoryBarrier2 buffer_barrier = vku::InitStructHelper();
buffer_barrier.dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT;
buffer_barrier.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT;
buffer_barrier.buffer = buffer.handle();
buffer_barrier.size = 32;
vkt::Image image(*m_device, 128, 128, 1, VK_FORMAT_B8G8R8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
image.SetLayout(VK_IMAGE_LAYOUT_GENERAL);
VkImageMemoryBarrier2 image_barrier = vku::InitStructHelper();
image_barrier.dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT;
image_barrier.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT;
image_barrier.image = image.handle();
image_barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
VkDependencyInfo dependency_info = vku::InitStructHelper();
dependency_info.memoryBarrierCount = 1;
dependency_info.pMemoryBarriers = &mem_barrier;
dependency_info.bufferMemoryBarrierCount = 1;
dependency_info.pBufferMemoryBarriers = &buffer_barrier;
dependency_info.imageMemoryBarrierCount = 1;
dependency_info.pImageMemoryBarriers = &image_barrier;
m_command_buffer.Begin();
mem_barrier.srcAccessMask = VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR;
mem_barrier.srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT;
buffer_barrier.srcAccessMask = VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR;
buffer_barrier.srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT;
image_barrier.srcAccessMask = VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR;
image_barrier.srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT;
mem_barrier.dstAccessMask = VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR;
mem_barrier.dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT;
buffer_barrier.dstAccessMask = VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR;
buffer_barrier.dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT;
image_barrier.dstAccessMask = VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR;
image_barrier.dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT;
vk::CmdPipelineBarrier2KHR(m_command_buffer.handle(), &dependency_info);
m_command_buffer.End();
}
TEST_F(PositiveRayTracing, BarrierAccessMaskAccelerationStructureRayQueryEnabledRTXEnabled) {
TEST_DESCRIPTION(
"Test barrier with access ACCELERATION_STRUCTURE bit."
"Ray query extension is enabled, as well as feature."
"RTX extensions are enabled.");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredExtensions(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_RAY_QUERY_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::rayTracingPipeline);
AddRequiredFeature(vkt::Feature::synchronization2);
AddRequiredFeature(vkt::Feature::rayQuery);
RETURN_IF_SKIP(Init());
VkMemoryBarrier2 mem_barrier = vku::InitStructHelper();
mem_barrier.dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT;
mem_barrier.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT;
vkt::Buffer buffer(*m_device, 32, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
VkBufferMemoryBarrier2 buffer_barrier = vku::InitStructHelper();
buffer_barrier.dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT;
buffer_barrier.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT;
buffer_barrier.buffer = buffer.handle();
buffer_barrier.size = 32;
vkt::Image image(*m_device, 128, 128, 1, VK_FORMAT_B8G8R8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
image.SetLayout(VK_IMAGE_LAYOUT_GENERAL);
VkImageMemoryBarrier2 image_barrier = vku::InitStructHelper();
image_barrier.dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT;
image_barrier.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT;
image_barrier.image = image.handle();
image_barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
VkDependencyInfo dependency_info = vku::InitStructHelper();
dependency_info.memoryBarrierCount = 1;
dependency_info.pMemoryBarriers = &mem_barrier;
dependency_info.bufferMemoryBarrierCount = 1;
dependency_info.pBufferMemoryBarriers = &buffer_barrier;
dependency_info.imageMemoryBarrierCount = 1;
dependency_info.pImageMemoryBarriers = &image_barrier;
m_command_buffer.Begin();
// specify VK_PIPELINE_STAGE_2_RAY_TRACING_SHADER_BIT_KHR as srcStageMask and dstStageMask
mem_barrier.srcAccessMask = VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR;
mem_barrier.srcStageMask = VK_PIPELINE_STAGE_2_RAY_TRACING_SHADER_BIT_KHR;
buffer_barrier.srcAccessMask = VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR;
buffer_barrier.srcStageMask = VK_PIPELINE_STAGE_2_RAY_TRACING_SHADER_BIT_KHR;
image_barrier.srcAccessMask = VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR;
image_barrier.srcStageMask = VK_PIPELINE_STAGE_2_RAY_TRACING_SHADER_BIT_KHR;
mem_barrier.dstAccessMask = VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR;
mem_barrier.dstStageMask = VK_PIPELINE_STAGE_2_RAY_TRACING_SHADER_BIT_KHR;
buffer_barrier.dstAccessMask = VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR;
buffer_barrier.dstStageMask = VK_PIPELINE_STAGE_2_RAY_TRACING_SHADER_BIT_KHR;
image_barrier.dstAccessMask = VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR;
image_barrier.dstStageMask = VK_PIPELINE_STAGE_2_RAY_TRACING_SHADER_BIT_KHR;
vk::CmdPipelineBarrier2KHR(m_command_buffer.handle(), &dependency_info);
m_command_buffer.End();
}
TEST_F(PositiveRayTracing, BarrierSync1NoCrash) {
TEST_DESCRIPTION("Regression test for nullptr crash when Sync1 barrier API is used for acceleration structure accesses");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredExtensions(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME);
RETURN_IF_SKIP(Init());
// This stage can not be used with ACCELERATION_STRUCTURE_READ access when ray query is disabled, but VVL also should not crash.
constexpr VkPipelineStageFlags invalid_src_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
VkMemoryBarrier barrier = vku::InitStructHelper();
barrier.srcAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR;
m_errorMonitor->SetUnexpectedError("VUID-vkCmdPipelineBarrier-srcAccessMask-06257");
m_command_buffer.Begin();
vk::CmdPipelineBarrier(m_command_buffer.handle(), invalid_src_stage, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 1, &barrier, 0,
nullptr, 0, nullptr);
m_command_buffer.End();
}
TEST_F(PositiveRayTracing, BuildAccelerationStructuresList) {
TEST_DESCRIPTION("Build a list of destination acceleration structures, then do an update build on that same list");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredFeature(vkt::Feature::accelerationStructure);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
AddRequiredFeature(vkt::Feature::rayQuery);
RETURN_IF_SKIP(InitFrameworkForRayTracingTest());
RETURN_IF_SKIP(InitState());
constexpr size_t blas_count = 10;
std::vector<vkt::as::BuildGeometryInfoKHR> blas_vec;
for (size_t i = 0; i < blas_count; ++i) {
auto blas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device);
blas.AddFlags(VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR);
blas_vec.emplace_back(std::move(blas));
}
m_command_buffer.Begin();
vkt::as::BuildAccelerationStructuresKHR(m_command_buffer.handle(), blas_vec);
m_command_buffer.End();
m_default_queue->Submit(m_command_buffer);
m_device->Wait();
for (auto& blas : blas_vec) {
blas.SetSrcAS(blas.GetDstAS());
blas.SetMode(VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR);
blas.SetDstAS(vkt::as::blueprint::AccelStructSimpleOnDeviceBottomLevel(*m_device, 4096));
}
m_command_buffer.Begin();
vkt::as::BuildAccelerationStructuresKHR(m_command_buffer.handle(), blas_vec);
m_command_buffer.End();
m_default_queue->Submit(m_command_buffer);
m_device->Wait();
}
TEST_F(PositiveRayTracing, BuildAccelerationStructuresList2) {
TEST_DESCRIPTION(
"Build a list of destination acceleration structures, with first build having a bigger build range than second.");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredFeature(vkt::Feature::accelerationStructure);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
AddRequiredFeature(vkt::Feature::rayQuery);
RETURN_IF_SKIP(InitFrameworkForRayTracingTest());
RETURN_IF_SKIP(InitState());
if (IsPlatformMockICD()) {
GTEST_SKIP() << "Test not supported by MockICD";
}
VkPhysicalDeviceAccelerationStructurePropertiesKHR as_props = vku::InitStructHelper();
VkPhysicalDeviceProperties2 phys_dev_props = vku::InitStructHelper(&as_props);
vk::GetPhysicalDeviceProperties2(m_device->Physical(), &phys_dev_props);
VkMemoryAllocateFlagsInfo alloc_flags = vku::InitStructHelper();
alloc_flags.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT;
auto scratch_buffer = std::make_shared<vkt::Buffer>(
*m_device, 4 * 1024 * 1024, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &alloc_flags);
std::vector<vkt::as::BuildGeometryInfoKHR> blas_vec;
auto blas_0 = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device);
std::vector<vkt::as::GeometryKHR> geometries;
geometries.emplace_back(vkt::as::blueprint::GeometrySimpleOnDeviceTriangleInfo(*m_device, 1000));
blas_0.SetGeometries(std::move(geometries));
blas_0.SetScratchBuffer(scratch_buffer);
auto blas_1 = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device);
blas_1.SetScratchBuffer(scratch_buffer);
auto size_info_1 = blas_1.GetSizeInfo();
// Scratch buffer used ranges:
// buffer start --> | blas_1 | <pad for alignment> | blas_0 |
// If scratch size if computed incorrectly, an overlap with scratch memory for blas_0 will be detected for blas_1
blas_0.SetDeviceScratchOffset(
Align<VkDeviceAddress>(size_info_1.buildScratchSize, as_props.minAccelerationStructureScratchOffsetAlignment));
blas_vec.emplace_back(std::move(blas_0));
blas_vec.emplace_back(std::move(blas_1));
m_command_buffer.Begin();
vkt::as::BuildAccelerationStructuresKHR(m_command_buffer.handle(), blas_vec);
m_command_buffer.End();
m_default_queue->Submit(m_command_buffer);
m_device->Wait();
}
TEST_F(PositiveRayTracing, AccelerationStructuresOverlappingMemory) {
TEST_DESCRIPTION(
"Validate acceleration structure building when source/destination acceleration structures and scratch buffers may "
"overlap.");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredFeature(vkt::Feature::accelerationStructure);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
AddRequiredFeature(vkt::Feature::rayQuery);
RETURN_IF_SKIP(InitFrameworkForRayTracingTest());
RETURN_IF_SKIP(InitState());
constexpr size_t blas_count = 3;
VkMemoryAllocateFlagsInfo alloc_flags = vku::InitStructHelper();
alloc_flags.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT;
VkMemoryAllocateInfo alloc_info = vku::InitStructHelper(&alloc_flags);
alloc_info.allocationSize = (1u << 18) * blas_count;
vkt::DeviceMemory buffer_memory(*m_device, alloc_info);
// Test using non overlapping memory chunks from the same buffer in multiple builds
// The scratch buffer is used in multiple builds but bound at different offsets, so no validation error should be issued
{
VkBufferCreateInfo scratch_buffer_ci = vku::InitStructHelper();
scratch_buffer_ci.size = alloc_info.allocationSize;
scratch_buffer_ci.usage = VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR |
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
auto scratch_buffer = std::make_shared<vkt::Buffer>(*m_device, scratch_buffer_ci, vkt::no_mem);
vk::BindBufferMemory(device(), scratch_buffer->handle(), buffer_memory.handle(), 0);
std::vector<vkt::as::BuildGeometryInfoKHR> blas_vec;
VkDeviceSize consumed_buffer_size = 0;
for (size_t i = 0; i < blas_count; ++i) {
auto blas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device);
blas.SetScratchBuffer(scratch_buffer);
blas.SetDeviceScratchOffset(consumed_buffer_size);
consumed_buffer_size += blas.GetSizeInfo().buildScratchSize;
consumed_buffer_size = Align<VkDeviceSize>(consumed_buffer_size, 4096);
blas_vec.emplace_back(std::move(blas));
}
m_command_buffer.Begin();
vkt::as::BuildAccelerationStructuresKHR(m_command_buffer.handle(), blas_vec);
m_command_buffer.End();
m_default_queue->Submit(m_command_buffer);
m_device->Wait();
}
}
TEST_F(PositiveRayTracing, AccelerationStructuresReuseScratchMemory) {
TEST_DESCRIPTION("Repro https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/6461");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredFeature(vkt::Feature::accelerationStructure);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
AddRequiredFeature(vkt::Feature::rayQuery);
RETURN_IF_SKIP(InitFrameworkForRayTracingTest());
RETURN_IF_SKIP(InitState());
// Allocate a memory chunk that will be used as backing memory for scratch buffer
VkMemoryAllocateFlagsInfo alloc_flags = vku::InitStructHelper();
alloc_flags.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT;
VkMemoryAllocateInfo alloc_info = vku::InitStructHelper(&alloc_flags);
alloc_info.allocationSize = 1u << 18;
vkt::DeviceMemory common_scratch_memory(*m_device, alloc_info);
vkt::CommandBuffer cmd_buffer_frame_0(*m_device, m_command_pool);
vkt::CommandBuffer cmd_buffer_frame_1(*m_device, m_command_pool);
vkt::CommandBuffer cmd_buffer_frame_2(*m_device, m_command_pool);
std::vector<vkt::as::BuildGeometryInfoKHR> blas_vec_frame_0;
std::vector<vkt::as::BuildGeometryInfoKHR> blas_vec_frame_1;
std::vector<vkt::as::BuildGeometryInfoKHR> blas_vec_frame_2;
auto scratch_buffer_frame_0 = std::make_shared<vkt::Buffer>();
auto scratch_buffer_frame_1 = std::make_shared<vkt::Buffer>();
auto scratch_buffer_frame_2 = std::make_shared<vkt::Buffer>();
vkt::Fence fence_frame_0(*m_device);
vkt::Fence fence_frame_1(*m_device);
vkt::Fence fence_frame_2(*m_device);
// Frame 0
{
// Nothing to wait for, resources used in frame 0 will be released in frame 2
// Create scratch buffer
VkBufferCreateInfo scratch_buffer_ci = vku::InitStructHelper();
scratch_buffer_ci.size = alloc_info.allocationSize;
scratch_buffer_ci.usage = VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR |
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
scratch_buffer_frame_0->InitNoMemory(*m_device, scratch_buffer_ci);
// Bind memory to scratch buffer
vk::BindBufferMemory(device(), scratch_buffer_frame_0->handle(), common_scratch_memory.handle(), 0);
// Build a dummy acceleration structure
auto blas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device);
blas.SetScratchBuffer(scratch_buffer_frame_0);
blas_vec_frame_0.emplace_back(std::move(blas));
cmd_buffer_frame_0.Begin();
vkt::as::BuildAccelerationStructuresKHR(cmd_buffer_frame_0.handle(), blas_vec_frame_0);
// Synchronize accesses to scratch buffer memory: next op will be a new acceleration structure build
VkBufferMemoryBarrier barrier = vku::InitStructHelper();
barrier.buffer = scratch_buffer_frame_0->handle();
barrier.size = scratch_buffer_ci.size;
barrier.srcAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR | VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR;
barrier.dstAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR | VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR;
vk::CmdPipelineBarrier(cmd_buffer_frame_0.handle(), VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR,
VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, 0, 0, nullptr, 1, &barrier, 0, nullptr);
cmd_buffer_frame_0.End();
m_default_queue->Submit(cmd_buffer_frame_0, fence_frame_0);
}
// Frame 1
{
// Still nothing to wait for
// Create scratch buffer
VkBufferCreateInfo scratch_buffer_ci = vku::InitStructHelper();
scratch_buffer_ci.size = alloc_info.allocationSize;
scratch_buffer_ci.usage = VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR |
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
scratch_buffer_frame_1->InitNoMemory(*m_device, scratch_buffer_ci);
// Bind memory to scratch buffer
vk::BindBufferMemory(device(), scratch_buffer_frame_1->handle(), common_scratch_memory.handle(), 0);
// Build a dummy acceleration structure
auto blas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device);
blas.SetScratchBuffer(scratch_buffer_frame_1);
blas_vec_frame_1.emplace_back(std::move(blas));
cmd_buffer_frame_1.Begin();
vkt::as::BuildAccelerationStructuresKHR(cmd_buffer_frame_1.handle(), blas_vec_frame_1);
// Synchronize accesses to scratch buffer memory: next op will be a new acceleration structure build
VkBufferMemoryBarrier barrier = vku::InitStructHelper();
barrier.buffer = scratch_buffer_frame_1->handle();
barrier.size = scratch_buffer_ci.size;
barrier.srcAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR | VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR;
barrier.dstAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR | VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR;
vk::CmdPipelineBarrier(cmd_buffer_frame_1.handle(), VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR,
VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, 0, 0, nullptr, 1, &barrier, 0, nullptr);
cmd_buffer_frame_1.End();
m_default_queue->Submit(cmd_buffer_frame_1, fence_frame_1);
}
// Frame 2
{
// Free resources from frame 0
fence_frame_0.Wait(kWaitTimeout);
// Destroying buffer triggers VUID-vkDestroyBuffer-buffer-00922, it is still considered in use by cmd_buffer_frame_0 this
// should not happen assuming synchronization is correct
// Adding "fence_frame_1.Wait(kWaitTimeout);" used to solve this issue.
// Using a dedicated memory chunk for each scratch buffer also used to solve it.
// The issue was that when recording a acceleration structure build command,
// any buffer indirectly mentioned through a device address used to be added using a call to GetBuffersByAddress.
// So when recording the build happening on frame 1, given that all scratch buffers have the same base device address,
// scratch_buffer_frame_0 was *also* be added as a child to cmd_buffer_frame_1.
// So when destroying it hereinafter, since frame 1 is still in flight, scratch_buffer_frame_0 is still
// considered in use, so 00922 is triggered.
// => Solution: buffers obtained through a call to GetBuffersByAddress should not get added as children,
// since there is no 1 to 1 mapping between a device address and a buffer.
scratch_buffer_frame_0 = nullptr; // Remove reference
blas_vec_frame_0.clear(); // scratch_buffer_frame_0 will be destroyed in this call
// Create scratch buffer
VkBufferCreateInfo scratch_buffer_ci = vku::InitStructHelper();
scratch_buffer_ci.size = alloc_info.allocationSize;
scratch_buffer_ci.usage = VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR |
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
scratch_buffer_frame_2->InitNoMemory(*m_device, scratch_buffer_ci);
// Bind memory to scratch buffer
vk::BindBufferMemory(device(), scratch_buffer_frame_2->handle(), common_scratch_memory.handle(), 0);
// Build a dummy acceleration structure
auto blas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device);
blas.SetScratchBuffer(scratch_buffer_frame_2);
blas_vec_frame_2.emplace_back(std::move(blas));
cmd_buffer_frame_2.Begin();
vkt::as::BuildAccelerationStructuresKHR(cmd_buffer_frame_2.handle(), blas_vec_frame_2);
// Synchronize accesses to scratch buffer memory: next op will be a new acceleration structure build
VkBufferMemoryBarrier barrier = vku::InitStructHelper();
barrier.buffer = scratch_buffer_frame_2->handle();
barrier.size = scratch_buffer_ci.size;
barrier.srcAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR | VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR;
barrier.dstAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR | VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR;
vk::CmdPipelineBarrier(cmd_buffer_frame_2.handle(), VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR,
VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, 0, 0, nullptr, 1, &barrier, 0, nullptr);
cmd_buffer_frame_2.End();
m_default_queue->Submit(cmd_buffer_frame_2, fence_frame_2);
}
fence_frame_1.Wait(kWaitTimeout);
fence_frame_2.Wait(kWaitTimeout);
}
TEST_F(PositiveRayTracing, AccelerationStructuresDedicatedScratchMemory) {
TEST_DESCRIPTION(
"Repro https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/6461"
"This time, each scratch buffer has its own memory");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredFeature(vkt::Feature::accelerationStructure);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
AddRequiredFeature(vkt::Feature::rayQuery);
RETURN_IF_SKIP(InitFrameworkForRayTracingTest());
RETURN_IF_SKIP(InitState());
vkt::CommandBuffer cmd_buffer_frame_0(*m_device, m_command_pool);
vkt::CommandBuffer cmd_buffer_frame_1(*m_device, m_command_pool);
vkt::CommandBuffer cmd_buffer_frame_2(*m_device, m_command_pool);
std::vector<vkt::as::BuildGeometryInfoKHR> blas_vec_frame_0;
std::vector<vkt::as::BuildGeometryInfoKHR> blas_vec_frame_1;
std::vector<vkt::as::BuildGeometryInfoKHR> blas_vec_frame_2;
vkt::Fence fence_frame_0(*m_device);
vkt::Fence fence_frame_1(*m_device);
vkt::Fence fence_frame_2(*m_device);
// Frame 0
{
// Nothing to wait for, resources used in frame 0 will be released in frame 2
// Build a dummy acceleration structure
auto blas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device);
blas_vec_frame_0.emplace_back(std::move(blas));
cmd_buffer_frame_0.Begin();
vkt::as::BuildAccelerationStructuresKHR(cmd_buffer_frame_0.handle(), blas_vec_frame_0);
// Synchronize accesses to scratch buffer memory: next op will be a new acceleration structure build
VkBufferMemoryBarrier barrier = vku::InitStructHelper();
barrier.buffer = blas_vec_frame_0[0].GetScratchBuffer()->handle();
barrier.size = blas_vec_frame_0[0].GetScratchBuffer()->CreateInfo().size;
barrier.srcAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR | VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR;
barrier.dstAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR | VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR;
vk::CmdPipelineBarrier(cmd_buffer_frame_0.handle(), VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR,
VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, 0, 0, nullptr, 1, &barrier, 0, nullptr);
cmd_buffer_frame_0.End();
m_default_queue->Submit(cmd_buffer_frame_0, fence_frame_0);
}
// Frame 1
{
// Still nothing to wait for
// Build a dummy acceleration structure
auto blas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device);
blas_vec_frame_1.emplace_back(std::move(blas));
cmd_buffer_frame_1.Begin();
vkt::as::BuildAccelerationStructuresKHR(cmd_buffer_frame_1.handle(), blas_vec_frame_1);
// Synchronize accesses to scratch buffer memory: next op will be a new acceleration structure build
VkBufferMemoryBarrier barrier = vku::InitStructHelper();
barrier.buffer = blas_vec_frame_1[0].GetScratchBuffer()->handle();
barrier.size = blas_vec_frame_1[0].GetScratchBuffer()->CreateInfo().size;
barrier.srcAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR | VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR;
barrier.dstAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR | VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR;
vk::CmdPipelineBarrier(cmd_buffer_frame_1.handle(), VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR,
VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, 0, 0, nullptr, 1, &barrier, 0, nullptr);
cmd_buffer_frame_1.End();
m_default_queue->Submit(cmd_buffer_frame_1, fence_frame_1);
}
// Frame 2
{
// Free resources from frame 0
fence_frame_0.Wait(kWaitTimeout);
blas_vec_frame_0.clear(); // No validation error
// Build a dummy acceleration structure
auto blas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device);
blas_vec_frame_2.emplace_back(std::move(blas));
cmd_buffer_frame_2.Begin();
vkt::as::BuildAccelerationStructuresKHR(cmd_buffer_frame_2.handle(), blas_vec_frame_2);
// Synchronize accesses to scratch buffer memory: next op will be a new acceleration structure build
VkBufferMemoryBarrier barrier = vku::InitStructHelper();
barrier.buffer = blas_vec_frame_2[0].GetScratchBuffer()->handle();
barrier.size = blas_vec_frame_2[0].GetScratchBuffer()->CreateInfo().size;
barrier.srcAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR | VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR;
barrier.dstAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR | VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR;
vk::CmdPipelineBarrier(cmd_buffer_frame_2.handle(), VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR,
VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, 0, 0, nullptr, 1, &barrier, 0, nullptr);
cmd_buffer_frame_2.End();
m_default_queue->Submit(cmd_buffer_frame_2, fence_frame_2);
}
fence_frame_1.Wait(kWaitTimeout);
fence_frame_2.Wait(kWaitTimeout);
}
TEST_F(PositiveRayTracing, CmdBuildAccelerationStructuresIndirect) {
TEST_DESCRIPTION("basic usage of vkCmdBuildAccelerationStructuresIndirectKHR.");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
AddRequiredFeature(vkt::Feature::accelerationStructure);
AddRequiredFeature(vkt::Feature::accelerationStructureIndirectBuild);
AddRequiredFeature(vkt::Feature::rayQuery);
RETURN_IF_SKIP(InitFrameworkForRayTracingTest());
RETURN_IF_SKIP(InitState());
auto blas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device);
m_command_buffer.Begin();
blas.BuildCmdBufferIndirect(m_command_buffer.handle());
m_command_buffer.End();
}
TEST_F(PositiveRayTracing, ScratchBufferCorrectAddressSpaceOpBuild) {
TEST_DESCRIPTION(
"Have two scratch buffers bound to the same memory, with one of them being not big enough for an acceleration structure "
"build, but the other one is. If the buffer addresses of those buffers are the same, 03671 should not fire");
SetTargetApiVersion(VK_API_VERSION_1_2);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
AddRequiredFeature(vkt::Feature::accelerationStructure);
AddRequiredFeature(vkt::Feature::rayTracingPipeline);
RETURN_IF_SKIP(InitFrameworkForRayTracingTest());
RETURN_IF_SKIP(InitState());
auto blas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device);
auto size_info = blas.GetSizeInfo();
if (size_info.buildScratchSize <= 64) {
GTEST_SKIP() << "Need a big scratch size, skipping test.";
}
VkPhysicalDeviceAccelerationStructurePropertiesKHR acc_struct_properties = vku::InitStructHelper();
GetPhysicalDeviceProperties2(acc_struct_properties);
VkDeviceSize scratch_size = size_info.buildScratchSize + acc_struct_properties.minAccelerationStructureScratchOffsetAlignment;
scratch_size = Align<VkDeviceSize>(scratch_size, acc_struct_properties.minAccelerationStructureScratchOffsetAlignment);
// Allocate buffer memory separately so that it can be large enough. Scratch buffer size will be smaller.
VkMemoryAllocateFlagsInfo alloc_flags = vku::InitStructHelper();
alloc_flags.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT;
VkMemoryAllocateInfo alloc_info = vku::InitStructHelper(&alloc_flags);
alloc_info.allocationSize = scratch_size;
vkt::DeviceMemory buffer_memory(*m_device, alloc_info);
VkBufferCreateInfo small_buffer_ci = vku::InitStructHelper();
small_buffer_ci.size = 64;
small_buffer_ci.usage = VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
auto small_scratch_buffer = std::make_shared<vkt::Buffer>(*m_device, small_buffer_ci, vkt::no_mem);
small_scratch_buffer->BindMemory(buffer_memory, 0);
small_buffer_ci.size = alloc_info.allocationSize;
auto big_scratch_buffer = std::make_shared<vkt::Buffer>(*m_device, small_buffer_ci, vkt::no_mem);
big_scratch_buffer->BindMemory(buffer_memory, 0);
const VkDeviceAddress big_scratch_address = big_scratch_buffer->Address();
if (big_scratch_address != small_scratch_buffer->Address()) {
GTEST_SKIP() << "Binding two buffers to the same memory does not yield identical buffer addresses, skipping test.";
}
m_command_buffer.Begin();
blas.SetScratchBuffer(small_scratch_buffer);
blas.BuildCmdBuffer(m_command_buffer);
m_command_buffer.End();
}
TEST_F(PositiveRayTracing, BasicTraceRays) {
TEST_DESCRIPTION(
"Setup a ray tracing pipeline (ray generation, miss and closest hit shaders) and acceleration structure, and trace one "
"ray. Only call traceRay in the ray generation shader");
SetTargetApiVersion(VK_API_VERSION_1_2);
AddRequiredFeature(vkt::Feature::rayTracingPipeline);
AddRequiredFeature(vkt::Feature::accelerationStructure);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
RETURN_IF_SKIP(InitFrameworkForRayTracingTest());
RETURN_IF_SKIP(InitState());
vkt::rt::Pipeline pipeline(*this, m_device);
// Set shaders
const char* ray_gen = R"glsl(
#version 460
#extension GL_EXT_ray_tracing : require // Requires SPIR-V 1.5 (Vulkan 1.2)
layout(binding = 0, set = 0) uniform accelerationStructureEXT tlas;
layout(location = 0) rayPayloadEXT vec3 hit;
void main() {
traceRayEXT(tlas, gl_RayFlagsOpaqueEXT, 0xff, 0, 0, 0, vec3(0,0,1), 0.1, vec3(0,0,1), 1000.0, 0);
}
)glsl";
pipeline.SetGlslRayGenShader(ray_gen);
const char* miss = R"glsl(
#version 460
#extension GL_EXT_ray_tracing : require
layout(location = 0) rayPayloadInEXT vec3 hit;
void main() {
hit = vec3(0.1, 0.2, 0.3);
}
)glsl";
pipeline.AddGlslMissShader(miss);
const char* closest_hit = R"glsl(
#version 460
#extension GL_EXT_ray_tracing : require
layout(location = 0) rayPayloadInEXT vec3 hit;
hitAttributeEXT vec2 baryCoord;
void main() {
const vec3 barycentricCoords = vec3(1.0f - baryCoord.x - baryCoord.y, baryCoord.x, baryCoord.y);
hit = barycentricCoords;
}
)glsl";
pipeline.AddGlslClosestHitShader(closest_hit);
pipeline.AddBinding(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 0);
pipeline.CreateDescriptorSet();
vkt::as::BuildGeometryInfoKHR tlas(vkt::as::blueprint::BuildOnDeviceTopLevel(*m_device, *m_default_queue, m_command_buffer));
pipeline.GetDescriptorSet().WriteDescriptorAccelStruct(0, 1, &tlas.GetDstAS()->handle());
pipeline.GetDescriptorSet().UpdateDescriptorSets();
// Build pipeline
pipeline.Build();
// Bind descriptor set, pipeline, and trace rays
m_command_buffer.Begin();
vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipeline.GetPipelineLayout(), 0, 1,
&pipeline.GetDescriptorSet().set_, 0, nullptr);
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipeline.Handle());
vkt::rt::TraceRaysSbt trace_rays_sbt = pipeline.GetTraceRaysSbt();
vk::CmdTraceRaysKHR(m_command_buffer, &trace_rays_sbt.ray_gen_sbt, &trace_rays_sbt.miss_sbt, &trace_rays_sbt.hit_sbt,
&trace_rays_sbt.callable_sbt, 1, 1, 1);
m_command_buffer.End();
m_default_queue->Submit(m_command_buffer);
m_device->Wait();
}
TEST_F(PositiveRayTracing, BasicTraceRaysDeferredBuild) {
TEST_DESCRIPTION(
"Setup a ray tracing pipeline (ray generation, miss and closest hit shaders, and deferred build) and acceleration "
"structure, and trace one "
"ray. Only call traceRay in the ray generation shader");
SetTargetApiVersion(VK_API_VERSION_1_2);
AddRequiredFeature(vkt::Feature::rayTracingPipeline);
AddRequiredFeature(vkt::Feature::accelerationStructure);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
RETURN_IF_SKIP(InitFrameworkForRayTracingTest());
RETURN_IF_SKIP(InitState());
vkt::rt::Pipeline pipeline(*this, m_device);
// Set shaders
const char* ray_gen = R"glsl(
#version 460
#extension GL_EXT_ray_tracing : require // Requires SPIR-V 1.5 (Vulkan 1.2)
layout(binding = 0, set = 0) uniform accelerationStructureEXT tlas;
layout(location = 0) rayPayloadEXT vec3 hit;
void main() {
traceRayEXT(tlas, gl_RayFlagsOpaqueEXT, 0xff, 0, 0, 0, vec3(0,0,1), 0.1, vec3(0,0,1), 1000.0, 0);
}
)glsl";
pipeline.SetGlslRayGenShader(ray_gen);
const char* miss = R"glsl(
#version 460
#extension GL_EXT_ray_tracing : require
layout(location = 0) rayPayloadInEXT vec3 hit;
void main() {
hit = vec3(0.1, 0.2, 0.3);
}
)glsl";
pipeline.AddGlslMissShader(miss);
const char* closest_hit = R"glsl(
#version 460
#extension GL_EXT_ray_tracing : require
layout(location = 0) rayPayloadInEXT vec3 hit;
hitAttributeEXT vec2 baryCoord;
void main() {
const vec3 barycentricCoords = vec3(1.0f - baryCoord.x - baryCoord.y, baryCoord.x, baryCoord.y);
hit = barycentricCoords;
}
)glsl";
pipeline.AddGlslClosestHitShader(closest_hit);
pipeline.AddBinding(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 0);
pipeline.CreateDescriptorSet();
vkt::as::BuildGeometryInfoKHR tlas(vkt::as::blueprint::BuildOnDeviceTopLevel(*m_device, *m_default_queue, m_command_buffer));
pipeline.GetDescriptorSet().WriteDescriptorAccelStruct(0, 1, &tlas.GetDstAS()->handle());
pipeline.GetDescriptorSet().UpdateDescriptorSets();
// Deferred pipeline build
RETURN_IF_SKIP(pipeline.DeferBuild());
RETURN_IF_SKIP(pipeline.Build());
// Bind descriptor set, pipeline, and trace rays
m_command_buffer.Begin();
vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipeline.GetPipelineLayout(), 0, 1,
&pipeline.GetDescriptorSet().set_, 0, nullptr);
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipeline.Handle());
vkt::rt::TraceRaysSbt trace_rays_sbt = pipeline.GetTraceRaysSbt();
vk::CmdTraceRaysKHR(m_command_buffer, &trace_rays_sbt.ray_gen_sbt, &trace_rays_sbt.miss_sbt, &trace_rays_sbt.hit_sbt,
&trace_rays_sbt.callable_sbt, 1, 1, 1);
m_command_buffer.End();
m_default_queue->Submit(m_command_buffer);
m_device->Wait();
}
TEST_F(PositiveRayTracing, GetAccelerationStructureAddressBadBuffer) {
TEST_DESCRIPTION(
"Call vkGetAccelerationStructureDeviceAddressKHR on an acceleration structure whose buffer is missing usage "
"VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, and whose memory has been destroyed");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredExtensions(VK_KHR_MAINTENANCE_5_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::accelerationStructure);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
AddRequiredFeature(vkt::Feature::rayQuery);
AddRequiredFeature(vkt::Feature::maintenance5);
RETURN_IF_SKIP(InitFrameworkForRayTracingTest());
RETURN_IF_SKIP(InitState());
VkBufferUsageFlags2CreateInfo buffer_usage = vku::InitStructHelper();
buffer_usage.usage = VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
VkBufferCreateInfo buffer_ci = vku::InitStructHelper(&buffer_usage);
buffer_ci.size = 4096;
vkt::Buffer buffer(*m_device, buffer_ci, vkt::no_mem);
VkMemoryRequirements mem_reqs;
vk::GetBufferMemoryRequirements(device(), buffer.handle(), &mem_reqs);
VkMemoryAllocateFlagsInfo alloc_flags = vku::InitStructHelper();
alloc_flags.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT;
VkMemoryAllocateInfo alloc_info = vku::InitStructHelper(&alloc_flags);
alloc_info.allocationSize = 4096;
vkt::DeviceMemory mem(*m_device, alloc_info);
vk::BindBufferMemory(device(), buffer.handle(), mem.handle(), 0);
VkAccelerationStructureKHR as;
VkAccelerationStructureCreateInfoKHR as_create_info = vku::InitStructHelper();
as_create_info.buffer = buffer.handle();
as_create_info.size = 4096;
as_create_info.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR;
vk::CreateAccelerationStructureKHR(device(), &as_create_info, nullptr, &as);
VkAccelerationStructureDeviceAddressInfoKHR as_address_info = vku::InitStructHelper();
as_address_info.accelerationStructure = as;
vk::GetAccelerationStructureDeviceAddressKHR(device(), &as_address_info);
vk::DestroyAccelerationStructureKHR(device(), as, nullptr);
}
// Use to be invalid, but VUID-vkCmdBuildAccelerationStructuresKHR-firstVertex-03770 was removed in
// https://gitlab.khronos.org/vulkan/vulkan/-/merge_requests/6733
TEST_F(PositiveRayTracing, UpdatedFirstVertex) {
TEST_DESCRIPTION(
"Build a list of destination acceleration structures, then do an update build on that same list but with a different "
"VkAccelerationStructureBuildRangeInfoKHR::firstVertex");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
AddRequiredFeature(vkt::Feature::accelerationStructure);
AddRequiredFeature(vkt::Feature::rayQuery);
RETURN_IF_SKIP(InitFrameworkForRayTracingTest());
RETURN_IF_SKIP(InitState());
m_command_buffer.Begin();
auto blas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device);
blas.AddFlags(VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR);
blas.BuildCmdBuffer(m_command_buffer.handle());
m_command_buffer.End();
m_default_queue->Submit(m_command_buffer);
m_device->Wait();
m_command_buffer.Begin();
blas.SetMode(VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR);
blas.SetSrcAS(blas.GetDstAS());
// Create custom build ranges, with the default valid as a template, then somehow supply it?
auto build_range_infos = blas.GetBuildRangeInfosFromGeometries();
build_range_infos[0].firstVertex = 666;
blas.SetBuildRanges(build_range_infos);
blas.BuildCmdBuffer(m_command_buffer.handle());
m_command_buffer.End();
}
TEST_F(PositiveRayTracing, BindGraphicsPipelineAfterRayTracingPipeline) {
TEST_DESCRIPTION("Bind a graphics pipeline width dynamic line width state after binding ray tracing pipeline");
SetTargetApiVersion(VK_API_VERSION_1_2);
AddRequiredFeature(vkt::Feature::rayTracingPipeline);
AddRequiredFeature(vkt::Feature::accelerationStructure);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
RETURN_IF_SKIP(InitFrameworkForRayTracingTest());
RETURN_IF_SKIP(InitState());
InitRenderTarget();
vkt::rt::Pipeline pipeline(*this, m_device);
// Set shaders
const char* ray_gen = R"glsl(
#version 460
#extension GL_EXT_ray_tracing : require // Requires SPIR-V 1.5 (Vulkan 1.2)
layout(binding = 0, set = 0) uniform accelerationStructureEXT tlas;
layout(location = 0) rayPayloadEXT vec3 hit;
void main() {
traceRayEXT(tlas, gl_RayFlagsOpaqueEXT, 0xff, 0, 0, 0, vec3(0,0,1), 0.1, vec3(0,0,1), 1000.0, 0);
}
)glsl";
pipeline.SetGlslRayGenShader(ray_gen);
const char* miss = R"glsl(
#version 460
#extension GL_EXT_ray_tracing : require
layout(location = 0) rayPayloadInEXT vec3 hit;
void main() {
hit = vec3(0.1, 0.2, 0.3);
}
)glsl";
pipeline.AddGlslMissShader(miss);
const char* closest_hit = R"glsl(
#version 460
#extension GL_EXT_ray_tracing : require
layout(location = 0) rayPayloadInEXT vec3 hit;
hitAttributeEXT vec2 baryCoord;
void main() {
const vec3 barycentricCoords = vec3(1.0f - baryCoord.x - baryCoord.y, baryCoord.x, baryCoord.y);
hit = barycentricCoords;
}
)glsl";
pipeline.AddGlslClosestHitShader(closest_hit);
pipeline.AddBinding(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 0);
pipeline.CreateDescriptorSet();
vkt::as::BuildGeometryInfoKHR tlas(vkt::as::blueprint::BuildOnDeviceTopLevel(*m_device, *m_default_queue, m_command_buffer));
pipeline.GetDescriptorSet().WriteDescriptorAccelStruct(0, 1, &tlas.GetDstAS()->handle());
pipeline.GetDescriptorSet().UpdateDescriptorSets();
// Build pipeline
pipeline.Build();
CreatePipelineHelper graphics_pipeline(*this);
graphics_pipeline.AddDynamicState(VK_DYNAMIC_STATE_LINE_WIDTH);
graphics_pipeline.ia_ci_.topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
graphics_pipeline.CreateGraphicsPipeline();
// Bind descriptor set, pipeline, and trace rays
m_command_buffer.Begin();
vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipeline.GetPipelineLayout(), 0, 1,
&pipeline.GetDescriptorSet().set_, 0, nullptr);
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipeline.Handle());
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline.Handle());
vk::CmdSetLineWidth(m_command_buffer, 1.0f);
vkt::rt::TraceRaysSbt trace_rays_sbt = pipeline.GetTraceRaysSbt();
vk::CmdTraceRaysKHR(m_command_buffer, &trace_rays_sbt.ray_gen_sbt, &trace_rays_sbt.miss_sbt, &trace_rays_sbt.hit_sbt,
&trace_rays_sbt.callable_sbt, 1, 1, 1);
m_command_buffer.End();
m_default_queue->Submit(m_command_buffer);
m_device->Wait();
}
TEST_F(PositiveRayTracing, InstanceBufferBadAddress) {
TEST_DESCRIPTION("Use an invalid address for an instance buffer, but also specify a primitiveCount of 0 => no errors");
SetTargetApiVersion(VK_API_VERSION_1_2);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
AddRequiredFeature(vkt::Feature::accelerationStructure);
AddRequiredFeature(vkt::Feature::rayTracingPipeline);
RETURN_IF_SKIP(InitFrameworkForRayTracingTest());
RETURN_IF_SKIP(InitState());
auto blas =
std::make_shared<vkt::as::BuildGeometryInfoKHR>(vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device));
m_command_buffer.Begin();
blas->BuildCmdBuffer(m_command_buffer);
m_command_buffer.End();
m_default_queue->Submit(m_command_buffer);
m_device->Wait();
auto tlas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceTopLevel(*m_device, blas);
m_command_buffer.Begin();
tlas.SetupBuild(*m_device, true);
auto build_range_infos = tlas.GetBuildRangeInfosFromGeometries();
build_range_infos[0].primitiveCount = 0;
tlas.SetBuildRanges(build_range_infos);
tlas.GetGeometries()[0].SetInstancesDeviceAddress(0);
tlas.VkCmdBuildAccelerationStructuresKHR(m_command_buffer);
m_command_buffer.End();
}
TEST_F(PositiveRayTracing, WriteAccelerationStructuresPropertiesDevice) {
TEST_DESCRIPTION("Test getting query results from vkCmdWriteAccelerationStructuresPropertiesKHR");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredExtensions(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_RAY_TRACING_MAINTENANCE_1_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::accelerationStructure);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
AddRequiredFeature(vkt::Feature::rayTracingMaintenance1);
RETURN_IF_SKIP(InitFrameworkForRayTracingTest());
RETURN_IF_SKIP(InitState());
vkt::Buffer buffer(*m_device, 4 * sizeof(uint64_t), VK_BUFFER_USAGE_TRANSFER_DST_BIT);
vkt::as::BuildGeometryInfoKHR blas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device);
blas.SetFlags(VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_KHR);
vkt::QueryPool query_pool(*m_device, VK_QUERY_TYPE_ACCELERATION_STRUCTURE_SIZE_KHR, 1);
m_command_buffer.Begin();
blas.BuildCmdBuffer(m_command_buffer.handle());
m_command_buffer.End();
m_default_queue->Submit(m_command_buffer);
m_device->Wait();
m_command_buffer.Begin();
vk::CmdResetQueryPool(m_command_buffer.handle(), query_pool.handle(), 0u, 1u);
vk::CmdWriteAccelerationStructuresPropertiesKHR(m_command_buffer.handle(), 1, &blas.GetDstAS()->handle(),
VK_QUERY_TYPE_ACCELERATION_STRUCTURE_SIZE_KHR, query_pool.handle(), 0);
vk::CmdCopyQueryPoolResults(m_command_buffer.handle(), query_pool.handle(), 0u, 1u, buffer, 0u, sizeof(uint64_t),
VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
m_command_buffer.End();
m_default_queue->Submit(m_command_buffer);
m_device->Wait();
}
TEST_F(PositiveRayTracing, BasicOpacityMicromapBuild) {
TEST_DESCRIPTION("Test building an opacity micromap then building an acceleration structure with that");
// Mask data for 2 levels of subdivision. Middle triangle is index 1, so drop that one out.
// Bit string for middle missing is '1011' (0 on the left). In number form, that's 0xd.
// Extending the Sierpinski-esque pattern out one level is 0xdd0d
uint32_t testMask = 0xdd0d;
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredExtensions(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME);
AddRequiredExtensions(VK_EXT_OPACITY_MICROMAP_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
AddRequiredFeature(vkt::Feature::accelerationStructure);
AddRequiredFeature(vkt::Feature::synchronization2);
AddRequiredFeature(vkt::Feature::rayTracingPipeline);
AddRequiredFeature(vkt::Feature::micromap);
AddRequiredFeature(vkt::Feature::micromapHostCommands);
RETURN_IF_SKIP(InitFrameworkForRayTracingTest());
RETURN_IF_SKIP(InitState());
if (IsPlatformMockICD()) {
GTEST_SKIP() << "Test not supported by MockICD";
}
VkMemoryAllocateFlagsInfo allocate_da_flag_info = vku::InitStructHelper();
allocate_da_flag_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT;
// Create a buffer with the mask and index data
vkt::Buffer micromapDataBuffer(*m_device, 2*1048576 /*XXX*/,
VK_BUFFER_USAGE_MICROMAP_BUILD_INPUT_READ_ONLY_BIT_EXT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
vkt::device_address);
VkDeviceAddress micromapAddress = micromapDataBuffer.Address();
// Fill out VkMicromapUsageEXT with size information
VkMicromapUsageEXT mmUsage = { };
mmUsage.count = 1;
const int TriangleOffset = 0;
const int IndexOffset = 256;
const int DataOffset = 512;
mmUsage.subdivisionLevel = 2;
mmUsage.format = VK_OPACITY_MICROMAP_FORMAT_2_STATE_EXT;
{
uint32_t* data = (uint32_t*)micromapDataBuffer.Memory().Map();
VkMicromapTriangleEXT* tri = (VkMicromapTriangleEXT*)&data[TriangleOffset/4];
tri->dataOffset = 0;
tri->subdivisionLevel = uint16_t(mmUsage.subdivisionLevel);
tri->format = uint16_t(mmUsage.format);
// Micromap data
// Just replicate for testing higher subdivision
{
uint32_t maskWord = testMask | (testMask << 16);
int words = ((1 << (2*mmUsage.subdivisionLevel)) + 31) / 32;
for (int i = 0; i<words; i++) {
data[DataOffset / 4 + i] = maskWord;
}
}
// Index information
data[IndexOffset/4] = 0;
micromapDataBuffer.Memory().Unmap();
}
VkMicromapBuildInfoEXT mmBuildInfo = { VK_STRUCTURE_TYPE_MICROMAP_BUILD_INFO_EXT };
mmBuildInfo.type = VK_MICROMAP_TYPE_OPACITY_MICROMAP_EXT;
mmBuildInfo.flags = 0;
mmBuildInfo.mode = VK_BUILD_MICROMAP_MODE_BUILD_EXT;
mmBuildInfo.dstMicromap = VK_NULL_HANDLE;
mmBuildInfo.usageCountsCount = 1;
mmBuildInfo.pUsageCounts = &mmUsage;
mmBuildInfo.data.deviceAddress = 0ull;
mmBuildInfo.triangleArray.deviceAddress = 0ull;
mmBuildInfo.triangleArrayStride = 0;
VkMicromapBuildSizesInfoEXT sizeInfo = { VK_STRUCTURE_TYPE_MICROMAP_BUILD_SIZES_INFO_EXT };
vk::GetMicromapBuildSizesEXT(device(), VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &mmBuildInfo, &sizeInfo);
// Create a buffer and micromap on top from the size
vkt::Buffer micromapBuffer(*m_device, sizeInfo.micromapSize, VK_BUFFER_USAGE_MICROMAP_STORAGE_BIT_EXT);
// Scratch buffer
vkt::Buffer msBuffer(*m_device, sizeInfo.buildScratchSize > 4 ? sizeInfo.buildScratchSize : 4,
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
&allocate_da_flag_info);
VkDeviceAddress msAddress = msBuffer.Address();
VkMicromapEXT micromap;
VkMicromapCreateInfoEXT maCreateInfo = { VK_STRUCTURE_TYPE_MICROMAP_CREATE_INFO_EXT };
maCreateInfo.createFlags = 0;
maCreateInfo.buffer = micromapBuffer;
maCreateInfo.offset = 0;
maCreateInfo.size = sizeInfo.micromapSize;
maCreateInfo.type = VK_MICROMAP_TYPE_OPACITY_MICROMAP_EXT;
maCreateInfo.deviceAddress = 0ull;
VkResult result = vk::CreateMicromapEXT(device(), &maCreateInfo, nullptr, &micromap);
ASSERT_EQ(VK_SUCCESS, result);
// Build the array with vkBuildmicromapsEXT
{
// Fill in the pointers we didn't have at size query
mmBuildInfo.dstMicromap = micromap;
mmBuildInfo.data.deviceAddress = micromapAddress+DataOffset;
mmBuildInfo.triangleArray.deviceAddress = micromapAddress+TriangleOffset;
mmBuildInfo.scratchData.deviceAddress = msAddress;
m_command_buffer.Begin();
vk::CmdBuildMicromapsEXT(m_command_buffer.handle(), 1, &mmBuildInfo);
{
VkMemoryBarrier2 memoryBarrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, NULL,
VK_PIPELINE_STAGE_2_MICROMAP_BUILD_BIT_EXT, VK_ACCESS_2_MICROMAP_WRITE_BIT_EXT,
VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, VK_ACCESS_2_MICROMAP_READ_BIT_EXT };
VkDependencyInfo dependencyInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };
dependencyInfo.memoryBarrierCount = 1;
dependencyInfo.pMemoryBarriers = &memoryBarrier;
vk::CmdPipelineBarrier2KHR(m_command_buffer.handle(), &dependencyInfo);
}
m_command_buffer.End();
m_default_queue->Submit(m_command_buffer);
m_device->Wait();
}
// Create a buffer with the triangle data in it
static float const vertexData[6*2] = {
0.25, 0.75,
0.5, 0.25,
0.75, 0.75,
};
static uint32_t const indexData[6] = { 0, 1, 2 };
vkt::Buffer vertexBuffer(*m_device, sizeof(vertexData) + sizeof(indexData),
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, vkt::device_address);
VkDeviceAddress vertexAddress = vertexBuffer.Address();
// Upload data to the vertex buffer.
{
char* ptr;
vk::MapMemory(device(), vertexBuffer.Memory(), 0, VK_WHOLE_SIZE, 0, (void**)&ptr);
memcpy(ptr, &vertexData[0], sizeof(vertexData));
memcpy(ptr+sizeof(vertexData), &indexData[0], sizeof(indexData));
vk::UnmapMemory(device(), vertexBuffer.Memory());
}
VkAccelerationStructureBuildSizesInfoKHR bottomASBuildSizesInfo{VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR};
VkAccelerationStructureBuildSizesInfoKHR topASBuildSizesInfo{VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR};
// Create a bottom-level acceleration structure with one triangle
VkAccelerationStructureGeometryKHR bottomASGeometry = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR };
bottomASGeometry.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR;
bottomASGeometry.geometry.triangles = vku::InitStructHelper();
bottomASGeometry.geometry.triangles.vertexFormat = VK_FORMAT_R32G32_SFLOAT;
bottomASGeometry.geometry.triangles.vertexData.deviceAddress = vertexAddress;
bottomASGeometry.geometry.triangles.vertexStride = 8;
bottomASGeometry.geometry.triangles.maxVertex = 3;
bottomASGeometry.geometry.triangles.indexType = VK_INDEX_TYPE_UINT32;
bottomASGeometry.geometry.triangles.indexData.deviceAddress = vertexAddress + sizeof(vertexData);
bottomASGeometry.geometry.triangles.transformData.deviceAddress = 0;
bottomASGeometry.flags = 0;
VkAccelerationStructureTrianglesOpacityMicromapEXT opacityGeometryMicromap = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_TRIANGLES_OPACITY_MICROMAP_EXT };
opacityGeometryMicromap.indexType = VK_INDEX_TYPE_UINT32;
opacityGeometryMicromap.indexBuffer.deviceAddress = micromapAddress + IndexOffset;
opacityGeometryMicromap.indexStride = 0;
opacityGeometryMicromap.baseTriangle = 0;
opacityGeometryMicromap.micromap = micromap;
bottomASGeometry.geometry.triangles.pNext = &opacityGeometryMicromap;
VkAccelerationStructureBuildGeometryInfoKHR bottomASInfo = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR };
bottomASInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR;
bottomASInfo.flags = 0;
bottomASInfo.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR;
bottomASInfo.srcAccelerationStructure = VK_NULL_HANDLE;
bottomASInfo.dstAccelerationStructure = VK_NULL_HANDLE;
bottomASInfo.geometryCount = 1;
bottomASInfo.pGeometries = &bottomASGeometry;
bottomASInfo.ppGeometries = NULL;
bottomASInfo.scratchData.deviceAddress = 0;
uint32_t bottomMaxPrimitiveCounts = 1;
vk::GetAccelerationStructureBuildSizesKHR(*m_device, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &bottomASInfo, &bottomMaxPrimitiveCounts, &bottomASBuildSizesInfo);
vkt::Buffer bottomASBuffer(*m_device, bottomASBuildSizesInfo.accelerationStructureSize,
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
&allocate_da_flag_info);
VkAccelerationStructureCreateInfoKHR asCreateInfo = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR };
asCreateInfo.createFlags = 0;
asCreateInfo.buffer = bottomASBuffer;
asCreateInfo.offset = 0;
asCreateInfo.size = bottomASBuildSizesInfo.accelerationStructureSize;
asCreateInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR;
asCreateInfo.deviceAddress = 0;
VkAccelerationStructureKHR bottomAS, topAS;
result = vk::CreateAccelerationStructureKHR(*m_device, &asCreateInfo, NULL, &bottomAS);
ASSERT_EQ(VK_SUCCESS, result);
vkt::Buffer instanceBuffer(*m_device, 2 * sizeof(VkAccelerationStructureInstanceKHR),
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, vkt::device_address);
VkDeviceAddress instanceAddress = instanceBuffer.Address();
{
VkAccelerationStructureInstanceKHR* instance = (VkAccelerationStructureInstanceKHR*)instanceBuffer.Memory().Map();
memset(instance, 0, 2 * sizeof(VkAccelerationStructureInstanceKHR));
instance[0].transform.matrix[0][0] = 1;
instance[0].transform.matrix[0][1] = 0;
instance[0].transform.matrix[0][2] = 0;
instance[0].transform.matrix[0][3] = 0;
instance[0].transform.matrix[1][0] = 0;
instance[0].transform.matrix[1][1] = 1;
instance[0].transform.matrix[1][2] = 0;
instance[0].transform.matrix[1][3] = 0;
instance[0].transform.matrix[2][0] = 0;
instance[0].transform.matrix[2][1] = 0;
instance[0].transform.matrix[2][2] = 1;
instance[0].transform.matrix[2][3] = 0;
instance[0].instanceCustomIndex = 0xdeadfe;
instance[0].mask = 0xff;
instance[0].instanceShaderBindingTableRecordOffset = 0;
instance[0].flags = 0;
VkAccelerationStructureDeviceAddressInfoKHR asDeviceAddressInfo = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR };
asDeviceAddressInfo.accelerationStructure = bottomAS;
instance[0].accelerationStructureReference = vk::GetAccelerationStructureDeviceAddressKHR(device(), &asDeviceAddressInfo);
instanceBuffer.Memory().Unmap();
}
VkAccelerationStructureGeometryKHR topASGeometry = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR };
topASGeometry.geometryType = VK_GEOMETRY_TYPE_INSTANCES_KHR;
topASGeometry.geometry.instances = vku::InitStructHelper();
topASGeometry.geometry.instances.pNext = NULL;
topASGeometry.geometry.instances.arrayOfPointers = VK_FALSE;
topASGeometry.geometry.instances.data.deviceAddress = instanceAddress;
topASGeometry.flags = 0;
VkAccelerationStructureBuildGeometryInfoKHR topASInfo = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR };
topASInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR;
topASInfo.flags = 0;
topASInfo.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR;
topASInfo.srcAccelerationStructure = VK_NULL_HANDLE;
topASInfo.dstAccelerationStructure = VK_NULL_HANDLE;
topASInfo.geometryCount = 1;
topASInfo.pGeometries = &topASGeometry;
topASInfo.ppGeometries = NULL;
topASInfo.scratchData.deviceAddress = 0;
uint32_t topMaxPrimitiveCounts = 1;
vk::GetAccelerationStructureBuildSizesKHR(device(), VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &topASInfo, &topMaxPrimitiveCounts, &topASBuildSizesInfo);
vkt::Buffer topASBuffer(*m_device, topASBuildSizesInfo.accelerationStructureSize,
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
&allocate_da_flag_info);
asCreateInfo.createFlags = 0;
asCreateInfo.buffer = topASBuffer;
asCreateInfo.offset = 0;
asCreateInfo.size = topASBuildSizesInfo.accelerationStructureSize;
asCreateInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR;
asCreateInfo.deviceAddress = 0;
result = vk::CreateAccelerationStructureKHR(device(), &asCreateInfo, NULL, &topAS);
ASSERT_EQ(VK_SUCCESS, result);
vkt::Buffer scratchBuffer(*m_device, std::max(bottomASBuildSizesInfo.buildScratchSize, topASBuildSizesInfo.buildScratchSize),
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
&allocate_da_flag_info);
VkDeviceAddress scratchAddress = scratchBuffer.Address();
{
bottomASInfo.dstAccelerationStructure = bottomAS;
bottomASInfo.scratchData.deviceAddress = scratchAddress;
VkAccelerationStructureBuildRangeInfoKHR buildRangeInfo = {
1, 0, 0, 0,
};
const VkAccelerationStructureBuildRangeInfoKHR *pBuildRangeInfo = &buildRangeInfo;
// Build the bottom-level acceleration structure
m_command_buffer.Begin();
vk::CmdBuildAccelerationStructuresKHR(m_command_buffer.handle(), 1, &bottomASInfo, &pBuildRangeInfo);
VkMemoryBarrier memoryBarrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER, NULL,
VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR, VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR };
vk::CmdPipelineBarrier(m_command_buffer.handle(), VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, 0, 1, &memoryBarrier, 0, 0, 0, 0);
m_command_buffer.End();
m_default_queue->Submit(m_command_buffer);
m_device->Wait();
}
{
topASInfo.dstAccelerationStructure = topAS;
topASInfo.scratchData.deviceAddress = scratchAddress;
VkAccelerationStructureBuildRangeInfoKHR buildRangeInfo = {
1, 0, 0, 0,
};
const VkAccelerationStructureBuildRangeInfoKHR *pBuildRangeInfo = &buildRangeInfo;
// Build the top-level acceleration structure
m_command_buffer.Begin();
vk::CmdBuildAccelerationStructuresKHR(m_command_buffer.handle(), 1, &topASInfo, &pBuildRangeInfo);
VkMemoryBarrier memoryBarrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER, NULL,
VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR, VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR };
vk::CmdPipelineBarrier(m_command_buffer.handle(), VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, 0, 1, &memoryBarrier, 0, 0, 0, 0);
m_command_buffer.End();
m_default_queue->Submit(m_command_buffer);
m_device->Wait();
}
vk::DestroyAccelerationStructureKHR(*m_device, topAS, NULL);
vk::DestroyAccelerationStructureKHR(*m_device, bottomAS, NULL);
vk::DestroyMicromapEXT(*m_device, micromap, NULL);
}
TEST_F(PositiveRayTracing, SerializeAccelerationStructure) {
TEST_DESCRIPTION("Build a list of destination acceleration structures, then do an update build on that same list");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredFeature(vkt::Feature::accelerationStructure);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
AddRequiredFeature(vkt::Feature::rayQuery);
RETURN_IF_SKIP(InitFrameworkForRayTracingTest());
RETURN_IF_SKIP(InitState());
if (IsPlatformMockICD()) {
GTEST_SKIP() << "Test not supported by MockICD: base buffer device addresses must be aligned";
}
auto blas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device);
blas.AddFlags(VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR);
m_command_buffer.Begin();
blas.BuildCmdBuffer(m_command_buffer.handle());
m_command_buffer.End();
m_default_queue->Submit(m_command_buffer);
m_device->Wait();
vkt::QueryPool serialization_query_pool(*m_device, VK_QUERY_TYPE_ACCELERATION_STRUCTURE_SERIALIZATION_SIZE_KHR, 1);
m_command_buffer.Begin();
vk::CmdResetQueryPool(m_command_buffer.handle(), serialization_query_pool.handle(), 0, 1);
vk::CmdWriteAccelerationStructuresPropertiesKHR(m_command_buffer.handle(), 1, &blas.GetDstAS()->handle(),
VK_QUERY_TYPE_ACCELERATION_STRUCTURE_SERIALIZATION_SIZE_KHR,
serialization_query_pool.handle(), 0);
m_command_buffer.End();
m_default_queue->Submit(m_command_buffer);
m_device->Wait();
uint32_t accel_struct_serialization_size = 0;
serialization_query_pool.Results(0, 1, sizeof(uint32_t), &accel_struct_serialization_size, 0);
// See https://vkdoc.net/man/vkCmdCopyAccelerationStructureToMemoryKHR
const VkDeviceSize serialized_accel_struct_size =
Align<VkDeviceSize>(2 * VK_UUID_SIZE + 3 * sizeof(uint64_t) * accel_struct_serialization_size, 256);
vkt::Buffer serialized_accel_struct_buffer(
*m_device, serialized_accel_struct_size,
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
vkt::device_address);
VkCopyAccelerationStructureToMemoryInfoKHR copy_accel_struct_to_memory_info = vku::InitStructHelper();
copy_accel_struct_to_memory_info.src = blas.GetDstAS()->handle();
copy_accel_struct_to_memory_info.dst.deviceAddress = serialized_accel_struct_buffer.Address();
copy_accel_struct_to_memory_info.mode = VK_COPY_ACCELERATION_STRUCTURE_MODE_SERIALIZE_KHR;
m_command_buffer.Begin();
vk::CmdCopyAccelerationStructureToMemoryKHR(m_command_buffer.handle(), &copy_accel_struct_to_memory_info);
m_command_buffer.End();
m_default_queue->Submit(m_command_buffer);
m_device->Wait();
vkt::Buffer de_serialized_accel_struct_buffer(
*m_device, serialized_accel_struct_size,
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
vkt::device_address);
auto deserialized_blas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device);
deserialized_blas.AddFlags(
VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR /*| VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_KHR*/);
deserialized_blas.GetDstAS()->SetSize(Align<VkDeviceSize>(serialized_accel_struct_size, 256));
deserialized_blas.GetDstAS()->Build();
VkCopyMemoryToAccelerationStructureInfoKHR copy_memory_to_accel_struct_info = vku::InitStructHelper();
copy_memory_to_accel_struct_info.src.deviceAddress = serialized_accel_struct_buffer.Address();
copy_memory_to_accel_struct_info.dst = deserialized_blas.GetDstAS()->handle();
copy_memory_to_accel_struct_info.mode = VK_COPY_ACCELERATION_STRUCTURE_MODE_DESERIALIZE_KHR;
m_command_buffer.Begin();
vk::CmdCopyMemoryToAccelerationStructureKHR(m_command_buffer.handle(), &copy_memory_to_accel_struct_info);
m_command_buffer.End();
m_default_queue->Submit(m_command_buffer);
m_device->Wait();
deserialized_blas.SetSrcAS(deserialized_blas.GetDstAS());
deserialized_blas.SetMode(VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR);
m_command_buffer.Begin();
deserialized_blas.BuildCmdBuffer(m_command_buffer.handle());
m_command_buffer.End();
m_default_queue->Submit(m_command_buffer);
m_device->Wait();
}