| /* Copyright (c) 2025 The Khronos Group Inc. |
| * Copyright (c) 2025 Valve Corporation |
| * Copyright (c) 2025 LunarG, 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 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "../framework/sync_val_tests.h" |
| #include "../framework/pipeline_helper.h" |
| #include "../framework/ray_tracing_objects.h" |
| |
| struct PositiveSyncValRayTracing : public VkSyncValTest {}; |
| |
| vkt::Buffer VkSyncValTest::GetSerializationDeserializationBuffer(const vkt::as::AccelerationStructureKHR& as) { |
| vkt::QueryPool query_pool(*m_device, VK_QUERY_TYPE_ACCELERATION_STRUCTURE_SERIALIZATION_SIZE_KHR, 1); |
| m_command_buffer.Begin(); |
| vk::CmdResetQueryPool(m_command_buffer, query_pool, 0, 1); |
| vk::CmdWriteAccelerationStructuresPropertiesKHR(m_command_buffer, 1, &as.handle(), |
| VK_QUERY_TYPE_ACCELERATION_STRUCTURE_SERIALIZATION_SIZE_KHR, query_pool, 0); |
| m_command_buffer.End(); |
| m_default_queue->SubmitAndWait(m_command_buffer); |
| uint32_t serialization_size = 0; |
| query_pool.Results(0, 1, sizeof(uint32_t), &serialization_size, 0); |
| return vkt::Buffer(*m_device, serialization_size, |
| VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, |
| vkt::device_address); |
| } |
| |
| CreateComputePipelineHelper VkSyncValTest::GetRayQueryComputePipeline(VkAccelerationStructureKHR as) { |
| const char* cs_source = R"glsl( |
| #version 460 |
| #extension GL_EXT_ray_query : require |
| layout(set = 0, binding = 0) uniform accelerationStructureEXT tlas; |
| void main() { |
| rayQueryEXT query; |
| rayQueryInitializeEXT(query, tlas, gl_RayFlagsTerminateOnFirstHitEXT, 0xff, vec3(0), 0.1, vec3(0,0,1), 1000.0); |
| rayQueryProceedEXT(query); |
| } |
| )glsl"; |
| CreateComputePipelineHelper pipeline(*this); |
| pipeline.cs_ = VkShaderObj(*m_device, cs_source, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_2); |
| pipeline.dsl_bindings_[0] = {0, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr}; |
| pipeline.CreateComputePipeline(); |
| pipeline.descriptor_set_.WriteDescriptorAccelStruct(0, 1, &as); |
| pipeline.descriptor_set_.UpdateDescriptorSets(); |
| return pipeline; |
| } |
| |
| std::unique_ptr<vkt::rt::Pipeline> VkSyncValTest::GetTraceRaysPipeline(VkAccelerationStructureKHR as) { |
| const char* ray_gen = R"glsl( |
| #version 460 |
| #extension GL_EXT_ray_tracing : require |
| 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"; |
| auto pipeline = std::make_unique<vkt::rt::Pipeline>(*this, m_device); |
| pipeline->SetGlslRayGenShader(ray_gen); |
| pipeline->AddBinding(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 0); |
| pipeline->CreateDescriptorSet(); |
| pipeline->GetDescriptorSet().WriteDescriptorAccelStruct(0, 1, &as); |
| pipeline->GetDescriptorSet().UpdateDescriptorSets(); |
| pipeline->Build(); |
| return pipeline; |
| } |
| |
| std::unique_ptr<vkt::as::BuildGeometryInfoKHR> VkSyncValTest::BuildBLAS() { |
| const VkBufferUsageFlags usage = VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_TRANSFER_DST_BIT | |
| VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; |
| auto blas = |
| std::make_unique<vkt::as::BuildGeometryInfoKHR>(vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device)); |
| blas->GetDstAS()->SetBufferUsageFlags(usage); |
| blas->SetupBuild(true); |
| m_command_buffer.Begin(); |
| blas->VkCmdBuildAccelerationStructuresKHR(m_command_buffer); |
| m_command_buffer.End(); |
| m_default_queue->SubmitAndWait(m_command_buffer); |
| return blas; |
| } |
| |
| std::unique_ptr<vkt::as::BuildGeometryInfoKHR> VkSyncValTest::BuildTLAS(const vkt::as::AccelerationStructureKHR& blas) { |
| const VkBufferUsageFlags usage = VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_TRANSFER_DST_BIT; |
| auto tlas = std::make_unique<vkt::as::BuildGeometryInfoKHR>( |
| vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceTopLevel(*m_device, blas)); |
| tlas->GetDstAS()->SetBufferUsageFlags(usage); |
| tlas->SetupBuild(true); |
| m_command_buffer.Begin(); |
| tlas->VkCmdBuildAccelerationStructuresKHR(m_command_buffer); |
| m_command_buffer.End(); |
| m_default_queue->SubmitAndWait(m_command_buffer); |
| return tlas; |
| } |
| |
| TEST_F(PositiveSyncValRayTracing, BuildAccelerationStructure) { |
| TEST_DESCRIPTION("Just build it"); |
| RETURN_IF_SKIP(InitRayTracing()); |
| |
| vkt::as::BuildGeometryInfoKHR blas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device); |
| |
| m_command_buffer.Begin(); |
| blas.BuildCmdBuffer(m_command_buffer); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(PositiveSyncValRayTracing, WriteToAccelerationStructureBuffer) { |
| TEST_DESCRIPTION("Write to a vacant region of acceleration structure buffer during acceleration structure build"); |
| RETURN_IF_SKIP(InitRayTracing()); |
| |
| vkt::as::BuildGeometryInfoKHR blas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device); |
| blas.GetDstAS()->SetBufferUsageFlags(VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| // Reserve initial buffer region for non-acceleration-structure business. |
| // Acceleration structure contents start after it. |
| blas.GetDstAS()->SetOffset(256); |
| blas.SetupBuild(true); |
| const vkt::Buffer& accel_buffer = blas.GetDstAS()->GetBuffer(); |
| |
| m_command_buffer.Begin(); |
| blas.VkCmdBuildAccelerationStructuresKHR(m_command_buffer); |
| // Write the answer into the first 4 bytes. |
| // This should not interfere with acceleration structure build accesses |
| vk::CmdFillBuffer(m_command_buffer, accel_buffer, 0, 4, 0x42); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(PositiveSyncValRayTracing, BuildAccelerationStructureThenBarrier) { |
| TEST_DESCRIPTION("Use barrier to wait for acceleration structure accesses"); |
| SetTargetApiVersion(VK_API_VERSION_1_3); |
| AddRequiredFeature(vkt::Feature::synchronization2); |
| RETURN_IF_SKIP(InitRayTracing()); |
| |
| vkt::as::BuildGeometryInfoKHR blas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device); |
| blas.GetDstAS()->SetBufferUsageFlags(VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| blas.SetupBuild(true); |
| |
| const vkt::Buffer& accel_buffer = blas.GetDstAS()->GetBuffer(); |
| |
| VkBufferMemoryBarrier2 barrier = vku::InitStructHelper(); |
| barrier.srcStageMask = VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR; |
| barrier.srcAccessMask = VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR | VK_ACCESS_2_ACCELERATION_STRUCTURE_WRITE_BIT_KHR; |
| barrier.dstStageMask = VK_PIPELINE_STAGE_2_CLEAR_BIT; |
| barrier.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT; |
| barrier.buffer = accel_buffer; |
| barrier.offset = 0; |
| barrier.size = 4; |
| |
| m_command_buffer.Begin(); |
| blas.VkCmdBuildAccelerationStructuresKHR(m_command_buffer); |
| m_command_buffer.Barrier(barrier); |
| vk::CmdFillBuffer(m_command_buffer, accel_buffer, 0, 4, 0x80386); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(PositiveSyncValRayTracing, UseSourceAccelerationStructureThenBarrier) { |
| TEST_DESCRIPTION("Use barrier to wait for the source acceleration structure when building in update mode"); |
| SetTargetApiVersion(VK_API_VERSION_1_3); |
| AddRequiredFeature(vkt::Feature::synchronization2); |
| RETURN_IF_SKIP(InitRayTracing()); |
| |
| // Prepare source acceleration structure |
| auto blas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device); |
| blas.GetDstAS()->SetBufferUsageFlags(VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| blas.AddFlags(VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR); |
| blas.SetupBuild(true); |
| |
| m_command_buffer.Begin(); |
| blas.VkCmdBuildAccelerationStructuresKHR(m_command_buffer); |
| m_command_buffer.End(); |
| m_default_queue->SubmitAndWait(m_command_buffer); |
| |
| // Create another acceleration structure to be built in update mode |
| auto blas2 = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device); |
| blas2.AddFlags(VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR); |
| blas2.SetMode(VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR); |
| blas2.SetSrcAS(blas.GetDstAS()); |
| blas2.SetupBuild(true); |
| const vkt::Buffer& src_accel_buffer = blas2.GetSrcAS()->GetBuffer(); |
| |
| // Execution dependency is sufficient to prevent WRITE after READ |
| VkBufferMemoryBarrier2 barrier = vku::InitStructHelper(); |
| barrier.srcStageMask = VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR; |
| barrier.dstStageMask = VK_PIPELINE_STAGE_2_CLEAR_BIT; |
| barrier.buffer = src_accel_buffer; |
| barrier.offset = 0; |
| barrier.size = 4; |
| |
| m_command_buffer.Begin(); |
| // Build in update mode READs source acceleration structure |
| blas2.VkCmdBuildAccelerationStructuresKHR(m_command_buffer); |
| m_command_buffer.Barrier(barrier); |
| // WRITE to source acceleration structure |
| vk::CmdFillBuffer(m_command_buffer, src_accel_buffer, 0, 4, 0x80486); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(PositiveSyncValRayTracing, UpdateAccelerationStructureInPlace) { |
| TEST_DESCRIPTION("In-place update reads and writes the same acceleration structure, ensure this does not trigger error"); |
| RETURN_IF_SKIP(InitRayTracing()); |
| |
| // Initial build |
| auto blas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device); |
| blas.AddFlags(VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR); |
| blas.SetupBuild(true); |
| |
| m_command_buffer.Begin(); |
| blas.VkCmdBuildAccelerationStructuresKHR(m_command_buffer); |
| m_command_buffer.End(); |
| m_default_queue->SubmitAndWait(m_command_buffer); |
| |
| // Update acceleration structure in-place |
| blas.SetMode(VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR); |
| blas.SetSrcAS(blas.GetDstAS()); |
| blas.SetupBuild(true); |
| |
| m_command_buffer.Begin(); |
| blas.VkCmdBuildAccelerationStructuresKHR(m_command_buffer); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(PositiveSyncValRayTracing, ReadVertexDataDuringBuild) { |
| TEST_DESCRIPTION("Use vertex buffer as a copy source while it is used by the AS build operation"); |
| RETURN_IF_SKIP(InitRayTracing()); |
| |
| auto geometry = vkt::as::blueprint::GeometrySimpleOnDeviceTriangleInfo(*m_device, VK_BUFFER_USAGE_TRANSFER_SRC_BIT); |
| auto blas = vkt::as::blueprint::BuildGeometryInfoOnDeviceBottomLevel(*m_device, std::move(geometry)); |
| const auto& triangles_geometry = blas.GetGeometries()[0].GetTriangles(); |
| const vkt::Buffer& vertex_buffer = triangles_geometry.device_vertex_buffer; |
| blas.SetupBuild(true); |
| |
| vkt::Buffer dst_buffer(*m_device, vertex_buffer.CreateInfo().size, VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| |
| m_command_buffer.Begin(); |
| blas.VkCmdBuildAccelerationStructuresKHR(m_command_buffer); |
| m_command_buffer.Copy(vertex_buffer, dst_buffer); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(PositiveSyncValRayTracing, WriteVertexDataDuringBuild) { |
| TEST_DESCRIPTION("Write to a vacant region of the vertex data buffer during AS build operation"); |
| RETURN_IF_SKIP(InitRayTracing()); |
| |
| auto geometry = vkt::as::blueprint::GeometrySimpleOnDeviceTriangleInfo(*m_device, VK_BUFFER_USAGE_TRANSFER_SRC_BIT); |
| |
| // Build custom vertex data buffer where data region starts at some non-zero offset |
| constexpr std::array vertices = {// Vertex 0 |
| 10.0f, 10.0f, 0.0f, |
| // Vertex 1 |
| -10.0f, 10.0f, 0.0f, |
| // Vertex 2 |
| 0.0f, -10.0f, 0.0f}; |
| |
| 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_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; |
| vkt::Buffer vertex_buffer(*m_device, 1024, buffer_usage, kHostVisibleMemProps, &alloc_flags); |
| auto vertex_buffer_ptr = static_cast<float*>(vertex_buffer.Memory().Map()); |
| std::copy(vertices.begin(), vertices.end(), vertex_buffer_ptr); |
| |
| // Specify offset (4 bytes) so vertex data does not start immediately from the beginning of the buffer. |
| geometry.SetTrianglesDeviceVertexBuffer(std::move(vertex_buffer), uint32_t(vertices.size() / 3) - 1, VK_FORMAT_R32G32B32_SFLOAT, |
| 3 * sizeof(float), 4); |
| |
| auto blas = vkt::as::blueprint::BuildGeometryInfoOnDeviceBottomLevel(*m_device, std::move(geometry)); |
| blas.SetupBuild(true); |
| m_command_buffer.Begin(); |
| blas.VkCmdBuildAccelerationStructuresKHR(m_command_buffer); |
| |
| // Write arbitrary data into the first 4 bytes. |
| // This should not interfere with acceleration structure build accesses that read vertex data |
| vk::CmdFillBuffer(m_command_buffer, blas.GetGeometries()[0].GetTriangles().device_vertex_buffer, 0, 4, 0x42); |
| |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(PositiveSyncValRayTracing, ReadIndexDataDuringBuild) { |
| TEST_DESCRIPTION("Use index buffer as a copy source while it is used by the AS build operation"); |
| RETURN_IF_SKIP(InitRayTracing()); |
| |
| auto geometry = vkt::as::blueprint::GeometrySimpleOnDeviceIndexedTriangleInfo(*m_device, 1, VK_BUFFER_USAGE_TRANSFER_SRC_BIT); |
| auto blas = vkt::as::blueprint::BuildGeometryInfoOnDeviceBottomLevel(*m_device, std::move(geometry)); |
| const auto& triangles_geometry = blas.GetGeometries()[0].GetTriangles(); |
| const vkt::Buffer& index_buffer = triangles_geometry.device_index_buffer; |
| blas.SetupBuild(true); |
| |
| vkt::Buffer dst_buffer(*m_device, index_buffer.CreateInfo().size, VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| |
| m_command_buffer.Begin(); |
| blas.VkCmdBuildAccelerationStructuresKHR(m_command_buffer); |
| m_command_buffer.Copy(index_buffer, dst_buffer); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(PositiveSyncValRayTracing, ReadTransformDataDuringBuild) { |
| TEST_DESCRIPTION("Use transform data as a copy source while it is used by the AS build operation"); |
| RETURN_IF_SKIP(InitRayTracing()); |
| |
| auto geometry = vkt::as::blueprint::GeometrySimpleOnDeviceTriangleInfo(*m_device, VK_BUFFER_USAGE_TRANSFER_SRC_BIT); |
| auto blas = vkt::as::blueprint::BuildGeometryInfoOnDeviceBottomLevel(*m_device, std::move(geometry)); |
| const auto& triangles_geometry = blas.GetGeometries()[0].GetTriangles(); |
| const vkt::Buffer& transform_data = triangles_geometry.device_transform_buffer; |
| blas.SetupBuild(true); |
| |
| vkt::Buffer dst_buffer(*m_device, transform_data.CreateInfo().size, VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| |
| m_command_buffer.Begin(); |
| blas.VkCmdBuildAccelerationStructuresKHR(m_command_buffer); |
| m_command_buffer.Copy(transform_data, dst_buffer); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(PositiveSyncValRayTracing, ReadAABBDataDuringBuild) { |
| TEST_DESCRIPTION("Use AABB data as a copy source while it is used by the AS build operation"); |
| RETURN_IF_SKIP(InitRayTracing()); |
| |
| auto geometry = vkt::as::blueprint::GeometrySimpleOnDeviceAABBInfo(*m_device, VK_BUFFER_USAGE_TRANSFER_SRC_BIT); |
| auto blas = vkt::as::blueprint::BuildGeometryInfoOnDeviceBottomLevel(*m_device, std::move(geometry)); |
| const auto& aabbs_geometry = blas.GetGeometries()[0].GetAABBs(); |
| const vkt::Buffer& aabb_buffer = aabbs_geometry.device_buffer; |
| blas.SetupBuild(true); |
| |
| vkt::Buffer dst_buffer(*m_device, aabb_buffer.CreateInfo().size, VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| |
| m_command_buffer.Begin(); |
| blas.VkCmdBuildAccelerationStructuresKHR(m_command_buffer); |
| m_command_buffer.Copy(aabb_buffer, dst_buffer); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(PositiveSyncValRayTracing, ReadInstanceDataDuringBuild) { |
| TEST_DESCRIPTION("Use instance data as a copy source while it is used by the AS build operation"); |
| RETURN_IF_SKIP(InitRayTracing()); |
| |
| std::unique_ptr<vkt::as::BuildGeometryInfoKHR> blas = BuildBLAS(); |
| vkt::as::BuildGeometryInfoKHR tlas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceTopLevel(*m_device, *blas->GetDstAS()); |
| const auto& instance = tlas.GetGeometries()[0].GetInstance(); |
| tlas.SetupBuild(true); |
| vkt::Buffer dst_buffer(*m_device, instance.buffer.CreateInfo().size, VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| |
| m_command_buffer.Begin(); |
| tlas.VkCmdBuildAccelerationStructuresKHR(m_command_buffer); |
| m_command_buffer.Copy(instance.buffer, dst_buffer); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(PositiveSyncValRayTracing, TraceAfterBuild) { |
| TEST_DESCRIPTION("Build TLAS and trace rays"); |
| SetTargetApiVersion(VK_API_VERSION_1_3); |
| AddRequiredFeature(vkt::Feature::synchronization2); |
| RETURN_IF_SKIP(InitRayTracing()); |
| |
| // Create TLAS (but not build it yet) |
| std::unique_ptr<vkt::as::BuildGeometryInfoKHR> blas = BuildBLAS(); |
| vkt::as::BuildGeometryInfoKHR tlas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceTopLevel(*m_device, *blas->GetDstAS()); |
| tlas.SetupBuild(true); |
| |
| std::unique_ptr<vkt::rt::Pipeline> pipeline = GetTraceRaysPipeline(tlas.GetDstAS()->handle()); |
| const vkt::rt::TraceRaysSbt trace_rays_sbt = pipeline->GetTraceRaysSbt(); |
| |
| VkMemoryBarrier2 barrier = vku::InitStructHelper(); |
| barrier.srcStageMask = VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR; |
| barrier.srcAccessMask = VK_ACCESS_2_ACCELERATION_STRUCTURE_WRITE_BIT_KHR; |
| barrier.dstStageMask = VK_PIPELINE_STAGE_2_RAY_TRACING_SHADER_BIT_KHR; |
| barrier.dstAccessMask = VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR; |
| |
| 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); |
| |
| // Build TLAS |
| tlas.VkCmdBuildAccelerationStructuresKHR(m_command_buffer); |
| // Wait for TLAS build completion |
| m_command_buffer.Barrier(barrier); |
| // Trace against TLAS |
| 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(); |
| } |
| |
| TEST_F(PositiveSyncValRayTracing, TraceAfterBuildIndirect) { |
| TEST_DESCRIPTION("Build TLAS and trace rays"); |
| SetTargetApiVersion(VK_API_VERSION_1_3); |
| AddRequiredFeature(vkt::Feature::rayTracingPipelineTraceRaysIndirect); |
| AddRequiredFeature(vkt::Feature::synchronization2); |
| RETURN_IF_SKIP(InitRayTracing()); |
| |
| // Create TLAS (but not build it yet) |
| std::unique_ptr<vkt::as::BuildGeometryInfoKHR> blas = BuildBLAS(); |
| vkt::as::BuildGeometryInfoKHR tlas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceTopLevel(*m_device, *blas->GetDstAS()); |
| tlas.SetupBuild(true); |
| |
| std::unique_ptr<vkt::rt::Pipeline> pipeline = GetTraceRaysPipeline(tlas.GetDstAS()->handle()); |
| const vkt::rt::TraceRaysSbt trace_rays_sbt = pipeline->GetTraceRaysSbt(); |
| |
| VkMemoryBarrier2 barrier = vku::InitStructHelper(); |
| barrier.srcStageMask = VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR; |
| barrier.srcAccessMask = VK_ACCESS_2_ACCELERATION_STRUCTURE_WRITE_BIT_KHR; |
| barrier.dstStageMask = VK_PIPELINE_STAGE_2_RAY_TRACING_SHADER_BIT_KHR; |
| barrier.dstAccessMask = VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR; |
| |
| vkt::Buffer indirect_buffer(*m_device, 3 * sizeof(uint32_t), VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT, vkt::device_address); |
| |
| 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); |
| tlas.VkCmdBuildAccelerationStructuresKHR(m_command_buffer); |
| m_command_buffer.Barrier(barrier); |
| vk::CmdTraceRaysIndirectKHR(m_command_buffer, &trace_rays_sbt.ray_gen_sbt, &trace_rays_sbt.miss_sbt, &trace_rays_sbt.hit_sbt, |
| &trace_rays_sbt.callable_sbt, indirect_buffer.Address()); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(PositiveSyncValRayTracing, TraceAfterBuildIndirect2) { |
| TEST_DESCRIPTION("Build TLAS and trace rays"); |
| SetTargetApiVersion(VK_API_VERSION_1_3); |
| AddRequiredExtensions(VK_KHR_RAY_TRACING_MAINTENANCE_1_EXTENSION_NAME); |
| AddRequiredFeature(vkt::Feature::rayTracingPipelineTraceRaysIndirect2); |
| AddRequiredFeature(vkt::Feature::synchronization2); |
| RETURN_IF_SKIP(InitRayTracing()); |
| |
| // Create TLAS (but not build it yet) |
| std::unique_ptr<vkt::as::BuildGeometryInfoKHR> blas = BuildBLAS(); |
| vkt::as::BuildGeometryInfoKHR tlas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceTopLevel(*m_device, *blas->GetDstAS()); |
| tlas.SetupBuild(true); |
| |
| std::unique_ptr<vkt::rt::Pipeline> pipeline = GetTraceRaysPipeline(tlas.GetDstAS()->handle()); |
| |
| VkMemoryBarrier2 barrier = vku::InitStructHelper(); |
| barrier.srcStageMask = VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR; |
| barrier.srcAccessMask = VK_ACCESS_2_ACCELERATION_STRUCTURE_WRITE_BIT_KHR; |
| barrier.dstStageMask = VK_PIPELINE_STAGE_2_RAY_TRACING_SHADER_BIT_KHR; |
| barrier.dstAccessMask = VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR; |
| |
| vkt::Buffer indirect_buffer(*m_device, 1024, VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT, vkt::device_address); |
| |
| 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); |
| tlas.VkCmdBuildAccelerationStructuresKHR(m_command_buffer); |
| m_command_buffer.Barrier(barrier); |
| vk::CmdTraceRaysIndirect2KHR(m_command_buffer, indirect_buffer.Address()); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(PositiveSyncValRayTracing, RayQueryAfterBuild) { |
| TEST_DESCRIPTION("Build TLAS and trace rays"); |
| SetTargetApiVersion(VK_API_VERSION_1_3); |
| AddRequiredExtensions(VK_KHR_RAY_QUERY_EXTENSION_NAME); |
| AddRequiredFeature(vkt::Feature::synchronization2); |
| AddRequiredFeature(vkt::Feature::rayQuery); |
| RETURN_IF_SKIP(InitRayTracing()); |
| |
| // Create TLAS (but not build it yet) |
| std::unique_ptr<vkt::as::BuildGeometryInfoKHR> blas = BuildBLAS(); |
| vkt::as::BuildGeometryInfoKHR tlas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceTopLevel(*m_device, *blas->GetDstAS()); |
| tlas.SetupBuild(true); |
| |
| CreateComputePipelineHelper pipeline = GetRayQueryComputePipeline(tlas.GetDstAS()->handle()); |
| |
| VkBufferMemoryBarrier2 barrier = vku::InitStructHelper(); |
| barrier.srcStageMask = VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR; |
| barrier.srcAccessMask = VK_ACCESS_2_ACCELERATION_STRUCTURE_WRITE_BIT_KHR; |
| barrier.dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT; |
| barrier.dstAccessMask = VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR; |
| barrier.buffer = tlas.GetDstAS()->GetBuffer(); |
| barrier.offset = 0; |
| barrier.size = tlas.GetDstAS()->GetBuffer().CreateInfo().size; |
| |
| m_command_buffer.Begin(); |
| vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); |
| vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline.pipeline_layout_, 0, 1, |
| &pipeline.descriptor_set_.set_, 0, nullptr); |
| |
| tlas.VkCmdBuildAccelerationStructuresKHR(m_command_buffer); |
| m_command_buffer.Barrier(barrier); |
| vk::CmdDispatch(m_command_buffer, 1, 1, 1); |
| |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(PositiveSyncValRayTracing, WriteIndexDataThenBuild) { |
| TEST_DESCRIPTION("Test that a barrier protects index data writes from subsequent acceleration structure build accesses"); |
| SetTargetApiVersion(VK_API_VERSION_1_3); |
| AddRequiredFeature(vkt::Feature::synchronization2); |
| RETURN_IF_SKIP(InitRayTracing()); |
| |
| auto geometry = vkt::as::blueprint::GeometrySimpleOnDeviceIndexedTriangleInfo(*m_device, 1, VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| auto blas = vkt::as::blueprint::BuildGeometryInfoOnDeviceBottomLevel(*m_device, std::move(geometry)); |
| const auto& triangles_geometry = blas.GetGeometries()[0].GetTriangles(); |
| const vkt::Buffer& index_buffer = triangles_geometry.device_index_buffer; |
| blas.SetupBuild(true); |
| |
| vkt::Buffer src_buffer(*m_device, index_buffer.CreateInfo().size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT); |
| |
| VkBufferMemoryBarrier2 barrier = vku::InitStructHelper(); |
| barrier.srcStageMask = VK_PIPELINE_STAGE_2_COPY_BIT; |
| barrier.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT; |
| barrier.dstStageMask = VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR; |
| barrier.dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT; |
| barrier.buffer = index_buffer; |
| barrier.offset = 0; |
| barrier.size = index_buffer.CreateInfo().size; |
| |
| m_command_buffer.Begin(); |
| m_command_buffer.Copy(src_buffer, index_buffer); |
| m_command_buffer.Barrier(barrier); |
| blas.VkCmdBuildAccelerationStructuresKHR(m_command_buffer); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(PositiveSyncValRayTracing, InvalidMaxVertexValue) { |
| // https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/9810 |
| TEST_DESCRIPTION("Test invalid maxVertex does not break internal tracking"); |
| RETURN_IF_SKIP(InitRayTracing()); |
| |
| vkt::as::BuildGeometryInfoKHR blas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device); |
| |
| // SyncVal operates with the assumption that API usage is valid according to core validation. |
| // For invalid input synval does basic checks to prevent crashes but usually does not do much above that. |
| // For out of bounds buffer access syncval does range clamp to avoid false positives but otherwise |
| // relies on the core checks to report such issues. |
| // |
| // Test that large maxVertex value does not cause invalid internal tracking when the range of AS geometry |
| // data overlaps with another resource tracked by syncval (in this case it's a regular buffer). |
| // In the case of regression it's possible to have false positive when access to one resource can be tracked |
| // as access to another resource (completed unrelated). |
| blas.GetGeometries()[0].SetTrianglesMaxVertex(vvl::kU32Max - 10000); |
| |
| vkt::Buffer buffer(*m_device, 1024, VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| |
| m_command_buffer.Begin(); |
| vk::CmdFillBuffer(m_command_buffer, buffer, 0, VK_WHOLE_SIZE, 0x314159); |
| blas.BuildCmdBuffer(m_command_buffer); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(PositiveSyncValRayTracing, CopyAS) { |
| TEST_DESCRIPTION("Clone acceleration structure"); |
| RETURN_IF_SKIP(InitRayTracing()); |
| |
| std::unique_ptr<vkt::as::BuildGeometryInfoKHR> blas_src = BuildBLAS(); |
| |
| vkt::as::BuildGeometryInfoKHR blas_dst = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device); |
| blas_dst.SetupBuild(true); |
| |
| VkCopyAccelerationStructureInfoKHR copy_info = vku::InitStructHelper(); |
| copy_info.src = blas_src->GetDstAS()->handle(); |
| copy_info.dst = blas_dst.GetDstAS()->handle(); |
| copy_info.mode = VK_COPY_ACCELERATION_STRUCTURE_MODE_CLONE_KHR; |
| |
| m_command_buffer.Begin(); |
| vk::CmdCopyAccelerationStructureKHR(m_command_buffer, ©_info); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(PositiveSyncValRayTracing, CompactAS) { |
| TEST_DESCRIPTION("Compact acceleration structure"); |
| RETURN_IF_SKIP(InitRayTracing()); |
| |
| vkt::as::BuildGeometryInfoKHR blas_src = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device); |
| blas_src.AddFlags(VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_KHR); |
| blas_src.SetupBuild(true); |
| m_command_buffer.Begin(); |
| blas_src.VkCmdBuildAccelerationStructuresKHR(m_command_buffer); |
| m_command_buffer.End(); |
| m_default_queue->SubmitAndWait(m_command_buffer); |
| |
| vkt::as::BuildGeometryInfoKHR blas_dst = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device); |
| blas_dst.SetupBuild(true); |
| |
| VkCopyAccelerationStructureInfoKHR copy_info = vku::InitStructHelper(); |
| copy_info.src = blas_src.GetDstAS()->handle(); |
| copy_info.dst = blas_dst.GetDstAS()->handle(); |
| copy_info.mode = VK_COPY_ACCELERATION_STRUCTURE_MODE_COMPACT_KHR; |
| |
| m_command_buffer.Begin(); |
| vk::CmdCopyAccelerationStructureKHR(m_command_buffer, ©_info); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(PositiveSyncValRayTracing, CopyASWithBarrier) { |
| TEST_DESCRIPTION("Use barrier to protect AS copy accesses"); |
| SetTargetApiVersion(VK_API_VERSION_1_3); |
| AddRequiredExtensions(VK_KHR_RAY_TRACING_MAINTENANCE_1_EXTENSION_NAME); |
| AddRequiredFeature(vkt::Feature::rayTracingMaintenance1); |
| AddRequiredFeature(vkt::Feature::synchronization2); |
| RETURN_IF_SKIP(InitRayTracing()); |
| |
| vkt::as::BuildGeometryInfoKHR blas_src = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device); |
| blas_src.GetDstAS()->SetBufferUsageFlags(VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | |
| VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| blas_src.SetupBuild(true); |
| m_command_buffer.Begin(); |
| blas_src.VkCmdBuildAccelerationStructuresKHR(m_command_buffer); |
| m_command_buffer.End(); |
| m_default_queue->SubmitAndWait(m_command_buffer); |
| |
| vkt::as::BuildGeometryInfoKHR blas_dst = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device); |
| blas_dst.SetupBuild(true); |
| |
| VkCopyAccelerationStructureInfoKHR copy_info = vku::InitStructHelper(); |
| copy_info.src = blas_src.GetDstAS()->handle(); |
| copy_info.dst = blas_dst.GetDstAS()->handle(); |
| copy_info.mode = VK_COPY_ACCELERATION_STRUCTURE_MODE_CLONE_KHR; |
| |
| const vkt::Buffer& blas_src_buffer = blas_src.GetDstAS()->GetBuffer(); |
| vkt::Buffer buffer(*m_device, blas_src_buffer.CreateInfo().size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT); |
| |
| VkMemoryBarrier2 barrier = vku::InitStructHelper(); |
| barrier.srcStageMask = VK_PIPELINE_STAGE_2_COPY_BIT; |
| barrier.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT; |
| barrier.dstStageMask = VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_COPY_BIT_KHR; |
| barrier.dstAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR; |
| |
| m_command_buffer.Begin(); |
| m_command_buffer.Copy(buffer, blas_src_buffer); |
| m_command_buffer.Barrier(barrier); // Prevent READ-AFTER-WRITE |
| vk::CmdCopyAccelerationStructureKHR(m_command_buffer, ©_info); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(PositiveSyncValRayTracing, CopyAndBuildStageEquivalence) { |
| TEST_DESCRIPTION("Test that both COPY and BUILD stages can be used to sync AS copy"); |
| SetTargetApiVersion(VK_API_VERSION_1_3); |
| AddRequiredExtensions(VK_KHR_RAY_TRACING_MAINTENANCE_1_EXTENSION_NAME); |
| AddRequiredFeature(vkt::Feature::rayTracingMaintenance1); |
| AddRequiredFeature(vkt::Feature::synchronization2); |
| RETURN_IF_SKIP(InitRayTracing()); |
| |
| vkt::as::BuildGeometryInfoKHR blas_src = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device); |
| blas_src.GetDstAS()->SetBufferUsageFlags(VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | |
| VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| blas_src.SetupBuild(true); |
| m_command_buffer.Begin(); |
| blas_src.VkCmdBuildAccelerationStructuresKHR(m_command_buffer); |
| m_command_buffer.End(); |
| m_default_queue->SubmitAndWait(m_command_buffer); |
| const vkt::Buffer& blas_src_buffer = blas_src.GetDstAS()->GetBuffer(); |
| |
| vkt::as::BuildGeometryInfoKHR blas_dst = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device); |
| blas_dst.GetDstAS()->SetBufferUsageFlags(VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | |
| VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| blas_dst.SetupBuild(true); |
| const vkt::Buffer& blas_dst_buffer = blas_dst.GetDstAS()->GetBuffer(); |
| |
| VkCopyAccelerationStructureInfoKHR copy_info = vku::InitStructHelper(); |
| copy_info.src = blas_src.GetDstAS()->handle(); |
| copy_info.dst = blas_dst.GetDstAS()->handle(); |
| copy_info.mode = VK_COPY_ACCELERATION_STRUCTURE_MODE_CLONE_KHR; |
| |
| vkt::Buffer buffer(*m_device, blas_src_buffer.CreateInfo().size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT); |
| |
| // Use ACCELERATION_STRUCTURE_BUILD as barrier stage mask. |
| // The spec allows to synchronize AS copy accesses using either ACCELERATION_STRUCTURE_COPY or |
| // ACCELERATION_STRUCTURE_BUILD stages. The test checks that syncval recognizes that |
| // ACCELERATION_STRUCTURE_BUILD barrier can protect ACCELERATION_STRUCTURE_COPY. |
| VkMemoryBarrier2 barrier = vku::InitStructHelper(); |
| barrier.srcStageMask = VK_PIPELINE_STAGE_2_COPY_BIT; |
| barrier.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT; |
| barrier.dstStageMask = VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR; |
| barrier.dstAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR; |
| |
| VkMemoryBarrier2 barrier2 = vku::InitStructHelper(); |
| barrier2.srcStageMask = VK_PIPELINE_STAGE_2_COPY_BIT; |
| barrier2.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT; |
| barrier2.dstStageMask = VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR; |
| barrier2.dstAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR; |
| |
| // Barrier protects read accesses on AS source buffer |
| m_command_buffer.Begin(); |
| m_command_buffer.Copy(buffer, blas_src_buffer); |
| m_command_buffer.Barrier(barrier); // Prevent READ-AFTER-WRITE |
| vk::CmdCopyAccelerationStructureKHR(m_command_buffer, ©_info); |
| m_command_buffer.End(); |
| |
| // Barrier protects write accesses on AS destination buffer |
| m_command_buffer.Begin(); |
| m_command_buffer.Copy(buffer, blas_dst_buffer); |
| m_command_buffer.Barrier(barrier2); // Prevent WRITE-AFTER-WRITE |
| vk::CmdCopyAccelerationStructureKHR(m_command_buffer, ©_info); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(PositiveSyncValRayTracing, CopyAndBuildStageEquivalence2) { |
| TEST_DESCRIPTION("Test that both COPY and BUILD stages can be used to sync AS copy"); |
| SetTargetApiVersion(VK_API_VERSION_1_3); |
| AddRequiredExtensions(VK_KHR_RAY_TRACING_MAINTENANCE_1_EXTENSION_NAME); |
| AddRequiredFeature(vkt::Feature::rayTracingMaintenance1); |
| AddRequiredFeature(vkt::Feature::synchronization2); |
| RETURN_IF_SKIP(InitRayTracing()); |
| |
| vkt::as::BuildGeometryInfoKHR blas_src = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device); |
| blas_src.GetDstAS()->SetBufferUsageFlags(VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | |
| VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| blas_src.SetupBuild(true); |
| m_command_buffer.Begin(); |
| blas_src.VkCmdBuildAccelerationStructuresKHR(m_command_buffer); |
| m_command_buffer.End(); |
| m_default_queue->SubmitAndWait(m_command_buffer); |
| const vkt::Buffer& blas_src_buffer = blas_src.GetDstAS()->GetBuffer(); |
| |
| vkt::as::BuildGeometryInfoKHR blas_dst = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device); |
| blas_dst.GetDstAS()->SetBufferUsageFlags(VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | |
| VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| blas_dst.SetupBuild(true); |
| const vkt::Buffer& blas_dst_buffer = blas_dst.GetDstAS()->GetBuffer(); |
| |
| VkCopyAccelerationStructureInfoKHR copy_info = vku::InitStructHelper(); |
| copy_info.src = blas_src.GetDstAS()->handle(); |
| copy_info.dst = blas_dst.GetDstAS()->handle(); |
| copy_info.mode = VK_COPY_ACCELERATION_STRUCTURE_MODE_CLONE_KHR; |
| |
| vkt::Buffer buffer(*m_device, blas_src_buffer.CreateInfo().size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT); |
| |
| // Use ACCELERATION_STRUCTURE_BUILD as barrier stage mask. |
| // The spec allows to synchronize AS copy accesses using either ACCELERATION_STRUCTURE_COPY or |
| // ACCELERATION_STRUCTURE_BUILD stages. The test checks that syncval recognizes that |
| // ACCELERATION_STRUCTURE_BUILD barrier can protect ACCELERATION_STRUCTURE_COPY. |
| VkMemoryBarrier2 barrier = vku::InitStructHelper(); |
| barrier.srcStageMask = VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR; |
| barrier.srcAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR; |
| barrier.dstStageMask = VK_PIPELINE_STAGE_2_COPY_BIT; |
| barrier.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT; |
| |
| VkMemoryBarrier2 barrier2 = vku::InitStructHelper(); |
| barrier2.srcStageMask = VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR; |
| barrier2.srcAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR; |
| barrier2.dstStageMask = VK_PIPELINE_STAGE_2_COPY_BIT; |
| barrier2.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT; |
| |
| // Barrier protects read accesses on AS source buffer |
| m_command_buffer.Begin(); |
| vk::CmdCopyAccelerationStructureKHR(m_command_buffer, ©_info); |
| m_command_buffer.Barrier(barrier); // Prevent WRITE-AFTER-READ |
| m_command_buffer.Copy(buffer, blas_src_buffer); |
| m_command_buffer.End(); |
| |
| // Barrier protects write accesses on AS destination buffer |
| m_command_buffer.Begin(); |
| vk::CmdCopyAccelerationStructureKHR(m_command_buffer, ©_info); |
| m_command_buffer.Barrier(barrier2); // Prevent WRITE-AFTER-WRITE |
| m_command_buffer.Copy(buffer, blas_dst_buffer); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(PositiveSyncValRayTracing, SerializeDeserializeAS) { |
| TEST_DESCRIPTION("Serialize AS to a buffer. Then deserialize the buffer into another AS object"); |
| RETURN_IF_SKIP(InitRayTracing()); |
| |
| if (IsPlatformMockICD()) { |
| GTEST_SKIP() << "Test not supported by TestICD: serialization size query"; |
| } |
| |
| // The source blas for serialization |
| std::unique_ptr<vkt::as::BuildGeometryInfoKHR> blas = BuildBLAS(); |
| |
| // Deserialize into blas2 |
| vkt::as::BuildGeometryInfoKHR blas2 = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device); |
| blas2.SetupBuild(true); |
| |
| vkt::Buffer serialization_buffer = GetSerializationDeserializationBuffer(*blas->GetDstAS()); |
| |
| VkCopyAccelerationStructureToMemoryInfoKHR copy_to_memory_info = vku::InitStructHelper(); |
| copy_to_memory_info.src = blas->GetDstAS()->handle(); |
| copy_to_memory_info.dst = VkDeviceOrHostAddressKHR{serialization_buffer.Address()}; |
| copy_to_memory_info.mode = VK_COPY_ACCELERATION_STRUCTURE_MODE_SERIALIZE_KHR; |
| m_command_buffer.Begin(); |
| vk::CmdCopyAccelerationStructureToMemoryKHR(m_command_buffer, ©_to_memory_info); |
| m_command_buffer.End(); |
| m_default_queue->SubmitAndWait(m_command_buffer); |
| |
| VkCopyMemoryToAccelerationStructureInfoKHR copy_from_memory_info = vku::InitStructHelper(); |
| copy_from_memory_info.src = VkDeviceOrHostAddressConstKHR{serialization_buffer.Address()}; |
| copy_from_memory_info.dst = blas2.GetDstAS()->handle(); |
| copy_from_memory_info.mode = VK_COPY_ACCELERATION_STRUCTURE_MODE_DESERIALIZE_KHR; |
| m_command_buffer.Begin(); |
| vk::CmdCopyMemoryToAccelerationStructureKHR(m_command_buffer, ©_from_memory_info); |
| m_command_buffer.End(); |
| m_default_queue->SubmitAndWait(m_command_buffer); |
| } |
| |
| TEST_F(PositiveSyncValRayTracing, SerializeDeserializeASWithBarrier) { |
| TEST_DESCRIPTION("Use barrier to protect accesses to serialize/deserialize buffer"); |
| SetTargetApiVersion(VK_API_VERSION_1_3); |
| AddRequiredExtensions(VK_KHR_RAY_TRACING_MAINTENANCE_1_EXTENSION_NAME); |
| AddRequiredFeature(vkt::Feature::rayTracingMaintenance1); |
| AddRequiredFeature(vkt::Feature::synchronization2); |
| RETURN_IF_SKIP(InitRayTracing()); |
| |
| if (IsPlatformMockICD()) { |
| GTEST_SKIP() << "Test not supported by TestICD: serialization size query"; |
| } |
| |
| // The source blas for serialization |
| std::unique_ptr<vkt::as::BuildGeometryInfoKHR> blas = BuildBLAS(); |
| |
| // Deserialize into blas2 |
| vkt::as::BuildGeometryInfoKHR blas2 = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device); |
| blas2.SetupBuild(true); |
| |
| vkt::Buffer serialization_buffer = GetSerializationDeserializationBuffer(*blas->GetDstAS()); |
| |
| VkBufferMemoryBarrier2 barrier = vku::InitStructHelper(); |
| barrier.srcStageMask = VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_COPY_BIT_KHR; |
| barrier.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT; |
| barrier.dstStageMask = VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_COPY_BIT_KHR; |
| barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; |
| barrier.buffer = serialization_buffer; |
| barrier.size = serialization_buffer.CreateInfo().size; |
| |
| VkCopyAccelerationStructureToMemoryInfoKHR copy_to_memory_info = vku::InitStructHelper(); |
| copy_to_memory_info.src = blas->GetDstAS()->handle(); |
| copy_to_memory_info.dst = VkDeviceOrHostAddressKHR{serialization_buffer.Address()}; |
| copy_to_memory_info.mode = VK_COPY_ACCELERATION_STRUCTURE_MODE_SERIALIZE_KHR; |
| |
| VkCopyMemoryToAccelerationStructureInfoKHR copy_from_memory_info = vku::InitStructHelper(); |
| copy_from_memory_info.src = VkDeviceOrHostAddressConstKHR{serialization_buffer.Address()}; |
| copy_from_memory_info.dst = blas2.GetDstAS()->handle(); |
| copy_from_memory_info.mode = VK_COPY_ACCELERATION_STRUCTURE_MODE_DESERIALIZE_KHR; |
| |
| m_command_buffer.Begin(); |
| vk::CmdCopyAccelerationStructureToMemoryKHR(m_command_buffer, ©_to_memory_info); |
| m_command_buffer.Barrier(barrier); |
| vk::CmdCopyMemoryToAccelerationStructureKHR(m_command_buffer, ©_from_memory_info); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(PositiveSyncValRayTracing, BuildAfterBuildWithMemoryBarrier) { |
| TEST_DESCRIPTION("Build BLAS. Wait for it using memory barrier. Buld BLAS again"); |
| SetTargetApiVersion(VK_API_VERSION_1_3); |
| AddRequiredFeature(vkt::Feature::synchronization2); |
| RETURN_IF_SKIP(InitRayTracing()); |
| |
| vkt::as::BuildGeometryInfoKHR blas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device); |
| blas.GetDstAS()->SetBufferUsageFlags(VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR); |
| blas.SetupBuild(true); |
| |
| VkMemoryBarrier2 barrier = vku::InitStructHelper(); |
| barrier.srcStageMask = VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR; |
| barrier.srcAccessMask = VK_ACCESS_2_ACCELERATION_STRUCTURE_WRITE_BIT_KHR; |
| barrier.dstStageMask = VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR; |
| barrier.dstAccessMask = VK_ACCESS_2_ACCELERATION_STRUCTURE_WRITE_BIT_KHR | VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR; |
| |
| m_command_buffer.Begin(); |
| blas.VkCmdBuildAccelerationStructuresKHR(m_command_buffer); |
| m_command_buffer.Barrier(barrier); |
| blas.VkCmdBuildAccelerationStructuresKHR(m_command_buffer); |
| m_command_buffer.End(); |
| } |
| |
| TEST_F(PositiveSyncValRayTracing, BuildAfterBuildWithBufferBarrier) { |
| TEST_DESCRIPTION("Build BLAS. Wait for it using buffer barrier. Buld BLAS again"); |
| SetTargetApiVersion(VK_API_VERSION_1_3); |
| AddRequiredFeature(vkt::Feature::synchronization2); |
| RETURN_IF_SKIP(InitRayTracing()); |
| |
| vkt::as::BuildGeometryInfoKHR blas = vkt::as::blueprint::BuildGeometryInfoSimpleOnDeviceBottomLevel(*m_device); |
| blas.GetDstAS()->SetBufferUsageFlags(VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR); |
| blas.SetupBuild(true); |
| |
| const vkt::Buffer& accel_buffer = blas.GetDstAS()->GetBuffer(); |
| |
| VkBufferMemoryBarrier2 blas_buffer_barrier = vku::InitStructHelper(); |
| blas_buffer_barrier.srcStageMask = VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR; |
| blas_buffer_barrier.srcAccessMask = VK_ACCESS_2_ACCELERATION_STRUCTURE_WRITE_BIT_KHR; |
| blas_buffer_barrier.dstStageMask = VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR; |
| blas_buffer_barrier.dstAccessMask = |
| VK_ACCESS_2_ACCELERATION_STRUCTURE_WRITE_BIT_KHR | VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR; |
| blas_buffer_barrier.buffer = accel_buffer; |
| blas_buffer_barrier.offset = 0; |
| blas_buffer_barrier.size = VK_WHOLE_SIZE; |
| |
| VkBufferMemoryBarrier2 scratch_buffer_barrier = vku::InitStructHelper(); |
| scratch_buffer_barrier.srcStageMask = VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR; |
| scratch_buffer_barrier.srcAccessMask = VK_ACCESS_2_ACCELERATION_STRUCTURE_WRITE_BIT_KHR; |
| scratch_buffer_barrier.dstStageMask = VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR; |
| scratch_buffer_barrier.dstAccessMask = |
| VK_ACCESS_2_ACCELERATION_STRUCTURE_WRITE_BIT_KHR | VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR; |
| scratch_buffer_barrier.buffer = *blas.GetScratchBuffer(); |
| scratch_buffer_barrier.offset = 0; |
| scratch_buffer_barrier.size = VK_WHOLE_SIZE; |
| |
| const VkBufferMemoryBarrier2 buffer_barriers[2] = {blas_buffer_barrier, scratch_buffer_barrier}; |
| |
| VkDependencyInfo dep_info = vku::InitStructHelper(); |
| dep_info.bufferMemoryBarrierCount = 2; |
| dep_info.pBufferMemoryBarriers = buffer_barriers; |
| |
| m_command_buffer.Begin(); |
| blas.VkCmdBuildAccelerationStructuresKHR(m_command_buffer); |
| m_command_buffer.Barrier(dep_info); |
| blas.VkCmdBuildAccelerationStructuresKHR(m_command_buffer); |
| m_command_buffer.End(); |
| } |