blob: 47f6f69ab5dac5acb8e9921be83c7ab0bb63c038 [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/feature_requirements.h"
#include "../framework/descriptor_helper.h"
#include "../framework/pipeline_helper.h"
#include "utils/math_utils.h"
#include <algorithm>
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
vkt::as::BuildGeometryInfoKHR blas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device);
blas.BuildCmdBuffer(m_command_buffer);
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.GetDstAS());
tlas.BuildCmdBuffer(m_command_buffer);
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;
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());
vkt::rt::Pipeline rt_pipeline(*this, m_device);
rt_pipeline.SetGlslRayGenShader(kRayTracingMinimalGlsl);
rt_pipeline.AddGlslMissShader(kRayTracingPayloadMinimalGlsl);
rt_pipeline.AddGlslClosestHitShader(kRayTracingPayloadMinimalGlsl);
rt_pipeline.AddBinding(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 0);
rt_pipeline.CreateDescriptorSet();
vkt::as::BuildGeometryInfoKHR tlas(vkt::as::blueprint::BuildOnDeviceTopLevel(*m_device, *m_default_queue, m_command_buffer));
rt_pipeline.GetDescriptorSet().WriteDescriptorAccelStruct(0, 1, &tlas.GetDstAS()->handle());
rt_pipeline.GetDescriptorSet().UpdateDescriptorSets();
rt_pipeline.Build();
vkt::rt::TraceRaysSbt sbt = rt_pipeline.GetTraceRaysSbt();
m_command_buffer.Begin();
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, rt_pipeline);
vk::CmdTraceRaysKHR(m_command_buffer, &sbt.ray_gen_sbt, &sbt.miss_sbt, &sbt.hit_sbt, &sbt.callable_sbt, 100, 100, 1);
// pMissShaderBindingTable->deviceAddress == 0
{
VkStridedDeviceAddressRegionKHR null_addr_miss_sbt = sbt.miss_sbt;
null_addr_miss_sbt.deviceAddress = 0;
vk::CmdTraceRaysKHR(m_command_buffer, &sbt.ray_gen_sbt, &null_addr_miss_sbt, &sbt.hit_sbt, &sbt.callable_sbt, 100, 100, 1);
}
// pMissShaderBindingTable->size == 0 => region is considered unused so no error
{
VkStridedDeviceAddressRegionKHR null_addr_miss_sbt = sbt.miss_sbt;
null_addr_miss_sbt.size = 0;
null_addr_miss_sbt.stride = 0;
vk::CmdTraceRaysKHR(m_command_buffer, &sbt.ray_gen_sbt, &null_addr_miss_sbt, &sbt.hit_sbt, &sbt.callable_sbt, 100, 100, 1);
}
m_command_buffer.End();
m_default_queue->Submit(m_command_buffer);
m_device->Wait();
}
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;
buffer_barrier.size = 32;
vkt::Image image(*m_device, 128, 128, VK_FORMAT_B8G8R8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
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;
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, &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;
buffer_barrier.size = 32;
vkt::Image image(*m_device, 128, 128, VK_FORMAT_B8G8R8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
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;
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, &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, 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, 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, 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::GeometrySimpleOnDeviceIndexedTriangleInfo(*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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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);
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);
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);
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, &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, mem, 0);
VkAccelerationStructureKHR as;
VkAccelerationStructureCreateInfoKHR as_create_info = vku::InitStructHelper();
as_create_info.buffer = buffer;
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);
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);
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);
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline);
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());
vkt::as::BuildGeometryInfoKHR blas = 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.GetDstAS());
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);
m_command_buffer.End();
m_default_queue->Submit(m_command_buffer);
m_device->Wait();
m_command_buffer.Begin();
vk::CmdResetQueryPool(m_command_buffer, query_pool, 0u, 1u);
vk::CmdWriteAccelerationStructuresPropertiesKHR(m_command_buffer, 1, &blas.GetDstAS()->handle(),
VK_QUERY_TYPE_ACCELERATION_STRUCTURE_SIZE_KHR, query_pool, 0);
vk::CmdCopyQueryPoolResults(m_command_buffer, query_pool, 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;
}
VkMicromapBuildInfoEXT mmBuildInfo = vku::InitStructHelper();
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 = vku::InitStructHelper();
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 = vku::InitStructHelper();
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, 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};
m_command_buffer.BarrierKHR(memoryBarrier);
}
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 = vku::InitStructHelper();
VkAccelerationStructureBuildSizesInfoKHR topASBuildSizesInfo = vku::InitStructHelper();
// Create a bottom-level acceleration structure with one triangle
VkAccelerationStructureGeometryKHR bottomASGeometry = vku::InitStructHelper();
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 = vku::InitStructHelper();
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 = vku::InitStructHelper();
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 = vku::InitStructHelper();
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 = vku::InitStructHelper();
asDeviceAddressInfo.accelerationStructure = bottomAS;
instance[0].accelerationStructureReference = vk::GetAccelerationStructureDeviceAddressKHR(device(), &asDeviceAddressInfo);
}
VkAccelerationStructureGeometryKHR topASGeometry = vku::InitStructHelper();
topASGeometry.geometryType = VK_GEOMETRY_TYPE_INSTANCES_KHR;
topASGeometry.geometry.instances = vku::InitStructHelper();
topASGeometry.geometry.instances.arrayOfPointers = VK_FALSE;
topASGeometry.geometry.instances.data.deviceAddress = instanceAddress;
topASGeometry.flags = 0;
VkAccelerationStructureBuildGeometryInfoKHR topASInfo = vku::InitStructHelper();
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, 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, 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, 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, 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 an acceleration structure, serialize then deserialize it");
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);
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, serialization_query_pool, 0, 1);
vk::CmdWriteAccelerationStructuresPropertiesKHR(m_command_buffer, 1, &blas.GetDstAS()->handle(),
VK_QUERY_TYPE_ACCELERATION_STRUCTURE_SERIALIZATION_SIZE_KHR,
serialization_query_pool, 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);
vkt::Buffer serialized_accel_struct_buffer(
*m_device, accel_struct_serialization_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, &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, accel_struct_serialization_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(accel_struct_serialization_size);
deserialized_blas.GetDstAS()->Create();
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, &copy_memory_to_accel_struct_info);
m_command_buffer.End();
m_default_queue->Submit(m_command_buffer);
m_device->Wait();
}
TEST_F(PositiveRayTracing, AccelerationStructuresAndScratchBuffersAddressSharing) {
TEST_DESCRIPTION(
"Make sure that buffers backing acceleration structures and scratch buffers backed by the same VkDeviceMemory share the "
"same base address");
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());
if (IsPlatformMockICD()) {
GTEST_SKIP() << "Only need to test on a real driver";
}
constexpr size_t build_info_count = 3;
// All buffers used to back destination acceleration struct and scratch will be bound to this memory chunk
VkMemoryAllocateFlagsInfo alloc_flags = vku::InitStructHelper();
alloc_flags.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT;
VkMemoryAllocateInfo alloc_info = vku::InitStructHelper(&alloc_flags);
// To get a valid VkAccelerationStructureBuildSizesInfoKHR
auto dummy_blas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device);
const VkAccelerationStructureBuildSizesInfoKHR dummy_size_info = dummy_blas.GetSizeInfo();
alloc_info.allocationSize = std::max(dummy_size_info.accelerationStructureSize, dummy_size_info.buildScratchSize);
VkBufferCreateInfo dst_blas_buffer_ci = vku::InitStructHelper();
dst_blas_buffer_ci.size = alloc_info.allocationSize;
dst_blas_buffer_ci.usage = VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR |
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
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;
{
vkt::Buffer dummy_blas_buffer(*m_device, dst_blas_buffer_ci, vkt::no_mem);
alloc_info.allocationSize = std::max(alloc_info.allocationSize, dummy_blas_buffer.MemoryRequirements().size);
vkt::Buffer dummy_scratch_buffer(*m_device, scratch_buffer_ci, vkt::no_mem);
alloc_info.allocationSize = std::max(alloc_info.allocationSize, dummy_scratch_buffer.MemoryRequirements().size);
}
vkt::DeviceMemory buffer_memory(*m_device, alloc_info);
// Test overlapping destination acceleration structure and scratch buffer
{
std::vector<vkt::Buffer> dst_blas_buffers(build_info_count);
std::vector<std::shared_ptr<vkt::Buffer>> scratch_buffers(build_info_count);
std::vector<vkt::as::BuildGeometryInfoKHR> blas_vec;
VkDeviceAddress ref_address = 0;
for (size_t i = 0; i < build_info_count; ++i) {
dst_blas_buffers[i].InitNoMemory(*m_device, dst_blas_buffer_ci);
vk::BindBufferMemory(device(), dst_blas_buffers[i], buffer_memory, 0);
scratch_buffers[i] = std::make_shared<vkt::Buffer>();
scratch_buffers[i]->InitNoMemory(*m_device, scratch_buffer_ci);
vk::BindBufferMemory(device(), scratch_buffers[i]->handle(), buffer_memory, 0);
const VkDeviceAddress scratch_address = scratch_buffers[i]->Address();
if (ref_address == 0) {
ref_address = scratch_address;
} else if (scratch_address != ref_address) {
ADD_FAILURE() << "Scratch buffer does not have the expected base address";
}
auto blas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device);
blas.GetDstAS()->SetDeviceBuffer(std::move(dst_blas_buffers[i]));
blas.GetDstAS()->SetSize(4096);
blas.SetScratchBuffer(std::move(scratch_buffers[i]));
blas.SetupBuild(true);
const VkDeviceAddress dst_as_address = blas.GetDstAS()->GetBuffer().Address();
if (dst_as_address != ref_address) {
ADD_FAILURE() << "Buffer backing destination acceleration structure does not have the expected base address";
}
blas_vec.emplace_back(std::move(blas));
}
}
}
TEST_F(PositiveRayTracing, ZeroPrimitiveCount) {
TEST_DESCRIPTION("https://gitlab.khronos.org/vulkan/vulkan/-/issues/4203");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredFeature(vkt::Feature::accelerationStructure);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
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->SetupBuild(true);
const VkAccelerationStructureGeometryKHR* pGeometries = &blas->GetGeometries()[0].GetVkObj();
VkAccelerationStructureBuildGeometryInfoKHR build_geometry_info = vku::InitStructHelper();
build_geometry_info.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR;
build_geometry_info.dstAccelerationStructure = *blas->GetDstAS();
build_geometry_info.geometryCount = 1u;
build_geometry_info.ppGeometries = &pGeometries;
build_geometry_info.scratchData = blas->GetInfo().scratchData;
VkAccelerationStructureBuildRangeInfoKHR build_range_info;
build_range_info.primitiveCount = 0u;
build_range_info.primitiveOffset = 0u;
build_range_info.firstVertex = 0u;
build_range_info.transformOffset = 0u;
const VkAccelerationStructureBuildRangeInfoKHR* p_build_range_info = &build_range_info;
vk::CmdBuildAccelerationStructuresKHR(m_command_buffer, 1u, &build_geometry_info, &p_build_range_info);
m_command_buffer.End();
m_default_queue->Submit(m_command_buffer);
m_device->Wait();
}
TEST_F(PositiveRayTracing, ZeroPrimitiveCountIndirect) {
TEST_DESCRIPTION("https://gitlab.khronos.org/vulkan/vulkan/-/issues/4203");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredFeature(vkt::Feature::accelerationStructure);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
AddRequiredFeature(vkt::Feature::accelerationStructureIndirectBuild);
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->SetupBuild(true);
VkMemoryAllocateFlagsInfo alloc_flags = vku::InitStructHelper();
alloc_flags.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT;
vkt::Buffer indirect_buffer_(*m_device, sizeof(VkAccelerationStructureBuildRangeInfoKHR),
VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
kHostVisibleMemProps, &alloc_flags);
auto* ranges_info = static_cast<VkAccelerationStructureBuildRangeInfoKHR*>(indirect_buffer_.Memory().Map());
const VkAccelerationStructureGeometryKHR* pGeometries;
pGeometries = &blas->GetGeometries()[0].GetVkObj();
*ranges_info = blas->GetGeometries()[0].GetFullBuildRange();
VkAccelerationStructureBuildGeometryInfoKHR build_geometry_info = blas->GetInfo();
build_geometry_info.geometryCount = 1u;
build_geometry_info.ppGeometries = &pGeometries;
const VkDeviceAddress indirect_address = indirect_buffer_.Address();
uint32_t stride = 0u;
uint32_t max_primitive_count = 1u;
const uint32_t* p_max_primitive_counts = &max_primitive_count;
vk::CmdBuildAccelerationStructuresIndirectKHR(m_command_buffer, 1u, &build_geometry_info, &indirect_address, &stride,
&p_max_primitive_counts);
m_command_buffer.End();
m_default_queue->Submit(m_command_buffer);
m_device->Wait();
}
TEST_F(PositiveRayTracing, BuildIndirectWithoutIndexBuffer) {
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());
vkt::as::GeometryKHR triangle_geometry;
triangle_geometry.SetType(vkt::as::GeometryKHR::Type::Triangle);
VkMemoryAllocateFlagsInfo alloc_flags = vku::InitStructHelper();
alloc_flags.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT;
const VkBufferUsageFlags buffer_usage = VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR |
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR |
VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
vkt::Buffer vertex_buffer(*m_device, 1024, buffer_usage, kHostVisibleMemProps, &alloc_flags);
vkt::Buffer transform_buffer(*m_device, sizeof(VkTransformMatrixKHR), buffer_usage, kHostVisibleMemProps, &alloc_flags);
triangle_geometry.SetPrimitiveCount(1);
constexpr std::array vertices = {10.0f, 10.0f, 0.0f, -10.0f, 10.0f, 0.0f, 0.0f, -10.0f, 0.0f};
auto vertex_buffer_ptr = static_cast<float*>(vertex_buffer.Memory().Map());
std::copy(vertices.begin(), vertices.end(), vertex_buffer_ptr);
VkTransformMatrixKHR transform_matrix = {{
{1.0f, 0.0f, 0.0f, 0.0f},
{0.0f, 1.0f, 0.0f, 0.0f},
{0.0f, 0.0f, 1.0f, 0.0f},
}};
auto transform_buffer_ptr = static_cast<VkTransformMatrixKHR*>(transform_buffer.Memory().Map());
std::memcpy(transform_buffer_ptr, &transform_matrix, sizeof(transform_matrix));
triangle_geometry.SetTrianglesDeviceVertexBuffer(std::move(vertex_buffer), uint32_t(vertices.size() / 3) - 1);
triangle_geometry.SetTrianglesIndexType(VK_INDEX_TYPE_UINT32);
triangle_geometry.SetTrianglesTransformBuffer(std::move(transform_buffer));
triangle_geometry.SetFlags(VK_GEOMETRY_OPAQUE_BIT_KHR);
vkt::as::BuildGeometryInfoKHR out_build_info(m_device);
out_build_info.SetType(VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR);
out_build_info.SetBuildType(VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR);
out_build_info.SetMode(VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR);
std::vector<vkt::as::GeometryKHR> geometries;
geometries.emplace_back(std::move(triangle_geometry));
out_build_info.SetGeometries(std::move(geometries));
out_build_info.SetBuildRanges(out_build_info.GetBuildRangeInfosFromGeometries());
auto null_as = std::make_shared<vkt::as::AccelerationStructureKHR>(m_device);
null_as->SetNull(true);
out_build_info.SetSrcAS(null_as);
auto dstAsSize = out_build_info.GetSizeInfo().accelerationStructureSize;
auto as = std::make_shared<vkt::as::AccelerationStructureKHR>(m_device);
as->SetSize(dstAsSize);
as->SetType(VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR);
as->SetDeviceBufferMemoryAllocateFlags(VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT);
as->SetDeviceBufferMemoryPropertyFlags(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
as->SetBufferUsageFlags(VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT |
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
as->SetDeviceBufferInitNoMem(false);
out_build_info.SetDstAS(as);
out_build_info.SetUpdateDstAccelStructSizeBeforeBuild(true);
out_build_info.SetInfoCount(1);
out_build_info.SetNullInfos(false);
out_build_info.SetNullBuildRangeInfos(false);
m_command_buffer.Begin();
out_build_info.BuildCmdBufferIndirect(m_command_buffer);
m_command_buffer.End();
}
TEST_F(PositiveRayTracing, MultipleGeometries) {
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());
if (IsPlatformMockICD()) {
GTEST_SKIP() << "Test not supported by MockICD: sometimes falil to get valid buffer device address";
}
m_command_buffer.Begin();
auto blas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device);
blas.AddFlags(VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR);
std::vector<vkt::as::GeometryKHR> geometries;
geometries.emplace_back(vkt::as::blueprint::GeometrySimpleOnDeviceIndexedTriangleInfo(*m_device, 1));
geometries.emplace_back(vkt::as::blueprint::GeometrySimpleOnDeviceIndexedTriangleInfo(*m_device, 2));
geometries[0].SetTrianglesIndexType(VK_INDEX_TYPE_NONE_KHR);
geometries[1].SetTrianglesIndexType(VK_INDEX_TYPE_NONE_KHR);
geometries[0].SetTrianglesMaxVertex(3);
geometries[1].SetTrianglesMaxVertex(6);
blas.SetGeometries(std::move(geometries));
auto build_range_infos = blas.GetBuildRangeInfosFromGeometries();
blas.SetBuildRanges(build_range_infos);
blas.SetupBuild(true);
std::vector<const VkAccelerationStructureGeometryKHR*> pGeometries;
pGeometries.resize(2);
std::vector<const VkAccelerationStructureBuildRangeInfoKHR*> pRange_infos(1);
VkAccelerationStructureBuildRangeInfoKHR range_infos[2];
range_infos[0].primitiveCount = 1u;
range_infos[0].primitiveOffset = 0u;
range_infos[0].firstVertex = 0u;
range_infos[0].transformOffset = 0u;
range_infos[1].primitiveCount = 2u;
range_infos[1].primitiveOffset = 0u;
range_infos[1].firstVertex = 0u;
range_infos[1].transformOffset = 0u;
pRange_infos[0] = range_infos;
for (size_t i = 0; i < 2; ++i) {
const auto& geometry = blas.GetGeometries()[i];
pGeometries[i] = &geometry.GetVkObj();
}
// Need bigger scratch buffer since there are more geometries to build
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,
vkt::device_address);
blas.SetScratchBuffer(scratch_buffer);
blas.GetInfo().scratchData.deviceAddress = scratch_buffer->Address();
VkAccelerationStructureBuildGeometryInfoKHR vk_info = vku::InitStructHelper();
vk_info.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR;
vk_info.dstAccelerationStructure = *blas.GetDstAS();
vk_info.geometryCount = 2u;
vk_info.ppGeometries = pGeometries.data();
vk_info.scratchData = blas.GetInfo().scratchData;
// Build acceleration structure
const VkAccelerationStructureBuildGeometryInfoKHR* pInfos = &vk_info;
const VkAccelerationStructureBuildRangeInfoKHR* const* ppBuildRangeInfos = pRange_infos.data();
vk::CmdBuildAccelerationStructuresKHR(m_command_buffer, 1u, pInfos, ppBuildRangeInfos);
m_command_buffer.End();
}
TEST_F(PositiveRayTracing, ZeroPrimitiveCountWithIndexTypeNone) {
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredFeature(vkt::Feature::accelerationStructure);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
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->SetupBuild(true);
blas->GetGeometries()[0].SetTrianglesIndexType(VK_INDEX_TYPE_NONE_KHR);
const VkAccelerationStructureGeometryKHR* pGeometries = &blas->GetGeometries()[0].GetVkObj();
VkAccelerationStructureBuildGeometryInfoKHR build_geometry_info = vku::InitStructHelper();
build_geometry_info.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR;
build_geometry_info.dstAccelerationStructure = *blas->GetDstAS();
build_geometry_info.geometryCount = 1u;
build_geometry_info.ppGeometries = &pGeometries;
build_geometry_info.scratchData = blas->GetInfo().scratchData;
VkAccelerationStructureBuildRangeInfoKHR build_range_info;
build_range_info.primitiveCount = 0u;
build_range_info.primitiveOffset = 0u;
build_range_info.firstVertex = 0u;
build_range_info.transformOffset = 0u;
const VkAccelerationStructureBuildRangeInfoKHR* p_build_range_info = &build_range_info;
vk::CmdBuildAccelerationStructuresKHR(m_command_buffer, 1u, &build_geometry_info, &p_build_range_info);
m_command_buffer.End();
m_default_queue->Submit(m_command_buffer);
m_device->Wait();
}
// https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/10621
TEST_F(PositiveRayTracing, DISABLED_CmdBuildPartitionedAccelerationStructuresNV) {
TEST_DESCRIPTION("Test vkCmdBuildPartitionedAccelerationStructuresNV can build a partitioned TLAS");
SetTargetApiVersion(VK_API_VERSION_1_3);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
AddRequiredFeature(vkt::Feature::accelerationStructure);
AddRequiredFeature(vkt::Feature::rayQuery);
AddRequiredFeature(vkt::Feature::partitionedAccelerationStructure);
AddRequiredExtensions(VK_NV_PARTITIONED_ACCELERATION_STRUCTURE_EXTENSION_NAME);
AddOptionalExtensions(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME);
RETURN_IF_SKIP(InitFrameworkForRayTracingTest());
RETURN_IF_SKIP(InitState());
uint32_t instance_count = 20;
uint32_t partition_count = 5;
VkPartitionedAccelerationStructureFlagsNV ptlas_flags = vku::InitStructHelper();
ptlas_flags.enablePartitionTranslation = true;
VkPartitionedAccelerationStructureInstancesInputNV input_info = vku::InitStructHelper(&ptlas_flags);
input_info.instanceCount = instance_count;
input_info.maxInstancePerPartitionCount = instance_count / partition_count;
input_info.partitionCount = partition_count;
input_info.maxInstanceInGlobalPartitionCount = instance_count / partition_count;
input_info.pNext = &ptlas_flags;
VkAccelerationStructureBuildSizesInfoKHR ptlas_size_info = vku::InitStructHelper();
vk::GetPartitionedAccelerationStructuresBuildSizesNV(*m_device, &input_info, &ptlas_size_info);
vkt::Buffer build_buffer(*m_device, ptlas_size_info.accelerationStructureSize,
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
vkt::device_address);
void* buffer_data = build_buffer.Memory().Map();
memset(buffer_data, 0, static_cast<size_t>(ptlas_size_info.accelerationStructureSize));
build_buffer.Memory().Unmap();
VkDeviceAddress ptlas_buffer_address = build_buffer.Address();
vkt::Buffer count_buffer(*m_device, sizeof(uint32_t),
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT |
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR,
vkt::device_address);
uint32_t input = 1;
auto* data = static_cast<uint32_t*>(count_buffer.Memory().Map());
memcpy(data, &input, sizeof(input));
count_buffer.Memory().Unmap();
VkDeviceAddress count_buffer_address = count_buffer.Address();
vkt::Buffer scratch_buffer(*m_device, ptlas_size_info.buildScratchSize,
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT |
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
vkt::device_address);
VkDeviceAddress scratch_buffer_address = scratch_buffer.Address();
vkt::Buffer write_partition_buffer(
*m_device, partition_count * sizeof(VkPartitionedAccelerationStructureWritePartitionTranslationDataNV),
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, vkt::device_address);
std::vector<VkPartitionedAccelerationStructureWritePartitionTranslationDataNV> writePartitionArgs;
// 5 here is partition count
static uint32_t partitionArray[5] = {3, 0xFFFFFFFF, 0, 2, 1}; // 0xFFFFFFFF is the global partition
static float partitionTranslationY[] = {0, 20, 40, 20, 0}; // Each partition is translated along Y
for (uint32_t t = 0; t < partition_count; t++) {
VkPartitionedAccelerationStructureWritePartitionTranslationDataNV writePartition{};
writePartition.partitionIndex = partitionArray[t];
writePartition.partitionTranslation[0] = 0;
writePartition.partitionTranslation[1] = partitionTranslationY[t];
writePartition.partitionTranslation[2] = 0;
writePartitionArgs.push_back(writePartition);
}
auto* write_partition_data =
static_cast<VkPartitionedAccelerationStructureWritePartitionTranslationDataNV*>(write_partition_buffer.Memory().Map());
memcpy(write_partition_data, writePartitionArgs.data(),
partition_count * sizeof(VkPartitionedAccelerationStructureWritePartitionTranslationDataNV));
VkDeviceAddress write_partition_buffer_address = write_partition_buffer.Address();
std::vector<VkBuildPartitionedAccelerationStructureIndirectCommandNV> ptlas_ops;
VkBuildPartitionedAccelerationStructureIndirectCommandNV ptlas_op = {};
ptlas_op.opType = VK_PARTITIONED_ACCELERATION_STRUCTURE_OP_TYPE_WRITE_PARTITION_TRANSLATION_NV;
ptlas_op.argCount = partition_count;
ptlas_op.argData.startAddress = write_partition_buffer_address;
ptlas_op.argData.strideInBytes = sizeof(VkPartitionedAccelerationStructureWriteInstanceDataNV);
ptlas_ops.push_back(ptlas_op);
vkt::Buffer src_info_buffer(*m_device, partition_count * sizeof(VkBuildPartitionedAccelerationStructureIndirectCommandNV),
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT |
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR,
vkt::device_address);
auto* src_info_data = static_cast<VkBuildPartitionedAccelerationStructureIndirectCommandNV*>(src_info_buffer.Memory().Map());
memcpy(src_info_data, ptlas_ops.data(), sizeof(VkBuildPartitionedAccelerationStructureIndirectCommandNV));
VkDeviceAddress src_info_buffer_address = src_info_buffer.Address();
VkBuildPartitionedAccelerationStructureInfoNV command_info = vku::InitStructHelper();
command_info.input = input_info;
command_info.srcAccelerationStructureData = 0;
command_info.dstAccelerationStructureData = ptlas_buffer_address;
command_info.scratchData = scratch_buffer_address;
command_info.srcInfos = src_info_buffer_address;
command_info.srcInfosCount = count_buffer_address;
m_command_buffer.Begin();
vk::CmdBuildPartitionedAccelerationStructuresNV(m_command_buffer.handle(), &command_info);
m_command_buffer.End();
m_default_queue->Submit(m_command_buffer);
m_device->Wait();
if (!IsPlatformMockICD()) {
void* mapped_memory = build_buffer.Memory().Map();
unsigned char* memory_data = (unsigned char*)mapped_memory;
bool has_data = false;
for (size_t i = 0; i < ptlas_size_info.accelerationStructureSize; i++) {
if (memory_data[i] != 0) {
has_data = true;
break;
}
}
// check if CmdBuildPartitionedAccelerationStructuresNV call return an non-empty output
ASSERT_TRUE(has_data);
}
}
TEST_F(PositiveRayTracing, PartitionedAccelerationStructuresBuildSizes) {
TEST_DESCRIPTION(
"Test vkGetPartitionedAccelerationStructuresBuildSizesNV can retrieve the buffer allocation requirements for partitioned "
"acceleration structure command");
SetTargetApiVersion(VK_API_VERSION_1_3);
AddRequiredFeature(vkt::Feature::partitionedAccelerationStructure);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
AddRequiredFeature(vkt::Feature::accelerationStructure);
AddRequiredFeature(vkt::Feature::rayQuery);
AddRequiredExtensions(VK_NV_PARTITIONED_ACCELERATION_STRUCTURE_EXTENSION_NAME);
AddOptionalExtensions(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME);
RETURN_IF_SKIP(InitFrameworkForRayTracingTest());
RETURN_IF_SKIP(InitState());
int instance_count = 20;
int partition_count = 5;
VkPartitionedAccelerationStructureInstancesInputNV input_info = vku::InitStructHelper();
input_info.instanceCount = instance_count;
input_info.maxInstancePerPartitionCount = instance_count / partition_count;
input_info.partitionCount = partition_count;
input_info.maxInstanceInGlobalPartitionCount = instance_count / partition_count;
input_info.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_KHR;
VkAccelerationStructureBuildSizesInfoKHR ptlas_size_info = vku::InitStructHelper();
vk::GetPartitionedAccelerationStructuresBuildSizesNV(*m_device, &input_info, &ptlas_size_info);
// check if the output of GetPartitionedAccelerationStructuresBuildSizesNV is valid
ASSERT_TRUE(ptlas_size_info.accelerationStructureSize != 0);
}
TEST_F(PositiveRayTracing, CmdBuildClusterAccelerationStructureIndirect) {
TEST_DESCRIPTION("Validate vkCmdBuildClusterAccelerationStructureIndirectNV can build cluster acceleration structures");
SetTargetApiVersion(VK_API_VERSION_1_3);
AddRequiredExtensions(VK_NV_CLUSTER_ACCELERATION_STRUCTURE_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::rayTracingPipeline);
AddRequiredFeature(vkt::Feature::accelerationStructure);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
AddRequiredFeature(vkt::Feature::rayQuery);
AddRequiredFeature(vkt::Feature::clusterAccelerationStructure);
RETURN_IF_SKIP(InitFrameworkForRayTracingTest());
RETURN_IF_SKIP(InitState());
uint32_t total_triangles = 1;
uint32_t totalVertices = 3 * total_triangles;
VkClusterAccelerationStructureTriangleClusterInputNV tri_cluster = vku::InitStructHelper();
tri_cluster.vertexFormat = VK_FORMAT_R32G32B32_SFLOAT;
tri_cluster.maxGeometryIndexValue = total_triangles - 1;
tri_cluster.maxClusterUniqueGeometryCount = 0;
tri_cluster.maxClusterTriangleCount = 1;
tri_cluster.maxClusterVertexCount = 3;
tri_cluster.maxTotalTriangleCount = total_triangles;
tri_cluster.maxTotalVertexCount = totalVertices;
tri_cluster.minPositionTruncateBitCount = 0;
VkClusterAccelerationStructureOpInputNV input = {};
input.pTriangleClusters = &tri_cluster;
VkClusterAccelerationStructureInputInfoNV input_info = vku::InitStructHelper();
input_info.maxAccelerationStructureCount = 1;
input_info.flags = VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR;
input_info.opType = VK_CLUSTER_ACCELERATION_STRUCTURE_OP_TYPE_BUILD_TRIANGLE_CLUSTER_TEMPLATE_NV;
input_info.opInput = input;
VkAccelerationStructureBuildSizesInfoKHR clas_size_info = vku::InitStructHelper();
vk::GetClusterAccelerationStructureBuildSizesNV(*m_device, &input_info, &clas_size_info);
vkt::Buffer scratch_buffer(*m_device, clas_size_info.buildScratchSize,
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT |
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR,
vkt::device_address);
uint32_t index_data_f[10];
for (uint32_t i = 0; i < 10; i++) {
index_data_f[i] = i;
}
vkt::Buffer index_buffer(
*m_device, 10 * sizeof(uint32_t),
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
vkt::device_address);
auto* index_data = static_cast<uint32_t*>(index_buffer.Memory().Map());
memcpy(index_data, index_data_f, sizeof(index_data_f));
const uint32_t numVerticesInCluster = totalVertices;
VkClusterAccelerationStructureBuildTriangleClusterTemplateInfoNV triClusterTemplateArg = {};
triClusterTemplateArg.clusterID = 0;
triClusterTemplateArg.clusterFlags = VK_CLUSTER_ACCELERATION_STRUCTURE_CLUSTER_ALLOW_DISABLE_OPACITY_MICROMAPS_NV;
triClusterTemplateArg.triangleCount = total_triangles;
triClusterTemplateArg.vertexCount = numVerticesInCluster;
triClusterTemplateArg.positionTruncateBitCount = 0;
triClusterTemplateArg.indexType = VK_CLUSTER_ACCELERATION_STRUCTURE_INDEX_FORMAT_32BIT_NV;
triClusterTemplateArg.opacityMicromapIndexType = VK_CLUSTER_ACCELERATION_STRUCTURE_INDEX_FORMAT_32BIT_NV;
triClusterTemplateArg.baseGeometryIndexAndGeometryFlags.geometryFlags =
VK_CLUSTER_ACCELERATION_STRUCTURE_GEOMETRY_OPAQUE_BIT_NV;
triClusterTemplateArg.indexBufferStride = sizeof(uint32_t);
triClusterTemplateArg.vertexBufferStride = 3 * sizeof(float);
triClusterTemplateArg.geometryIndexAndFlagsBufferStride = 0;
triClusterTemplateArg.opacityMicromapIndexBufferStride = 0;
triClusterTemplateArg.indexBuffer = index_buffer.Address();
triClusterTemplateArg.vertexBuffer = 0;
triClusterTemplateArg.geometryIndexAndFlagsBuffer = 0;
triClusterTemplateArg.opacityMicromapArray = 0;
triClusterTemplateArg.opacityMicromapIndexBuffer = 0;
triClusterTemplateArg.instantiationBoundingBoxLimit = 0;
vkt::Buffer src_info_buffer(
*m_device, sizeof(VkClusterAccelerationStructureBuildTriangleClusterTemplateInfoNV),
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
vkt::device_address);
auto* src_info_data =
static_cast<VkClusterAccelerationStructureBuildTriangleClusterTemplateInfoNV*>(src_info_buffer.Memory().Map());
memcpy(src_info_data, &triClusterTemplateArg, sizeof(triClusterTemplateArg));
vkt::Buffer count_buffer(*m_device, sizeof(uint32_t),
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT |
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR,
vkt::device_address);
int input_value = 1;
auto* count_data = static_cast<uint32_t*>(count_buffer.Memory().Map());
memcpy(count_data, &input_value, sizeof(input_value));
vkt::Buffer build_buffer(*m_device, clas_size_info.accelerationStructureSize,
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
vkt::device_address);
vkt::Buffer dst_build_buffer(*m_device, 1 * sizeof(VkStridedDeviceAddressNV),
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR |
VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT |
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR,
vkt::device_address);
VkClusterAccelerationStructureCommandsInfoNV command_info = vku::InitStructHelper();
command_info.input = input_info;
command_info.scratchData = scratch_buffer.Address();
command_info.srcInfosArray.deviceAddress = src_info_buffer.Address();
command_info.srcInfosArray.stride = sizeof(VkClusterAccelerationStructureBuildTriangleClusterTemplateInfoNV);
command_info.srcInfosCount = count_buffer.Address();
command_info.input.opMode = VK_CLUSTER_ACCELERATION_STRUCTURE_OP_MODE_IMPLICIT_DESTINATIONS_NV;
command_info.dstImplicitData = build_buffer.Address();
command_info.dstAddressesArray.deviceAddress = dst_build_buffer.Address();
command_info.dstAddressesArray.stride = sizeof(VkDeviceAddress);
m_command_buffer.Begin();
vk::CmdBuildClusterAccelerationStructureIndirectNV(m_command_buffer.handle(), &command_info);
m_command_buffer.End();
m_default_queue->Submit(m_command_buffer);
m_device->Wait();
void* mapped_memory = build_buffer.Memory().Map();
unsigned char* memory_data = (unsigned char*)mapped_memory;
bool has_data = false;
for (size_t i = 0; i < clas_size_info.accelerationStructureSize; i++) {
if (memory_data[i] != 0) {
has_data = true;
break;
}
}
// validate CmdBuildClusterAccelerationStructureIndirectNV has the valid output
ASSERT_TRUE(has_data);
}
TEST_F(PositiveRayTracing, GetClusterAccelerationStructureBuildSizes) {
TEST_DESCRIPTION(
"Test vKGetClusterAccelerationStructureBuildSizes can retrieve the buffer allocation requirements for cluster geometry "
"command");
SetTargetApiVersion(VK_API_VERSION_1_2);
AddRequiredExtensions(VK_NV_CLUSTER_ACCELERATION_STRUCTURE_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::rayTracingPipeline);
AddRequiredFeature(vkt::Feature::accelerationStructure);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
AddRequiredFeature(vkt::Feature::rayQuery);
AddRequiredFeature(vkt::Feature::clusterAccelerationStructure);
RETURN_IF_SKIP(InitFrameworkForRayTracingTest());
RETURN_IF_SKIP(InitState());
uint32_t total_triangles = 10;
uint32_t max_triangles_per_cluster = 10;
uint32_t totalVertices = 3 * total_triangles;
VkClusterAccelerationStructureTriangleClusterInputNV tri_cluster = vku::InitStructHelper();
tri_cluster.vertexFormat = VK_FORMAT_R32G32B32_SFLOAT;
tri_cluster.maxGeometryIndexValue = total_triangles - 1;
tri_cluster.maxClusterUniqueGeometryCount = max_triangles_per_cluster - 1;
tri_cluster.maxClusterTriangleCount = max_triangles_per_cluster;
tri_cluster.maxClusterVertexCount = max_triangles_per_cluster * 3;
tri_cluster.maxTotalTriangleCount = total_triangles;
tri_cluster.maxTotalVertexCount = totalVertices;
tri_cluster.minPositionTruncateBitCount = 0;
VkClusterAccelerationStructureOpInputNV input = {};
input.pTriangleClusters = &tri_cluster;
VkClusterAccelerationStructureInputInfoNV input_info = vku::InitStructHelper();
input_info.maxAccelerationStructureCount = 1;
input_info.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_KHR;
input_info.opType = VK_CLUSTER_ACCELERATION_STRUCTURE_OP_TYPE_BUILD_TRIANGLE_CLUSTER_TEMPLATE_NV;
input_info.opInput = input;
VkAccelerationStructureBuildSizesInfoKHR clas_size_info = vku::InitStructHelper();
vk::GetClusterAccelerationStructureBuildSizesNV(*m_device, &input_info, &clas_size_info);
// check if clas_size_info.accelerationStructureSize should not be zero
ASSERT_TRUE(clas_size_info.accelerationStructureSize != 0);
}
TEST_F(PositiveRayTracing, DisableShaderValidationTraceRays) {
const VkLayerSettingEXT setting = {OBJECT_LAYER_NAME, "check_shaders", VK_LAYER_SETTING_TYPE_BOOL32_EXT, 1, &kVkFalse};
VkLayerSettingsCreateInfoEXT layer_setting_ci = {VK_STRUCTURE_TYPE_LAYER_SETTINGS_CREATE_INFO_EXT, nullptr, 1, &setting};
SetTargetApiVersion(VK_API_VERSION_1_2);
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);
AddRequiredFeature(vkt::Feature::rayTracingPipeline);
AddRequiredFeature(vkt::Feature::accelerationStructure);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
RETURN_IF_SKIP(InitFramework(&layer_setting_ci));
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);
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();
}