From 2cf8d6e0764056b606ef4a078777da7896e3cc3d Mon Sep 17 00:00:00 2001 From: atdunbg Date: Thu, 8 Oct 2026 23:23:15 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=A2=8E=E7=89=87=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E5=8D=B8=E8=BD=BD=E7=9A=84=E5=9C=A8=E9=A3=9E=20GPU=20?= =?UTF-8?q?=E5=BC=95=E7=94=A8=EF=BC=88vkDestroyBuffer=20in-use=EF=BC=89?= =?UTF-8?q?=EF=BC=9A=E9=97=A8=E9=9D=A2=E6=89=B9=E9=87=8F=E5=8D=B8=E8=BD=BD?= =?UTF-8?q?=E5=85=88=20WaitDeviceIdle=20=E5=86=8D=E9=94=80=E6=AF=81?= =?UTF-8?q?=EF=BC=88=E4=B8=8E=20ReloadMaterialAsset=20=E5=90=8C=E4=B8=80?= =?UTF-8?q?=E5=85=88=E4=BE=8B=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App/scripts/WallFracture.cpp | 28 ++++++++++----------- Engine/include/Engine/Assets/AssetManager.h | 14 ++++++++--- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/App/scripts/WallFracture.cpp b/App/scripts/WallFracture.cpp index a191ec5..d818bbd 100644 --- a/App/scripts/WallFracture.cpp +++ b/App/scripts/WallFracture.cpp @@ -259,19 +259,20 @@ namespace { } // 墙销毁(PIE stop 换血 / 手动删除):碎片实体由场景清亡处理, - // 独占碎片模型全部回收(模型不被快照恢复的场景引用——唯一代键) + // 独占碎片模型统一批量回收(模型不被快照恢复的场景引用—— + // 唯一代键;批量接口内部 GPU 停飞后再销毁) void OnDestroy() override { - auto& assets = App().GetAssets(); + std::vector paths; + paths.reserve(m_Fragments.size() + m_PendingUnload.size()); for (const Fragment& f : m_Fragments) { - if (!f.modelPath.empty()) { - assets.UnloadModelByPath(f.modelPath); - } + if (!f.modelPath.empty()) paths.push_back(f.modelPath); } m_Fragments.clear(); - for (const std::string& p : m_PendingUnload) { - assets.UnloadModelByPath(p); - } + paths.insert(paths.end(), + std::make_move_iterator(m_PendingUnload.begin()), + std::make_move_iterator(m_PendingUnload.end())); m_PendingUnload.clear(); + App().GetAssets().UnloadModelsByPaths(paths); } private: @@ -449,13 +450,12 @@ namespace { } } - // ---- 模型回收:延迟一帧执行(Destroy 是帧尾延迟,碎片实体 - // 本帧渲染收集仍会引用模型;下一帧实体已灭,卸载才安全)---- + // ---- 模型回收:延迟一帧 + 批量停飞卸载(Destroy 是帧尾延迟, + // 碎片实体本帧渲染收集仍会引用模型;下一帧实体已灭,但上一帧 + // 提交的命令缓冲仍可能引用顶点缓冲/sampler——批量接口先 + // WaitDeviceIdle 再销毁,与资产重建换绑同一引擎先例)---- void ApplyPendingUnload() { - auto& assets = App().GetAssets(); - for (const std::string& p : m_PendingUnload) { - assets.UnloadModelByPath(p); - } + App().GetAssets().UnloadModelsByPaths(m_PendingUnload); m_PendingUnload.clear(); } diff --git a/Engine/include/Engine/Assets/AssetManager.h b/Engine/include/Engine/Assets/AssetManager.h index c22905e..63f7b83 100644 --- a/Engine/include/Engine/Assets/AssetManager.h +++ b/Engine/include/Engine/Assets/AssetManager.h @@ -182,11 +182,17 @@ namespace Engine ModelRef LoadModel(const std::string& path, ParsedModel&& parsed); Model* GetModel(ModelRef h) const { return m_Models.Get(h); } void UnloadModel(ModelRef h) { m_Models.Unload(h); } - // 按路径卸载模型(未加载为 no-op;手上只有路径的场景, - // 如脚本程序化碎片模型回收) - void UnloadModelByPath(const std::string& path) + // 批量按路径卸载模型(未加载为 no-op)。先 GPU 全停飞再销毁 + //(在飞命令缓冲可能仍引用顶点缓冲/默认纹理 sampler—— + // 与 ReloadMaterialAsset 的 WaitDeviceIdle 同一先例);一次 + // 停飞摊薄整批,调用方攒批降频(脚本碎片回收每帧一批) + void UnloadModelsByPaths(const std::vector& paths) { - m_Models.UnloadByPath(path); + if (paths.empty()) return; + m_Uploader.WaitDeviceIdle(); + for (const std::string& p : paths) { + m_Models.UnloadByPath(p); + } } // 按路径查已加载的模型(未加载返回 null,不触发加载)—— // 面板 hover 信息卡用