From b023907df8c909f7f599077067f05195997a4c58 Mon Sep 17 00:00:00 2001 From: hydrogendeuteride Date: Sat, 22 Nov 2025 18:37:12 +0900 Subject: [PATCH] memory bug fixed-/vk_engine_ui.cpp, vk_loader.h --- src/core/texture_cache.cpp | 43 ++++++++++++++++++++++++++++++++++++-- src/core/vk_engine_ui.cpp | 40 +++++++++++++++++++++++++++++++++-- src/scene/vk_loader.cpp | 24 ++++++++++++++++++++- src/scene/vk_loader.h | 8 ++++++- src/scene/vk_scene.cpp | 27 ++++++++++++++++++++++++ src/scene/vk_scene.h | 1 + 6 files changed, 137 insertions(+), 6 deletions(-) diff --git a/src/core/texture_cache.cpp b/src/core/texture_cache.cpp index 9ee92bc..91044c8 100644 --- a/src/core/texture_cache.cpp +++ b/src/core/texture_cache.cpp @@ -42,10 +42,15 @@ void TextureCache::cleanup() } if (!_context || !_context->getResources()) return; auto *rm = _context->getResources(); - for (auto &e : _entries) + for (TextureHandle h = 0; h < _entries.size(); ++h) { + auto &e = _entries[h]; if (e.state == EntryState::Resident && e.image.image) { + fmt::println("[TextureCache] cleanup destroy handle={} path='{}' bytes={}", + h, + e.path.empty() ? "" : e.path, + e.sizeBytes); rm->destroy_image(e.image); e.image = {}; } @@ -102,6 +107,13 @@ TextureCache::TextureHandle TextureCache::request(const TextureKey &key, VkSampl e.bytes = normKey.bytes; _cpuSourceBytes += e.bytes.size(); } + fmt::println("[TextureCache] request handle={} kind={} path='{}' srgb={} mipmapped={} hash=0x{:016x}", + h, + (normKey.kind == TextureKey::SourceKind::FilePath ? "FilePath" : "Bytes"), + normKey.kind == TextureKey::SourceKind::FilePath ? normKey.path : "", + normKey.srgb, + normKey.mipmapped, + normKey.hash); _entries.push_back(std::move(e)); return h; } @@ -386,6 +398,11 @@ void TextureCache::evictToBudget(size_t budgetBytes) // Rewrite watchers back to fallback before destroying patch_to_fallback(e); + fmt::println("[TextureCache] evictToBudget destroy handle={} path='{}' bytes={} residentBytesBefore={}", + h, + e.path.empty() ? "" : e.path, + e.sizeBytes, + _residentBytes); _context->getResources()->destroy_image(e.image); e.image = {}; e.state = EntryState::Evicted; @@ -720,6 +737,14 @@ size_t TextureCache::drain_ready_uploads(ResourceManager &rm, size_t budgetBytes { levels.push_back(ResourceManager::MipLevelCopy{ lv.offset, lv.length, lv.width, lv.height }); } + fmt::println("[TextureCache] upload KTX2 handle={} fmt={} levels={} size={}x{} srgb={} path='{}'", + res.handle, + string_VkFormat(fmt), + res.ktxMipLevels, + extent.width, + extent.height, + res.srgb, + e.path); e.image = rm.create_image_compressed(res.ktx.bytes.data(), res.ktx.bytes.size(), fmt, levels); e.sizeBytes = expectedBytes; } @@ -757,6 +782,14 @@ size_t TextureCache::drain_ready_uploads(ResourceManager &rm, size_t budgetBytes } uint32_t mipOverride = (res.mipmapped ? desiredLevels : 1); + fmt::println("[TextureCache] upload raster handle={} fmt={} levels={} size={}x{} srgb={} path='{}'", + res.handle, + string_VkFormat(fmt), + mipOverride, + extent.width, + extent.height, + res.srgb, + e.path); e.image = rm.create_image(src, extent, fmt, VK_IMAGE_USAGE_SAMPLED_BIT, res.mipmapped, mipOverride); e.sizeBytes = expectedBytes; } @@ -847,10 +880,16 @@ bool TextureCache::try_make_space(size_t bytesNeeded, uint32_t now) for (auto &pair : order) { if (freed >= bytesNeeded) break; - Entry &e = _entries[pair.first]; + TextureHandle h = pair.first; + Entry &e = _entries[h]; if (e.state != EntryState::Resident) continue; patch_to_fallback(e); + fmt::println("[TextureCache] try_make_space destroy handle={} path='{}' bytes={} residentBytesBefore={}", + h, + e.path.empty() ? "" : e.path, + e.sizeBytes, + _residentBytes); _context->getResources()->destroy_image(e.image); e.image = {}; e.state = EntryState::Evicted; diff --git a/src/core/vk_engine_ui.cpp b/src/core/vk_engine_ui.cpp index 71213dd..bba4613 100644 --- a/src/core/vk_engine_ui.cpp +++ b/src/core/vk_engine_ui.cpp @@ -620,8 +620,44 @@ namespace else if (pick->ownerType == RenderObject::OwnerType::GLTFInstance) { bool ok = eng->_sceneManager->removeGLTFInstance(pick->ownerName); - deleteStatus = ok ? "Removed glTF instance: " + pick->ownerName - : "glTF instance not found: " + pick->ownerName; + if (ok) + { + deleteStatus = "Removed glTF instance: " + pick->ownerName; + + // Debug: log and clear any picks that still reference the deleted instance. + fmt::println("[Debug] GLTF delete requested for '{}'; clearing picks if they match.", + pick->ownerName); + + if (eng->_lastPick.valid && + eng->_lastPick.ownerType == RenderObject::OwnerType::GLTFInstance && + eng->_lastPick.ownerName == pick->ownerName) + { + fmt::println("[Debug] Clearing _lastPick for deleted GLTF instance '{}'", pick->ownerName); + eng->_lastPick.valid = false; + eng->_lastPick.ownerName.clear(); + eng->_lastPick.ownerType = RenderObject::OwnerType::None; + eng->_lastPick.mesh = nullptr; + eng->_lastPick.scene = nullptr; + eng->_lastPick.node = nullptr; + } + + if (eng->_hoverPick.valid && + eng->_hoverPick.ownerType == RenderObject::OwnerType::GLTFInstance && + eng->_hoverPick.ownerName == pick->ownerName) + { + fmt::println("[Debug] Clearing _hoverPick for deleted GLTF instance '{}'", pick->ownerName); + eng->_hoverPick.valid = false; + eng->_hoverPick.ownerName.clear(); + eng->_hoverPick.ownerType = RenderObject::OwnerType::None; + eng->_hoverPick.mesh = nullptr; + eng->_hoverPick.scene = nullptr; + eng->_hoverPick.node = nullptr; + } + } + else + { + deleteStatus = "glTF instance not found: " + pick->ownerName; + } } else { diff --git a/src/scene/vk_loader.cpp b/src/scene/vk_loader.cpp index 1339133..b9eac3b 100644 --- a/src/scene/vk_loader.cpp +++ b/src/scene/vk_loader.cpp @@ -164,7 +164,7 @@ VkSamplerMipmapMode extract_mipmap_mode(fastgltf::Filter filter) std::optional > loadGltf(VulkanEngine *engine, std::string_view filePath) { //> load_1 - fmt::print("Loading GLTF: {}", filePath); + fmt::println("[GLTF] loadGltf begin: '{}'", filePath); std::shared_ptr scene = std::make_shared(); scene->creator = engine; @@ -849,6 +849,13 @@ std::optional > loadGltf(VulkanEngine *engine, std:: } }, img.data); } + fmt::println("[GLTF] loadGltf done: meshes={} materials={} images={} samplers={} animations={} debugName='{}'", + file.meshes.size(), + file.materials.size(), + file.images.size(), + file.samplers.size(), + file.animations.size(), + file.debugName.empty() ? "" : file.debugName); return scene; //< load_graph } @@ -1050,6 +1057,14 @@ void LoadedGLTF::updateAnimation(float dt) void LoadedGLTF::clearAll() { + const char *name = debugName.empty() ? "" : debugName.c_str(); + fmt::println("[GLTF] clearAll begin for '{}' (meshes={} images={} materials={} samplers={})", + name, + meshes.size(), + images.size(), + materials.size(), + samplers.size()); + VkDevice dv = creator->_deviceManager->device(); // Before destroying descriptor pools, unregister descriptor-set watches so @@ -1097,4 +1112,11 @@ void LoadedGLTF::clearAll() descriptorPool.destroy_pools(dv); creator->_resourceManager->destroy_buffer(materialBuffer); + + fmt::println("[GLTF] clearAll done for '{}' (meshes={}, images={}, materials={}, samplers={})", + name, + meshes.size(), + images.size(), + materials.size(), + samplers.size()); } diff --git a/src/scene/vk_loader.h b/src/scene/vk_loader.h index 0b2b5a9..9091731 100644 --- a/src/scene/vk_loader.h +++ b/src/scene/vk_loader.h @@ -113,7 +113,13 @@ struct LoadedGLTF : public IRenderable void setActiveAnimation(int index, bool resetTime = true); void setActiveAnimation(const std::string &name, bool resetTime = true); - ~LoadedGLTF() { clearAll(); }; + ~LoadedGLTF() + { + const char *name = debugName.empty() ? "" : debugName.c_str(); + fmt::println("[GLTF] ~LoadedGLTF destructor begin for '{}' ({})", name, static_cast(this)); + clearAll(); + fmt::println("[GLTF] ~LoadedGLTF destructor end for '{}' ({})", name, static_cast(this)); + }; void clearMeshes(){ clearAll(); }; diff --git a/src/scene/vk_scene.cpp b/src/scene/vk_scene.cpp index 1330793..252ca26 100644 --- a/src/scene/vk_scene.cpp +++ b/src/scene/vk_scene.cpp @@ -17,6 +17,15 @@ #include "frame_resources.h" #include "core/config.h" +#include + +SceneManager::~SceneManager() +{ + fmt::println("[SceneManager] dtor: loadedScenes={} dynamicGLTFInstances={} pendingGLTFRelease={}", + loadedScenes.size(), + dynamicGLTFInstances.size(), + pendingGLTFRelease.size()); +} void SceneManager::init(EngineContext *context) { @@ -41,6 +50,13 @@ void SceneManager::update_scene() // until the GPU has finished work that might still reference their resources. if (_context && _context->currentFrame) { + if (!pendingGLTFRelease.empty()) + { + fmt::println("[SceneManager] update_scene: scheduling {} pending GLTF releases (hasContext={}, hasFrame={})", + pendingGLTFRelease.size(), + true, + true); + } for (auto &sp : pendingGLTFRelease) { auto keepAlive = sp; // copy to keep ref count in the lambda @@ -332,6 +348,9 @@ void SceneManager::addGLTFInstance(const std::string &name, std::shared_ptrdebugName.empty() ? "" : scene->debugName.c_str()); dynamicGLTFInstances[name] = GLTFInstance{std::move(scene), transform}; } @@ -346,6 +365,9 @@ bool SceneManager::removeGLTFInstance(const std::string &name) if (_context && _context->currentFrame) { auto keepAlive = it->second.scene; + fmt::println("[SceneManager] removeGLTFInstance '{}' scheduling deferred destroy (scene='{}')", + name, + keepAlive && !keepAlive->debugName.empty() ? keepAlive->debugName.c_str() : ""); _context->currentFrame->_deletionQueue.push_function([keepAlive]() mutable { keepAlive.reset(); }); } else @@ -369,6 +391,9 @@ bool SceneManager::setGLTFInstanceTransform(const std::string &name, const glm:: void SceneManager::clearGLTFInstances() { + fmt::println("[SceneManager] clearGLTFInstances: dynamicGLTFInstances={} pendingBefore={}", + dynamicGLTFInstances.size(), + pendingGLTFRelease.size()); for (auto &kv : dynamicGLTFInstances) { if (kv.second.scene) @@ -377,6 +402,8 @@ void SceneManager::clearGLTFInstances() } } dynamicGLTFInstances.clear(); + fmt::println("[SceneManager] clearGLTFInstances: pendingAfter={}", + pendingGLTFRelease.size()); } bool SceneManager::setSceneAnimation(const std::string &sceneName, int animationIndex, bool resetTime) diff --git a/src/scene/vk_scene.h b/src/scene/vk_scene.h index 9d12912..573e9e5 100644 --- a/src/scene/vk_scene.h +++ b/src/scene/vk_scene.h @@ -55,6 +55,7 @@ struct DrawContext class SceneManager { public: + ~SceneManager(); void init(EngineContext *context); void cleanup();