From fdb8835a3c415ac3bcea338299aecf8c27df7455 Mon Sep 17 00:00:00 2001 From: hydrogendeuteride Date: Mon, 24 Nov 2025 14:46:36 +0900 Subject: [PATCH] FIX: position swapchain to RGBA32F(RT shadow quality in far dist), memory allocation errors --- src/core/ibl_manager.cpp | 13 +++++++++---- src/core/vk_device.cpp | 28 +++++++++++++++++++-------- src/core/vk_engine.cpp | 8 ++++++++ src/core/vk_swapchain.cpp | 2 +- src/render/vk_renderpass_lighting.cpp | 14 ++++++++++++++ src/scene/vk_scene.cpp | 10 ++++++++++ 6 files changed, 62 insertions(+), 13 deletions(-) diff --git a/src/core/ibl_manager.cpp b/src/core/ibl_manager.cpp index dc0186c..0ec160d 100644 --- a/src/core/ibl_manager.cpp +++ b/src/core/ibl_manager.cpp @@ -255,15 +255,20 @@ void IBLManager::unload() if (_spec.image) { rm->destroy_image(_spec); - _spec = {}; } - if (_diff.image && _diff.image != _spec.image) { rm->destroy_image(_diff); } - _diff = {}; + // Handle potential aliasing: _diff may have been set to _spec in load(). + if (_diff.image && _diff.image != _spec.image) + { + rm->destroy_image(_diff); + } if (_brdf.image) { rm->destroy_image(_brdf); - _brdf = {}; } + + _spec = {}; + _diff = {}; + _brdf = {}; if (_iblSetLayout && _ctx && _ctx->getDevice()) { vkDestroyDescriptorSetLayout(_ctx->getDevice()->device(), _iblSetLayout, nullptr); diff --git a/src/core/vk_device.cpp b/src/core/vk_device.cpp index f39c124..81a54ca 100644 --- a/src/core/vk_device.cpp +++ b/src/core/vk_device.cpp @@ -121,17 +121,29 @@ void DeviceManager::init_vulkan(SDL_Window *window) void DeviceManager::cleanup() { - // Optional VMA stats print - if (_allocator && vmaDebugEnabled()) + // Always query VMA stats once before destroying the allocator so we can + // spot leaks that would trigger the vk_mem_alloc.h assertion: + // "Some allocations were not freed before destruction of this memory block!" + if (_allocator) { VmaTotalStatistics stats{}; vmaCalculateStatistics(_allocator, &stats); - const VmaStatistics& s = stats.total.statistics; - fmt::print("[VMA] Blocks: {} | Allocations: {} | BlockBytes: {} | AllocationBytes: {}\n", - (size_t)s.blockCount, - (size_t)s.allocationCount, - (unsigned long long)s.blockBytes, - (unsigned long long)s.allocationBytes); + const VmaStatistics &s = stats.total.statistics; + + if (s.allocationCount != 0) + { + fmt::print("[VMA] WARNING: {} live allocations ({} bytes) remain before allocator destruction – this will trip vk_mem_alloc.h assertion: \"Some allocations were not freed before destruction of this memory block!\"\n", + (size_t)s.allocationCount, + (unsigned long long)s.allocationBytes); + } + else if (vmaDebugEnabled()) + { + fmt::print("[VMA] Blocks: {} | Allocations: {} | BlockBytes: {} | AllocationBytes: {}\n", + (size_t)s.blockCount, + (size_t)s.allocationCount, + (unsigned long long)s.blockBytes, + (unsigned long long)s.allocationBytes); + } } vkDestroySurfaceKHR(_instance, _surface, nullptr); _deletionQueue.flush(); diff --git a/src/core/vk_engine.cpp b/src/core/vk_engine.cpp index dc19b80..325230a 100644 --- a/src/core/vk_engine.cpp +++ b/src/core/vk_engine.cpp @@ -420,6 +420,14 @@ void VulkanEngine::cleanup() print_vma_stats(_deviceManager.get(), "after AssetManager"); dump_vma_json(_deviceManager.get(), "after_AssetManager"); + // Release IBL GPU resources (spec/diffuse cubes + BRDF LUT) + if (_iblManager) + { + _iblManager->unload(); + } + print_vma_stats(_deviceManager.get(), "after IBLManager"); + dump_vma_json(_deviceManager.get(), "after_IBLManager"); + // Ensure ray tracing resources (BLAS/TLAS/instance buffers) are freed before VMA is destroyed if (_rayManager) { _rayManager->cleanup(); } print_vma_stats(_deviceManager.get(), "after RTManager"); diff --git a/src/core/vk_swapchain.cpp b/src/core/vk_swapchain.cpp index c7cbca5..1cb49f7 100644 --- a/src/core/vk_swapchain.cpp +++ b/src/core/vk_swapchain.cpp @@ -64,7 +64,7 @@ void SwapchainManager::init_swapchain() VK_CHECK(vkCreateImageView(_deviceManager->device(), &dview_info, nullptr, &_depthImage.imageView)); // GBuffer (SRGB not used to keep linear lighting) - _gBufferPosition = _resourceManager->create_image(drawImageExtent, VK_FORMAT_R16G16B16A16_SFLOAT, + _gBufferPosition = _resourceManager->create_image(drawImageExtent, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); _gBufferNormal = _resourceManager->create_image(drawImageExtent, VK_FORMAT_R16G16B16A16_SFLOAT, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); diff --git a/src/render/vk_renderpass_lighting.cpp b/src/render/vk_renderpass_lighting.cpp index 5200d81..07faa03 100644 --- a/src/render/vk_renderpass_lighting.cpp +++ b/src/render/vk_renderpass_lighting.cpp @@ -310,6 +310,20 @@ void LightingPass::draw_lighting(VkCommandBuffer cmd, void LightingPass::cleanup() { + if (_context && _context->getResources()) + { + if (_fallbackIbl2D.image) + { + _context->getResources()->destroy_image(_fallbackIbl2D); + _fallbackIbl2D = {}; + } + if (_fallbackBrdfLut2D.image) + { + _context->getResources()->destroy_image(_fallbackBrdfLut2D); + _fallbackBrdfLut2D = {}; + } + } + _deletionQueue.flush(); fmt::print("LightingPass::cleanup()\n"); } diff --git a/src/scene/vk_scene.cpp b/src/scene/vk_scene.cpp index 252ca26..494172d 100644 --- a/src/scene/vk_scene.cpp +++ b/src/scene/vk_scene.cpp @@ -317,6 +317,16 @@ void SceneManager::cleanup() clearMeshInstances(); clearGLTFInstances(); + // On engine shutdown we know VulkanEngine::cleanup() has already called + // vkDeviceWaitIdle(), so it is safe to destroy all remaining GLTF scenes + // immediately instead of deferring them through pendingGLTFRelease. + if (!pendingGLTFRelease.empty()) + { + fmt::println("[SceneManager] cleanup: forcing {} pending GLTF releases before shutdown", + pendingGLTFRelease.size()); + pendingGLTFRelease.clear(); // drop strong refs → ~LoadedGLTF::clearAll() runs + } + // Drop our references to GLTF scenes. Their destructors call clearAll() // exactly once to release GPU resources. loadedScenes.clear();