资产管理
数据流向
- 扫描
Assets/ - 使用
AssetDatabase发现资产、维护GUID / meta - 通过
ImporterRegistry把硬盘上的资产转化成 Runtime 可使用的资源 - 在 AssetManager 中管理 CPU 资产、异步加载、热重载
- 在 ResourceManager 中将 CPU 资产上传为 GPU 资源
AssetDatabase
目的是不需要完整的把资源加载到内存中,就可以通过记录信息,在 Runtime 的时候查询资产类型、路径和元数据
此处维护了
struct AssetRecord
{
AssetGuid guid;
AssetType type = AssetType::Unknown;
std::filesystem::path sourcePath;
std::filesystem::path metaPath;
std::filesystem::path importedPath;
std::string importer;
std::filesystem::file_time_type sourceWriteTime{};
};主要维护 guid,type,path 三个核心数据。
guid 和 meta
meta 设计
{
"guid": "122515072b7d31c965a64a1eba21da4e",
"imported": "Shaders/test.vert",
"importer": "shader",
"type": "shader_source",
"version": 1
}实际上可以理解为把 Record 的数据做持久化,这样 ——
- 文件重命名或移动后,只要 .meta 一起移动,GUID 不变。
- Importer 配置不污染 PNG、GLTF、Shader 等源文件
guid 目前比较简单的使用字符串做,后期可以优化
特别注意的,用于着色器需要编译成.spv,此时如果又为其分配一个 guid,会导致歧义—— 一个逻辑 Shader 却拥有两个身份(runtime 和 源文件,不明确到底指向哪个)
启示 —— 创作数据 != 编辑器内部资产 != 平台运行数据
Importer Registry
把解析功能从 AssetDatabase 中抽离出来(单一职责这块),做的是类似GLTF,Shader等磁盘上的文件的具体解析
目前实现了
- PassthroughImporter:源文件本身就是运行时输入(人话,直接复制)
- ShaderImporter:通过 glslc 将 Shader 源码编译为 SPIR-V。
但这非常好的预留了Strategy,未来如何需要在中间插入什么预处理 / 其他什么格式… 就可以直接在ImporterRegistry 中做注册
AssetManager
还是和之前的设计没什么区别 —— 是总的一个磁盘资产的管理者
- 初始化 Asset Database 和 Importer
- 根据路径或 GUID 加载资产
- 缓存 CPU 数据
这一次加入了异步加载、热重载和显示卸载的功能
顺带提下之前的内容 —— 加载后,AssetManager 使用 SlotMap 保存 CPU 数据(定义在 Core 中的不错的结构,基于 Handle 的查找
值得注意,现在是直接返回
std::shared_future<TextureHandle>
std::shared_future<MeshHandle>
std::shared_future<ShaderHandle>可以但在关闭时会拒绝新任务,并等待活动任务结束后清理缓存,期待一手未来实现 Job System
每隔一段时间 Tick 一下 热重载方法,检测源文件时间变化,如果变化了则重新执行 Importer,同时原位替换已加载 CPU 数据(保持原 AssetHandle 有效),最后广播 AssetReloadEvent
ResourceManager
ResourceManager 做了一层 CPU Asset 到 GPU Resource 的适配层,
MeshData -> MeshGPU -> Vertex / Index Buffer TextureData -> TextureGPU -> VkImage MaterialData -> MaterialGPU -> Shader / Pipeline / UBO / Bindings
订阅了 AssetReloadEvent ,会在热重载的时候执行 UnloadAll 然后让 GPU 资源在下一次提交渲染指令的时候按需上传
不过这样做比较贵,最好是建立资产依赖图,只失效真正依赖变化资产的 Material、Pipeline、Texture 或 Mesh 之类的什么资源