feat: 环境异步加载——HDR 解码移后台线程,运行时切换不卡主线程
decode 是切换耗时大头(4k ≈ 130-160ms),搬出主线程后运行时切换 只剩 GPU 段(~40ms)。IblGenerator 拆两段式:DecodeHdr(static, 纯 CPU 无线程状态,worker 可调)+ FromDecoded(GPU 生成,主线程); FromHdrEquirect 保留为同步便捷路径。Assets 层经 IAssetUploader 重载 + AssetManager 重载传递预解码产物(路径已缓存则丢弃去重)。 App 层单 in-flight 状态机:worker 只产出纯 CPU 像素数据,忙肋期 新请求记 pending 接力(只保留最终意图),OnUpdate 每帧收割 + SetEnvironment 换绑,析构 join。 实测(RTX 5060 Laptop,4 实例网格 + 运行中连切 3 环境): ~180fps 无感知尖峰;0 validation。
This commit is contained in:
@@ -27,13 +27,15 @@
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace Engine {
|
||||
namespace Assets {
|
||||
|
||||
namespace Engine
|
||||
{
|
||||
namespace Assets
|
||||
{
|
||||
// ---------- 通用资产缓存(不感知 Vulkan / 磁盘细节由 loader 决定) ----------
|
||||
|
||||
template<typename T>
|
||||
class AssetCache {
|
||||
template <typename T>
|
||||
class AssetCache
|
||||
{
|
||||
public:
|
||||
// loader:path → 资产实例(失败返回 nullptr)
|
||||
using LoaderFn = std::function<Scope<T>(const std::string&)>;
|
||||
@@ -41,23 +43,28 @@ namespace Engine {
|
||||
// 加载(带路径去重)。失败返回无效句柄。
|
||||
AssetRef<T> Load(const std::string& path, const LoaderFn& loader)
|
||||
{
|
||||
if (auto it = m_ByPath.find(path); it != m_ByPath.end()) {
|
||||
if (auto it = m_ByPath.find(path); it != m_ByPath.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
|
||||
Scope<T> asset = loader(path);
|
||||
if (!asset) {
|
||||
if (!asset)
|
||||
{
|
||||
ENGINE_ERROR("AssetCache: failed to load '{}'", path);
|
||||
return {};
|
||||
}
|
||||
|
||||
AssetRef<T> h;
|
||||
if (!m_Free.empty()) {
|
||||
if (!m_Free.empty())
|
||||
{
|
||||
h.index = m_Free.back();
|
||||
m_Free.pop_back();
|
||||
h.generation = m_Entries[h.index].generation;
|
||||
m_Entries[h.index] = Entry{path, h.generation, std::move(asset), true};
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
h.index = static_cast<u32>(m_Entries.size());
|
||||
h.generation = 1;
|
||||
m_Entries.push_back(Entry{path, h.generation, std::move(asset), true});
|
||||
@@ -70,12 +77,14 @@ namespace Engine {
|
||||
// 句柄 → 资产指针。悬空句柄 FATAL(bug 信号);已卸载返回 nullptr。
|
||||
T* Get(AssetRef<T> h) const
|
||||
{
|
||||
if (!h.Valid() || h.index >= m_Entries.size()) {
|
||||
if (!h.Valid() || h.index >= m_Entries.size())
|
||||
{
|
||||
ENGINE_FATAL("AssetCache::Get: invalid handle (index={}, gen={})",
|
||||
h.index, h.generation);
|
||||
}
|
||||
const Entry& e = m_Entries[h.index];
|
||||
if (e.generation != h.generation) {
|
||||
if (e.generation != h.generation)
|
||||
{
|
||||
ENGINE_FATAL("AssetCache::Get: stale handle for '{}' (gen {} != {})",
|
||||
e.path, h.generation, e.generation);
|
||||
}
|
||||
@@ -114,7 +123,8 @@ namespace Engine {
|
||||
}
|
||||
|
||||
private:
|
||||
struct Entry {
|
||||
struct Entry
|
||||
{
|
||||
std::string path;
|
||||
u32 generation = 1;
|
||||
Scope<T> asset;
|
||||
@@ -128,7 +138,8 @@ namespace Engine {
|
||||
|
||||
// ---------- AssetManager:全引擎资产入口 ----------
|
||||
|
||||
class AssetManager {
|
||||
class AssetManager
|
||||
{
|
||||
public:
|
||||
// 上传服务由组合根(Application)注入:渲染层实现接口,
|
||||
// 资产层不感知 Vulkan 具体类
|
||||
@@ -148,6 +159,10 @@ namespace Engine {
|
||||
// 环境资产(HDR → IBL 预积分四件套)。路径缓存去重,
|
||||
// 重复 Load 同一路径零成本(动态切换场景直接反复调用)
|
||||
EnvRef LoadEnvironment(const std::string& path);
|
||||
// 异步路径:解码已在后台线程完成(若路径已缓存,解码产物被
|
||||
// 丢弃去重——上层可先查缓存再起 worker,此处不做强约束)
|
||||
EnvRef LoadEnvironment(const std::string& path,
|
||||
DecodedEquirect&& decoded);
|
||||
Cubemap* GetEnvironment(EnvRef h) const { return m_Environments.Get(h); }
|
||||
|
||||
AssetCache<Model>& Models() { return m_Models; }
|
||||
@@ -161,7 +176,6 @@ namespace Engine {
|
||||
AssetCache<Model> m_Models;
|
||||
AssetCache<Cubemap> m_Environments;
|
||||
};
|
||||
|
||||
} // namespace Assets
|
||||
} // namespace Engine
|
||||
|
||||
|
||||
@@ -35,6 +35,10 @@ namespace Engine::Assets {
|
||||
|
||||
// 环境 HDR → IBL 预积分四件套(compute 生成,毫秒级;失败返回 nullptr)
|
||||
virtual Scope<Cubemap> LoadEnvironment(const std::string& path) = 0;
|
||||
|
||||
// 异步加载路径:解码已在后台线程完成,此调用只做 GPU 生成(主线程)
|
||||
virtual Scope<Cubemap> LoadEnvironment(const std::string& path,
|
||||
DecodedEquirect&& decoded) = 0;
|
||||
};
|
||||
|
||||
} // namespace Engine::Assets
|
||||
|
||||
Reference in New Issue
Block a user