//SPDX-License-Identifier: LGPL-3.0-only //SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® // The dependencies RenderingElement3D::BuildTLAS records around its build. // // After it, the build is ordered before the shaders that read the TLAS: // traceRayEXT in a ray-tracing pass, rayQueryEXT in a compute pass, recorded // after it in the same command buffer (BuildTLAS in onUpdate, then RTPass, is // the documented per-frame flow). It used to be an execution-only barrier with // no memory barrier, which orders the commands but never makes the build's // acceleration-structure writes available and visible to those reads. // // Before it, the build is ordered after the earlier work it must follow. That // used to cover only earlier acceleration-structure builds, so a refit, or a // rebuild into the same storage, recorded after a trace or a compute ray query // of the TLAS could overwrite it while they still read it. // // The dependencies are built by BuildTlasWriteBarrier and BuildTlasReadBarrier, // pure CPU logic that this test drives directly with no GPU device, like // SwapchainBarrierScope. #include #include "vulkan/vulkan.h" import Crafter.Graphics; import std; using namespace Crafter; namespace { int failures = 0; void Check(bool ok, std::string_view what) { std::println("{} {}", ok ? "PASS" : "FAIL", what); if (!ok) ++failures; } } // namespace int main() { const TlasBarrier before = BuildTlasWriteBarrier(); Check(before.dstStage == VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, "the dependency before the build ends at the acceleration-structure build"); Check((before.srcStage & VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR) != 0, "it follows earlier builds (the BLASes it reads, an earlier build of this TLAS)"); Check((before.srcStage & VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR) != 0, "and earlier ray-tracing shaders (traceRayEXT reading the TLAS)"); Check((before.srcStage & VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT) != 0, "and earlier compute shaders (rayQueryEXT reading the TLAS)"); Check(before.memory.sType == VK_STRUCTURE_TYPE_MEMORY_BARRIER, "it carries a memory barrier"); Check(before.memory.srcAccessMask == VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR, "earlier builds' acceleration-structure writes are made available, and no reads " "(a read bit does nothing but trip synchronization validation)"); constexpr VkAccessFlags kBuildAccess = VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR | VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR; Check((before.memory.dstAccessMask & kBuildAccess) == kBuildAccess, "and visible to the build's reads and writes"); const TlasBarrier after = BuildTlasReadBarrier(); Check(after.srcStage == VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, "the dependency after the build starts at the acceleration-structure build"); Check((after.dstStage & VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR) != 0, "it reaches ray-tracing shaders (traceRayEXT)"); Check((after.dstStage & VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT) != 0, "it reaches compute shaders (rayQueryEXT)"); Check(after.memory.sType == VK_STRUCTURE_TYPE_MEMORY_BARRIER, "it carries a memory barrier"); Check((after.memory.srcAccessMask & VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR) != 0, "the build's acceleration-structure writes are made available"); Check((after.memory.dstAccessMask & VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR) != 0, "and visible to acceleration-structure reads"); std::println("{}", failures == 0 ? "all checks passed" : "check(s) failed"); return failures == 0 ? EXIT_SUCCESS : EXIT_FAILURE; }