test(rt): BuildTLAS's read barrier makes the build visible to TLAS reads
TLASBuildBarrier drives BuildTlasReadBarrier with no GPU device: the dependency starts at the acceleration-structure build, reaches the ray-tracing and compute shader stages, and carries the acceleration-structure write -> read memory dependency that the old execution-only barrier lacked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
824436e67c
commit
5d25c1f825
2 changed files with 77 additions and 0 deletions
26
project.cpp
26
project.cpp
|
|
@ -502,6 +502,32 @@ extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> a
|
||||||
tlc.GetInterfacesAndImplementations(ifaces, tlasImpls);
|
tlc.GetInterfacesAndImplementations(ifaces, tlasImpls);
|
||||||
cfg.tests.push_back(std::move(tlasTest));
|
cfg.tests.push_back(std::move(tlasTest));
|
||||||
|
|
||||||
|
// The barrier BuildTLAS records after the build carries the
|
||||||
|
// acceleration-structure memory dependency to the shaders that read
|
||||||
|
// the TLAS, not just execution order. BuildTlasReadBarrier is pure
|
||||||
|
// CPU logic, so no GPU device is needed at runtime.
|
||||||
|
Test tlasBarrierTest;
|
||||||
|
Configuration& tbc = tlasBarrierTest.config;
|
||||||
|
tbc.path = cfg.path;
|
||||||
|
tbc.name = "TLASBuildBarrier";
|
||||||
|
tbc.outputName = "TLASBuildBarrier";
|
||||||
|
tbc.type = ConfigurationType::Executable;
|
||||||
|
tbc.target = cfg.target;
|
||||||
|
tbc.march = cfg.march;
|
||||||
|
tbc.mtune = cfg.mtune;
|
||||||
|
tbc.debug = cfg.debug;
|
||||||
|
tbc.sysroot = cfg.sysroot;
|
||||||
|
tbc.dependencies = cfg.dependencies;
|
||||||
|
tbc.externalDependencies = cfg.externalDependencies;
|
||||||
|
tbc.compileFlags = cfg.compileFlags;
|
||||||
|
tbc.linkFlags = cfg.linkFlags;
|
||||||
|
tbc.defines = cfg.defines;
|
||||||
|
tbc.cFiles = cfg.cFiles;
|
||||||
|
std::vector<fs::path> tlasBarrierImpls(impls.begin(), impls.end());
|
||||||
|
tlasBarrierImpls.emplace_back("tests/TLASBuildBarrier/main");
|
||||||
|
tbc.GetInterfacesAndImplementations(ifaces, tlasBarrierImpls);
|
||||||
|
cfg.tests.push_back(std::move(tlasBarrierTest));
|
||||||
|
|
||||||
// Issue #118: the per-frame TLAS instance+metadata host rebuild copies
|
// Issue #118: the per-frame TLAS instance+metadata host rebuild copies
|
||||||
// (and flushes) only the slots whose element's host-authored data
|
// (and flushes) only the slots whose element's host-authored data
|
||||||
// changed, tracked via RenderingElement3D::hostDataVersion against the
|
// changed, tracked via RenderingElement3D::hostDataVersion against the
|
||||||
|
|
|
||||||
51
tests/TLASBuildBarrier/main.cpp
Normal file
51
tests/TLASBuildBarrier/main.cpp
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
//SPDX-License-Identifier: LGPL-3.0-only
|
||||||
|
//SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||||
|
|
||||||
|
// The dependency RenderingElement3D::BuildTLAS records after its build, which
|
||||||
|
// orders the build 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. The
|
||||||
|
// dependency is built by BuildTlasReadBarrier, pure CPU logic that this test
|
||||||
|
// drives directly with no GPU device, like SwapchainBarrierScope.
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#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 TlasReadBarrier barrier = BuildTlasReadBarrier();
|
||||||
|
|
||||||
|
Check(barrier.srcStage == VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR,
|
||||||
|
"the dependency starts at the acceleration-structure build");
|
||||||
|
Check((barrier.dstStage & VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR) != 0,
|
||||||
|
"it reaches ray-tracing shaders (traceRayEXT)");
|
||||||
|
Check((barrier.dstStage & VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT) != 0,
|
||||||
|
"it reaches compute shaders (rayQueryEXT)");
|
||||||
|
|
||||||
|
Check(barrier.memory.sType == VK_STRUCTURE_TYPE_MEMORY_BARRIER, "it carries a memory barrier");
|
||||||
|
Check((barrier.memory.srcAccessMask & VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR) != 0,
|
||||||
|
"the build's acceleration-structure writes are made available");
|
||||||
|
Check((barrier.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;
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue