Files
XCEngine/docs/used/Library资产导入与缓存系统收口计划_完成归档_2026-04-03.md

537 lines
20 KiB
Markdown
Raw Normal View History

2026-04-08 16:07:03 +08:00
# Library资产导入与缓存系统收口计划
归档状态2026-04-03当前 `project` 范围内的 Library 导入与缓存系统收口已完成,本文档转为归档记录。
## 0. 文档定位
这份文档不是上一轮“从零搭一套 Unity 式 Library 体系”的继续描述,而是当前阶段的正式收口文档。
### 0.1 当前执行进度2026-04-03
| 阶段 | 状态 | 当前结果 |
| --- | --- | --- |
| 阶段 A协议定稿与旧兼容删除 | 已完成 | 已删除 `mesh=` / `materials=` 旧兼容读取,项目资产序列化正式收敛到 `AssetRef`路径字段仅保留给虚拟资源scene 迁移工具链已移除。 |
| 阶段 B导入与缓存边界收口 | 已完成 | `AssetImportService` 已接管 Library 根目录、快照构建、缓存重建与 artifact 保证入口;`ProjectAssetIndex` 已只消费服务快照;`ResourceManager` 已收口为运行时缓存/异步调度入口,并提供 `RefreshProjectAssets` / `RebuildProjectAssetCache`。 |
| 阶段 C进行中 | 已完成 `xcmat` 纹理绑定的 `AssetRef + lazy resolve` 首轮收口;下一步继续压缩缓存命中后的恢复成本,并检查是否还存在主线程等待点。 |
| 阶段 D最小工具闭环 | 已完成 | 已完成 `Reimport Selected Asset / Reimport All Assets / Clear Library` 正式入口;已补 `Project` 面板最小导入状态输出;已接入 orphan artifact 清理。 |
### 0.2 当前已完成的收口结果2026-04-03
- 阶段 A 已完成并验证通过:
- `MeshFilterComponent` 对项目 mesh 只序列化 `meshRef``meshPath` 只保留给 `builtin://...`
- `MeshRendererComponent` 对项目 material 只序列化 `materialRefs``materialPaths` 只保留给 `builtin://...`
- 旧的 `mesh=` / `materials=` 兼容读取已移除
- scene 迁移命令、菜单、report 链路已删除
- 阶段 B 已完成并验证通过:
- `AssetImportService` 对外新增 `LookupSnapshot``ImportedAsset``GetLibraryRoot()``RebuildLibraryCache()`
- `ProjectAssetIndex` 不再直接拼装 `AssetDatabase` 细节,而是只刷新服务快照
- `ResourceManager` 不再暴露旧式导入数据库心智,新增 `RefreshProjectAssets()``RebuildProjectAssetCache()``GetProjectLibraryRoot()`
- `AssetDatabase` 初始化后会落盘 `ArtifactDB/artifacts.db`,使空 Library 结构稳定成型
- 阶段 C 已开始并完成首个子项:
- `Material` 纹理绑定新增稳定 `AssetRef` 元数据,运行时按需把 `AssetRef` 解析为加载路径
- `xcmat` 协议已升级为纹理绑定写入 `AssetRef + load path hint`
- `AssetDatabase` importer version 已提升,旧 material artifact 将自动失效并重建
- 阶段 D 已开始并完成首轮工具口:
- `AssetDatabase` 已新增 `TryGetImportableResourceType()``ReimportAsset()``ReimportAllAssets()`
- `AssetImportService` 已新增 `ClearLibraryCache()``ReimportAllAssets()``ReimportAsset()`
- `ResourceManager` 已新增 `CanReimportProjectAsset()``ReimportProjectAsset()``ClearProjectLibraryCache()`
- editor `Assets` 菜单已新增 `Reimport Selected Asset``Reimport All Assets``Clear Library`
- `AssetImportService` 已新增导入状态快照,`Project` 面板工具栏会显示最近一次导入/清库/清理状态
- `AssetDatabase` 已新增 orphan artifact 清理,`Refresh / Reimport / 自动重导入` 后都会回收未被 `ArtifactDB` 引用的 `Library/Artifacts/*` 目录
- 已完成 focused 验证:
- `asset_tests`
- `ResourceManager_Test.ConcurrentAsyncLoadsCoalesceSameMeshPath`
- `ResourceManager_Test.AssetLookupFallbackRefreshesSnapshotForNewProjectAsset`
- `ProjectAssetIndex_Test.RefreshesSnapshotThroughImportServiceOnCacheMiss`
- `AssetImportService_Test.RebuildLibraryCacheKeepsStableAssetRefs`
- `ResourceManager_Test.RebuildProjectAssetCacheRefreshesLookupState`
- `AssetImportService_Test.ClearLibraryAndReimportAllAssetsManageArtifactsExplicitly`
- `AssetImportService_Test.ImportStatusTracksExplicitOperationsAndRefreshCleanup`
- `ResourceManager_Test.ReimportProjectAssetBuildsArtifactForSelectedPath`
- `editor_tests`
- `EditorActionRoutingTest.ProjectCommandsExposeAssetCacheMaintenanceActions`
- `EditorActionRoutingTest.ProjectCommandsReimportSelectedAssetAndClearLibraryDriveAssetCache`
- `EditorActionRoutingTest.ProjectCommandsReportWhenScriptAssembliesCanBeRebuilt`
- `components_tests`
- `MeshRendererComponent_Test.SerializeAndDeserializeLoadsProjectMaterialByAssetRef`
- `MeshRendererComponent_Test.DeferredSceneDeserializeLoadsProjectMaterialAsync`
- `material_tests`46/46 通过
- `mesh_tests`33/33 通过
- `scene_tests`
- `Scene_ProjectSample.DeferredLoadBackpackSceneEventuallyRestoresBackpackMesh`
- `Scene_ProjectSample.DeferredLoadBackpackSceneEventuallyProducesVisibleRenderItems`
- `XCEditor` Release 已成功编译
旧方案文档已经归档到:
- `docs/plan/used/Unity式Library资产导入与缓存系统重构方案.md`
归档原因很简单:
- 旧文档解决的是“从没有 Library到建立 Library 基础设施”的问题。
- 现在的问题已经不是“要不要做 Library”而是“怎么把当前这套半过渡、半正式的实现收成一套干净的正式系统”。
本轮收口以以下前提为准:
- 只服务当前 `project` 这一个项目。
- 不兼容旧版本 scene / component 资产引用格式。
- 不再提供旧格式迁移工具。
- 可以直接改写 `project` 下现有场景和资源源文件。
- 目标不是继续堆补丁,而是形成一套边界清晰、行为稳定、可长期维护的正式实现。
---
## 1. 当前收口基线
当前系统已经具备以下基础,这些不是本轮要推倒重来,而是本轮收口的起点:
- 已有 Unity 风格的 `Library` 目录结构:
- `Library/SourceAssetDB`
- `Library/ArtifactDB`
- `Library/Artifacts`
- 已有 `.meta + AssetGUID` 机制,项目资产已经具备稳定身份。
- 已有 `AssetRef` 结构,运行时已经可以通过 `AssetRef` 回查项目资产。
- 已有纹理、材质、模型 artifact
- `xctex`
- `xcmat`
- `xcmesh`
- 已有 `AssetImportService`,用于承接导入与 artifact 保证逻辑。
- 已有 `ProjectAssetIndex`,用于承接项目资产路径与 `AssetRef` 快照索引。
- `ResourceManager` 已不再直接承担全部导入数据库职责,而是开始回到运行时调度器角色。
- scene 打开链路已经接入 deferred restore`MeshFilterComponent` / `MeshRendererComponent` 能在反序列化后异步恢复项目资源。
- 当前 `project/Assets/Scenes/Main.xc``project/Assets/Scenes/Backpack.xc` 已经具备 `AssetRef` 版本数据,可作为最终格式收口的直接修改对象。
当前真正没有收口的,不是“功能不存在”,而是下面这三类问题还在:
- 还保留着旧格式兼容和双写逻辑。
- 组件 / scene / editor 工具链里还残留过渡期代码。
- 材质与贴图恢复仍不够轻,缓存命中后仍有额外同步成本。
---
## 2. 本轮收口的核心原则
### 2.1 不保留旧版本兼容
这是本轮与上一轮最大的区别。
本轮明确不做以下事情:
- 不兼容历史 `mesh=` / `materials=` 场景字段。
- 不兼容“项目资产仍按普通文件路径序列化”的旧格式。
- 不保留 `path + AssetRef` 双写作为长期状态。
- 不保留 `Migrate Scene AssetRefs` 之类的迁移工具与菜单。
允许保留的只有两类稳定引用:
- `AssetRef`
- 用于 `project/Assets` 下的正式项目资产。
- `builtin://...`
- 用于内置几何体、内置默认材质等虚拟内置资源。
这里的 `builtin://` 不是旧版本兼容,而是引擎内部稳定资源协议,可以继续保留。
### 2.2 只对当前项目做干净落地
因为当前只服务一个项目,所以这轮不需要为了“以后可能导入旧项目”保留复杂迁移链路。
本轮允许直接做以下事情:
- 直接改写 `project/Assets/Scenes/*.xc`
- 直接删除旧序列化字段的读写逻辑
- 直接删除旧迁移命令、菜单、报告结构
- 直接调整测试数据到最终格式
### 2.3 运行时只负责“用缓存”,不负责“解释历史”
收口完成后,运行时层要满足:
- scene 反序列化只恢复最终格式数据
- `ResourceManager` 只做运行时缓存、异步调度、artifact 读取入口
- 导入与索引职责留在 `AssetImportService` / `ProjectAssetIndex`
- 不再在运行时主链路里背负旧协议兼容判断
### 2.4 收口优先级高于“继续设计更大系统”
本轮目标是把现有系统收干净,不追求继续扩成更庞大的框架。
因此本轮只做两件事:
- 删除过渡态
- 补齐正式版闭环
不在本轮追求:
- prefab 全面资产管线化
- 动画、音频、shader 全类型导入器
- 远程缓存
- 多项目兼容框架
- 一步到位把所有内部类都拆到最细
---
## 3. 收口后的目标形态
### 3.1 最终数据协议
收口后scene / component 的资产引用协议定为:
- 项目 mesh只写 `meshRef`
- 项目 material只写 `materialRefs`
- 内置 mesh`meshPath=builtin://...`
- 内置 material`materialPaths=builtin://...`
不再允许以下状态作为正式协议存在:
- 项目资产依旧只存普通文件路径
- 项目资产同时写路径和 `AssetRef`
- 反序列化时优先猜测旧字段再兜底
### 3.2 最终运行时边界
收口后各层职责如下:
- `AssetImportService`
- 工程扫描
- `.meta` 管理
- SourceAssetDB / ArtifactDB 维护
- `EnsureArtifact`
- `Refresh`
- `Reimport` / `Clear Library` 入口
- `ProjectAssetIndex`
- 项目资产路径快照
- `AssetRef <-> 项目路径` 映射
- 项目资产查找缓存
- `ResourceManager`
- runtime object cache
- async load coalescing
- artifact runtime load dispatch
- deferred scene load 控制
- Component / Scene
- 只保存最终资产引用
- 不承担旧协议迁移职责
### 3.3 最终用户行为
收口后应达到以下体验:
- 第一次导入可能慢,但之后再打开同一个 scene不应再同步重跑原始 `obj/png/jpg` 导入。
- 打开含有大型 OBJ 的 scene 时,不应长时间卡死 editor 主窗口。
- 命中 artifact 后mesh/material 恢复应尽量走异步路径。
- 关闭 editor 再打开后,项目资产引用应稳定恢复,不出现“模型丢失、材质丢失、内置 sphere 丢失”这类状态回退问题。
- 用户能明确知道当前是“命中缓存”还是“正在导入/重建缓存”。
---
## 4. 本轮必须删除的过渡态
这部分是收口计划的关键,不删掉这些内容,系统就始终处于过渡状态。
### 4.1 删除旧序列化兼容
目标:
- 删除 `MeshFilterComponent` 对旧 `mesh=` 字段的长期兼容读取。
- 删除 `MeshRendererComponent` 对旧 `materials=` 字段的长期兼容读取。
- 删除“项目资产 path 与 `AssetRef` 双写”的长期行为。
允许保留:
-`builtin://` 的显式处理。
禁止保留:
- “先读 path不行再猜 `AssetRef`
- “为了兼容老场景继续支持项目文件路径”
### 4.2 删除场景迁移工具
以下过渡期工具应整体移除:
- `Migrate Scene AssetRefs` 菜单项
- 对应 action / command / project manager migration report
- 专门为旧场景迁移设计的 editor 流程
原因:
- 当前项目只有一个,已有 scene 可以直接重写。
- 继续保留迁移工具,只会让新系统一直背着旧协议。
### 4.3 删除长期双轨测试
需要同步清理测试中的过渡态假设:
- 旧格式 scene 兼容测试
- 双写格式测试
- 迁移工具测试
替换成正式版测试:
- 最终 scene 协议测试
- 关闭 editor 重开后的稳定恢复测试
- artifact 命中路径测试
- 异步恢复与渲染可见性测试
---
## 5. 本轮必须补齐的正式能力
### 5.1 材质与贴图真正 lazy 化
当前模型 artifact 虽然已经存在,但材质恢复后仍然容易把关联贴图一起拉进来,导致:
- 场景虽然不在反序列化阶段阻塞
- 但资源真正恢复时仍然偏重
本轮必须补齐:
- `xcmat` 成为正式稳定 artifact
- `Material` 运行时对象只持有 texture `AssetRef`
- texture runtime handle 按槽位、按需解析
- mesh 恢复不顺手触发整套材质贴图同步加载
这是“缓存命中了但还是觉得重”的核心收口项。
### 5.2 明确导入与运行时边界
虽然最近已经引入:
- `AssetImportService`
- `ProjectAssetIndex`
但本轮要进一步明确:
- `ResourceManager` 外部不再暴露旧式导入心智
- 项目资产查找统一经过 `ProjectAssetIndex`
- artifact 生成统一经过 `AssetImportService`
- `AssetDatabase` 作为底层实现细节继续收口到 service 后面
本轮的目标不是一定把 `AssetDatabase` 拆成三个类,而是先把“对外边界”收干净。
也就是说,本轮要求:
- 类内可暂时还偏胖
- 但对外职责必须已经清晰
### 5.3 缓存运维闭环
正式版至少要有以下最小运维能力:
- 单资源 Reimport
- `Reimport All`
- `Clear Library`
- 项目启动时检查并重建缺失缓存
- 删除源资源后识别 orphan artifact
如果这些没有,系统虽然能工作,但一旦缓存脏掉就难以恢复,仍然不算收口。
### 5.4 可观测性闭环
本轮至少要做到用户能看见:
- 当前资源是命中 artifact 还是触发重导入
- 正在导入哪个资源
- 导入失败原因
- scene 正在等待哪些资源恢复
不要求本轮先做完整面板,但至少要把日志、状态文案、必要的调试输出整理成稳定可用的最小版本。
---
## 6. 实施阶段
## 阶段 A协议定稿与旧兼容删除
### 目标
把当前半兼容、半正式的 scene / component 数据协议收成最终格式。
### 任务
-`MeshFilterComponent`
- 项目资产只写 `meshRef`
- 只对 `builtin://` 保留 `meshPath`
- 删除旧 `mesh=` 历史兼容读取
-`MeshRendererComponent`
- 项目资产只写 `materialRefs`
- 只对 `builtin://` 保留 `materialPaths`
- 删除旧 `materials=` 历史兼容读取
- 直接改写 `project/Assets/Scenes/Main.xc`
- 直接改写 `project/Assets/Scenes/Backpack.xc`
- 如果项目里还有其他 scene / snapshot / prefab 使用旧项目资产路径,一并直接改成最终格式
### 同步删除
- `Migrate Scene AssetRefs` action
- `ProjectCommands` 中对应 migration command
- `ProjectManager` 中对应 migration report / migration logic
- 相关测试
### 阶段完成标准
- 项目场景中不再存在旧项目资产路径协议
- engine/editor 中不再存在迁移工具入口
- 组件反序列化代码只处理最终协议与 builtin 协议
## 阶段 B导入与缓存边界收口
### 目标
`Library` 系统对外呈现为一套明确的正式接口,而不是几个类拼在一起的过渡实现。
### 任务
- 把项目资产索引访问统一走 `ProjectAssetIndex`
- 把 artifact 生成与保证统一走 `AssetImportService`
- 收敛 `ResourceManager` 中剩余的导入数据库耦合
- 梳理 `AssetDatabase` 内部接口,限制它作为 service 内部实现使用
- 增加明确的 `Refresh / Reimport / Clear Library` 入口
### 阶段完成标准
- editor / runtime 外部调用时,心智模型已经稳定为:
- 找项目资产:`ProjectAssetIndex`
- 产出或校验 artifact`AssetImportService`
- 取 runtime object`ResourceManager`
## 阶段 C性能闭环
### 目标
把“看起来已经异步,但恢复仍然偏重”的成本继续压下去。
### 任务
- 材质贴图 lazy resolve
- 减少 mesh 恢复时顺手加载整套关联资源
- 校验首次导入、二次打开、关闭重开三种路径的时间差异
- 清理仍可能在主线程触发的大锁与同步等待点
- 必要时加入轻量 placeholder / loading state 文案
### 阶段完成标准
- 含 OBJ 的 scene 再次打开时editor 主窗口不出现长时间假死
- artifact 命中时恢复成本显著低于首次导入
- 关闭 editor 重开后的恢复链路仍然稳定
## 阶段 D最小工具闭环
### 目标
把这套系统变成“可维护”的,而不是只能靠调试日志救火。
### 任务
- 已完成:增加 `Reimport Asset`
- 已完成:增加 `Reimport All`
- 已完成:增加 `Clear Library`
- 已完成:增加最小导入状态输出
- 已完成:增加 orphan artifact 清理
### 阶段完成标准
- 用户可以主动重建缓存
- 缓存脏掉时不需要手工删文件再碰运气
- 导入失败时能直接看到原因
---
## 7. 验收标准
本轮收口完成,必须同时满足以下条件:
### 7.1 协议层
- 项目资产引用正式统一为 `AssetRef`
- 不再保留旧 scene / component 项目资产路径兼容协议
- 不再保留迁移工具
### 7.2 架构层
- `AssetImportService` / `ProjectAssetIndex` / `ResourceManager` 的对外职责稳定
- 运行时不再承担旧协议解释职责
- `Library` 已经是正式依赖,而不是临时缓存补丁
### 7.3 功能层
- OBJ / 贴图 / 材质二次打开优先命中 artifact
- 关闭 editor 重开后项目 scene 能稳定恢复
- builtin sphere / cube / default material 不会因为缓存系统而丢失
### 7.4 性能层
- 打开含大型 OBJ 的 scene 时不长时间卡死 editor
- 二次打开与首次导入的体感耗时有明显区分
- 命中 artifact 后不再退回源文件同步导入主路径
### 7.5 工具层
- 具备最小 `Reimport / Reimport All / Clear Library` 能力
- 有最小可观测性输出
---
## 8. 本轮不做的内容
为了尽快收口,本轮明确不做以下扩展:
- prefab 全量资产协议翻新
- 动画 / 音频 / shader 新 importer
- 远程缓存
- 多项目历史版本兼容框架
- 完整图形化导入面板
- 一次性把所有 `AssetDatabase` 内部实现拆到最细
这些内容后续可以继续做,但不应该阻塞当前正式收口。
---
## 9. 推荐执行顺序
建议按下面顺序推进,避免边做边反复回滚:
1. 先做阶段 A直接把项目协议和旧兼容删干净。
2. 再做阶段 B把对外边界稳定下来。
3. 然后做阶段 C把“缓存命中了但恢复仍然重”的问题收掉。
4. 最后做阶段 D补齐最小运维与可观测性。
原因是:
- 不先删旧兼容,后面所有逻辑都要双轨维护。
- 不先把边界定清,性能问题会一直和历史兼容问题缠在一起。
- 不先把主链路收稳,工具做出来也只是给过渡态续命。
---
## 10. 本轮涉及的主要代码范围
本轮预计重点落在以下文件:
- `engine/src/Components/MeshFilterComponent.cpp`
- `engine/src/Components/MeshRendererComponent.cpp`
- `engine/include/XCEngine/Core/Asset/ResourceManager.h`
- `engine/src/Core/Asset/ResourceManager.cpp`
- `engine/include/XCEngine/Core/Asset/AssetImportService.h`
- `engine/src/Core/Asset/AssetImportService.cpp`
- `engine/include/XCEngine/Core/Asset/ProjectAssetIndex.h`
- `engine/src/Core/Asset/ProjectAssetIndex.cpp`
- `engine/include/XCEngine/Core/Asset/AssetDatabase.h`
- `engine/src/Core/Asset/AssetDatabase.cpp`
- `editor/src/Managers/ProjectManager.cpp`
- `editor/src/Commands/ProjectCommands.h`
- `editor/src/Actions/EditorActions.h`
- `editor/src/Actions/MainMenuActionRouter.h`
- `tests/core/Asset/test_resource_manager.cpp`
- `tests/Components/test_mesh_render_components.cpp`
- `tests/Scene/test_scene.cpp`
- `project/Assets/Scenes/Main.xc`
- `project/Assets/Scenes/Backpack.xc`
---
## 11. 一句话结论
从这一刻开始,这个模块的目标不再是“继续兼容旧系统”,而是:
把当前已经跑起来的 `Library`、artifact、`AssetRef`、异步恢复链路,收成一套只服务当前项目、无旧包袱、可稳定重开的正式资产系统。