fix(vulkan): tear the allocator down under validation so the exit is clean

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.
This commit is contained in:
Kelsi
2026-07-31 08:31:08 -07:00
parent d36ec92153
commit 2cc909b2aa
2 changed files with 21 additions and 5 deletions

View File

@@ -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

View File

@@ -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) {