20 KiB
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 hintAssetDatabaseimporter 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.ConcurrentAsyncLoadsCoalesceSameMeshPathResourceManager_Test.AssetLookupFallbackRefreshesSnapshotForNewProjectAssetProjectAssetIndex_Test.RefreshesSnapshotThroughImportServiceOnCacheMissAssetImportService_Test.RebuildLibraryCacheKeepsStableAssetRefsResourceManager_Test.RebuildProjectAssetCacheRefreshesLookupStateAssetImportService_Test.ClearLibraryAndReimportAllAssetsManageArtifactsExplicitlyAssetImportService_Test.ImportStatusTracksExplicitOperationsAndRefreshCleanupResourceManager_Test.ReimportProjectAssetBuildsArtifactForSelectedPath
editor_tests:EditorActionRoutingTest.ProjectCommandsExposeAssetCacheMaintenanceActionsEditorActionRoutingTest.ProjectCommandsReimportSelectedAssetAndClearLibraryDriveAssetCacheEditorActionRoutingTest.ProjectCommandsReportWhenScriptAssembliesCanBeRebuilt
components_tests:MeshRendererComponent_Test.SerializeAndDeserializeLoadsProjectMaterialByAssetRefMeshRendererComponent_Test.DeferredSceneDeserializeLoadsProjectMaterialAsync
material_tests:46/46 通过mesh_tests:33/33 通过scene_tests:Scene_ProjectSample.DeferredLoadBackpackSceneEventuallyRestoresBackpackMeshScene_ProjectSample.DeferredLoadBackpackSceneEventuallyProducesVisibleRenderItems
XCEditorRelease 已成功编译
旧方案文档已经归档到:
docs/plan/used/Unity式Library资产导入与缓存系统重构方案.md
归档原因很简单:
- 旧文档解决的是“从没有 Library,到建立 Library 基础设施”的问题。
- 现在的问题已经不是“要不要做 Library”,而是“怎么把当前这套半过渡、半正式的实现收成一套干净的正式系统”。
本轮收口以以下前提为准:
- 只服务当前
project这一个项目。 - 不兼容旧版本 scene / component 资产引用格式。
- 不再提供旧格式迁移工具。
- 可以直接改写
project下现有场景和资源源文件。 - 目标不是继续堆补丁,而是形成一套边界清晰、行为稳定、可长期维护的正式实现。
1. 当前收口基线
当前系统已经具备以下基础,这些不是本轮要推倒重来,而是本轮收口的起点:
- 已有 Unity 风格的
Library目录结构:Library/SourceAssetDBLibrary/ArtifactDBLibrary/Artifacts
- 已有
.meta + AssetGUID机制,项目资产已经具备稳定身份。 - 已有
AssetRef结构,运行时已经可以通过AssetRef回查项目资产。 - 已有纹理、材质、模型 artifact:
xctexxcmatxcmesh
- 已有
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 维护
EnsureArtifactRefreshReimport/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成为正式稳定 artifactMaterial运行时对象只持有 textureAssetRef- texture runtime handle 按槽位、按需解析
- mesh 恢复不顺手触发整套材质贴图同步加载
这是“缓存命中了但还是觉得重”的核心收口项。
5.2 明确导入与运行时边界
虽然最近已经引入:
AssetImportServiceProjectAssetIndex
但本轮要进一步明确:
ResourceManager外部不再暴露旧式导入心智- 项目资产查找统一经过
ProjectAssetIndex - artifact 生成统一经过
AssetImportService AssetDatabase作为底层实现细节继续收口到 service 后面
本轮的目标不是一定把 AssetDatabase 拆成三个类,而是先把“对外边界”收干净。
也就是说,本轮要求:
- 类内可暂时还偏胖
- 但对外职责必须已经清晰
5.3 缓存运维闭环
正式版至少要有以下最小运维能力:
- 单资源 Reimport
Reimport AllClear 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 AssetRefsactionProjectCommands中对应 migration commandProjectManager中对应 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. 推荐执行顺序
建议按下面顺序推进,避免边做边反复回滚:
- 先做阶段 A,直接把项目协议和旧兼容删干净。
- 再做阶段 B,把对外边界稳定下来。
- 然后做阶段 C,把“缓存命中了但恢复仍然重”的问题收掉。
- 最后做阶段 D,补齐最小运维与可观测性。
原因是:
- 不先删旧兼容,后面所有逻辑都要双轨维护。
- 不先把边界定清,性能问题会一直和历史兼容问题缠在一起。
- 不先把主链路收稳,工具做出来也只是给过渡态续命。
10. 本轮涉及的主要代码范围
本轮预计重点落在以下文件:
engine/src/Components/MeshFilterComponent.cppengine/src/Components/MeshRendererComponent.cppengine/include/XCEngine/Core/Asset/ResourceManager.hengine/src/Core/Asset/ResourceManager.cppengine/include/XCEngine/Core/Asset/AssetImportService.hengine/src/Core/Asset/AssetImportService.cppengine/include/XCEngine/Core/Asset/ProjectAssetIndex.hengine/src/Core/Asset/ProjectAssetIndex.cppengine/include/XCEngine/Core/Asset/AssetDatabase.hengine/src/Core/Asset/AssetDatabase.cppeditor/src/Managers/ProjectManager.cppeditor/src/Commands/ProjectCommands.heditor/src/Actions/EditorActions.heditor/src/Actions/MainMenuActionRouter.htests/core/Asset/test_resource_manager.cpptests/Components/test_mesh_render_components.cpptests/Scene/test_scene.cppproject/Assets/Scenes/Main.xcproject/Assets/Scenes/Backpack.xc
11. 一句话结论
从这一刻开始,这个模块的目标不再是“继续兼容旧系统”,而是:
把当前已经跑起来的 Library、artifact、AssetRef、异步恢复链路,收成一套只服务当前项目、无旧包袱、可稳定重开的正式资产系统。