diff --git a/interfaces/Crafter.Graphics-RenderingElement3D.cppm b/interfaces/Crafter.Graphics-RenderingElement3D.cppm index dee9ebf..576736a 100644 --- a/interfaces/Crafter.Graphics-RenderingElement3D.cppm +++ b/interfaces/Crafter.Graphics-RenderingElement3D.cppm @@ -115,7 +115,14 @@ export namespace Crafter { inline static std::uint64_t hostDataVersionCounter = 0; static std::vector elements; - inline static TlasWithBuffer tlases[Window::numFrames]; + // Never destroyed, like Device::device. As a plain static its + // destructor would run at process exit after the Vulkan driver and + // layers, which are loaded during Device::Initialize (after this was + // constructed), have torn down their own state, and destroying the + // buffers would call into that freed state: the validation layer + // segfaults on it. Exit reclaims them instead. + inline static std::array& tlases = + *new std::array(); // Build (or in-place refit) the TLAS for frame `index`. `preference` // maps onto the FAST_TRACE/FAST_BUILD bits exactly as Mesh::Build // does for a BLAS: FastTrace (the default) spends more build time for diff --git a/tests/TLASHighWaterMark/main.cpp b/tests/TLASHighWaterMark/main.cpp index 99174f4..746a66e 100644 --- a/tests/TLASHighWaterMark/main.cpp +++ b/tests/TLASHighWaterMark/main.cpp @@ -351,22 +351,6 @@ int main() { Check(Device::validationErrorCount == 0, std::format("no Vulkan validation errors ({} seen)", Device::validationErrorCount)); - // Tear down the frame-0 TLAS while the Vulkan device is still alive. The - // static tlases[] array is destroyed at process exit, by which point the - // device (also static) may already be gone — so its VulkanBuffer - // destructors would fault. Releasing here keeps the exit path clean. - { - auto& t0 = RenderingElement3D::tlases[0]; - if (t0.accelerationStructure != VK_NULL_HANDLE) { - Device::vkDestroyAccelerationStructureKHR(Device::device, t0.accelerationStructure, nullptr); - t0.accelerationStructure = VK_NULL_HANDLE; - } - if (t0.instanceBuffer.buffer != VK_NULL_HANDLE) t0.instanceBuffer.Clear(); - if (t0.metadataBuffer.buffer != VK_NULL_HANDLE) t0.metadataBuffer.Clear(); - if (t0.scratchBuffer.buffer != VK_NULL_HANDLE) t0.scratchBuffer.Clear(); - if (t0.buffer.buffer != VK_NULL_HANDLE) t0.buffer.Clear(); - } - if (failures != 0) { std::println("{} check(s) failed", failures); return EXIT_FAILURE; diff --git a/tests/TLASInstanceDirtyTracking/main.cpp b/tests/TLASInstanceDirtyTracking/main.cpp index 4ba711a..b2526fc 100644 --- a/tests/TLASInstanceDirtyTracking/main.cpp +++ b/tests/TLASInstanceDirtyTracking/main.cpp @@ -213,20 +213,6 @@ int main() { Check(Device::validationErrorCount == 0, std::format("no Vulkan validation errors ({} seen)", Device::validationErrorCount)); - // Tear down the frame-0 TLAS while the device is still alive (same rationale - // as TLASHighWaterMark — the static tlases[] outlive the static device). - { - auto& t0 = RenderingElement3D::tlases[0]; - if (t0.accelerationStructure != VK_NULL_HANDLE) { - Device::vkDestroyAccelerationStructureKHR(Device::device, t0.accelerationStructure, nullptr); - t0.accelerationStructure = VK_NULL_HANDLE; - } - if (t0.instanceBuffer.buffer != VK_NULL_HANDLE) t0.instanceBuffer.Clear(); - if (t0.metadataBuffer.buffer != VK_NULL_HANDLE) t0.metadataBuffer.Clear(); - if (t0.scratchBuffer.buffer != VK_NULL_HANDLE) t0.scratchBuffer.Clear(); - if (t0.buffer.buffer != VK_NULL_HANDLE) t0.buffer.Clear(); - } - if (failures != 0) { std::println("{} check(s) failed", failures); return EXIT_FAILURE;