From 2cc909b2aa894c00f656d4e5891a8b6e221ed161 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Fri, 31 Jul 2026 08:31:08 -0700 Subject: [PATCH] fix(vulkan): tear the allocator down under validation so the exit is clean MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VkContext::shutdown deliberately skips vmaDestroyAllocator, because walking every allocation costs seconds with thousands of loaded textures and models, and the driver reclaims device memory anyway. The cost is that everything the caches still hold is reported one object at a time at vkDestroyDevice: ninety thousand errors in a single run, which buries any real problem the layers find — the rendering faults fixed earlier today were needles in exactly this haystack. Keep the fast path for players and destroy the allocator properly when the layers are active. A few seconds on the way out buys a validation signal that can be read, and a genuine leak now stands out instead of hiding in the noise. --- include/rendering/vk_context.hpp | 2 ++ src/rendering/vk_context.cpp | 24 +++++++++++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/include/rendering/vk_context.hpp b/include/rendering/vk_context.hpp index f4e1c0db..cea58b58 100644 --- a/include/rendering/vk_context.hpp +++ b/include/rendering/vk_context.hpp @@ -307,6 +307,8 @@ private: #else bool enableValidation = false; #endif + // Whether the layers actually came up this run, including via the env var. + bool validationActive_ = false; }; } // namespace rendering diff --git a/src/rendering/vk_context.cpp b/src/rendering/vk_context.cpp index 9504a4cd..fb12b539 100644 --- a/src/rendering/vk_context.cpp +++ b/src/rendering/vk_context.cpp @@ -170,11 +170,24 @@ void VkContext::shutdown() { LOG_DEBUG("VkContext::shutdown - destroySwapchain..."); destroySwapchain(); - // Skip vmaDestroyAllocator — it walks every allocation to free it, which - // takes many seconds with thousands of loaded textures/models. The driver - // reclaims all device memory when we destroy the device, and the OS reclaims - // everything on process exit. Skipping this makes shutdown instant. - allocator = VK_NULL_HANDLE; + // Normally skip vmaDestroyAllocator: it walks every allocation to free it, + // which takes many seconds with thousands of loaded textures and models. The + // driver reclaims all device memory when the device is destroyed and the OS + // reclaims the rest at process exit, so skipping it makes shutdown instant. + // + // Under validation, tear it down properly. Whatever the caches still hold is + // otherwise reported object by object at vkDestroyDevice — ninety thousand + // errors in one run — and that flood buries any real problem the layers find. + // Paying a few seconds on the way out is worth a usable validation signal, + // and it also means a genuine leak still shows up rather than hiding in the + // noise. Players never take this path. + if (allocator) { + if (validationActive_) { + LOG_INFO("Validation active — destroying VMA allocator (slow, but keeps the exit clean)"); + vmaDestroyAllocator(allocator); + } + allocator = VK_NULL_HANDLE; + } LOG_DEBUG("VkContext::shutdown - vkDestroyDevice..."); if (device) { vkDestroyDevice(device, nullptr); device = VK_NULL_HANDLE; } @@ -291,6 +304,7 @@ bool VkContext::createInstance(SDL_Window* window) { .set_debug_callback(debugCallback); LOG_INFO("Vulkan validation layers requested"); } + validationActive_ = enableValidationEffective; auto instRet = builder.build(); if (!instRet) {