From 824436e67c181f07664fc99340191ef7911e30a9 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Mon, 19 Oct 2026 03:03:16 +0200 Subject: [PATCH] fix(rt): make BuildTLAS's read barrier a memory dependency BuildTLAS ended with an execution-only barrier from the acceleration-structure build to the ray-tracing and compute shader stages. That orders the commands, but without a memory barrier the build's writes are never made available and visible to the traceRayEXT and rayQueryEXT reads recorded after it in the same command buffer, which is the documented per-frame flow (BuildTLAS in onUpdate, then RTPass): the trace may read a stale or half-written BVH. The barrier now carries ACCELERATION_STRUCTURE_WRITE -> ACCELERATION_STRUCTURE_READ. BuildTlasReadBarrier builds it as pure CPU logic, so a test can check it without a GPU, as with the swapchain barriers. Co-Authored-By: Claude Opus 5.5 --- .../Crafter.Graphics-RenderingElement3D.cpp | 11 ++------- .../Crafter.Graphics-RenderingElement3D.cppm | 23 +++++++++++++++++++ 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/implementations/Crafter.Graphics-RenderingElement3D.cpp b/implementations/Crafter.Graphics-RenderingElement3D.cpp index 9622c94..e1754aa 100644 --- a/implementations/Crafter.Graphics-RenderingElement3D.cpp +++ b/implementations/Crafter.Graphics-RenderingElement3D.cpp @@ -295,13 +295,6 @@ void RenderingElement3D::BuildTLAS(VkCommandBuffer cmd, std::uint32_t index, RTB VkAccelerationStructureBuildRangeInfoKHR* tlasRangeInfoPP = &tlasRangeInfo; Device::vkCmdBuildAccelerationStructuresKHR(cmd, 1, &tlasBuildGeometryInfo, &tlasRangeInfoPP); - vkCmdPipelineBarrier( - cmd, - VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, - VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - 0, - 0, nullptr, - 0, nullptr, - 0, nullptr - ); + const TlasReadBarrier readBarrier = BuildTlasReadBarrier(); + vkCmdPipelineBarrier(cmd, readBarrier.srcStage, readBarrier.dstStage, 0, 1, &readBarrier.memory, 0, nullptr, 0, nullptr); } \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-RenderingElement3D.cppm b/interfaces/Crafter.Graphics-RenderingElement3D.cppm index f350aa3..dee9ebf 100644 --- a/interfaces/Crafter.Graphics-RenderingElement3D.cppm +++ b/interfaces/Crafter.Graphics-RenderingElement3D.cppm @@ -134,6 +134,29 @@ export namespace Crafter { static void Add(RenderingElement3D* e); static void Remove(RenderingElement3D* e); }; + + // The dependency BuildTLAS records after its build, ordering it before + // the shaders that read the TLAS later in the same command buffer: + // traceRayEXT in a ray-tracing pass, rayQueryEXT in a compute pass. It + // has to carry the acceleration-structure memory dependency; ordering + // execution alone never makes the build's writes visible to those reads. + struct TlasReadBarrier { + VkPipelineStageFlags srcStage; + VkPipelineStageFlags dstStage; + VkMemoryBarrier memory; + }; + + inline TlasReadBarrier BuildTlasReadBarrier() { + return { + .srcStage = VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, + .dstStage = VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + .memory = { + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, + .srcAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR, + .dstAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR, + }, + }; + } } #endif // !CRAFTER_GRAPHICS_WINDOW_DOM