# 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`、异步恢复链路,收成一套只服务当前项目、无旧包袱、可稳定重开的正式资产系统。