diff --git a/docs/api/XCEngine/Core/Asset/ArtifactFormats/ArtifactFormats.md b/docs/api/XCEngine/Core/Asset/ArtifactFormats/ArtifactFormats.md new file mode 100644 index 00000000..b550b338 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/ArtifactFormats/ArtifactFormats.md @@ -0,0 +1,226 @@ +# ArtifactFormats + +**命名空间**: `XCEngine::Resources` + +**类型**: `header-overview` + +**头文件**: `XCEngine/Core/Asset/ArtifactFormats.h` + +**描述**: 定义 `Texture`、`Material`、`Mesh` 与 `Shader` artifact 的二进制头结构和 schema 常量,供 `AssetDatabase` 写出、各 loader 回读共享。 + +## 角色概述 + +`ArtifactFormats.h` 是项目资产导入链路的磁盘格式契约层。 + +它回答的是: + +- `AssetDatabase` 把 source asset 导入到 `Library/Artifacts/...` 时,头结构和 magic 是什么 +- `TextureLoader`、`MaterialLoader`、`MeshLoader`、`ShaderLoader` 回读时,二进制布局该怎样解释 + +它不负责: + +- 扫描 `Assets` +- 生成 GUID +- 维护 source / artifact 数据库 +- 直接触发运行时异步加载 + +## 当前 schema 常量 + +- `kTextureArtifactSchemaVersion = 1` +- `kMaterialArtifactSchemaVersion = 2` +- `kMeshArtifactSchemaVersion = 2` +- `kShaderArtifactSchemaVersion = 1` + +## Texture artifact: `.xctex` + +- 头结构: `TextureArtifactHeader` +- magic: `XCTEX01` + +`TextureArtifactHeader` 当前记录: + +- `textureType` +- `textureFormat` +- `width` +- `height` +- `depth` +- `mipLevels` +- `arraySize` +- `pixelDataSize` + +写入顺序当前是: + +1. `TextureArtifactHeader` +2. 原始像素 payload + +## Material artifact: `.xcmat` + +- 文件头: `MaterialArtifactFileHeader` +- 主头结构: `MaterialArtifactHeader` +- 属性结构: `MaterialPropertyArtifact` +- schema: `2` +- magic: `XCMAT02` + +### 当前正文布局 + +`.xcmat` 当前不是“一个大 struct 一次性写完”,而是“文件头 + 变长字符串段 + 固定头 + 变长数组”的布局: + +1. `MaterialArtifactFileHeader` +2. 材质名字符串 +3. source 材质路径字符串 +4. shader 路径字符串 +5. shader pass 字符串 +6. `MaterialArtifactHeader` +7. `tagCount` 对 tag 名/值字符串 +8. `propertyCount` 个属性名字符串 + `MaterialPropertyArtifact` +9. `textureBindingCount` 组 texture binding 三元组: + - binding name + - 编码后的 `AssetRef` 字符串 + - 可选 texture path 字符串 + +### `MaterialArtifactHeader` + +当前记录: + +- `renderQueue` +- `renderState` +- `tagCount` +- `propertyCount` +- `textureBindingCount` + +### `MaterialPropertyArtifact` + +只记录: + +- `propertyType` +- `MaterialProperty::Value` + +当前 writer 只把非 texture 属性写进 property 段;texture / cubemap 绑定统一走 texture binding 段。 + +### texture binding v2 语义 + +texture binding 当前不再只保存 path。 + +写入侧 `AssetDatabase::WriteMaterialArtifactFile(...)` 会尽量为每个 binding 同时保存: + +- 编码 `AssetRef` + 格式是 `guid,localID,resourceTypeInt` +- 可选 path + +`AssetRef` 的求值优先级当前是: + +1. binding 自己显式持有的 `AssetRef` +2. 已加载纹理句柄映射出的 `AssetRef` +3. 纹理 path 反查得到的 `AssetRef` + +path 的求值优先级当前是: + +1. 已加载纹理映射出的 artifact / source path +2. binding 自己保存的 `texturePath` + +回读侧 `MaterialLoader` 当前会: + +- 先解码 `AssetRef` +- 再解析 path +- 有效 `AssetRef` 优先走 `Material::SetTextureAssetRef(...)` +- 否则退到 `Material::SetTexturePath(...)` + +这意味着 `.xcmat` v2 可以只靠 `AssetRef` 恢复稳定引用,path 只作为可选辅助信息存在。 + +## Mesh artifact: `.xcmesh` + +- 头结构: `MeshArtifactHeader` +- schema: `2` +- magic: `XCMESH2` + +`MeshArtifactHeader` 当前记录: + +- `vertexCount` +- `vertexStride` +- `vertexAttributes` +- `indexCount` +- `use32BitIndex` +- `sectionCount` +- `materialCount` +- `boundsMin` +- `boundsMax` +- `vertexDataSize` +- `indexDataSize` + +`.xcmesh` 当前写入顺序是: + +1. `MeshArtifactHeader` +2. `sectionCount` 个 `MeshSection` +3. 顶点数据 blob +4. 索引数据 blob +5. `materialCount` 个材质 artifact 路径字符串 + +## Shader artifact + +- 文件头: `ShaderArtifactFileHeader` +- 主头结构: `ShaderArtifactHeader` +- pass 头结构: `ShaderPassArtifactHeader` +- 属性结构: `ShaderPropertyArtifact` +- 资源结构: `ShaderResourceArtifact` +- variant 头结构: `ShaderVariantArtifactHeader` +- magic: `XCSHD01` + +当前 `ArtifactFormats.h` 已经把 shader artifact 的头结构一起纳入契约。 + +## 导入与消费链路 + +### 写入侧 + +- `AssetDatabase::ImportTextureAsset()` 产出 `main.xctex` +- `AssetDatabase::ImportMaterialAsset()` 产出 `main.xcmat` +- `AssetDatabase::ImportModelAsset()` 产出 `main.xcmesh` +- `AssetDatabase::ImportShaderAsset()` 产出 shader artifact + +模型导入还会在同一个 artifact 目录里附带生成: + +- `texture_.xctex` +- `material_.xcmat` + +### 读取侧 + +- `TextureLoader` 回读 `.xctex` +- `MaterialLoader` 回读 `.xcmat` +- `MeshLoader` 回读 `.xcmesh` + +`ResourceManager::LoadResource()` 在项目模式下会先经 `AssetImportService::EnsureArtifact()` 准备 `ImportedAsset`,再把 `runtimeLoadPath` 交给具体 loader。 + +## 兼容性与边界 + +### 当前版本语义 + +- loader 目前显式检查的仍主要是 magic +- `schemaVersion` 已经写进文件头,但当前代码没有基于它做多版本分支读取 + +因此当前兼容策略更接近“magic + 整套代码同步升级”,而不是长期维护多代 reader。 + +### 二进制稳定性边界 + +当前 artifact 文件仍直接写入/读回这些 C++ 原生布局: + +- `TextureArtifactHeader` +- `MaterialArtifactFileHeader` +- `MaterialArtifactHeader` +- `MaterialPropertyArtifact` +- `MeshArtifactHeader` +- `ShaderArtifactFileHeader` +- `ShaderArtifactHeader` +- `ShaderPassArtifactHeader` +- `ShaderPropertyArtifact` +- `ShaderResourceArtifact` +- `ShaderVariantArtifactHeader` + +这意味着它本质上仍是引擎内部缓存格式,不是长期稳定的外部交换格式。 + +## 相关文档 + +- [当前模块](../Asset.md) +- [AssetDatabase](../AssetDatabase/AssetDatabase.md) +- [AssetRef](../AssetRef/AssetRef.md) +- [ResourceManager](../ResourceManager/ResourceManager.md) +- [Material](../../../Resources/Material/Material/Material.md) +- [MaterialLoader](../../../Resources/Material/MaterialLoader/MaterialLoader.md) +- [API 总索引](../../../../main.md) diff --git a/docs/api/XCEngine/Core/Asset/Asset.md b/docs/api/XCEngine/Core/Asset/Asset.md index 5c730536..9fe42315 100644 --- a/docs/api/XCEngine/Core/Asset/Asset.md +++ b/docs/api/XCEngine/Core/Asset/Asset.md @@ -4,41 +4,68 @@ **类型**: `submodule` -**描述**: 定义运行时资源系统的标识、句柄、缓存、加载和依赖管理基础设施。 +**描述**: 定义项目资产数据库、artifact 缓存、运行时资源加载、句柄、缓存与项目资产查询快照这一整套资源基础设施。 -## 概览 +## 概述 -`Core/Asset` 是当前引擎运行时资源系统的基础层。它并不直接实现材质、着色器、网格或音频资源本身,而是提供一套更底层的通用约定: +当前 `Core/Asset` 已经不只是“运行时按路径加载资源”的薄层接口,而是可以分成三层来看: -- 用 [ResourceTypes](ResourceTypes/ResourceTypes.md) 定义资源类型和 GUID 规则 -- 用 [IResource](IResource/IResource.md) 约束资源对象的最小公共接口 -- 用 [ImportSettings](ImportSettings/ImportSettings.md) 传递资源导入配置 -- 用 [ResourceManager](ResourceManager/ResourceManager.md) 做同步加载、加载器注册和引用计数入口 -- 用 [ResourceHandle](ResourceHandle/ResourceHandle.md) 在上层代码里传递带类型的资源引用 -- 用 [ResourceCache](ResourceCache/ResourceCache.md)、[AsyncLoader](AsyncLoader/AsyncLoader.md)、[ResourceDependencyGraph](ResourceDependencyGraph/ResourceDependencyGraph.md) 为缓存、异步加载和依赖卸载预留基础结构 +1. [AssetDatabase](AssetDatabase/AssetDatabase.md) + 负责扫描项目 `Assets`、维护 `.meta`、保存 source/artifact 索引,并在需要时把源资产导入成 `Library/Artifacts/...` 下的 artifact。 +2. [AssetImportService](AssetImportService/AssetImportService.md) + [ProjectAssetIndex](ProjectAssetIndex/ProjectAssetIndex.md) + 负责把 `AssetDatabase` 包装成线程安全服务,并导出供热路径查询使用的 path/GUID snapshot。 +3. [ResourceManager](ResourceManager/ResourceManager.md) + 负责运行时加载、缓存、句柄与 loader 分发;当设置了项目根目录后,它会先经 `AssetImportService` 准备 artifact,再通过 `ProjectAssetIndex` 做 `AssetRef` / 路径查询。 -从设计意图上看,这一层更接近商业引擎里“运行时资源服务”的雏形,而不是 Unity `AssetDatabase` 那类编辑器资产数据库。它强调的是运行时可加载、可引用、可扩展,而不是导入流水线和编辑器管理界面。 +这意味着当前资源系统的真实链路是: + +```text +Assets/... source files +-> AssetDatabase +-> AssetImportService +-> ProjectAssetIndex snapshot +-> ResourceManager +-> concrete loader / runtime resource +``` + +其中,[AssetGUID](AssetGUID/AssetGUID.md)、[AssetRef](AssetRef/AssetRef.md) 和 [ArtifactFormats](ArtifactFormats/ArtifactFormats.md) 分别补上了“资产身份”“资产引用”和“artifact 磁盘格式”这三块基础协议。 + +## 当前实现关系 + +- `editor/src/Application.cpp` 初始化编辑器时会调用 `ResourceManager::SetResourceRoot(projectRoot)`,从而绑定项目根、初始化 `ResourceFileSystem`,并刷新 `ProjectAssetIndex` 快照。 +- `ResourceManager::LoadResource()` 在加载项目资产时,会先调用 `AssetImportService::EnsureArtifact()`;只有 artifact 就绪后,才把 `ImportedAsset::runtimeLoadPath` 交给具体 loader。 +- `ProjectAssetIndex::TryGetAssetRef()` 当前会优先查本地 snapshot,cache miss 时再回退到 `AssetImportService`;必要时会先 `Refresh()` 数据库再整体重建 snapshot。 +- `ProjectAssetIndex::TryResolveAssetPath()` 当前同样优先查 snapshot,但 miss 时只回退到 `AssetImportService::TryGetPrimaryAssetPath()`,不会主动刷新整份 snapshot。 +- `engine/src/Resources/Material/MaterialLoader.cpp` 现在已经把材质纹理路径解析、`.xcmat` 中的 texture `AssetRef` 回读,以及首次访问时的懒加载纳入真实行为范围,因此 `AssetDatabase` 的材质依赖快照不再只是预留设计。 +- `.xcmat` 当前已经是 material artifact v2:texture binding 会同时写出编码后的 `AssetRef` 和可选路径字符串,后续由 `MaterialLoader` 与 `Material` 协作懒解析成真正贴图句柄。 ## 设计要点 -- 资源身份和资源对象分离。`ResourceGUID` 负责标识,`IResource` 负责对象实例,`ResourceHandle` 负责类型化访问。 -- 资源类型分派依赖 `GetResourceType()` 模板特化,而不是 RTTI 或字符串注册。 -- `ResourceManager` 当前是主入口,但它和 `ResourceCache`、`AsyncLoader`、`ResourceDependencyGraph` 的集成还没有完全做完。 -- 当前自动注册的内建 loader 只有 `MaterialLoader` 和 `ShaderLoader`。 -- 这套接口已经有明显的“未来做流式加载和资源依赖卸载”的方向,但当前很多能力仍处于占位或半成品状态。 +- 把“项目资产身份与导入缓存”和“运行时资源实例加载”拆成两层,避免 `ResourceManager` 直接承载 GUID、`.meta` 和 artifact 细节。 +- 再引入 `AssetImportService + ProjectAssetIndex` 作为服务层和只读快照层,把线程同步、项目根切换和高频查询从 `AssetDatabase` 与 `ResourceManager` 本体里拆出来。 +- `AssetDatabase` 通过 `guid + importer + source/meta/dependency snapshot` 生成 `artifactKey`,让重导入条件可复现。 +- `ResourceManager` 仍保留 `builtin://` 这类虚拟路径直载能力;只有能解析到项目 `Assets` 的路径才会进入项目资产链路。 +- `ResourceHandle`、`ResourceCache` 和 `AsyncLoader` 继续围绕运行时 `IResource` 实例运转,而不是直接把 source asset 当作运行时对象。 -## 当前实现现状 +## 当前实现边界 -- 同步加载路径是当前最可靠的使用方式。 -- `Load()` 直接把传入路径交给 loader,当前不会自动调用 `ResolvePath()`,所以 `SetResourceRoot()` 只是一个路径拼接辅助,不是统一的实际加载入口。 -- `LoadAsync()` 已经有 API 形状,但当前队列不会被后台线程消费,成功回调路径也没有闭环,不能把它当成成熟的异步流式系统。 -- `ResourceManager`、`ResourceCache` 和句柄引用计数之间还没有完全打通,文档里会明确标出这些差异,而不把它们包装成“完整资源生命周期管理”。 +- `AssetDatabase` 当前只有 `TextureImporter`、`MaterialImporter` 和 `ModelImporter` 会产出 artifact;`FolderImporter` 和 `DefaultImporter` 只保留 source 记录。 +- `.meta` 文件当前只保存 `guid`、`folderAsset`、`importer` 和 `importerVersion`,还没有持久化 importer 专属设置。 +- `ProjectAssetIndex` 当前只缓存主资产路径,生成的 `AssetRef` 仍默认使用 `kMainAssetLocalID`。 +- `ResourceManager` 会在同步加载路径里触发 artifact 生成,但当前没有后台导入队列或文件监听闭环。 +- `LoadAsync()` / `AsyncLoader` 当前已经具备真实的工作线程与完成队列闭环,但回调仍依赖 `Update()` 手动轮询分发,取消与配置所有权语义也还比较原始。 ## 头文件 +- [ArtifactFormats](ArtifactFormats/ArtifactFormats.md) - `ArtifactFormats.h` +- [AssetDatabase](AssetDatabase/AssetDatabase.md) - `AssetDatabase.h` +- [AssetGUID](AssetGUID/AssetGUID.md) - `AssetGUID.h` +- [AssetImportService](AssetImportService/AssetImportService.md) - `AssetImportService.h` +- [AssetRef](AssetRef/AssetRef.md) - `AssetRef.h` - [AsyncLoader](AsyncLoader/AsyncLoader.md) - `AsyncLoader.h` - [ImportSettings](ImportSettings/ImportSettings.md) - `ImportSettings.h` - [IResource](IResource/IResource.md) - `IResource.h` +- [ProjectAssetIndex](ProjectAssetIndex/ProjectAssetIndex.md) - `ProjectAssetIndex.h` - [ResourceCache](ResourceCache/ResourceCache.md) - `ResourceCache.h` - [ResourceDependencyGraph](ResourceDependencyGraph/ResourceDependencyGraph.md) - `ResourceDependencyGraph.h` - [ResourceHandle](ResourceHandle/ResourceHandle.md) - `ResourceHandle.h` @@ -47,10 +74,11 @@ ## 推荐阅读顺序 -1. 先读 [ResourceTypes](ResourceTypes/ResourceTypes.md),理解 `ResourceType` 和 `ResourceGUID`。 -2. 再读 [IResource](IResource/IResource.md) 与 [ImportSettings](ImportSettings/ImportSettings.md),理解资源对象和导入配置契约。 -3. 然后读 [ResourceManager](ResourceManager/ResourceManager.md) 与 [ResourceHandle](ResourceHandle/ResourceHandle.md),这是当前实际最常用的运行时入口。 -4. 最后再读 [ResourceCache](ResourceCache/ResourceCache.md)、[AsyncLoader](AsyncLoader/AsyncLoader.md)、[ResourceDependencyGraph](ResourceDependencyGraph/ResourceDependencyGraph.md),它们更偏向扩展机制和当前限制说明。 +1. 先读 [AssetGUID](AssetGUID/AssetGUID.md) 与 [AssetRef](AssetRef/AssetRef.md),理解项目资产的身份与引用契约。 +2. 再读 [AssetDatabase](AssetDatabase/AssetDatabase.md) 与 [ArtifactFormats](ArtifactFormats/ArtifactFormats.md),理解 source asset 如何导入为 `Library/Artifacts` 下的 artifact。 +3. 然后读 [AssetImportService](AssetImportService/AssetImportService.md) 与 [ProjectAssetIndex](ProjectAssetIndex/ProjectAssetIndex.md),理解数据库如何被包装成线程安全服务和热路径查询缓存。 +4. 接着读 [ResourceManager](ResourceManager/ResourceManager.md) 与 [ResourceHandle](ResourceHandle/ResourceHandle.md),理解运行时如何消费这些 artifact 和 snapshot。 +5. 最后再看 [ResourceCache](ResourceCache/ResourceCache.md)、[AsyncLoader](AsyncLoader/AsyncLoader.md) 和 [ResourceDependencyGraph](ResourceDependencyGraph/ResourceDependencyGraph.md),理解缓存、异步与依赖管理的现状。 ## 相关指南 diff --git a/docs/api/XCEngine/Core/Asset/AssetDatabase/ArtifactDependencyRecord.md b/docs/api/XCEngine/Core/Asset/AssetDatabase/ArtifactDependencyRecord.md new file mode 100644 index 00000000..1f306e4d --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetDatabase/ArtifactDependencyRecord.md @@ -0,0 +1,30 @@ +# AssetDatabase::ArtifactDependencyRecord + +**命名空间**: `XCEngine::Resources` + +**类型**: `struct` + +**头文件**: `XCEngine/Core/Asset/AssetDatabase.h` + +**描述**: 记录单个依赖文件的规范化路径与内容快照,用于判断 artifact 是否需要因依赖变化而重导入。 + +## 字段 + +| 字段 | 类型 | 说明 | +|------|------|------| +| `path` | `Containers::String` | 依赖文件路径。优先记录为项目相对路径,否则保留规范化后的路径字符串。 | +| `hash` | `Containers::String` | 依赖文件内容哈希。 | +| `fileSize` | `Core::uint64` | 依赖文件大小快照。 | +| `writeTime` | `Core::uint64` | 依赖文件写时间快照。 | + +## 当前实现语义 + +- 材质导入会把纹理绑定路径收集成依赖记录;这些路径来自 `MaterialLoader` 解析后的真实绑定路径。 +- 模型导入会收集 `obj -> mtl -> texture` 这条声明依赖链路,也会补充导入后 mesh 上暴露出的纹理路径。 +- `AreDependenciesCurrent()` 会同时比较文件是否存在、大小、写时间和哈希;任意一个变化都会触发重导入。 +- 如果某个依赖在记录时就不存在,则它会以空哈希和零大小/写时间写入;之后该文件一旦出现,也会被视为依赖变化并触发重导入。 + +## 相关文档 + +- [AssetDatabase](AssetDatabase.md) +- [ArtifactRecord](ArtifactRecord.md) diff --git a/docs/api/XCEngine/Core/Asset/AssetDatabase/ArtifactRecord.md b/docs/api/XCEngine/Core/Asset/AssetDatabase/ArtifactRecord.md new file mode 100644 index 00000000..3f62da57 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetDatabase/ArtifactRecord.md @@ -0,0 +1,40 @@ +# AssetDatabase::ArtifactRecord + +**命名空间**: `XCEngine::Resources` + +**类型**: `struct` + +**头文件**: `XCEngine/Core/Asset/AssetDatabase.h` + +**描述**: 保存某个 GUID 当前 artifact 结果的索引记录,是 `Library/ArtifactDB/artifacts.db` 的核心行结构。 + +## 字段 + +| 字段 | 类型 | 说明 | +|------|------|------| +| `artifactKey` | `Containers::String` | 基于 GUID、导入器、source/meta/dependency 快照计算出的唯一 key。 | +| `assetGuid` | `AssetGUID` | 对应的 source asset GUID。 | +| `importerName` | `Containers::String` | 生成该 artifact 时使用的 importer 名称。 | +| `importerVersion` | `Core::uint32` | 生成该 artifact 时使用的 importer 版本。 | +| `resourceType` | `ResourceType` | 该 artifact 的主资源类型。 | +| `artifactDirectory` | `Containers::String` | artifact 目录的项目相对路径,通常位于 `Library/Artifacts/...`。 | +| `mainArtifactPath` | `Containers::String` | 主 artifact 文件的项目相对路径。 | +| `sourceHash` | `Containers::String` | 导入时的 source 内容哈希。 | +| `metaHash` | `Containers::String` | 导入时的 `.meta` 哈希。 | +| `sourceFileSize` | `Core::uint64` | 导入时的 source 文件大小。 | +| `sourceWriteTime` | `Core::uint64` | 导入时的 source 写时间。 | +| `mainLocalID` | `LocalID` | 当前主资产 local ID,默认是 `kMainAssetLocalID`。 | +| `dependencies` | `std::vector` | 导入时捕获的依赖快照。 | + +## 当前实现语义 + +- `artifactDirectory` / `mainArtifactPath` 在数据库里保存的是相对项目根的 `Library/...` 路径;`EnsureArtifact()` 返回给调用方时才会展开成绝对路径。 +- `ShouldReimport()` 会拿这里的 importer 版本、source/meta 快照和依赖快照与当前文件系统状态比较,决定是否需要重导入。 +- 主 artifact 文件缺失时,即使其他快照字段都没变,也会触发重建。 +- 当前主 `localID` 还没有扩展成完整子资产系统,因此大多数调用都保持主资产值。 + +## 相关文档 + +- [AssetDatabase](AssetDatabase.md) +- [ArtifactDependencyRecord](ArtifactDependencyRecord.md) +- [ResolvedAsset](ResolvedAsset.md) diff --git a/docs/api/XCEngine/Core/Asset/AssetDatabase/AssetDatabase.md b/docs/api/XCEngine/Core/Asset/AssetDatabase/AssetDatabase.md new file mode 100644 index 00000000..497b6a0f --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetDatabase/AssetDatabase.md @@ -0,0 +1,231 @@ +# AssetDatabase + +**命名空间**: `XCEngine::Resources` + +**类型**: `class` + +**头文件**: `XCEngine/Core/Asset/AssetDatabase.h` + +**描述**: 管理项目 `Assets` 目录里的 source asset、`.meta` sidecar、source/artifact 快照以及 artifact 导入结果的项目资产数据库。 + +## 概览 + +`AssetDatabase` 处理的是“项目资产”和“artifact 索引”,而不是已经加载进内存的运行时资源实例。 + +它当前的真实职责是: + +1. 扫描项目根目录下的 `Assets` +2. 为每个 source asset 或文件夹确保存在 `.meta` sidecar,并维持稳定 GUID +3. 保存 source 侧快照和 artifact 侧快照 +4. 在需要时把 source asset 导入到 `Library/Artifacts///...` +5. 为 [AssetImportService](../AssetImportService/AssetImportService.md) 和 [ProjectAssetIndex](../ProjectAssetIndex/ProjectAssetIndex.md) 提供 GUID / 路径查询与 lookup snapshot + +当前它并不直接暴露给 `ResourceManager` 调用点,而是经 `AssetImportService` 包装后再进入运行时路径。 + +## 生命周期 + +- [Initialize](Initialize.md) + 设置项目根、`Assets` 根和 `Library` 根,确保目录结构存在,然后加载 source/artifact 数据库并扫描当前资产树。 +- [Refresh](Refresh.md) + 重新扫描 `Assets` 并更新 source 记录,不会批量重导 artifact。 +- [Shutdown](Shutdown.md) + 把 source/artifact 两份数据库写回磁盘,然后清空内存状态。 + +当前 `AssetDatabase` 自身没有内部锁。线程同步由外层 [AssetImportService](../AssetImportService/AssetImportService.md) 的 `recursive_mutex` 提供。 + +## 持久化布局 + +### Source 侧 + +- 每个 source asset 或文件夹都对应一个 `.meta` sidecar +- `.meta` 当前记录: + - `guid` + - `folderAsset` + - `importer` + - `importerVersion` +- `Library/SourceAssetDB/assets.db` 保存 `SourceAssetRecord` 快照 + +`assets.db` 当前是制表符分隔的文本快照,而不是二进制数据库。 + +### Artifact 侧 + +- `Library/ArtifactDB/artifacts.db` 保存 `ArtifactRecord` 快照 +- artifact 目录按 `artifactKey` 前两位分片: + - `Library/Artifacts/<2-char-shard>//...` +- 主 artifact 文件当前约定为: + - 纹理: `main.xctex` + - 材质: `main.xcmat` + - 模型: `main.xcmesh` + - Shader: `main.xcshader` + +模型 artifact 还会在同一目录下产出内嵌材质和纹理 artifact,例如 `material_0.xcmat`、`texture_0.xctex`。 + +`artifacts.db` 当前同样是文本快照,依赖记录以附加字段顺序存储。 + +## 查询与快照链路 + +### 路径解析 + +- [ResolvePath](ResolvePath.md) 只把项目内 `Assets/...` 或可相对当前项目根还原为 `Assets/...` 的绝对路径视为正式项目资产路径 +- 带 `://` 的虚拟路径会直接失败,不进入项目资产数据库 +- 其他普通相对路径虽然可以被展开为绝对路径,但 `relativePath` 会保持为空,因此不会被认作正式项目资产 + +### GUID 与 `AssetRef` + +- [TryGetAssetGuid](TryGetAssetGuid.md) + 通过规范化、大小写无关的 `Assets/...` 路径 key 查 GUID +- [TryGetAssetRef](TryGetAssetRef.md) + 只是把 GUID 包装成主资产 `AssetRef`,并原样写入调用方给定的 `ResourceType` +- [TryGetPrimaryAssetPath](TryGetPrimaryAssetPath.md) + 做 GUID 到主 source 路径的反查 + +### Lookup snapshot + +[BuildLookupSnapshot](BuildLookupSnapshot.md) 会从当前 source 快照导出两张查找表: + +- `pathKey -> AssetGUID` +- `AssetGUID -> relativePath` + +它不做扫描,也不触发导入。当前真实链路是: + +```text +AssetDatabase::BuildLookupSnapshot +-> AssetImportService::BuildLookupSnapshot +-> ProjectAssetIndex::RefreshFrom +-> ResourceManager hot-path queries +``` + +## 导入与重导链路 + +### 当前导入器映射 + +| importer | 扩展名/来源 | 主资源类型 | 当前 artifact | +|------|------|------|------| +| `TextureImporter` | `.png` `.jpg` `.jpeg` `.bmp` `.tga` `.gif` `.hdr` | `Texture` | `main.xctex` | +| `MaterialImporter` | `.mat` `.material` `.json` | `Material` | `main.xcmat` | +| `ModelImporter` | `.obj` `.fbx` `.gltf` `.glb` `.dae` `.stl` | `Mesh` | `main.xcmesh` + 内嵌材质/纹理 artifact | +| `ShaderImporter` | `.shader` `.hlsl` `.glsl` `.vert` `.frag` `.geom` `.comp` | `Shader` | `main.xcshader` | +| `FolderImporter` | 文件夹 | `Unknown` | 无 artifact | +| `DefaultImporter` | 其他扩展名 | `Unknown` | 无 artifact | + +`SourceAssetRecord::importerVersion` 当前统一取头文件里的 `kCurrentImporterVersion`。按 `engine/include/XCEngine/Core/Asset/AssetDatabase.h` 的当前定义,这个值是 `5`。 + +### 重导入判定 + +`ShouldReimport()` 当前会在以下任一条件满足时要求重导入: + +- 没有现存 `ArtifactRecord` +- `artifactKey` 或 `mainArtifactPath` 为空 +- 主 artifact 文件已经不存在 +- importer 版本变化 +- source 内容哈希变化 +- `.meta` 哈希变化 +- source 文件大小或写时间变化 +- 任一依赖文件快照不再匹配 + +因此当前 artifact key 并不是唯一判定条件;真正的重导入 gate 是一组 source / meta / dependency snapshot。 + +### `EnsureArtifact()` + +[EnsureArtifact](EnsureArtifact.md) 是当前最关键的入口。它会: + +1. 解析请求路径并确认它落在项目 `Assets` 下 +2. 检查 source 文件是否存在 +3. 调用 `EnsureMetaForPath()`,确保 source 记录、GUID、`.meta` 和 importerVersion 有效 +4. 根据 importer 推导主资源类型,并检查是否与 `requestedType` 一致 +5. 查找已有 `ArtifactRecord` +6. 若 `ShouldReimport()` 要求重建,则同步调用对应 importer: + - 纹理 -> `ImportTextureAsset()` + - 材质 -> `ImportMaterialAsset()` + - 模型 -> `ImportModelAsset()` + - Shader -> `ImportShaderAsset()` +7. 成功后更新 `m_artifactsByGuid`,并把 `lastKnownArtifactKey` 回写到两份 source 记录索引 +8. 调用 `SaveArtifactDB()` 和 `SaveSourceAssetDB()` 持久化结果 +9. 把 source 绝对路径、相对路径、GUID、artifact 绝对目录和主 artifact 绝对路径写入 `ResolvedAsset` + +### 材质 artifact 写入 + +`ImportMaterialAsset()` 当前不是简单把 source 文件原样拷进 `Library`。 + +它的真实路径是: + +1. 用 `MaterialLoader` 先把 source 材质解析成运行时 `Material` +2. 通过 `CollectMaterialDependencies()` 收集 shader 和 texture binding 依赖快照 +3. 基于 source/meta/dependencies 生成新的 `artifactKey` +4. 调用 `WriteMaterialArtifactFile(...)` 写出 `main.xcmat` + +当前写出的 `.xcmat` 已经是 v2: + +- magic `XCMAT02` +- schema version `2` +- texture binding 段同时保存编码 `AssetRef` 与可选 path + +这让 artifact 可以同时保留: + +- 稳定的项目资产身份 +- 当前可用的定位 path + +### 依赖快照 + +`ArtifactDependencyRecord` 当前已经进入真实行为范围,不再只是预留结构。 + +- 材质导入会通过 `CollectMaterialDependencies()` 收集: + - shader path + - 每个 texture binding 的 path +- 模型导入会通过 `CollectModelDependencies()` 收集: + - `obj -> mtl` 声明依赖 + - `mtl -> texture` 声明依赖 + - 导入后 mesh 暴露出的纹理路径 +- Shader 导入会通过 `CollectShaderDependencies()` 收集 shader manifest 和 `ShaderLoader::CollectSourceDependencies()` 解析出的源码依赖 +- `AreDependenciesCurrent()` 会同时比较文件是否存在、大小、写时间和内容哈希 + +这意味着贴图、`.mtl`、shader manifest、shader 源文件或其他已记录依赖变化时,当前都会触发新的 artifact 重建。 + +## 公开数据结构 + +- [ArtifactDependencyRecord](ArtifactDependencyRecord.md) - 单个依赖文件的快照记录 +- [SourceAssetRecord](SourceAssetRecord.md) - source 资产记录 +- [ArtifactRecord](ArtifactRecord.md) - artifact 数据库记录 +- [ResolvedAsset](ResolvedAsset.md) - `EnsureArtifact()` 返回给调用方的解析结果 + +## 公开方法 + +| 方法 | 说明 | +|------|------| +| [Initialize](Initialize.md) | 初始化项目根与数据库状态。 | +| [Shutdown](Shutdown.md) | 写回数据库并清理内存状态。 | +| [Refresh](Refresh.md) | 重新扫描 `Assets`。 | +| [ResolvePath](ResolvePath.md) | 解析请求路径到绝对/相对项目路径。 | +| [TryGetAssetGuid](TryGetAssetGuid.md) | 通过路径查 GUID。 | +| [TryGetAssetRef](TryGetAssetRef.md) | 通过路径组装主资产 `AssetRef`。 | +| [EnsureArtifact](EnsureArtifact.md) | 确保 source asset 对应的 artifact 可用。 | +| [TryGetPrimaryAssetPath](TryGetPrimaryAssetPath.md) | 通过 GUID 反查主 source 路径。 | +| [BuildLookupSnapshot](BuildLookupSnapshot.md) | 导出 path/GUID lookup snapshot。 | +| [GetProjectRoot](GetProjectRoot.md) | 获取项目根目录。 | +| [GetAssetsRoot](GetAssetsRoot.md) | 获取 `Assets` 根目录。 | +| [GetLibraryRoot](GetLibraryRoot.md) | 获取 `Library` 根目录。 | + +## 真实使用位置 + +- [AssetImportService](../AssetImportService/AssetImportService.md) 当前持有 `AssetDatabase`,并负责所有外部访问的加锁与项目根切换 +- [ProjectAssetIndex](../ProjectAssetIndex/ProjectAssetIndex.md) 通过 `BuildLookupSnapshot()` 构建热路径 snapshot +- `ResourceManager::LoadResource()` 通过 `AssetImportService::EnsureArtifact()` 准备 artifact,再交给具体 loader +- `tests/core/Asset/test_resource_manager.cpp` 覆盖了新建项目资产后 `TryGetAssetRef()` cache miss 刷新 snapshot、随后 `TryResolveAssetPath()` 成功的真实链路 +- `tests/Resources/Texture/test_texture_loader.cpp`、`tests/Resources/Material/test_material_loader.cpp`、`tests/Resources/Mesh/test_mesh_loader.cpp` 覆盖了 artifact 生成、复用和依赖变化触发重导的主路径 + +## 当前实现边界 + +- 当前只处理项目 `Assets` 下的资产,`builtin://` 等虚拟路径不会进入这里 +- `.meta` 只存导入器名称和版本,不存 importer 细节设置 +- `FolderImporter` / `DefaultImporter` 不会产出 artifact +- 当前没有文件监听器,也没有后台导入任务;artifact 通常在首次加载或显式调用时同步生成 +- `TryGetAssetRef()` 只保证“路径有 GUID”,不保证请求的 `ResourceType` 与实际 importer 一致;真正的类型匹配要到 `EnsureArtifact()` 阶段才检查 + +## 相关文档 + +- [当前模块](../Asset.md) +- [AssetImportService](../AssetImportService/AssetImportService.md) +- [ProjectAssetIndex](../ProjectAssetIndex/ProjectAssetIndex.md) +- [ResourceManager](../ResourceManager/ResourceManager.md) +- [ImportSettings](../ImportSettings/ImportSettings.md) +- [API 总索引](../../../../main.md) diff --git a/docs/api/XCEngine/Core/Asset/AssetDatabase/BuildLookupSnapshot.md b/docs/api/XCEngine/Core/Asset/AssetDatabase/BuildLookupSnapshot.md new file mode 100644 index 00000000..c50538d9 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetDatabase/BuildLookupSnapshot.md @@ -0,0 +1,47 @@ +# AssetDatabase::BuildLookupSnapshot + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/AssetDatabase.h` + +## 签名 + +```cpp +void BuildLookupSnapshot( + std::unordered_map& outPathToGuid, + std::unordered_map& outGuidToPath) const; +``` + +## 作用 + +从当前 source 资产记录导出一份 path/GUID 双向查询快照,供外层热路径缓存使用。 + +## 当前实现行为 + +- 先清空两张输出 map。 +- 以当前 `m_sourcesByPathKey` 的容量为基准预留空间。 +- 遍历 `m_sourcesByPathKey`,把有效 GUID 和非空相对路径写入 `outPathToGuid`。 +- 遍历 `m_sourcesByGuid`,把有效 GUID 和非空相对路径写入 `outGuidToPath`。 + +## 关键语义 + +- `outPathToGuid` 的 key 不是原始路径,而是已经规范化并转小写的 `pathKey`。 +- `outGuidToPath` 保存的是规范化后的 `Assets/...` 相对路径,而不是绝对路径。 +- 该方法本身不做扫描、不触发导入,也不刷新数据库;它只导出当前内存中的 source 快照。 + +当前真实消费链路是: + +```text +AssetDatabase::BuildLookupSnapshot +-> AssetImportService::BuildLookupSnapshot +-> ProjectAssetIndex::RefreshFrom +``` + +## 相关文档 + +- [AssetDatabase](AssetDatabase.md) +- [AssetImportService](../AssetImportService/BuildLookupSnapshot.md) +- [ProjectAssetIndex](../ProjectAssetIndex/RefreshFrom.md) +- [TryGetPrimaryAssetPath](TryGetPrimaryAssetPath.md) diff --git a/docs/api/XCEngine/Core/Asset/AssetDatabase/EnsureArtifact.md b/docs/api/XCEngine/Core/Asset/AssetDatabase/EnsureArtifact.md new file mode 100644 index 00000000..95662e6a --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetDatabase/EnsureArtifact.md @@ -0,0 +1,60 @@ +# AssetDatabase::EnsureArtifact + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/AssetDatabase.h` + +## 签名 + +```cpp +bool EnsureArtifact( + const Containers::String& requestPath, + ResourceType requestedType, + ResolvedAsset& outAsset); +``` + +## 作用 + +确保指定 project asset 的主 artifact 已经存在且与当前 source / `.meta` / dependency snapshot 一致,并把可直接加载的 artifact 路径回传给调用方。 + +## 当前实现流程 + +1. 把 `outAsset` 重置为默认值 +2. 调用 [ResolvePath](ResolvePath.md) 解析请求路径;若无法得到 `Assets/...` 相对路径则失败 +3. 检查 source 文件是否存在 +4. 调用内部 `EnsureMetaForPath()`,确保 source 记录、GUID、`.meta` 和 `importerVersion` 有效 +5. 根据 importer 推导主资源类型,并检查是否与 `requestedType` 一致 +6. 查找已有 `ArtifactRecord` +7. 若 `ShouldReimport()` 判定需要重建,则同步调用对应 importer: + - 纹理 -> `ImportTextureAsset()` + - 材质 -> `ImportMaterialAsset()` + - 模型 -> `ImportModelAsset()` + - Shader -> `ImportShaderAsset()` +8. 成功重建后,更新 `m_artifactsByGuid`,并把 `lastKnownArtifactKey` 回写到两份 source 记录索引 +9. 调用 `SaveArtifactDB()` 和 `SaveSourceAssetDB()` 持久化结果 +10. 把 source 绝对路径、相对路径、GUID、artifact 绝对目录和主 artifact 绝对路径写入 `outAsset` + +## 返回值 + +- `true` + 表示 source 存在、类型匹配,且主 artifact 已可直接使用 +- `false` + 表示路径不合法、source 不存在、类型不匹配、导入失败,或当前 importer 不支持产出 artifact + +## 关键语义 + +- 当前成功路径里会把 `exists` 和 `artifactReady` 一起置为 `true` +- `artifactKey` 由 GUID、importer、版本、source/meta 哈希、文件大小、写时间和依赖快照共同决定 +- `ShouldReimport()` 不只比较 `artifactKey`;它还会检查主 artifact 文件是否仍然存在,以及依赖快照是否仍匹配 +- 材质、模型和 Shader 导入的依赖快照都已经进入真实行为范围,因此贴图、`.mtl`、shader manifest 或 shader 源文件变化时都会触发新的 artifact +- 返回给调用方的 `artifactDirectory` 与 `artifactMainPath` 都是绝对路径,适合直接交给 loader 或文件系统操作 + +## 相关文档 + +- [AssetDatabase](AssetDatabase.md) +- [ResolvedAsset](ResolvedAsset.md) +- [ArtifactRecord](ArtifactRecord.md) +- [ArtifactDependencyRecord](ArtifactDependencyRecord.md) +- [ResolvePath](ResolvePath.md) diff --git a/docs/api/XCEngine/Core/Asset/AssetDatabase/GetAssetsRoot.md b/docs/api/XCEngine/Core/Asset/AssetDatabase/GetAssetsRoot.md new file mode 100644 index 00000000..1bf03425 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetDatabase/GetAssetsRoot.md @@ -0,0 +1,27 @@ +# AssetDatabase::GetAssetsRoot + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/AssetDatabase.h` + +## 签名 + +```cpp +const Containers::String& GetAssetsRoot() const; +``` + +## 作用 + +返回当前项目 `Assets` 根目录的绝对路径字符串。 + +## 当前实现行为 + +- 直接返回内部缓存的 `m_assetsRoot`。 +- 它由 [Initialize](Initialize.md) 通过 `projectRoot / "Assets"` 推导得到。 + +## 相关文档 + +- [AssetDatabase](AssetDatabase.md) +- [GetProjectRoot](GetProjectRoot.md) diff --git a/docs/api/XCEngine/Core/Asset/AssetDatabase/GetLibraryRoot.md b/docs/api/XCEngine/Core/Asset/AssetDatabase/GetLibraryRoot.md new file mode 100644 index 00000000..02b3e996 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetDatabase/GetLibraryRoot.md @@ -0,0 +1,27 @@ +# AssetDatabase::GetLibraryRoot + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/AssetDatabase.h` + +## 签名 + +```cpp +const Containers::String& GetLibraryRoot() const; +``` + +## 作用 + +返回当前项目 `Library` 根目录的绝对路径字符串。 + +## 当前实现行为 + +- 直接返回内部缓存的 `m_libraryRoot`。 +- 它由 [Initialize](Initialize.md) 通过 `projectRoot / "Library"` 推导得到。 + +## 相关文档 + +- [AssetDatabase](AssetDatabase.md) +- [GetProjectRoot](GetProjectRoot.md) diff --git a/docs/api/XCEngine/Core/Asset/AssetDatabase/GetProjectRoot.md b/docs/api/XCEngine/Core/Asset/AssetDatabase/GetProjectRoot.md new file mode 100644 index 00000000..eb774b54 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetDatabase/GetProjectRoot.md @@ -0,0 +1,28 @@ +# AssetDatabase::GetProjectRoot + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/AssetDatabase.h` + +## 签名 + +```cpp +const Containers::String& GetProjectRoot() const; +``` + +## 作用 + +返回当前初始化后的项目根目录。 + +## 当前实现行为 + +- 直接返回内部缓存的 `m_projectRoot`。 +- 该值在 [Initialize](Initialize.md) 中设置,在 [Shutdown](Shutdown.md) 中清空。 + +## 相关文档 + +- [AssetDatabase](AssetDatabase.md) +- [GetAssetsRoot](GetAssetsRoot.md) +- [GetLibraryRoot](GetLibraryRoot.md) diff --git a/docs/api/XCEngine/Core/Asset/AssetDatabase/Initialize.md b/docs/api/XCEngine/Core/Asset/AssetDatabase/Initialize.md new file mode 100644 index 00000000..aa98a480 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetDatabase/Initialize.md @@ -0,0 +1,36 @@ +# AssetDatabase::Initialize + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/AssetDatabase.h` + +## 签名 + +```cpp +void Initialize(const Containers::String& projectRoot); +``` + +## 作用 + +设置项目根目录并初始化 `Assets` / `Library` 相关数据库状态。 + +## 当前实现行为 + +1. 规范化 `projectRoot`。 +2. 推导 `Assets`、`Library`、`Library/SourceAssetDB/assets.db`、`Library/ArtifactDB/artifacts.db` 路径。 +3. 确保 `Assets`、`Library/SourceAssetDB`、`Library/ArtifactDB`、`Library/Artifacts` 目录存在。 +4. 从磁盘加载 source 与 artifact 数据库。 +5. 立即扫描一次 `Assets`,补齐 `.meta` 并更新 source 记录。 + +## 关键语义 + +- 初始化阶段不会预先为所有 source asset 生成 artifact;artifact 仍然是按需生成。 +- 当前没有增量监听器,因此这一步是“加载旧快照 + 重新扫目录”的模型。 + +## 相关文档 + +- [AssetDatabase](AssetDatabase.md) +- [Refresh](Refresh.md) +- [Shutdown](Shutdown.md) diff --git a/docs/api/XCEngine/Core/Asset/AssetDatabase/Refresh.md b/docs/api/XCEngine/Core/Asset/AssetDatabase/Refresh.md new file mode 100644 index 00000000..d3f9b651 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetDatabase/Refresh.md @@ -0,0 +1,34 @@ +# AssetDatabase::Refresh + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/AssetDatabase.h` + +## 签名 + +```cpp +void Refresh(); +``` + +## 作用 + +重新扫描项目 `Assets` 目录,更新 source 资产索引与 `.meta`。 + +## 当前实现行为 + +- 当前实现只是调用 `ScanAssets()`。 +- 会补齐缺失 `.meta`、更新 source 文件快照、移除已经不存在的 source 记录。 +- 若发现 source 记录被删除,会同步从 artifact 索引中移除对应记录。 + +## 当前限制 + +- 不会主动为所有资产重建 artifact。 +- 也不会自动通知编辑器 UI 或运行时缓存刷新;这些动作仍需外层系统自己串起来。 + +## 相关文档 + +- [AssetDatabase](AssetDatabase.md) +- [Initialize](Initialize.md) +- [BuildLookupSnapshot](BuildLookupSnapshot.md) diff --git a/docs/api/XCEngine/Core/Asset/AssetDatabase/ResolvePath.md b/docs/api/XCEngine/Core/Asset/AssetDatabase/ResolvePath.md new file mode 100644 index 00000000..03415ec6 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetDatabase/ResolvePath.md @@ -0,0 +1,38 @@ +# AssetDatabase::ResolvePath + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/AssetDatabase.h` + +## 签名 + +```cpp +bool ResolvePath( + const Containers::String& requestPath, + Containers::String& outAbsolutePath, + Containers::String& outRelativePath) const; +``` + +## 作用 + +把调用方给出的请求路径解析成绝对路径,并在可能时补出项目相对路径。 + +## 当前实现行为 + +- 空路径返回 `false`。 +- 带 `://` 的虚拟路径返回 `false`。 +- 若传入绝对路径,会直接规范化成 `outAbsolutePath`,并在它位于项目 `Assets` 下时补出 `outRelativePath`。 +- 若传入 `Assets/...` 形式的相对路径,会补出绝对路径和相同的相对路径。 +- 若传入其他项目相对路径,只会给出绝对路径,`outRelativePath` 保持为空。 + +## 关键语义 + +- `EnsureArtifact()` 需要 `outRelativePath` 非空,因此真正可导入的仍然只有项目 `Assets` 下的资产。 +- 这个方法不检查目标文件是否存在,只做路径规范化和归类。 + +## 相关文档 + +- [AssetDatabase](AssetDatabase.md) +- [EnsureArtifact](EnsureArtifact.md) diff --git a/docs/api/XCEngine/Core/Asset/AssetDatabase/ResolvedAsset.md b/docs/api/XCEngine/Core/Asset/AssetDatabase/ResolvedAsset.md new file mode 100644 index 00000000..6a8cb2ef --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetDatabase/ResolvedAsset.md @@ -0,0 +1,34 @@ +# AssetDatabase::ResolvedAsset + +**命名空间**: `XCEngine::Resources` + +**类型**: `struct` + +**头文件**: `XCEngine/Core/Asset/AssetDatabase.h` + +**描述**: `EnsureArtifact()` 返回给调用方的解析结果,汇总 source 路径、GUID 和可直接加载的 artifact 主文件信息。 + +## 字段 + +| 字段 | 类型 | 说明 | +|------|------|------| +| `exists` | `bool` | 当前 source asset 与 artifact 均成功解析时为 `true`。 | +| `artifactReady` | `bool` | 当前 artifact 已可用时为 `true`。 | +| `absolutePath` | `Containers::String` | source asset 的绝对路径。 | +| `relativePath` | `Containers::String` | source asset 的项目相对路径,通常是 `Assets/...`。 | +| `assetGuid` | `AssetGUID` | source asset GUID。 | +| `resourceType` | `ResourceType` | artifact 的主资源类型。 | +| `artifactMainPath` | `Containers::String` | 主 artifact 文件的绝对路径。 | +| `artifactDirectory` | `Containers::String` | artifact 目录的绝对路径。 | +| `mainLocalID` | `LocalID` | 主资产 local ID。 | + +## 当前实现语义 + +- 当前实现里,`exists` 和 `artifactReady` 只会在完全成功时一起置为 `true`。 +- 失败时整个结构会保持默认值,因此它还不能表达“source 已存在但 artifact 尚未生成”的中间态。 +- `artifactMainPath` 与 `artifactDirectory` 都已经被展开为绝对路径,适合直接交给 loader 或文件系统调用。 + +## 相关文档 + +- [AssetDatabase](AssetDatabase.md) +- [EnsureArtifact](EnsureArtifact.md) diff --git a/docs/api/XCEngine/Core/Asset/AssetDatabase/Shutdown.md b/docs/api/XCEngine/Core/Asset/AssetDatabase/Shutdown.md new file mode 100644 index 00000000..3ae1c207 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetDatabase/Shutdown.md @@ -0,0 +1,32 @@ +# AssetDatabase::Shutdown + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/AssetDatabase.h` + +## 签名 + +```cpp +void Shutdown(); +``` + +## 作用 + +把数据库状态写回磁盘,并清空当前 `AssetDatabase` 的路径与索引缓存。 + +## 当前实现行为 + +- 先调用 `SaveSourceAssetDB()` 与 `SaveArtifactDB()`。 +- 然后清空项目根、`Assets` 根、`Library` 根、数据库路径以及三张内存索引表。 + +## 关键语义 + +- 当前析构函数没有自动调用这一逻辑,因此显式生命周期管理仍然重要。 +- 关闭不会删除 `Library` 下的 artifact 文件,只是保存索引并释放内存状态。 + +## 相关文档 + +- [AssetDatabase](AssetDatabase.md) +- [Initialize](Initialize.md) diff --git a/docs/api/XCEngine/Core/Asset/AssetDatabase/SourceAssetRecord.md b/docs/api/XCEngine/Core/Asset/AssetDatabase/SourceAssetRecord.md new file mode 100644 index 00000000..d2a16c08 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetDatabase/SourceAssetRecord.md @@ -0,0 +1,37 @@ +# AssetDatabase::SourceAssetRecord + +**命名空间**: `XCEngine::Resources` + +**类型**: `struct` + +**头文件**: `XCEngine/Core/Asset/AssetDatabase.h` + +**描述**: 保存 source asset 在扫描后的核心索引信息,包括 GUID、`.meta`、导入器、source 快照和最近一次成功构建的 artifact key。 + +## 字段 + +| 字段 | 类型 | 说明 | +|------|------|------| +| `guid` | `AssetGUID` | 该 source asset 的稳定 GUID。 | +| `relativePath` | `Containers::String` | 相对项目根的路径,通常是 `Assets/...`。 | +| `metaPath` | `Containers::String` | sidecar `.meta` 的相对路径。 | +| `isFolder` | `bool` | 是否为文件夹资产。 | +| `importerName` | `Containers::String` | 当前推断或从 `.meta` 读出的 importer 名称。 | +| `importerVersion` | `Core::uint32` | 当前 importer 版本;扫描阶段会强制对齐到 `kCurrentImporterVersion`。 | +| `metaHash` | `Containers::String` | `.meta` 文件内容哈希。 | +| `sourceHash` | `Containers::String` | 源文件内容哈希;文件夹资产为空。 | +| `sourceFileSize` | `Core::uint64` | 源文件大小;文件夹资产为 `0`。 | +| `sourceWriteTime` | `Core::uint64` | 源文件写时间;文件夹资产为 `0`。 | +| `lastKnownArtifactKey` | `Containers::String` | 最近一次成功导入后记录的 artifact key。 | + +## 当前实现语义 + +- `EnsureMetaForPath()` 会在扫描时维护这里的大部分字段。 +- 若 `.meta` 缺失、无效、GUID 冲突,或 importerVersion 过旧,当前实现会重写 `.meta`。 +- 若文件大小和写时间未变化,扫描阶段会复用旧的 `sourceHash`,避免重复计算文件哈希。 +- `lastKnownArtifactKey` 会在 `EnsureArtifact()` 成功重建后回写,但真正的重导入判定仍依赖 `ArtifactRecord` 与当前文件系统快照,而不是只看这个字段。 + +## 相关文档 + +- [AssetDatabase](AssetDatabase.md) +- [ArtifactRecord](ArtifactRecord.md) diff --git a/docs/api/XCEngine/Core/Asset/AssetDatabase/TryGetAssetGuid.md b/docs/api/XCEngine/Core/Asset/AssetDatabase/TryGetAssetGuid.md new file mode 100644 index 00000000..406ec8d1 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetDatabase/TryGetAssetGuid.md @@ -0,0 +1,30 @@ +# AssetDatabase::TryGetAssetGuid + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/AssetDatabase.h` + +## 签名 + +```cpp +bool TryGetAssetGuid(const Containers::String& requestPath, AssetGUID& outGuid) const; +``` + +## 作用 + +通过 source 路径查询对应的 `AssetGUID`。 + +## 当前实现行为 + +- 先调用 [ResolvePath](ResolvePath.md)。 +- 只有能解析到项目相对路径的 `Assets/...` 请求才会继续查表。 +- 内部使用规范化、转小写后的路径 key 查 `m_sourcesByPathKey`。 +- 找到后把记录中的 GUID 写到 `outGuid`,并要求 GUID 本身有效。 + +## 相关文档 + +- [AssetDatabase](AssetDatabase.md) +- [TryGetAssetRef](TryGetAssetRef.md) +- [TryGetPrimaryAssetPath](TryGetPrimaryAssetPath.md) diff --git a/docs/api/XCEngine/Core/Asset/AssetDatabase/TryGetAssetRef.md b/docs/api/XCEngine/Core/Asset/AssetDatabase/TryGetAssetRef.md new file mode 100644 index 00000000..4e6a8612 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetDatabase/TryGetAssetRef.md @@ -0,0 +1,43 @@ +# AssetDatabase::TryGetAssetRef + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/AssetDatabase.h` + +## 签名 + +```cpp +bool TryGetAssetRef( + const Containers::String& requestPath, + ResourceType resourceType, + AssetRef& outRef) const; +``` + +## 作用 + +把 source 路径包装成当前主资产的 `AssetRef`。 + +## 当前实现行为 + +- 内部先通过 [TryGetAssetGuid](TryGetAssetGuid.md) 查 GUID。 +- 成功后把: + - `assetGuid` + - `localID = kMainAssetLocalID` + - `resourceType = 调用方传入值` + 写入 `outRef`。 + +## 关键语义 + +- 这个方法不检查传入的 `resourceType` 是否与 source asset 的 importer 类型一致。 +- 这个方法也不会触发 `EnsureArtifact()`;只要路径能稳定映射到 GUID,它就可以成功。 +- 真正的类型匹配要到 [EnsureArtifact](EnsureArtifact.md) 或后续 `ResourceManager::LoadResource()` 阶段才会校验。 + +因此它更适合被理解成“主资产身份查询”,而不是“可加载资源可用性检查”。 + +## 相关文档 + +- [AssetDatabase](AssetDatabase.md) +- [TryGetAssetGuid](TryGetAssetGuid.md) +- [ResolvedAsset](ResolvedAsset.md) diff --git a/docs/api/XCEngine/Core/Asset/AssetDatabase/TryGetPrimaryAssetPath.md b/docs/api/XCEngine/Core/Asset/AssetDatabase/TryGetPrimaryAssetPath.md new file mode 100644 index 00000000..26782712 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetDatabase/TryGetPrimaryAssetPath.md @@ -0,0 +1,29 @@ +# AssetDatabase::TryGetPrimaryAssetPath + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/AssetDatabase.h` + +## 签名 + +```cpp +bool TryGetPrimaryAssetPath(const AssetGUID& guid, Containers::String& outRelativePath) const; +``` + +## 作用 + +通过 GUID 反查主 source asset 的项目相对路径。 + +## 当前实现行为 + +- 直接查 `m_sourcesByGuid`。 +- 命中后返回记录里的 `relativePath`,通常是 `Assets/...`。 +- 不会返回 artifact 路径,也不会检查 artifact 当前是否可用。 + +## 相关文档 + +- [AssetDatabase](AssetDatabase.md) +- [TryGetAssetGuid](TryGetAssetGuid.md) +- [BuildLookupSnapshot](BuildLookupSnapshot.md) diff --git a/docs/api/XCEngine/Core/Asset/AssetGUID/AssetGUID.md b/docs/api/XCEngine/Core/Asset/AssetGUID/AssetGUID.md new file mode 100644 index 00000000..fb990b65 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetGUID/AssetGUID.md @@ -0,0 +1,160 @@ +# AssetGUID + +**命名空间**: `XCEngine::Resources` + +**类型**: `header-overview` + +**头文件**: `XCEngine/Core/Asset/AssetGUID.h` + +**描述**: 定义项目资产的 128 位标识、`LocalID` 约定,以及字符串/字节到 `AssetGUID` 的辅助生成函数。 + +## 角色概述 + +`AssetGUID` 是当前项目资产层的稳定身份基石。 + +在这套资源系统里,运行时实例的缓存键和项目资产的身份不是一回事: + +- 运行时 `IResource` 仍有自己那套 `ResourceGUID` +- 项目里的 source asset、`.meta`、artifact 数据库、`AssetRef` 则统一围绕 `AssetGUID` + +这让系统可以把“项目资产身份”与“某次加载出来的运行时对象”分层处理。 + +## 当前公开内容 + +### `AssetGUID` + +`AssetGUID` 本体由两个 `uint64` 组成: + +- `high` +- `low` + +它提供: + +- `IsValid()` +- `operator==` +- `operator!=` +- `Generate()` +- `TryParse()` +- `ParseOrDefault()` +- `ToString()` + +### `LocalID` + +头文件还同时定义了: + +- `using LocalID = Core::uint64` +- `kInvalidLocalID = 0` +- `kMainAssetLocalID = 1` + +这套约定是给 `AssetRef` 用的,用来在同一个 `AssetGUID` 下进一步区分主资产和潜在子资产。 + +### 辅助函数与哈希 + +- `HashBytesToAssetGUID(const void*, size_t)` +- `HashStringToAssetGUID(const Containers::String&)` +- `std::hash` + +其中 `std::hash` 主要服务 `unordered_map` / `unordered_set` 这类容器。 + +## 生成与解析语义 + +### 随机生成 + +`AssetGUID::Generate()` 当前使用 `std::mt19937_64` 连续生成 `high` 和 `low`,并循环跳过全零 GUID。 + +这说明它的定位是: + +- 可以生成新的随机资产身份 +- 但它不是 RFC 风格 UUID,也没有版本位语义 + +真正能让 GUID 跨重启稳定的,不是 `Generate()` 本身,而是它被写进 `.meta` 并持久化下来。 + +### 字符串格式 + +`ToString()` 当前固定输出: + +- 32 个十六进制字符 +- 先 `high` 后 `low` +- 小写 +- 不带连字符、花括号或前缀 + +例如形状是: + +`0123456789abcdeffedcba9876543210` + +### 解析规则 + +`TryParse()` 当前要求非常严格: + +- 长度必须正好是 `32` +- 每一位都必须是十六进制字符 +- 不接受 `-` 分隔格式 +- 不接受大括号包裹格式 + +但它同时接受大小写十六进制字符;真正写回时,`ToString()` 会统一成小写。 + +`ParseOrDefault()` 则是在解析失败时直接返回全零无效 GUID。 + +## 确定性哈希语义 + +除了随机生成,这个头文件还提供“从内容派生 GUID”的辅助函数。 + +### `HashBytesToAssetGUID()` + +- 对字节序列做两次不同 seed 的 FNV-1a 64 位哈希 +- 结果分别填进 `high` 和 `low` +- `data == nullptr` 或 `size == 0` 时返回无效全零 GUID + +### `HashStringToAssetGUID()` + +- 直接对 `text.CStr()` 与 `text.Length()` 做哈希 +- 不额外附带终止符 + +这类哈希 GUID 当前主要用于“稳定签名”而不是“随机身份”: + +- `AssetDatabase` 用它计算 `.meta` 内容哈希 +- 也用它参与 artifact key / 内容签名相关逻辑 + +需要把它理解成轻量、可复现的标识生成器,而不是抗碰撞的密码学摘要。 + +## 在当前资产链路中的作用 + +### 项目资产身份 + +`AssetDatabase` 会把 `AssetGUID` 持久化到每个 `.meta` sidecar,并用它连接: + +- source asset 记录 +- artifact 记录 +- path/GUID 查询表 + +这让文件重命名、相对路径变化和 artifact 重建可以围绕同一资产身份继续工作。 + +### 资产反查与引用 + +- `AssetDatabase::TryGetPrimaryAssetPath()` 做 GUID 到主 source 路径的反查 +- `AssetDatabase::TryGetAssetRef()` 和 `ResourceManager::TryGetAssetRef()` 用它组装 `AssetRef` +- `ResourceManager::TryResolveAssetPath()` 也最终依赖 `AssetGUID` 找回 `Assets/...` 路径 + +### `LocalID` 现状 + +虽然已经定义了 `LocalID`,但当前主链路几乎都只使用: + +- `kMainAssetLocalID` + +也就是说,现在的系统主要还是“一个 GUID 对应一个主资产”的模型,子资产寻址接口已经预留,但还没有真正展开。 + +## 当前实现边界 + +- `TryParse()` 只接受 32 位纯十六进制文本,格式容忍度很低。 +- 全零 GUID 是统一的无效哨兵值;空输入哈希也会落到这个值。 +- `HashBytesToAssetGUID()` / `HashStringToAssetGUID()` 不是密码学安全哈希,理论上存在碰撞可能。 +- `Generate()` 生成的是随机值,不持久化就不会天然稳定。 +- `LocalID` 概念已经存在,但当前没有公开的子资产 ID 分配规则。 + +## 相关文档 + +- [当前模块](../Asset.md) +- [AssetDatabase](../AssetDatabase/AssetDatabase.md) +- [AssetRef](../AssetRef/AssetRef.md) +- [ResourceManager](../ResourceManager/ResourceManager.md) +- [API 总索引](../../../../main.md) diff --git a/docs/api/XCEngine/Core/Asset/AssetImportService/AssetImportService.md b/docs/api/XCEngine/Core/Asset/AssetImportService/AssetImportService.md new file mode 100644 index 00000000..bec0342f --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetImportService/AssetImportService.md @@ -0,0 +1,145 @@ +# AssetImportService + +**命名空间**: `XCEngine::Resources` + +**类型**: `class` + +**头文件**: `XCEngine/Core/Asset/AssetImportService.h` + +**描述**: 以线程安全服务层的形式封装 `AssetDatabase`,向 `ResourceManager` 和 `ProjectAssetIndex` 暴露项目资产导入、`AssetRef` 查询、`Library` 根目录查询以及 lookup snapshot 导出能力。 + +## 概览 + +`AssetImportService` 是当前 `Core/Asset` 分层里的“受控数据库入口”: + +- [AssetDatabase](../AssetDatabase/AssetDatabase.md) 负责真实的扫描、`.meta`、artifact 与导入逻辑 +- `AssetImportService` 用一把 `std::recursive_mutex` 串行化这些数据库入口,并把结果整理成更适合运行时消费的服务 API +- [ProjectAssetIndex](../ProjectAssetIndex/ProjectAssetIndex.md) 再基于它导出的 [LookupSnapshot](LookupSnapshot.md) 构建热路径查询缓存 + +## 公开类型 + +### `LookupSnapshot` + +[LookupSnapshot](LookupSnapshot.md) 是一个值对象,里面保存两张表: + +- `path key -> AssetGUID` +- `AssetGUID -> relative path` + +它的用途不是长期持有数据库状态,而是给 `ProjectAssetIndex::RefreshFrom()` 做一次性整体拉取。 + +### `ImportedAsset` + +[ImportedAsset](ImportedAsset.md) 是 [EnsureArtifact](EnsureArtifact.md) 的输出对象。它把 `AssetDatabase::ResolvedAsset` 转成更适合运行时装配的结果: + +- 保留 source 绝对路径、`Assets/...` 相对路径和 `AssetGUID` +- 保留 `artifactDirectory` 与 `mainLocalID` +- 暴露 `runtimeLoadPath`,供 `ResourceManager::LoadResource()` 决定真正交给 loader 的路径 + +按 `ConvertResolvedAsset()` 的字段转换规则,`runtimeLoadPath` 会在 `artifactReady=true` 时取主 artifact 绝对路径,否则退回 source 绝对路径;但按当前 `AssetDatabase::EnsureArtifact()` 的实现,`AssetImportService::EnsureArtifact()` 只有在主 artifact 已经可用时才会成功返回,因此当前成功路径里的 `runtimeLoadPath` 实际上总是指向 `Library/Artifacts/.../main.*`。 + +## 生命周期 + +### `Initialize()` + +当前实现是空函数。它更像一个生命周期锚点,给未来后台导入队列或统计能力预留挂接位置。 + +### `Shutdown()` + +`Shutdown()` 会在锁内执行: + +- `m_assetDatabase.Shutdown()` +- 清空 `m_projectRoot` + +这会彻底断开当前项目的资产数据库状态,而不只是清理临时缓存。 + +### `SetProjectRoot()` / `GetProjectRoot()` / `GetLibraryRoot()` + +- `SetProjectRoot()` 是最关键的状态切换入口 +- 如果新旧项目根相同,直接返回 +- 如果旧项目根非空,先关闭旧的 `AssetDatabase` +- 写入新的 `m_projectRoot` 后,若新项目根非空则重新初始化 `AssetDatabase` +- [GetLibraryRoot](GetLibraryRoot.md) 返回当前项目 `Library` 根目录的绝对路径,供测试和更高层缓存重建入口使用 + +因此它当前是单项目绑定服务,不支持同时托管多个项目根。 + +## 当前公开能力 + +### `Refresh()` + +在项目根有效时转发到 `AssetDatabase::Refresh()`,用于重新扫描 source 资产并刷新数据库状态。 + +### `RebuildLibraryCache()` + +[RebuildLibraryCache](RebuildLibraryCache.md) 是更重的入口: + +1. 记录当前 `projectRoot` 和 `libraryRoot` +2. `m_assetDatabase.Shutdown()` +3. 删除整个 `Library` 目录 +4. 用原项目根重新 `Initialize()` + +测试 `AssetImportService_Test.RebuildLibraryCacheKeepsStableAssetRefs` 说明它会清空缓存产物,但不会破坏现有 `.meta` 驱动的 `AssetGUID` 稳定性。 + +### `EnsureArtifact()` + +[EnsureArtifact](EnsureArtifact.md) 会在项目根有效时转发到底层数据库,并把结果转换成 [ImportedAsset](ImportedAsset.md)。 + +当前成功路径里: + +- `artifactReady` 为 `true` +- `runtimeLoadPath` 指向主 artifact 的绝对路径 +- `absolutePath` / `relativePath` / `assetGuid` 仍保留 source 资产身份 + +`ResourceManager::LoadResource()` 用它决定真正交给 loader 的加载路径。 + +### `TryGetAssetRef()` / `TryGetPrimaryAssetPath()` + +- `TryGetAssetRef()` 按请求路径解析主资产 `AssetRef` +- `TryGetPrimaryAssetPath()` 通过 `AssetGUID` 反查主相对路径 + +它们都只负责数据库查询本身;高频缓存和 cache miss 刷新策略由 `ProjectAssetIndex` 负责。 + +### `BuildLookupSnapshot()` + +[BuildLookupSnapshot](BuildLookupSnapshot.md) 会先清空传入的 [LookupSnapshot](LookupSnapshot.md),再导出当前双向查询表。这是 `ProjectAssetIndex` 刷新本地 snapshot 的唯一上游入口。 + +## 与 `ResourceManager` 的关系 + +`ResourceManager` 不再直接把 `AssetDatabase` 当成自己的内部实现细节,而是通过 `AssetImportService` 访问项目资产层: + +- `SetResourceRoot()` 用它切换项目根 +- `LoadResource()` 用它确保 artifact 就绪 +- [RefreshProjectAssets](../ResourceManager/RefreshProjectAssets.md) 用它刷新数据库,再同步重建 `ProjectAssetIndex` +- [RebuildProjectAssetCache](../ResourceManager/RebuildProjectAssetCache.md) 用它重建整个 `Library` 缓存 + +这让“数据库导入能力”和“运行时资源加载器”之间多了一层清晰职责边界。 + +## 当前实现边界 + +- `Initialize()` 仍是空实现,没有后台导入线程或事件订阅 +- 没有项目根时,所有数据库相关入口都会直接失败或返回空结果 +- 线程模型当前很直接: 一把 `recursive_mutex` 串行化所有服务入口 +- `LookupSnapshot` 是整份重建,不是增量同步 + +## 相关方法 + +- [Initialize](Initialize.md) +- [Shutdown](Shutdown.md) +- [SetProjectRoot](SetProjectRoot.md) +- [GetProjectRoot](GetProjectRoot.md) +- [GetLibraryRoot](GetLibraryRoot.md) +- [Refresh](Refresh.md) +- [RebuildLibraryCache](RebuildLibraryCache.md) +- [EnsureArtifact](EnsureArtifact.md) +- [TryGetAssetRef](TryGetAssetRef.md) +- [TryGetPrimaryAssetPath](TryGetPrimaryAssetPath.md) +- [BuildLookupSnapshot](BuildLookupSnapshot.md) +- [LookupSnapshot](LookupSnapshot.md) +- [ImportedAsset](ImportedAsset.md) + +## 相关文档 + +- [当前模块](../Asset.md) +- [AssetDatabase](../AssetDatabase/AssetDatabase.md) +- [ProjectAssetIndex](../ProjectAssetIndex/ProjectAssetIndex.md) +- [ResourceManager](../ResourceManager/ResourceManager.md) +- [API 总索引](../../../../main.md) diff --git a/docs/api/XCEngine/Core/Asset/AssetImportService/BuildLookupSnapshot.md b/docs/api/XCEngine/Core/Asset/AssetImportService/BuildLookupSnapshot.md new file mode 100644 index 00000000..ecc0f06f --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetImportService/BuildLookupSnapshot.md @@ -0,0 +1,37 @@ +# AssetImportService::BuildLookupSnapshot + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/AssetImportService.h` + +## 签名 + +```cpp +void BuildLookupSnapshot(LookupSnapshot& outSnapshot) const; +``` + +## 作用 + +导出当前项目资产的 `path <-> AssetGUID` 查询快照。 + +## 当前实现行为 + +- 在 `std::recursive_mutex` 保护下执行。 +- 先调用 `outSnapshot.Clear()` 清空已有内容。 +- 如果当前没有项目根,直接返回空快照。 +- 否则转发到 `AssetDatabase::BuildLookupSnapshot()`,填充: + - `outSnapshot.assetGuidByPathKey` + - `outSnapshot.assetPathByGuid` + +## 当前语义 + +- 这是一次性值拷贝,不是把 `AssetDatabase` 的内部 map 直接暴露给外部。 +- `ProjectAssetIndex::RefreshFrom()` 会在锁外构建完整 snapshot,再整体替换本地索引。 + +## 相关文档 + +- [AssetImportService](AssetImportService.md) +- [LookupSnapshot](LookupSnapshot.md) +- [ProjectAssetIndex::RefreshFrom](../ProjectAssetIndex/RefreshFrom.md) diff --git a/docs/api/XCEngine/Core/Asset/AssetImportService/EnsureArtifact.md b/docs/api/XCEngine/Core/Asset/AssetImportService/EnsureArtifact.md new file mode 100644 index 00000000..dcd0b158 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetImportService/EnsureArtifact.md @@ -0,0 +1,44 @@ +# AssetImportService::EnsureArtifact + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/AssetImportService.h` + +## 签名 + +```cpp +bool EnsureArtifact( + const Containers::String& requestPath, + ResourceType requestedType, + ImportedAsset& outAsset); +``` + +## 作用 + +确保指定项目资产请求已经具备可供运行时加载的主 artifact,并把数据库层结果转换成服务层返回对象。 + +## 当前实现行为 + +- 在 `std::recursive_mutex` 保护下执行 +- 如果当前没有项目根,直接返回 `false` +- 否则先调用 `AssetDatabase::EnsureArtifact()`,再把得到的 `ResolvedAsset` 转成 [ImportedAsset](ImportedAsset.md) +- 字段转换时,`ConvertResolvedAsset()` 会把 `runtimeLoadPath` 设为: + - `artifactMainPath`,当 `artifactReady=true` + - `absolutePath`,当 `artifactReady=false` + +但按当前实现,`AssetDatabase::EnsureArtifact()` 只有在主 artifact 已可直接使用时才会返回成功,因此这条服务 API 的当前成功路径里,`runtimeLoadPath` 实际总是主 artifact 的绝对路径。 + +## 当前语义 + +- 这一层不再把 `AssetDatabase::ResolvedAsset` 直接暴露给上游 +- `ResourceManager::LoadResource()` 用它决定后续应该让 loader 读取 source 文件还是读取 `Library/Artifacts/.../main.*` +- 以当前代码路径来看,只要本方法返回 `true`,调用方就可以把 `runtimeLoadPath` 当作可直接交给 loader 的最终路径 + +## 相关文档 + +- [AssetImportService](AssetImportService.md) +- [ImportedAsset](ImportedAsset.md) +- [AssetDatabase::EnsureArtifact](../AssetDatabase/EnsureArtifact.md) +- [ResourceManager](../ResourceManager/ResourceManager.md) diff --git a/docs/api/XCEngine/Core/Asset/AssetImportService/GetLibraryRoot.md b/docs/api/XCEngine/Core/Asset/AssetImportService/GetLibraryRoot.md new file mode 100644 index 00000000..111c904b --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetImportService/GetLibraryRoot.md @@ -0,0 +1,30 @@ +# AssetImportService::GetLibraryRoot + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/AssetImportService.h` + +## 签名 + +```cpp +Containers::String GetLibraryRoot() const; +``` + +## 作用 + +返回当前项目 `Library` 目录的根路径。 + +## 当前实现行为 + +- 在 `std::recursive_mutex` 保护下执行。 +- 直接返回 `m_assetDatabase.GetLibraryRoot()` 的按值副本。 +- 如果当前还没有设置项目根,通常返回空字符串。 + +## 相关文档 + +- [AssetImportService](AssetImportService.md) +- [SetProjectRoot](SetProjectRoot.md) +- [RebuildLibraryCache](RebuildLibraryCache.md) +- [ResourceManager::GetProjectLibraryRoot](../ResourceManager/GetProjectLibraryRoot.md) diff --git a/docs/api/XCEngine/Core/Asset/AssetImportService/GetProjectRoot.md b/docs/api/XCEngine/Core/Asset/AssetImportService/GetProjectRoot.md new file mode 100644 index 00000000..2a2016dc --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetImportService/GetProjectRoot.md @@ -0,0 +1,28 @@ +# AssetImportService::GetProjectRoot + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/AssetImportService.h` + +## 签名 + +```cpp +Containers::String GetProjectRoot() const; +``` + +## 作用 + +返回当前项目根路径副本。 + +## 当前实现行为 + +- 在 `std::recursive_mutex` 保护下执行。 +- 返回的是 `Containers::String` 副本,不暴露内部引用。 +- 当前未绑定项目根时返回空字符串。 + +## 相关文档 + +- [AssetImportService](AssetImportService.md) +- [SetProjectRoot](SetProjectRoot.md) diff --git a/docs/api/XCEngine/Core/Asset/AssetImportService/ImportedAsset.md b/docs/api/XCEngine/Core/Asset/AssetImportService/ImportedAsset.md new file mode 100644 index 00000000..3d9f4c7c --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetImportService/ImportedAsset.md @@ -0,0 +1,49 @@ +# AssetImportService::ImportedAsset + +**命名空间**: `XCEngine::Resources` + +**类型**: `struct` + +**头文件**: `XCEngine/Core/Asset/AssetImportService.h` + +## 概览 + +`ImportedAsset` 是 `AssetImportService::EnsureArtifact()` 向上层返回的轻量结果对象。它把 `AssetDatabase::ResolvedAsset` 转成更适合运行时消费的形态。 + +## 当前字段 + +- `exists` + - source 资产是否存在 +- `artifactReady` + - 对应主 artifact 是否已经就绪 +- `absolutePath` + - source 资产绝对路径 +- `relativePath` + - 项目内相对路径,通常是 `Assets/...` +- `assetGuid` + - source 资产的稳定身份 +- `resourceType` + - 当前导入得到的主资源类型 +- `runtimeLoadPath` + - 运行时实际应交给 loader 的路径 + - `ConvertResolvedAsset()` 会在 `artifactReady=true` 时写入主 artifact 绝对路径 + - 否则回退到 `absolutePath` +- `artifactDirectory` + - 当前 artifact 目录的绝对路径 +- `mainLocalID` + - 当前主资产的 local ID,默认值是 `kMainAssetLocalID` + +## 当前实现语义 + +- 它不是持久化格式,也不是数据库记录本身;只是服务层返回给运行时装配链路的结果快照 +- `ResourceManager::LoadResource()` 主要依赖它决定: + - 是否把请求路径切换成 artifact 主文件路径 + - 是否把 `assetGuid -> relativePath` 关系回填给 `ProjectAssetIndex` +- 虽然结构体字段允许表达“source 已知但 artifact 尚未就绪”的形态,但按当前 `AssetImportService::EnsureArtifact()` 的公开成功路径,调用方实际拿到的结果总是 `artifactReady=true` + +## 相关文档 + +- [AssetImportService](AssetImportService.md) +- [EnsureArtifact](EnsureArtifact.md) +- [AssetDatabase::ResolvedAsset](../AssetDatabase/ResolvedAsset.md) +- [ResourceManager](../ResourceManager/ResourceManager.md) diff --git a/docs/api/XCEngine/Core/Asset/AssetImportService/Initialize.md b/docs/api/XCEngine/Core/Asset/AssetImportService/Initialize.md new file mode 100644 index 00000000..837c93f2 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetImportService/Initialize.md @@ -0,0 +1,28 @@ +# AssetImportService::Initialize + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/AssetImportService.h` + +## 签名 + +```cpp +void Initialize(); +``` + +## 作用 + +初始化 `AssetImportService`。 + +## 当前实现行为 + +- 当前实现为空。 +- 不会创建 `AssetDatabase`,也不会设置项目根。 +- 真正的数据库切换入口是 [SetProjectRoot](SetProjectRoot.md)。 + +## 相关文档 + +- [AssetImportService](AssetImportService.md) +- [SetProjectRoot](SetProjectRoot.md) diff --git a/docs/api/XCEngine/Core/Asset/AssetImportService/LookupSnapshot.md b/docs/api/XCEngine/Core/Asset/AssetImportService/LookupSnapshot.md new file mode 100644 index 00000000..d02dfde0 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetImportService/LookupSnapshot.md @@ -0,0 +1,36 @@ +# AssetImportService::LookupSnapshot + +**命名空间**: `XCEngine::Resources` + +**类型**: `struct` + +**头文件**: `XCEngine/Core/Asset/AssetImportService.h` + +## 概述 + +`LookupSnapshot` 是 `AssetImportService` 导出的只读查询快照,供 [ProjectAssetIndex](../ProjectAssetIndex/ProjectAssetIndex.md) 一次性拉取当前 `path <-> AssetGUID` 映射。 + +## 当前字段 + +- `assetGuidByPathKey` + - 规范化路径键到 `AssetGUID` 的映射。 +- `assetPathByGuid` + - `AssetGUID` 到主相对路径的映射。 + +## 当前公开能力 + +### `Clear()` + +- 清空两张 lookup 表。 +- [BuildLookupSnapshot](BuildLookupSnapshot.md) 会先调用它,再决定是否从 `AssetDatabase` 填充新快照。 + +## 设计位置 + +- 这个结构体不是长期持有的数据库对象,而是一次性导出的值对象。 +- `ProjectAssetIndex::RefreshFrom()` 会在锁外构建完整快照,再在锁内整体替换本地索引,避免读线程看到半更新状态。 + +## 相关文档 + +- [AssetImportService](AssetImportService.md) +- [BuildLookupSnapshot](BuildLookupSnapshot.md) +- [ProjectAssetIndex::RefreshFrom](../ProjectAssetIndex/RefreshFrom.md) diff --git a/docs/api/XCEngine/Core/Asset/AssetImportService/RebuildLibraryCache.md b/docs/api/XCEngine/Core/Asset/AssetImportService/RebuildLibraryCache.md new file mode 100644 index 00000000..e0c64bd4 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetImportService/RebuildLibraryCache.md @@ -0,0 +1,41 @@ +# AssetImportService::RebuildLibraryCache + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/AssetImportService.h` + +## 签名 + +```cpp +bool RebuildLibraryCache(); +``` + +## 作用 + +删除当前项目的 `Library` 目录,并重新初始化 `AssetDatabase`。 + +## 当前实现行为 + +- 在 `std::recursive_mutex` 保护下执行 +- 如果当前没有项目根,直接返回 `false` +- 否则会: + 1. 记录当前 `projectRoot` 和 `libraryRoot` + 2. `m_assetDatabase.Shutdown()` + 3. 调用 `std::filesystem::remove_all(libraryRoot, ec)` + 4. 用原来的 `projectRoot` 重新 `Initialize()` +- 返回值表示 `remove_all` 是否没有报告错误,而不是“实际删除了多少文件” + +## 当前语义 + +- 这是导入缓存层的重建入口,比普通的 [Refresh](Refresh.md) 更重 +- 它不会改动 `.meta` 里的 `AssetGUID`,因此已生成 `AssetRef` 的稳定性仍由 source 资产身份保证 +- `tests/core/Asset/test_resource_manager.cpp` 中的 `AssetImportService_Test.RebuildLibraryCacheKeepsStableAssetRefs` 专门覆盖了这条语义 + +## 相关文档 + +- [AssetImportService](AssetImportService.md) +- [GetLibraryRoot](GetLibraryRoot.md) +- [Refresh](Refresh.md) +- [ResourceManager::RebuildProjectAssetCache](../ResourceManager/RebuildProjectAssetCache.md) diff --git a/docs/api/XCEngine/Core/Asset/AssetImportService/Refresh.md b/docs/api/XCEngine/Core/Asset/AssetImportService/Refresh.md new file mode 100644 index 00000000..ff92e9e5 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetImportService/Refresh.md @@ -0,0 +1,28 @@ +# AssetImportService::Refresh + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/AssetImportService.h` + +## 签名 + +```cpp +void Refresh(); +``` + +## 作用 + +刷新当前项目根对应的资产数据库。 + +## 当前实现行为 + +- 在 `std::recursive_mutex` 保护下执行。 +- 只有 `m_projectRoot` 非空时才会调用 `AssetDatabase::Refresh()`。 +- 空项目根下调用会被安全忽略。 + +## 相关文档 + +- [AssetImportService](AssetImportService.md) +- [BuildLookupSnapshot](BuildLookupSnapshot.md) diff --git a/docs/api/XCEngine/Core/Asset/AssetImportService/SetProjectRoot.md b/docs/api/XCEngine/Core/Asset/AssetImportService/SetProjectRoot.md new file mode 100644 index 00000000..fa2cd926 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetImportService/SetProjectRoot.md @@ -0,0 +1,30 @@ +# AssetImportService::SetProjectRoot + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/AssetImportService.h` + +## 签名 + +```cpp +void SetProjectRoot(const Containers::String& projectRoot); +``` + +## 作用 + +切换当前绑定的项目根目录。 + +## 当前实现行为 + +- 在 `std::recursive_mutex` 保护下执行。 +- 若新旧 `projectRoot` 相同,直接返回。 +- 若旧项目根非空,会先关闭内部 `AssetDatabase`。 +- 更新 `m_projectRoot` 后,只有新项目根非空时才会调用 `AssetDatabase::Initialize()`。 + +## 相关文档 + +- [AssetImportService](AssetImportService.md) +- [GetProjectRoot](GetProjectRoot.md) +- [Shutdown](Shutdown.md) diff --git a/docs/api/XCEngine/Core/Asset/AssetImportService/Shutdown.md b/docs/api/XCEngine/Core/Asset/AssetImportService/Shutdown.md new file mode 100644 index 00000000..5851b773 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetImportService/Shutdown.md @@ -0,0 +1,28 @@ +# AssetImportService::Shutdown + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/AssetImportService.h` + +## 签名 + +```cpp +void Shutdown(); +``` + +## 作用 + +关闭内部资产数据库并清空当前项目根。 + +## 当前实现行为 + +- 在 `std::recursive_mutex` 保护下执行。 +- 调用内部 `AssetDatabase::Shutdown()`。 +- 调用后会把 `m_projectRoot` 清空。 + +## 相关文档 + +- [AssetImportService](AssetImportService.md) +- [SetProjectRoot](SetProjectRoot.md) diff --git a/docs/api/XCEngine/Core/Asset/AssetImportService/TryGetAssetRef.md b/docs/api/XCEngine/Core/Asset/AssetImportService/TryGetAssetRef.md new file mode 100644 index 00000000..55f7c78a --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetImportService/TryGetAssetRef.md @@ -0,0 +1,32 @@ +# AssetImportService::TryGetAssetRef + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/AssetImportService.h` + +## 签名 + +```cpp +bool TryGetAssetRef( + const Containers::String& requestPath, + ResourceType resourceType, + AssetRef& outRef) const; +``` + +## 作用 + +按请求路径解析主资产 `AssetRef`。 + +## 当前实现行为 + +- 在 `std::recursive_mutex` 保护下执行。 +- 若当前没有项目根,直接返回 `false`。 +- 否则转发到 `AssetDatabase::TryGetAssetRef()`。 +- 读优化和 cache miss 刷新逻辑不在这里,而在 [ProjectAssetIndex](../ProjectAssetIndex/ProjectAssetIndex.md)。 + +## 相关文档 + +- [AssetImportService](AssetImportService.md) +- [ProjectAssetIndex::TryGetAssetRef](../ProjectAssetIndex/TryGetAssetRef.md) diff --git a/docs/api/XCEngine/Core/Asset/AssetImportService/TryGetPrimaryAssetPath.md b/docs/api/XCEngine/Core/Asset/AssetImportService/TryGetPrimaryAssetPath.md new file mode 100644 index 00000000..70ea7cbf --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetImportService/TryGetPrimaryAssetPath.md @@ -0,0 +1,31 @@ +# AssetImportService::TryGetPrimaryAssetPath + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/AssetImportService.h` + +## 签名 + +```cpp +bool TryGetPrimaryAssetPath( + const AssetGUID& guid, + Containers::String& outRelativePath) const; +``` + +## 作用 + +按 `AssetGUID` 反查主资产相对路径。 + +## 当前实现行为 + +- 在 `std::recursive_mutex` 保护下执行。 +- 若当前没有项目根,直接返回 `false`。 +- 否则转发到 `AssetDatabase::TryGetPrimaryAssetPath()`。 +- 返回路径通常是相对项目根的 `Assets/...` 形式。 + +## 相关文档 + +- [AssetImportService](AssetImportService.md) +- [ProjectAssetIndex::TryResolveAssetPath](../ProjectAssetIndex/TryResolveAssetPath.md) diff --git a/docs/api/XCEngine/Core/Asset/AssetRef/AssetRef.md b/docs/api/XCEngine/Core/Asset/AssetRef/AssetRef.md new file mode 100644 index 00000000..010f4b65 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/AssetRef/AssetRef.md @@ -0,0 +1,129 @@ +# AssetRef + +**命名空间**: `XCEngine::Resources` + +**类型**: `header-overview` + +**头文件**: `XCEngine/Core/Asset/AssetRef.h` + +**描述**: 定义项目资产引用的最小身份元组,用 `AssetGUID + LocalID + ResourceType` 表达一个可序列化的资产目标。 + +## 角色概述 + +`AssetRef` 解决的问题不是“如何加载一个路径”,而是“如何在路径变化后仍然能找到同一个项目资产”。 + +当前资源链路里,路径只适合做即时查找,但不适合做长期引用标识;`AssetRef` 则是那层更稳定的序列化外壳。它通常经历这样的流程: + +1. 上层拿 `Assets/...` 路径向 `ResourceManager` 或 `AssetDatabase` 请求一个 `AssetRef` +2. 组件、场景或其他序列化数据保存这个引用 +3. 反序列化时再把 `AssetRef` 解析回主 source 路径 +4. 最终仍通过正常资源加载链路去拿运行时资源实例 + +## 当前结构 + +`AssetRef` 由三部分组成: + +- `assetGuid` +- `localID` +- `resourceType` + +它的有效性规则也很直接: + +- `assetGuid` 必须有效 +- `localID` 不能是 `kInvalidLocalID` +- `resourceType` 不能是 `ResourceType::Unknown` + +只要这三者任一缺失,`IsValid()` 就会返回 `false`。 + +## 当前公开能力 + +### 基础方法 + +- `IsValid()` 检查引用是否完整 +- `Reset()` 把三元组清零回默认无效状态 +- `ToString()` 返回人类可读的调试字符串 + +`ToString()` 的格式当前是: + +`::` + +例如: + +`0123456789abcdeffedcba9876543210:1:Mesh` + +这更适合日志和调试,不是当前组件序列化真正落盘的格式。 + +### `MakeMainAssetRef()` + +模板辅助函数 `MakeMainAssetRef()` 会: + +- 写入调用方给定的 `assetGuid` +- 把 `localID` 设为 `kMainAssetLocalID` +- 通过 `GetResourceType()` 推导 `resourceType` + +它表达的是“给我这个 GUID 对应的主资产引用”,而不是任意子资产引用。 + +## 在当前系统里的真实语义 + +### 主资产引用而非 artifact 路径 + +当前 `AssetRef` 指向的是项目资产身份,不是 `Library/Artifacts/...` 里的某个缓存文件。 + +`ResourceManager::TryResolveAssetPath()` 会把它还原成主 source 路径,例如: + +- `Assets/runtime.material` + +随后 `Load(const AssetRef&)` 再复用普通 `Load(path)` 流程,必要时由 `AssetDatabase::EnsureArtifact()` 重新找到或重建 artifact。 + +### `localID` 现状 + +虽然结构里有 `localID`,但当前主链路基本都固定写: + +- `kMainAssetLocalID` + +`AssetDatabase::TryGetAssetRef()` 和 `ResourceManager::TryGetAssetRef()` 当前都会直接生成主资产引用,而不是给模型子材质、子纹理或其他子对象分配独立 local ID。 + +### `resourceType` 现状 + +`resourceType` 是引用元组的一部分,但当前生成它时,`AssetDatabase::TryGetAssetRef()` 只是接受调用方传入的类型并原样写入,不会回头校验这个类型是否真的和 importer 匹配。 + +真正的类型匹配仍然主要发生在后续加载与导入链路里。 + +## 序列化现实 + +### 调试字符串和落盘格式不是一回事 + +头文件提供的 `ToString()` 使用冒号和资源类型名,适合人读。 + +但组件实际落盘时,`MeshFilterComponent` 和 `MeshRendererComponent` 当前采用的是另一套更紧凑的编码: + +- 单个引用: `guid,localID,resourceTypeInt` +- 多个材质引用: 用 `|` 连接多个单个引用 + +所以如果你在排查场景或组件序列化问题,不能把 `ToString()` 输出误当成真实持久化格式。 + +### 反序列化路径 + +当前组件反序列化时会: + +1. 先把 `guid,localID,resourceTypeInt` 文本解码回 `AssetRef` +2. 再调用 `ResourceManager::TryResolveAssetPath()` 反查回 `Assets/...` +3. 根据是否启用 deferred scene load,立即或延后去加载资源 + +这就是 `AssetRef` 真正承担“路径可迁移引用”的地方。 + +## 当前实现边界 + +- 当前主要服务项目 `Assets` 资产,不适合表示 `builtin://` 这类虚拟路径。 +- `localID` 子资产寻址能力还没有真正展开,实际几乎都等于 `kMainAssetLocalID`。 +- `resourceType` 当前更多是调用约束和调试信息,不是强校验凭证。 +- `MakeMainAssetRef()` 依赖 `GetResourceType()` 的显式特化;不是任意类型都能安全使用。 +- `AssetRef` 解析到的是主 source 路径,不是 artifact 缓存文件路径。 + +## 相关文档 + +- [当前模块](../Asset.md) +- [AssetGUID](../AssetGUID/AssetGUID.md) +- [AssetDatabase](../AssetDatabase/AssetDatabase.md) +- [ResourceManager](../ResourceManager/ResourceManager.md) +- [API 总索引](../../../../main.md) diff --git a/docs/api/XCEngine/Core/Asset/ProjectAssetIndex/ProjectAssetIndex.md b/docs/api/XCEngine/Core/Asset/ProjectAssetIndex/ProjectAssetIndex.md new file mode 100644 index 00000000..ed5c20dd --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/ProjectAssetIndex/ProjectAssetIndex.md @@ -0,0 +1,132 @@ +# ProjectAssetIndex + +**命名空间**: `XCEngine::Resources` + +**类型**: `class` + +**头文件**: `XCEngine/Core/Asset/ProjectAssetIndex.h` + +**描述**: 维护项目资产 `path/GUID` 的本地热路径索引缓存,为 `ResourceManager` 提供快速 `AssetRef` 查询与 `AssetGUID -> path` 反查。 + +## 概述 + +`ProjectAssetIndex` 是当前项目资产链路里的“轻量 snapshot 缓存层”: + +- [AssetDatabase](../AssetDatabase/AssetDatabase.md) 负责真实数据和导入逻辑。 +- [AssetImportService](../AssetImportService/AssetImportService.md) 负责线程安全数据库访问。 +- `ProjectAssetIndex` 负责把这些能力转成高频查询友好的本地缓存。 + +它的职责不是拥有数据库真相,而是: + +- 保存一份适合频繁读取的 `path/GUID` snapshot +- 优先走本地共享锁查询 +- miss 时回退到 `AssetImportService` +- 在成功查询后把结果记回本地缓存 + +## 当前存储模型 + +内部当前维护两张表: + +- `m_assetGuidByPathKey` +- `m_assetPathByGuid` + +其中 path key 会先规范成相对项目路径,再统一转成小写字符串,因此当前查找对路径大小写不敏感。 + +`m_projectRoot` 记录当前绑定的项目根,并由 `ResetProjectRoot()` 或 `RefreshFrom()` 统一更新。 + +## 路径规范化语义 + +`ProjectAssetIndex` 当前只接受正式项目资产路径: + +- `Assets/...` +- 或者能够相对当前项目根还原成 `Assets/...` 的绝对路径 + +以下路径不会进入索引系统: + +- 带 `://` 的虚拟路径,例如 `builtin://...` +- 不落在项目 `Assets` 目录下的绝对路径 +- 其他普通相对路径 + +因此它缓存的是“项目资产索引”,不是所有资源路径索引。 + +## 当前公开能力 + +### `ResetProjectRoot()` + +设置新的项目根,并清空两张 lookup 表。`ResourceManager::SetResourceRoot("")` 会走这条路径,确保旧项目 snapshot 不会泄漏到新上下文。 + +### `RefreshFrom()` + +从 `AssetImportService` 拉取一整份新的 [LookupSnapshot](../AssetImportService/LookupSnapshot.md)。 + +当前流程是: + +1. 在锁外读取当前项目根 +2. 如果项目根有效,则调用 `BuildLookupSnapshot(snapshot)` +3. 在独占锁下一次性替换 `m_projectRoot`、`m_assetGuidByPathKey` 和 `m_assetPathByGuid` + +这让读线程只会看到旧 snapshot 或新 snapshot,不会看到半更新状态。 + +### `TryGetAssetRef()` + +这是最核心的查询入口,当前策略是: + +1. 先用共享锁查本地 `path -> guid` +2. 命中后直接组装主资产 `AssetRef` +3. miss 时回退到 `AssetImportService::TryGetAssetRef()` +4. 如果第一次回退仍失败,且路径像项目资产路径,则触发一次: + - `importService.Refresh()` + - `RefreshFrom(importService)` + - 再重试 +5. 成功后再通过 `TryGetPrimaryAssetPath()` 调用 `RememberResolvedPath()` 回填缓存 + +因此它不是纯只读缓存,而是“可自我修复的查询索引”。 + +### `TryResolveAssetPath()` + +反查逻辑和上面类似,但方向相反: + +1. 先查本地 `guid -> path` +2. miss 时回退到 `AssetImportService::TryGetPrimaryAssetPath()` +3. 成功后记回缓存 + +它支撑的是 `ResourceManager::Load(const AssetRef&)` 这条链路。 + +### `RememberResolvedPath()` + +把已知的 `AssetGUID -> relativePath` 写回两张表。当前只接受: + +- 有效的 `AssetGUID` +- 非空相对路径 + +`ResourceManager::LoadResource()` 在 `EnsureArtifact()` 成功后,会通过它把最新 GUID/path 关系记回索引。 + +## 与 `ResourceManager` 的关系 + +`ResourceManager` 当前把项目资产职责拆成三层: + +- [AssetImportService](../AssetImportService/AssetImportService.md):导入与数据库服务 +- `ProjectAssetIndex`:本地 snapshot 缓存 +- `ResourceManager` 自己:运行时加载、缓存、句柄与日志 + +因此: + +- `SetResourceRoot()` 会在设置项目根后立刻刷新这份 snapshot +- [RefreshProjectAssets](../ResourceManager/RefreshProjectAssets.md) 会刷新导入服务,再整体刷新索引 +- `TryGetAssetRef()` / `TryResolveAssetPath()` 对 `ResourceManager` 来说只是转发到这里 + +## 当前实现边界 + +- 当前只缓存主资产路径,`localID` 基本固定为 `kMainAssetLocalID` +- 本地索引不持久化,完全依赖 `AssetImportService` 提供的新 snapshot 重建 +- miss 时的恢复策略是“整份 snapshot 刷新”,不是细粒度增量同步 +- 只对项目 `Assets` 路径生效,不覆盖 `builtin://` 这类虚拟资源 + +## 相关文档 + +- [当前模块](../Asset.md) +- [AssetImportService](../AssetImportService/AssetImportService.md) +- [AssetDatabase](../AssetDatabase/AssetDatabase.md) +- [AssetRef](../AssetRef/AssetRef.md) +- [ResourceManager](../ResourceManager/ResourceManager.md) +- [API 总索引](../../../../main.md) diff --git a/docs/api/XCEngine/Core/Asset/ProjectAssetIndex/RefreshFrom.md b/docs/api/XCEngine/Core/Asset/ProjectAssetIndex/RefreshFrom.md new file mode 100644 index 00000000..52c22a91 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/ProjectAssetIndex/RefreshFrom.md @@ -0,0 +1,28 @@ +# ProjectAssetIndex::RefreshFrom + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/ProjectAssetIndex.h` + +## 签名 + +```cpp +void RefreshFrom(const AssetImportService& importService); +``` + +## 作用 + +从 `AssetImportService` 重建 path/GUID 快照。 + +## 当前实现行为 + +- 先在锁外向 `importService` 请求项目根和双向 lookup snapshot。 +- 然后在独占锁下整体替换 `m_projectRoot`、`m_assetGuidByPathKey` 和 `m_assetPathByGuid`。 +- 这让读线程只会看到旧快照或新快照,不会看到半更新状态。 + +## 相关文档 + +- [ProjectAssetIndex](ProjectAssetIndex.md) +- [AssetImportService::BuildLookupSnapshot](../AssetImportService/BuildLookupSnapshot.md) diff --git a/docs/api/XCEngine/Core/Asset/ProjectAssetIndex/RememberResolvedPath.md b/docs/api/XCEngine/Core/Asset/ProjectAssetIndex/RememberResolvedPath.md new file mode 100644 index 00000000..8158e7b8 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/ProjectAssetIndex/RememberResolvedPath.md @@ -0,0 +1,33 @@ +# ProjectAssetIndex::RememberResolvedPath + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/ProjectAssetIndex.h` + +## 签名 + +```cpp +void RememberResolvedPath( + const AssetGUID& assetGuid, + const Containers::String& relativePath); +``` + +## 作用 + +把一次成功解析的 GUID/path 结果写回索引缓存。 + +## 当前实现行为 + +- 当 `assetGuid` 无效或 `relativePath` 为空时直接返回。 +- 否则在独占锁下同时更新: + - 小写路径键到 `AssetGUID` 的映射 + - `AssetGUID` 到原始相对路径的映射 +- 这是一次局部缓存写回,不会重建整份 snapshot。 + +## 相关文档 + +- [ProjectAssetIndex](ProjectAssetIndex.md) +- [TryGetAssetRef](TryGetAssetRef.md) +- [TryResolveAssetPath](TryResolveAssetPath.md) diff --git a/docs/api/XCEngine/Core/Asset/ProjectAssetIndex/ResetProjectRoot.md b/docs/api/XCEngine/Core/Asset/ProjectAssetIndex/ResetProjectRoot.md new file mode 100644 index 00000000..70c28776 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/ProjectAssetIndex/ResetProjectRoot.md @@ -0,0 +1,30 @@ +# ProjectAssetIndex::ResetProjectRoot + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/ProjectAssetIndex.h` + +## 签名 + +```cpp +void ResetProjectRoot( + const Containers::String& projectRoot = Containers::String()); +``` + +## 作用 + +切换项目根并清空当前索引缓存。 + +## 当前实现行为 + +- 在 `std::shared_mutex` 的独占锁下执行。 +- 会更新 `m_projectRoot`。 +- 同时清空 `m_assetGuidByPathKey` 与 `m_assetPathByGuid`。 +- 不会主动向 `AssetImportService` 拉取新快照。 + +## 相关文档 + +- [ProjectAssetIndex](ProjectAssetIndex.md) +- [RefreshFrom](RefreshFrom.md) diff --git a/docs/api/XCEngine/Core/Asset/ProjectAssetIndex/TryGetAssetRef.md b/docs/api/XCEngine/Core/Asset/ProjectAssetIndex/TryGetAssetRef.md new file mode 100644 index 00000000..b40914a0 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/ProjectAssetIndex/TryGetAssetRef.md @@ -0,0 +1,39 @@ +# ProjectAssetIndex::TryGetAssetRef + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/ProjectAssetIndex.h` + +## 签名 + +```cpp +bool TryGetAssetRef( + AssetImportService& importService, + const Containers::String& path, + ResourceType resourceType, + AssetRef& outRef) const; +``` + +## 作用 + +优先通过本地索引、必要时回退到导入服务,把路径解析成主资产 `AssetRef`。 + +## 当前实现行为 + +- 先把输入路径规范化到 `Assets/...` 语义,并生成小写 lookup key。 +- 若本地 `pathKey -> AssetGUID` 命中: + - 直接写出 `assetGuid` + - 把 `localID` 设为 `kMainAssetLocalID` + - 把调用方传入的 `resourceType` 写入结果 +- 若本地 miss: + - 先调用 `importService.TryGetAssetRef()` + - 若仍失败且路径看起来属于当前项目 `Assets`,则触发一次 `importService.Refresh()` + [RefreshFrom](RefreshFrom.md) 后重试 +- 最终成功时,会尝试反查主路径并通过 [RememberResolvedPath](RememberResolvedPath.md) 写回缓存。 + +## 相关文档 + +- [ProjectAssetIndex](ProjectAssetIndex.md) +- [RefreshFrom](RefreshFrom.md) +- [RememberResolvedPath](RememberResolvedPath.md) diff --git a/docs/api/XCEngine/Core/Asset/ProjectAssetIndex/TryResolveAssetPath.md b/docs/api/XCEngine/Core/Asset/ProjectAssetIndex/TryResolveAssetPath.md new file mode 100644 index 00000000..fbbc9bf0 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/ProjectAssetIndex/TryResolveAssetPath.md @@ -0,0 +1,33 @@ +# ProjectAssetIndex::TryResolveAssetPath + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/ProjectAssetIndex.h` + +## 签名 + +```cpp +bool TryResolveAssetPath( + const AssetImportService& importService, + const AssetRef& assetRef, + Containers::String& outPath) const; +``` + +## 作用 + +优先通过本地索引、必要时回退到导入服务,把 `AssetRef` 反查成主资产路径。 + +## 当前实现行为 + +- 对无效 `AssetRef` 直接返回 `false`。 +- 若本地 `AssetGUID -> path` 缓存命中,直接返回缓存路径。 +- 若本地 miss,则调用 `importService.TryGetPrimaryAssetPath()`。 +- fallback 成功后,会通过 [RememberResolvedPath](RememberResolvedPath.md) 写回缓存。 + +## 相关文档 + +- [ProjectAssetIndex](ProjectAssetIndex.md) +- [RememberResolvedPath](RememberResolvedPath.md) +- [AssetImportService::TryGetPrimaryAssetPath](../AssetImportService/TryGetPrimaryAssetPath.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/AddRef.md b/docs/api/XCEngine/Core/Asset/ResourceManager/AddRef.md index ef86edef..631cb81e 100644 --- a/docs/api/XCEngine/Core/Asset/ResourceManager/AddRef.md +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/AddRef.md @@ -1,31 +1,30 @@ # ResourceManager::AddRef -添加元素或建立关联。 +增加指定资源 GUID 的句柄引用计数。 ```cpp void AddRef(ResourceGUID guid); ``` -该方法声明于 `XCEngine/Core/Asset/ResourceManager.h`,当前页面用于固定 `ResourceManager` 类目录下的方法级 canonical 路径。 +## 当前行为 -**参数:** -- `guid` - 参数语义详见头文件声明。 +按当前实现: -**返回:** `void` - 无返回值。 +1. 在 `m_refCounts` 中把该 GUID 的计数加一;若之前不存在,则插入为 `1` +2. 如果当前 `m_resourceCache` 里还没有该 GUID,对应路径会尝试调用内部 `ReloadResource(guid)` -**示例:** +## 当前语义 -```cpp -#include +- 这是 [ResourceHandle](../ResourceHandle/ResourceHandle.md) 构造、拷贝和赋值链路里的底层计数接口。 +- 计数增加本身不等于资源一定已在缓存中。 -void Example() { - XCEngine::Resources::ResourceManager object; - // 根据上下文补齐参数后调用 ResourceManager::AddRef(...)。 - (void)object; -} -``` +## 当前实现边界 + +- 当前 `ReloadResource(guid)` 仍基本是 stub,且依赖 `m_guidToPath` 里已有路径记录。 +- 因此 `AddRef()` 现在并不保证“计数一加就能把缺失资源重新加载回来”。 ## 相关文档 -- [返回类总览](ResourceManager.md) -- [返回模块目录](../Asset.md) +- [Release](Release.md) +- [GetRefCount](GetRefCount.md) +- [ResourceHandle](../ResourceHandle/ResourceHandle.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/BeginDeferredSceneLoad.md b/docs/api/XCEngine/Core/Asset/ResourceManager/BeginDeferredSceneLoad.md new file mode 100644 index 00000000..4c9c41f7 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/BeginDeferredSceneLoad.md @@ -0,0 +1,28 @@ +# ResourceManager::BeginDeferredSceneLoad + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/ResourceManager.h` + +## 签名 + +```cpp +void BeginDeferredSceneLoad(); +``` + +## 作用 + +进入 deferred scene load 状态,使内部深度计数加一。 + +## 当前实现行为 + +- 当前只是对 `m_deferredSceneLoadDepth` 做原子自增。 +- 没有额外锁,也没有附带回调或事件。 + +## 相关文档 + +- [ResourceManager](ResourceManager.md) +- [EndDeferredSceneLoad](EndDeferredSceneLoad.md) +- [ScopedDeferredSceneLoad](ScopedDeferredSceneLoad.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/EndDeferredSceneLoad.md b/docs/api/XCEngine/Core/Asset/ResourceManager/EndDeferredSceneLoad.md new file mode 100644 index 00000000..fc229ec2 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/EndDeferredSceneLoad.md @@ -0,0 +1,29 @@ +# ResourceManager::EndDeferredSceneLoad + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/ResourceManager.h` + +## 签名 + +```cpp +void EndDeferredSceneLoad(); +``` + +## 作用 + +退出一层 deferred scene load 状态。 + +## 当前实现行为 + +- 先读取当前原子深度。 +- 如果深度已经是 `0`,直接返回,避免下溢。 +- 否则执行一次原子自减。 + +## 相关文档 + +- [ResourceManager](ResourceManager.md) +- [BeginDeferredSceneLoad](BeginDeferredSceneLoad.md) +- [ScopedDeferredSceneLoad](ScopedDeferredSceneLoad.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/Exists.md b/docs/api/XCEngine/Core/Asset/ResourceManager/Exists.md index 038333b7..6c93c5c2 100644 --- a/docs/api/XCEngine/Core/Asset/ResourceManager/Exists.md +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/Exists.md @@ -1,44 +1,31 @@ # ResourceManager::Exists -公开方法,详见头文件声明。 - -该方法在 `XCEngine/Core/Asset/ResourceManager.h` 中提供了 2 个重载,当前页面统一汇总这些公开声明。 - -## 重载 1: 声明 +判断某个资源当前是否已经存在于运行时缓存。 ```cpp bool Exists(const Containers::String& path) const; -``` - -**参数:** -- `path` - 参数语义详见头文件声明。 - -**返回:** `bool` - 返回值语义详见头文件声明。 - -## 重载 2: 声明 - -```cpp bool Exists(ResourceGUID guid) const; ``` -**参数:** -- `guid` - 参数语义详见头文件声明。 +## 当前行为 -**返回:** `bool` - 返回值语义详见头文件声明。 +### `Exists(path)` -**示例:** +- 先把路径转成 `ResourceGUID::Generate(path)` +- 再转发到 `Exists(guid)` -```cpp -#include +### `Exists(guid)` -void Example() { - XCEngine::Resources::ResourceManager object; - // 根据上下文补齐参数后调用 ResourceManager::Exists(...)。 - (void)object; -} -``` +- 直接查询 `m_resourceCache.Contains(guid)` + +## 当前语义 + +- 这不是磁盘存在性检查。 +- 也不是项目资产数据库存在性检查。 +- 它只回答“这个资源当前是否已经在运行时缓存里”。 ## 相关文档 -- [返回类总览](ResourceManager.md) -- [返回模块目录](../Asset.md) +- [Find](Find.md) +- [Load](Load.md) +- [Unload](Unload.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/Find.md b/docs/api/XCEngine/Core/Asset/ResourceManager/Find.md index 169b4718..0268f10c 100644 --- a/docs/api/XCEngine/Core/Asset/ResourceManager/Find.md +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/Find.md @@ -1,44 +1,37 @@ # ResourceManager::Find -查找并返回匹配对象。 - -该方法在 `XCEngine/Core/Asset/ResourceManager.h` 中提供了 2 个重载,当前页面统一汇总这些公开声明。 - -## 重载 1: 声明 +在当前运行时缓存中查找资源对象。 ```cpp IResource* Find(const Containers::String& path); -``` - -**参数:** -- `path` - 参数语义详见头文件声明。 - -**返回:** `IResource*` - 返回值语义详见头文件声明。 - -## 重载 2: 声明 - -```cpp IResource* Find(ResourceGUID guid); ``` -**参数:** -- `guid` - 参数语义详见头文件声明。 +## 当前行为 -**返回:** `IResource*` - 返回值语义详见头文件声明。 +### `Find(path)` -**示例:** +- 先用 `ResourceGUID::Generate(path)` 生成 GUID +- 再转发到 `Find(guid)` -```cpp -#include +### `Find(guid)` -void Example() { - XCEngine::Resources::ResourceManager object; - // 根据上下文补齐参数后调用 ResourceManager::Find(...)。 - (void)object; -} -``` +- 调用内部 `FindInCache(guid)` +- 只查询当前 `m_resourceCache` + +## 当前语义 + +- 这不是“若没找到就去加载”的接口。 +- 它也不会回退到 `AssetDatabase`、磁盘文件系统或 loader。 +- 只有资源已经在运行时缓存里,才会返回非空指针。 + +## 注意事项 + +- 路径版查找依赖传入路径生成的 GUID 与加载时使用的原始请求路径一致。 +- 对项目资产来说,通常应传 `Assets/...` 这类原始 source 路径,而不是 artifact 路径。 ## 相关文档 -- [返回类总览](ResourceManager.md) -- [返回模块目录](../Asset.md) +- [Exists](Exists.md) +- [Load](Load.md) +- [Unload](Unload.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/FlushCache.md b/docs/api/XCEngine/Core/Asset/ResourceManager/FlushCache.md index 74c47ffa..6d4060e2 100644 --- a/docs/api/XCEngine/Core/Asset/ResourceManager/FlushCache.md +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/FlushCache.md @@ -1,30 +1,22 @@ # ResourceManager::FlushCache -公开方法,详见头文件声明。 +把底层 `ResourceCache` 执行一次显式刷新。 ```cpp void FlushCache(); ``` -该方法声明于 `XCEngine/Core/Asset/ResourceManager.h`,当前页面用于固定 `ResourceManager` 类目录下的方法级 canonical 路径。 +## 当前行为 -**参数:** 无。 +- 直接调用 `m_cache.Flush()` -**返回:** `void` - 无返回值。 +## 当前语义 -**示例:** - -```cpp -#include - -void Example() { - XCEngine::Resources::ResourceManager object; - // 根据上下文补齐参数后调用 ResourceManager::FlushCache(...)。 - (void)object; -} -``` +- 它操作的是 `ResourceCache`,不是直接清空 `m_resourceCache`。 +- 当前实现下,这并不等同于“卸载所有资源”。 ## 相关文档 -- [返回类总览](ResourceManager.md) -- [返回模块目录](../Asset.md) +- [UnloadAll](UnloadAll.md) +- [UnloadUnused](UnloadUnused.md) +- [ResourceCache](../ResourceCache/ResourceCache.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/Get.md b/docs/api/XCEngine/Core/Asset/ResourceManager/Get.md index 17f17d83..44629479 100644 --- a/docs/api/XCEngine/Core/Asset/ResourceManager/Get.md +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/Get.md @@ -1,29 +1,28 @@ # ResourceManager::Get -获取相关状态或对象。 +获取 `ResourceManager` 的进程级单例。 ```cpp static ResourceManager& Get(); ``` -该方法声明于 `XCEngine/Core/Asset/ResourceManager.h`,当前页面用于固定 `ResourceManager` 类目录下的方法级 canonical 路径。 +## 当前行为 -**参数:** 无。 - -**返回:** `ResourceManager&` - 返回值语义详见头文件声明。 - -**示例:** +当前实现使用函数内静态对象: ```cpp -#include - -void Example() { - auto& instance = XCEngine::Resources::ResourceManager::Get(); - (void)instance; -} +static ResourceManager instance; +return instance; ``` +这意味着: + +- 整个进程当前只有一个 `ResourceManager` +- 调用方拿到的是同一个共享实例 +- 返回值不表达所有权,调用方不应自行销毁 + ## 相关文档 -- [返回类总览](ResourceManager.md) -- [返回模块目录](../Asset.md) +- [ResourceManager](ResourceManager.md) +- [Initialize](Initialize.md) +- [Shutdown](Shutdown.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/GetAsyncPendingCount.md b/docs/api/XCEngine/Core/Asset/ResourceManager/GetAsyncPendingCount.md new file mode 100644 index 00000000..cc60f8d1 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/GetAsyncPendingCount.md @@ -0,0 +1,28 @@ +# ResourceManager::GetAsyncPendingCount + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/ResourceManager.h` + +## 签名 + +```cpp +Core::uint32 GetAsyncPendingCount() const; +``` + +## 作用 + +返回异步加载器当前记录的待处理请求数量。 + +## 当前实现行为 + +- 当 `m_asyncLoader` 为空时返回 `0`。 +- 否则直接返回 `m_asyncLoader->GetPendingCount()`。 + +## 相关文档 + +- [ResourceManager](ResourceManager.md) +- [LoadAsync](LoadAsync.md) +- [IsAsyncLoading](IsAsyncLoading.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/GetLoader.md b/docs/api/XCEngine/Core/Asset/ResourceManager/GetLoader.md index 7a0e2fe0..d929dd36 100644 --- a/docs/api/XCEngine/Core/Asset/ResourceManager/GetLoader.md +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/GetLoader.md @@ -1,31 +1,23 @@ # ResourceManager::GetLoader -获取相关状态或对象。 +查询某个资源类型当前注册的 loader。 ```cpp IResourceLoader* GetLoader(ResourceType type) const; ``` -该方法声明于 `XCEngine/Core/Asset/ResourceManager.h`,当前页面用于固定 `ResourceManager` 类目录下的方法级 canonical 路径。 +## 当前行为 -**参数:** -- `type` - 参数语义详见头文件声明。 +- 若 `m_loaders` 中存在该 `ResourceType`,返回对应 `IResourceLoader*` +- 否则返回 `nullptr` -**返回:** `IResourceLoader*` - 返回值语义详见头文件声明。 +## 当前语义 -**示例:** - -```cpp -#include - -void Example() { - XCEngine::Resources::ResourceManager object; - // 根据上下文补齐参数后调用 ResourceManager::GetLoader(...)。 - (void)object; -} -``` +- 返回的是非拥有型原始指针。 +- `LoadResource()` 正是通过它决定某种资源类型该交给哪个 loader。 ## 相关文档 -- [返回类总览](ResourceManager.md) -- [返回模块目录](../Asset.md) +- [RegisterLoader](RegisterLoader.md) +- [UnregisterLoader](UnregisterLoader.md) +- [Load](Load.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/GetMemoryBudget.md b/docs/api/XCEngine/Core/Asset/ResourceManager/GetMemoryBudget.md index 7a2c4a30..031076e1 100644 --- a/docs/api/XCEngine/Core/Asset/ResourceManager/GetMemoryBudget.md +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/GetMemoryBudget.md @@ -1,30 +1,17 @@ # ResourceManager::GetMemoryBudget -获取相关状态或对象。 +返回当前配置的资源缓存内存预算。 ```cpp size_t GetMemoryBudget() const; ``` -该方法声明于 `XCEngine/Core/Asset/ResourceManager.h`,当前页面用于固定 `ResourceManager` 类目录下的方法级 canonical 路径。 +## 当前行为 -**参数:** 无。 - -**返回:** `size_t` - 返回值语义详见头文件声明。 - -**示例:** - -```cpp -#include - -void Example() { - XCEngine::Resources::ResourceManager object; - // 根据上下文补齐参数后调用 ResourceManager::GetMemoryBudget(...)。 - (void)object; -} -``` +- 直接返回 `m_memoryBudget` ## 相关文档 -- [返回类总览](ResourceManager.md) -- [返回模块目录](../Asset.md) +- [SetMemoryBudget](SetMemoryBudget.md) +- [GetMemoryUsage](GetMemoryUsage.md) +- [ResourceManager](ResourceManager.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/GetMemoryUsage.md b/docs/api/XCEngine/Core/Asset/ResourceManager/GetMemoryUsage.md index e8ab2ba7..d7d46161 100644 --- a/docs/api/XCEngine/Core/Asset/ResourceManager/GetMemoryUsage.md +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/GetMemoryUsage.md @@ -1,30 +1,22 @@ # ResourceManager::GetMemoryUsage -获取相关状态或对象。 +返回当前 `ResourceManager` 跟踪到的缓存资源内存使用量。 ```cpp size_t GetMemoryUsage() const; ``` -该方法声明于 `XCEngine/Core/Asset/ResourceManager.h`,当前页面用于固定 `ResourceManager` 类目录下的方法级 canonical 路径。 +## 当前行为 -**参数:** 无。 +- 直接返回 `m_memoryUsage` -**返回:** `size_t` - 返回值语义详见头文件声明。 +## 当前语义 -**示例:** - -```cpp -#include - -void Example() { - XCEngine::Resources::ResourceManager object; - // 根据上下文补齐参数后调用 ResourceManager::GetMemoryUsage(...)。 - (void)object; -} -``` +- 这个值反映的是 `AddToCache()` / `Unload*()` 更新的资源缓存字节数。 +- 它不是整个进程的真实内存占用,也不是 GPU 侧显存统计。 ## 相关文档 -- [返回类总览](ResourceManager.md) -- [返回模块目录](../Asset.md) +- [GetMemoryBudget](GetMemoryBudget.md) +- [SetMemoryBudget](SetMemoryBudget.md) +- [ResourceManager](ResourceManager.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/GetProjectLibraryRoot.md b/docs/api/XCEngine/Core/Asset/ResourceManager/GetProjectLibraryRoot.md new file mode 100644 index 00000000..30ee9234 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/GetProjectLibraryRoot.md @@ -0,0 +1,34 @@ +# ResourceManager::GetProjectLibraryRoot + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/ResourceManager.h` + +## 签名 + +```cpp +Containers::String GetProjectLibraryRoot() const; +``` + +## 作用 + +返回当前项目 `Library` 目录的根路径。 + +## 当前实现行为 + +- 直接转发到 `m_assetImportService.GetLibraryRoot()`。 +- 返回值是一个按值复制出来的 `Containers::String`,不是内部成员引用。 +- 如果当前没有绑定项目根,通常会返回空字符串。 + +## 主要用途 + +- 用于测试或工具层定位当前项目的 `Library` 目录。 +- 在执行 [RebuildProjectAssetCache](RebuildProjectAssetCache.md) 前后,可以用它确认实际操作的缓存目录。 + +## 相关文档 + +- [ResourceManager](ResourceManager.md) +- [RebuildProjectAssetCache](RebuildProjectAssetCache.md) +- [AssetImportService::GetLibraryRoot](../AssetImportService/GetLibraryRoot.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/GetRefCount.md b/docs/api/XCEngine/Core/Asset/ResourceManager/GetRefCount.md index ae3beb55..bfd88e51 100644 --- a/docs/api/XCEngine/Core/Asset/ResourceManager/GetRefCount.md +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/GetRefCount.md @@ -1,31 +1,22 @@ # ResourceManager::GetRefCount -获取相关状态或对象。 +查询指定资源 GUID 的当前句柄引用计数。 ```cpp Core::uint32 GetRefCount(ResourceGUID guid) const; ``` -该方法声明于 `XCEngine/Core/Asset/ResourceManager.h`,当前页面用于固定 `ResourceManager` 类目录下的方法级 canonical 路径。 +## 当前行为 -**参数:** -- `guid` - 参数语义详见头文件声明。 +- 若 `m_refCounts` 中存在该 GUID,返回当前计数 +- 否则返回 `0` -**返回:** `Core::uint32` - 返回值语义详见头文件声明。 +## 当前语义 -**示例:** - -```cpp -#include - -void Example() { - XCEngine::Resources::ResourceManager object; - // 根据上下文补齐参数后调用 ResourceManager::GetRefCount(...)。 - (void)object; -} -``` +- 这里返回的是 `ResourceHandle` 侧维护的引用计数,不是资源对象内部的引用计数,也不是缓存命中次数。 ## 相关文档 -- [返回类总览](ResourceManager.md) -- [返回模块目录](../Asset.md) +- [AddRef](AddRef.md) +- [Release](Release.md) +- [ResourceHandle](../ResourceHandle/ResourceHandle.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/GetResourcePaths.md b/docs/api/XCEngine/Core/Asset/ResourceManager/GetResourcePaths.md index 5ec97a25..f14f2157 100644 --- a/docs/api/XCEngine/Core/Asset/ResourceManager/GetResourcePaths.md +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/GetResourcePaths.md @@ -1,30 +1,23 @@ # ResourceManager::GetResourcePaths -获取相关状态或对象。 +返回当前运行时缓存里已知资源路径集合。 ```cpp Containers::Array GetResourcePaths() const; ``` -该方法声明于 `XCEngine/Core/Asset/ResourceManager.h`,当前页面用于固定 `ResourceManager` 类目录下的方法级 canonical 路径。 +## 当前行为 -**参数:** 无。 +- 遍历 `m_guidToPath` +- 把每个 value 追加到返回数组 -**返回:** `Containers::Array` - 返回值语义详见头文件声明。 +## 当前语义 -**示例:** - -```cpp -#include - -void Example() { - XCEngine::Resources::ResourceManager object; - // 根据上下文补齐参数后调用 ResourceManager::GetResourcePaths(...)。 - (void)object; -} -``` +- 它返回的是当前缓存资源对应的“原始请求路径”集合。 +- 这不是项目 `Assets` 的全量清单,也不是 `AssetDatabase` 的路径索引导出接口。 ## 相关文档 -- [返回类总览](ResourceManager.md) -- [返回模块目录](../Asset.md) +- [Find](Find.md) +- [UnloadAll](UnloadAll.md) +- [ResourceManager](ResourceManager.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/GetResourceRoot.md b/docs/api/XCEngine/Core/Asset/ResourceManager/GetResourceRoot.md index 7924925b..6631527b 100644 --- a/docs/api/XCEngine/Core/Asset/ResourceManager/GetResourceRoot.md +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/GetResourceRoot.md @@ -1,30 +1,22 @@ # ResourceManager::GetResourceRoot -获取相关状态或对象。 +返回当前绑定的项目资源根目录。 ```cpp const Containers::String& GetResourceRoot() const; ``` -该方法声明于 `XCEngine/Core/Asset/ResourceManager.h`,当前页面用于固定 `ResourceManager` 类目录下的方法级 canonical 路径。 +## 当前行为 -**参数:** 无。 +- 直接返回 `m_resourceRoot` -**返回:** `const Containers::String&` - 返回值语义详见头文件声明。 +## 当前语义 -**示例:** - -```cpp -#include - -void Example() { - XCEngine::Resources::ResourceManager object; - // 根据上下文补齐参数后调用 ResourceManager::GetResourceRoot(...)。 - (void)object; -} -``` +- 返回值为空时,通常表示当前没有处于项目资产模式。 +- 这个值由 [SetResourceRoot](SetResourceRoot.md) 写入,也是 `AssetImportService` / `ProjectAssetIndex` 是否启用的根入口。 ## 相关文档 -- [返回类总览](ResourceManager.md) -- [返回模块目录](../Asset.md) +- [SetResourceRoot](SetResourceRoot.md) +- [RefreshProjectAssets](RefreshProjectAssets.md) +- [ResourceManager](ResourceManager.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/Initialize.md b/docs/api/XCEngine/Core/Asset/ResourceManager/Initialize.md index 97a363f1..c67fd3e5 100644 --- a/docs/api/XCEngine/Core/Asset/ResourceManager/Initialize.md +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/Initialize.md @@ -1,30 +1,37 @@ # ResourceManager::Initialize -初始化内部状态。 +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/ResourceManager.h` + +## 签名 ```cpp void Initialize(); ``` -该方法声明于 `XCEngine/Core/Asset/ResourceManager.h`,当前页面用于固定 `ResourceManager` 类目录下的方法级 canonical 路径。 +## 作用 -**参数:** 无。 +初始化资源管理器的核心运行时状态。 -**返回:** `void` - 无返回值。 +## 当前实现行为 -**示例:** +- 当前只是转发到内部 `EnsureInitialized()`。 +- 首次初始化时会: + - 创建 `AsyncLoader` 并调用 `Initialize(2)` + - 注册内建 `MaterialLoader`、`MeshLoader`、`ShaderLoader`、`TextureLoader` + - 初始化内部 `AssetImportService` +- 如果 `m_asyncLoader` 已存在,则直接返回,不会重复构造。 -```cpp -#include +## 当前实现边界 -void Example() { - XCEngine::Resources::ResourceManager object; - // 根据上下文补齐参数后调用 ResourceManager::Initialize(...)。 - (void)object; -} -``` +- 当前 `AsyncLoader` 是否真正产生后台并发能力,仍取决于它自己的实现。 +- 这一步不会自动设置 `resourceRoot`;项目资产模式要到 [SetResourceRoot](SetResourceRoot.md) 之后才真正接入。 ## 相关文档 -- [返回类总览](ResourceManager.md) -- [返回模块目录](../Asset.md) +- [ResourceManager](ResourceManager.md) +- [Shutdown](Shutdown.md) +- [SetResourceRoot](SetResourceRoot.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/IsAsyncLoading.md b/docs/api/XCEngine/Core/Asset/ResourceManager/IsAsyncLoading.md new file mode 100644 index 00000000..ce9f46fb --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/IsAsyncLoading.md @@ -0,0 +1,28 @@ +# ResourceManager::IsAsyncLoading + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/ResourceManager.h` + +## 签名 + +```cpp +bool IsAsyncLoading() const; +``` + +## 作用 + +判断异步加载器当前是否仍有活跃加载状态。 + +## 当前实现行为 + +- 当 `m_asyncLoader` 为空时返回 `false`。 +- 否则直接返回 `m_asyncLoader->IsLoading()`。 + +## 相关文档 + +- [ResourceManager](ResourceManager.md) +- [LoadAsync](LoadAsync.md) +- [GetAsyncPendingCount](GetAsyncPendingCount.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/IsDeferredSceneLoadEnabled.md b/docs/api/XCEngine/Core/Asset/ResourceManager/IsDeferredSceneLoadEnabled.md new file mode 100644 index 00000000..1aa287aa --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/IsDeferredSceneLoadEnabled.md @@ -0,0 +1,29 @@ +# ResourceManager::IsDeferredSceneLoadEnabled + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/ResourceManager.h` + +## 签名 + +```cpp +bool IsDeferredSceneLoadEnabled() const; +``` + +## 作用 + +判断当前是否处于 deferred scene load 状态。 + +## 当前实现行为 + +- 当前直接返回 `m_deferredSceneLoadDepth.load() > 0`。 +- 这意味着它反映的是“嵌套深度是否非零”,而不是某个单独布尔开关。 + +## 相关文档 + +- [ResourceManager](ResourceManager.md) +- [BeginDeferredSceneLoad](BeginDeferredSceneLoad.md) +- [EndDeferredSceneLoad](EndDeferredSceneLoad.md) +- [ScopedDeferredSceneLoad](ScopedDeferredSceneLoad.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/Load.md b/docs/api/XCEngine/Core/Asset/ResourceManager/Load.md index 4cad2f5e..ebd796f5 100644 --- a/docs/api/XCEngine/Core/Asset/ResourceManager/Load.md +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/Load.md @@ -1,32 +1,43 @@ # ResourceManager::Load -加载资源或数据。 +**命名空间**: `XCEngine::Resources` + +**类型**: `method-template` + +**头文件**: `XCEngine/Core/Asset/ResourceManager.h` + +## 签名 ```cpp -template ResourceHandle Load(const Containers::String& path, ImportSettings* settings = nullptr); +template +ResourceHandle Load(const Containers::String& path, ImportSettings* settings = nullptr); ``` -该方法声明于 `XCEngine/Core/Asset/ResourceManager.h`,当前页面用于固定 `ResourceManager` 类目录下的方法级 canonical 路径。 +## 作用 -**参数:** -- `path` - 参数语义详见头文件声明。 -- `settings` - 参数语义详见头文件声明。 +按资源类型模板参数发起同步加载,并在成功时返回对应的 `ResourceHandle`。 -**返回:** `template ResourceHandle` - 返回值语义详见头文件声明。 +## 当前实现行为 -**示例:** +- 这是对内部 `LoadResource(path, GetResourceType(), settings)` 的类型安全包装。 +- 若 `LoadResource(...)` 失败或返回空资源,则返回空句柄。 +- 成功时把结果中的 `IResource*` 转成 `T*`,并构造 `ResourceHandle`。 -```cpp -#include +## 关键语义 -void Example() { - XCEngine::Resources::ResourceManager object; - // 根据上下文补齐参数后调用 ResourceManager::Load(...)。 - (void)object; -} -``` +- 当 `path` 是项目资产路径且当前已设置 `resourceRoot` 时,内部实际会先经 `AssetImportService::EnsureArtifact()` 准备 `ImportedAsset`,再把其中的 `runtimeLoadPath` 交给具体 loader。 +- 当 `path` 是 `builtin://...` 或其他不进入项目资产数据库的路径时,会直接走 loader。 +- 资源对象最终暴露给调用方的 `m_path` 仍是原始请求路径,而不是 artifact 路径。 +- `Load(const AssetRef&)` 的另一组重载会先用 `TryResolveAssetPath()` 还原回主 source 路径,再复用这条普通 `Load(path)` 链路。 + +## 当前限制 + +- `GetResourceType()` 需要存在对应特化;否则并不会自动推断资源类型。 +- 当前只适合当作同步加载主路径;异步版本的能力仍未完全闭环。 ## 相关文档 -- [返回类总览](ResourceManager.md) -- [返回模块目录](../Asset.md) +- [ResourceManager](ResourceManager.md) +- [TryResolveAssetPath](TryResolveAssetPath.md) +- [TryGetAssetRef](TryGetAssetRef.md) +- [AssetDatabase::EnsureArtifact](../AssetDatabase/EnsureArtifact.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/LoadAsync.md b/docs/api/XCEngine/Core/Asset/ResourceManager/LoadAsync.md index ec1f810f..7a8eefe6 100644 --- a/docs/api/XCEngine/Core/Asset/ResourceManager/LoadAsync.md +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/LoadAsync.md @@ -1,49 +1,45 @@ # ResourceManager::LoadAsync -加载资源或数据。 +**命名空间**: `XCEngine::Resources` -该方法在 `XCEngine/Core/Asset/ResourceManager.h` 中提供了 2 个重载,当前页面统一汇总这些公开声明。 +**类型**: `method` -## 重载 1: 声明 +**头文件**: `XCEngine/Core/Asset/ResourceManager.h` + +## 签名 ```cpp -void LoadAsync(const Containers::String& path, ResourceType type, std::function callback); +void LoadAsync( + const Containers::String& path, + ResourceType type, + std::function callback); + +void LoadAsync( + const Containers::String& path, + ResourceType type, + ImportSettings* settings, + std::function callback); ``` -**参数:** -- `path` - 参数语义详见头文件声明。 -- `type` - 参数语义详见头文件声明。 -- `callback` - 参数语义详见头文件声明。 +## 作用 -**返回:** `void` - 无返回值。 +把一次资源加载请求提交给异步加载器。 -## 重载 2: 声明 +## 当前实现行为 -```cpp -void LoadAsync(const Containers::String& path, ResourceType type, ImportSettings* settings, std::function callback); -``` +- 无 `settings` 的重载只是转发到带 `settings` 的版本,并传 `nullptr`。 +- 正式重载会先调用 `EnsureInitialized()`。 +- 若 `m_asyncLoader` 仍不可用,则直接用错误 `LoadResult` 触发回调。 +- 否则把请求原样转发给 `m_asyncLoader->Submit(path, type, settings, callback)`。 -**参数:** -- `path` - 参数语义详见头文件声明。 -- `type` - 参数语义详见头文件声明。 -- `settings` - 参数语义详见头文件声明。 -- `callback` - 参数语义详见头文件声明。 +## 当前实现边界 -**返回:** `void` - 无返回值。 - -**示例:** - -```cpp -#include - -void Example() { - XCEngine::Resources::ResourceManager object; - // 根据上下文补齐参数后调用 ResourceManager::LoadAsync(...)。 - (void)object; -} -``` +- 当前异步系统的真实执行能力仍取决于 `AsyncLoader`;不能把这组 API 当成成熟的后台流式加载系统。 +- 请求提交后,调用方仍需要配合 [UpdateAsyncLoads](UpdateAsyncLoads.md) 驱动完成队列更新。 ## 相关文档 -- [返回类总览](ResourceManager.md) -- [返回模块目录](../Asset.md) +- [ResourceManager](ResourceManager.md) +- [UpdateAsyncLoads](UpdateAsyncLoads.md) +- [IsAsyncLoading](IsAsyncLoading.md) +- [GetAsyncPendingCount](GetAsyncPendingCount.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/LoadGroup.md b/docs/api/XCEngine/Core/Asset/ResourceManager/LoadGroup.md index 6725d76d..5781bc28 100644 --- a/docs/api/XCEngine/Core/Asset/ResourceManager/LoadGroup.md +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/LoadGroup.md @@ -1,32 +1,36 @@ # ResourceManager::LoadGroup -加载资源或数据。 +对一组路径逐个发起异步加载,并在每个资源完成时回调一次 typed handle。 ```cpp -template void LoadGroup(const Containers::Array& paths, std::function)> callback); +template +void LoadGroup( + const Containers::Array& paths, + std::function)> callback); ``` -该方法声明于 `XCEngine/Core/Asset/ResourceManager.h`,当前页面用于固定 `ResourceManager` 类目录下的方法级 canonical 路径。 +## 当前行为 -**参数:** -- `paths` - 参数语义详见头文件声明。 -- `callback` - 参数语义详见头文件声明。 +当前模板实现非常直接: -**返回:** `template void` - 返回值语义详见头文件声明。 +1. 遍历 `paths` +2. 对每个路径调用 `LoadAsync(path, GetResourceType(), ...)` +3. 在异步回调里: + - 成功时把 `LoadResult.resource` 包装成 `ResourceHandle` + - 失败时回调空句柄 -**示例:** +## 当前语义 -```cpp -#include +- `callback` 是“每个资源一次”,不是“整组全部完成一次”。 +- 当前没有 group-level 聚合状态、完成计数或 barrier。 -void Example() { - XCEngine::Resources::ResourceManager object; - // 根据上下文补齐参数后调用 ResourceManager::LoadGroup(...)。 - (void)object; -} -``` +## 当前实现边界 + +- 它的实际执行能力仍然依赖 [LoadAsync](LoadAsync.md) / [AsyncLoader](../AsyncLoader/AsyncLoader.md) 的当前完成度。 +- 不适合把它当成成熟的批量后台流式加载系统接口。 ## 相关文档 -- [返回类总览](ResourceManager.md) -- [返回模块目录](../Asset.md) +- [LoadAsync](LoadAsync.md) +- [UpdateAsyncLoads](UpdateAsyncLoads.md) +- [ResourceHandle](../ResourceHandle/ResourceHandle.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/RebuildProjectAssetCache.md b/docs/api/XCEngine/Core/Asset/ResourceManager/RebuildProjectAssetCache.md new file mode 100644 index 00000000..2b710a50 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/RebuildProjectAssetCache.md @@ -0,0 +1,39 @@ +# ResourceManager::RebuildProjectAssetCache + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/ResourceManager.h` + +## 签名 + +```cpp +bool RebuildProjectAssetCache(); +``` + +## 作用 + +清空当前运行时资源缓存,重建项目 `Library` 缓存目录,并刷新项目资产索引状态。 + +## 当前实现行为 + +- 如果 `m_resourceRoot` 为空,直接返回 `false` +- 否则固定按下面的顺序执行: + 1. `UnloadAll()` + 2. `m_assetImportService.RebuildLibraryCache()` + 3. `m_projectAssetIndex.RefreshFrom(m_assetImportService)` +- 返回值直接透传 `AssetImportService::RebuildLibraryCache()` 的结果 + +## 当前语义 + +- 这是“清空并重建当前项目 Library 缓存”的高层入口,不是普通的 snapshot 刷新 +- 它会丢掉当前 `ResourceManager` 中已经加载的运行时资源对象,因此比 [RefreshProjectAssets](RefreshProjectAssets.md) 更重 +- `tests/core/Asset/test_resource_manager.cpp` 里的 `ResourceManager_Test.RebuildProjectAssetCacheRefreshesLookupState` 验证了重建后 `AssetRef` 查询仍能恢复,并能重新命中新建的 Library 产物 + +## 相关文档 + +- [ResourceManager](ResourceManager.md) +- [RefreshProjectAssets](RefreshProjectAssets.md) +- [GetProjectLibraryRoot](GetProjectLibraryRoot.md) +- [AssetImportService::RebuildLibraryCache](../AssetImportService/RebuildLibraryCache.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/RefreshProjectAssets.md b/docs/api/XCEngine/Core/Asset/ResourceManager/RefreshProjectAssets.md new file mode 100644 index 00000000..bf1681b7 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/RefreshProjectAssets.md @@ -0,0 +1,38 @@ +# ResourceManager::RefreshProjectAssets + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/ResourceManager.h` + +## 签名 + +```cpp +void RefreshProjectAssets(); +``` + +## 作用 + +显式刷新当前项目的资产数据库,并重建 `ProjectAssetIndex` 的本地 snapshot。 + +## 当前实现行为 + +- 只有在 `m_resourceRoot` 非空时才执行实际刷新。 +- 固定按下面的顺序执行: + - `m_assetImportService.Refresh()` + - `m_projectAssetIndex.RefreshFrom(m_assetImportService)` +- 它不会直接重载已经在运行时缓存里的资源对象;只负责更新项目资产查询链路。 + +## 适用场景 + +- 项目里新增、删除或改写了 `Assets/...` 源文件后,需要让 `AssetRef` 查询和路径反查看到最新状态。 +- `TryGetAssetRef()` 的 cache miss 自动恢复不够时,可以显式调用这条入口做一次整体刷新。 + +## 相关文档 + +- [ResourceManager](ResourceManager.md) +- [SetResourceRoot](SetResourceRoot.md) +- [TryGetAssetRef](TryGetAssetRef.md) +- [AssetImportService::Refresh](../AssetImportService/Refresh.md) +- [ProjectAssetIndex::RefreshFrom](../ProjectAssetIndex/RefreshFrom.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/RegisterLoader.md b/docs/api/XCEngine/Core/Asset/ResourceManager/RegisterLoader.md index 8e74ca8a..8c143382 100644 --- a/docs/api/XCEngine/Core/Asset/ResourceManager/RegisterLoader.md +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/RegisterLoader.md @@ -1,31 +1,28 @@ # ResourceManager::RegisterLoader -注册对象、回调或映射。 +注册一个资源 loader,使其可按资源类型参与加载。 ```cpp void RegisterLoader(IResourceLoader* loader); ``` -该方法声明于 `XCEngine/Core/Asset/ResourceManager.h`,当前页面用于固定 `ResourceManager` 类目录下的方法级 canonical 路径。 +## 当前行为 -**参数:** -- `loader` - 参数语义详见头文件声明。 - -**返回:** `void` - 无返回值。 - -**示例:** +当前实现会在锁内把: ```cpp -#include - -void Example() { - XCEngine::Resources::ResourceManager object; - // 根据上下文补齐参数后调用 ResourceManager::RegisterLoader(...)。 - (void)object; -} +loader->GetResourceType() -> loader ``` +写入 `m_loaders` 映射。 + +## 当前语义 + +- `ResourceManager` 保存的是原始指针,不接管 loader 所有权。 +- 调用方需要保证该 loader 在注册期间保持有效。 + ## 相关文档 -- [返回类总览](ResourceManager.md) -- [返回模块目录](../Asset.md) +- [GetLoader](GetLoader.md) +- [UnregisterLoader](UnregisterLoader.md) +- [IResourceLoader](../../IO/IResourceLoader/IResourceLoader.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/Release.md b/docs/api/XCEngine/Core/Asset/ResourceManager/Release.md index 0e509436..9e20f05b 100644 --- a/docs/api/XCEngine/Core/Asset/ResourceManager/Release.md +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/Release.md @@ -1,31 +1,34 @@ # ResourceManager::Release -释放引用或底层资源。 +减少指定资源 GUID 的句柄引用计数。 ```cpp void Release(ResourceGUID guid); ``` -该方法声明于 `XCEngine/Core/Asset/ResourceManager.h`,当前页面用于固定 `ResourceManager` 类目录下的方法级 canonical 路径。 +## 当前行为 -**参数:** -- `guid` - 参数语义详见头文件声明。 +按当前实现: -**返回:** `void` - 无返回值。 +1. 在 `m_refCounts` 中查找该 GUID +2. 若不存在,直接返回 +3. 若存在,则做一次自减 +4. 当计数减到 `0` 时: + - 从 `m_refCounts` 移除该 GUID + - 调用 `m_cache.OnZeroRefCount(guid)` -**示例:** +## 当前语义 -```cpp -#include +- 这条接口只负责“引用计数归零通知”,不直接执行卸载。 +- 当前真实卸载链路是否继续发生,还取决于 [ResourceCache](../ResourceCache/ResourceCache.md) 的后续行为。 -void Example() { - XCEngine::Resources::ResourceManager object; - // 根据上下文补齐参数后调用 ResourceManager::Release(...)。 - (void)object; -} -``` +## 当前实现边界 + +- `Release()` 本身不会直接从 `m_resourceCache` 中删除资源。 +- 它也不会主动减少 `m_memoryUsage`。 ## 相关文档 -- [返回类总览](ResourceManager.md) -- [返回模块目录](../Asset.md) +- [AddRef](AddRef.md) +- [GetRefCount](GetRefCount.md) +- [Unload](Unload.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/ResolvePath.md b/docs/api/XCEngine/Core/Asset/ResourceManager/ResolvePath.md index 6415b180..728ed766 100644 --- a/docs/api/XCEngine/Core/Asset/ResourceManager/ResolvePath.md +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/ResolvePath.md @@ -1,31 +1,34 @@ # ResourceManager::ResolvePath -解析并返回目标结果。 +把一个相对资源路径拼接到当前资源根目录下。 ```cpp Containers::String ResolvePath(const Containers::String& relativePath) const; ``` -该方法声明于 `XCEngine/Core/Asset/ResourceManager.h`,当前页面用于固定 `ResourceManager` 类目录下的方法级 canonical 路径。 +## 当前行为 -**参数:** -- `relativePath` - 参数语义详见头文件声明。 - -**返回:** `Containers::String` - 返回值语义详见头文件声明。 - -**示例:** +当前实现只是做简单字符串拼接: ```cpp -#include - -void Example() { - XCEngine::Resources::ResourceManager object; - // 根据上下文补齐参数后调用 ResourceManager::ResolvePath(...)。 - (void)object; -} +return m_resourceRoot + "/" + relativePath; ``` +## 当前语义 + +- 它不是 `std::filesystem` 风格的规范化解析器。 +- 不检查路径是否存在。 +- 不参与 `AssetDatabase` / `ProjectAssetIndex` 的项目资产解析。 + +因此这个接口更接近一个“当前 root 前缀拼接 helper”,而不是项目资产语义解析入口。 + +## 注意事项 + +- 当前调用方应自己保证 `m_resourceRoot` 已正确设置。 +- 若 `relativePath` 本身不是相对路径,这里也不会帮你修正。 + ## 相关文档 -- [返回类总览](ResourceManager.md) -- [返回模块目录](../Asset.md) +- [SetResourceRoot](SetResourceRoot.md) +- [GetResourceRoot](GetResourceRoot.md) +- [AssetDatabase::ResolvePath](../AssetDatabase/ResolvePath.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/ResourceManager.md b/docs/api/XCEngine/Core/Asset/ResourceManager/ResourceManager.md index ea082390..4c5fedfd 100644 --- a/docs/api/XCEngine/Core/Asset/ResourceManager/ResourceManager.md +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/ResourceManager.md @@ -6,131 +6,212 @@ **头文件**: `XCEngine/Core/Asset/ResourceManager.h` -**描述**: 运行时资源系统的中心入口,负责 loader 注册、同步加载、引用计数和部分缓存管理。 +**描述**: 运行时资源系统的中心入口,负责 loader 注册、项目资产服务桥接、同步/异步加载、句柄引用计数、in-flight 去重以及部分缓存管理。 ## 角色概述 -`ResourceManager` 是当前资源模块里最像“服务定位入口”的类。上层系统通常不会直接操作 `ResourceCache` 或 `AsyncLoader`,而是通过这里来做几件事: +`ResourceManager` 是当前资源模块里最接近“运行时入口”的类。它当前串起四层职责: -- 以 `ResourceType` 为键注册和查找 `IResourceLoader` -- 根据路径生成 `ResourceGUID` -- 执行同步 `Load()` -- 记录句柄引用计数 -- 暴露资源根路径、内存预算和缓存查询入口 +- loader 注册与查找 +- 项目资产服务桥接 +- 运行时加载与缓存 +- 句柄引用计数与异步入口 -这种设计和商业引擎里常见的资源服务层比较接近。好处是资源调用点集中,上层 API 简洁;代价是很多子系统都要依赖这个全局单例,而且当前实现里缓存、引用计数和异步加载还没有完全闭环。 +其中项目资产桥接链路不是“直接调用 `AssetDatabase`”,而是: + +```text +ResourceManager +-> AssetImportService +-> ProjectAssetIndex snapshot +-> AssetDatabase +``` + +这条分层决定了: + +- `SetResourceRoot()` 负责绑定项目根并初始化 snapshot +- `LoadResource()` 负责在真正加载前确保 artifact 就绪 +- `TryGetAssetRef()` / `TryResolveAssetPath()` 先走 snapshot,再视情况回退到服务层 ## 生命周期 -### 初始化 +### `Initialize()` -`Initialize()` 当前会做三件事: +`Initialize()` 当前只转发到内部 `EnsureInitialized()`。 -1. 创建一个 `AsyncLoader` 实例并调用 `Initialize(2)` -2. 注册内建 `MaterialLoader` -3. 注册内建 `ShaderLoader` +`EnsureInitialized()` 会: -这里有两个实际语义需要注意: +1. 创建 `AsyncLoader` 并调用 `Initialize(2)` +2. 注册 builtin `MaterialLoader`、`MeshLoader`、`ShaderLoader`、`TextureLoader` +3. 初始化 `AssetImportService` +4. 持有初始化后的 `AsyncLoader` -- `workerThreadCount = 2` 只是传入了 `AsyncLoader`,但当前 `AsyncLoader::Initialize()` 并不会真的创建工作线程。 -- 只有材质和着色器 loader 会自动注册,其他资源类型需要调用方自行注册,或者当前根本还没有成熟 loader。 +因此当前自动注册的 builtin loader 一共四类,而项目资产链路真正是否生效仍取决于后续 `SetResourceRoot()` 是否绑定项目根。 -### 关闭 +### `Shutdown()` -`Shutdown()` 会调用: +`Shutdown()` 会: - `UnloadAll()` -- `m_asyncLoader->Shutdown()` -- `m_asyncLoader.reset()` +- 关闭并释放 `m_asyncLoader` +- `m_assetImportService.Shutdown()` +- `ResourceFileSystem::Get().Shutdown()` +- `m_projectAssetIndex.ResetProjectRoot()` +- 清空 `m_inFlightLoads` -当前没有空指针保护,所以它默认假设你已经成功调用过 `Initialize()`。把它当成“可随时重复调用的幂等 API”并不安全。 +这不仅卸载运行时资源,也会彻底断开项目资产服务和本地 snapshot。 + +## 项目资产链路 + +### `SetResourceRoot(rootPath)` + +这不是一个单纯的路径前缀 setter。 + +当 `rootPath` 非空时,它会: + +- 写入 `m_resourceRoot` +- 初始化 `ResourceFileSystem` +- `m_assetImportService.SetProjectRoot(rootPath)` +- `m_projectAssetIndex.RefreshFrom(m_assetImportService)` + +当 `rootPath` 为空时,它会: + +- 清空 `AssetImportService` 的项目根 +- 关闭 `ResourceFileSystem` +- 重置 `ProjectAssetIndex` + +所以它实际上是“进入 / 退出项目资产模式”的总开关。 + +### `RefreshProjectAssets()` + +这是显式重建项目资产 snapshot 的轻量入口: + +```text +ResourceManager::RefreshProjectAssets +-> AssetImportService::Refresh +-> ProjectAssetIndex::RefreshFrom +``` + +它只在 `m_resourceRoot` 非空时生效,也不会直接重载已经在缓存里的运行时对象。 + +### `RebuildProjectAssetCache()` + +这是更重的项目缓存重建入口: + +```text +ResourceManager::RebuildProjectAssetCache +-> UnloadAll +-> AssetImportService::RebuildLibraryCache +-> ProjectAssetIndex::RefreshFrom +``` + +它会清掉当前运行时资源缓存并重建整个 `Library` 目录。`tests/core/Asset/test_resource_manager.cpp` 里的 `ResourceManager_Test.RebuildProjectAssetCacheRefreshesLookupState` 已经覆盖了这条链路。 + +### `GetProjectLibraryRoot()` + +直接返回当前项目 `Library` 根目录,底层转发到 `AssetImportService::GetLibraryRoot()`。这条入口主要用于测试和调试工具。 + +### `TryGetAssetRef()` + +当前直接委托给 `ProjectAssetIndex::TryGetAssetRef(...)`。真实行为是: + +1. 先查本地 `path -> guid` snapshot +2. miss 时回退到 `AssetImportService::TryGetAssetRef(...)` +3. 如果仍失败,而且路径像项目资产路径,则触发一次: + - `importService.Refresh()` + - `ProjectAssetIndex::RefreshFrom(importService)` + - 再重试 +4. 成功后再通过 `TryGetPrimaryAssetPath()` 回填本地 snapshot + +这就是当前 cache miss 刷新路径的真实位置。 + +### `TryResolveAssetPath()` + +当前委托给 `ProjectAssetIndex::TryResolveAssetPath(...)`。行为和上面不完全对称: + +1. 先查本地 `guid -> path` snapshot +2. miss 时回退到 `AssetImportService::TryGetPrimaryAssetPath(...)` +3. 成功后记回 snapshot + +它不会像 `TryGetAssetRef()` 那样主动刷新整份 snapshot,而是只做一次数据库回退查询。 ## 当前加载模型 ### 同步加载 -模板 `Load()` 是目前最可依赖的主路径。按当前实现,它会: +路径版模板 [Load](Load.md) 最终都会走内部 `LoadResource(path, type, settings)`。当前主流程是: -1. 用原始 `path` 调用 `ResourceGUID::Generate(path)` -2. 在 `m_resourceCache` 里查缓存 -3. 用 `GetResourceType()` 找到资源类型,再从 `m_loaders` 找 loader -4. 直接调用 `loader->Load(path, settings)` -5. 加入缓存并返回 `ResourceHandle` +1. `EnsureInitialized()` +2. 用原始请求路径生成 `ResourceGUID` +3. 先查运行时缓存 +4. 做 in-flight 去重;相同 `guid + type` 的并发请求会共享一次真实加载 +5. 找到对应 `IResourceLoader` +6. 如果 `m_resourceRoot` 非空,尝试 `m_assetImportService.EnsureArtifact(path, type, resolvedAsset)` +7. 若 `artifactReady=true`: + - `m_projectAssetIndex.RememberResolvedPath(resolvedAsset.assetGuid, resolvedAsset.relativePath)` + - 把真正传给 loader 的 `loadPath` 切到 `resolvedAsset.runtimeLoadPath` +8. 调用 `loader->Load(loadPath, settings)` +9. 成功后把 `resource->m_path` 设回原始请求路径 +10. 写入缓存,并把 `guid -> 原始请求路径` 记入 `m_guidToPath` -这里有几个重要结论: +这里有三个关键结论: -- `Load()` 并不会调用 `ResolvePath()`,所以 `SetResourceRoot()` 不会自动影响同步加载路径。 -- `GetResourceType()` 只有少数显式特化;对没有特化的类型使用 `Load()`,并不是“自动推断资源类型”。 -- loader 查找失败时会记录 warning 并返回空句柄。 -- loader 加载失败时会记录 error 并返回空句柄。 +- 项目资产路径真正被 loader 读取的通常已经是 `Library/Artifacts/.../main.*` +- 资源对象对外暴露的 `m_path` 仍是原始请求路径 +- `Load(const AssetRef&)` 只是先用 `TryResolveAssetPath()` 还原成源路径,再复用这条普通路径加载链路 ### 异步加载 -`LoadAsync()` 只是把请求转发给 `m_asyncLoader->Submit(...)`。当前版本中: +`LoadAsync()` 只把请求转发给 `AsyncLoader::Submit(...)`。 -- `Submit()` 只把请求压入 `m_pendingQueue` -- 没有后台线程消费这个队列 -- `QueueCompleted()` 也没有真正把成功结果放回完成队列 +当前版本里: -所以它的 API 形状已经存在,但不能当成成熟的异步资源流式系统来依赖。 +- `AsyncLoader` 会启动真实工作线程 +- 后台线程实际调用 `LoadResource()` +- 结果先进入完成队列 +- 只有调用 `UpdateAsyncLoads()` 时,回调才会分发到当前线程 -## 引用计数与句柄语义 +因此它已经是可运行的异步加载路径,但仍更接近“后台加载 + 主线程轮询分发”模型,而不是全自动流式资源系统。 -[ResourceHandle](../ResourceHandle/ResourceHandle.md) 的构造、拷贝、析构都会调用 `AddRef()` / `Release()`。这意味着 `ResourceManager` 记录的是“句柄引用数”,不是资源对象的真实所有权。 +## 延迟场景加载开关 -当前实现里还有两个关键现实: +`ScopedDeferredSceneLoad` 是 `BeginDeferredSceneLoad()` / `EndDeferredSceneLoad()` 的 RAII 包装器: -- `Release()` 在引用数归零时只会调用 `m_cache.OnZeroRefCount(guid)`,而这个函数当前是空实现。 -- `AddRef()` 发现资源不在 `m_resourceCache` 时会调用 `ReloadResource(guid)`,但 `ReloadResource()` 现在基本是 stub,而且 `m_guidToPath` 在正常加载路径里也没有被填充。 +- 构造时递增 `m_deferredSceneLoadDepth` +- 析构时在非空管理器上递减 +- [IsDeferredSceneLoadEnabled](IsDeferredSceneLoadEnabled.md) 通过原子计数判断是否开启 -结果就是: +这条能力当前只表达“是否处于延迟场景加载区间”,不直接替代真正的异步恢复调度器。 -- “最后一个句柄释放后自动卸载资源”当前并没有真正成立。 -- “句柄重新引用一个已卸载资源时自动重载”当前也没有真正成立。 +## 引用计数与缓存现状 -## 缓存与内存预算 +[ResourceHandle](../ResourceHandle/ResourceHandle.md) 的构造、拷贝、析构都会调用 `AddRef()` / `Release()`,因此 `ResourceManager` 记录的是“句柄引用数”。 -`ResourceManager` 内部同时维护: +当前实现仍有几个现实限制: -- `m_resourceCache` -- `m_memoryUsage` -- 一个独立的 `ResourceCache m_cache` +- `Release()` 在引用数归零时只会调用 `m_cache.OnZeroRefCount(guid)`,这条行为链还没有完全打通成真实自动卸载 +- `ReloadResource()` 仍基本是 stub +- `UnloadUnused()` 仍是空实现 +- `m_resourceCache` / `m_memoryUsage` 和 `ResourceCache m_cache` 的状态并不总是严格同步 -从设计意图上看,这是想把“管理器级索引”和“缓存策略对象”分层。但按当前实现,这两层并没有完全同步: +所以当前版本里,“自动卸载”“自动重载”和“缓存统计完全一致”都还不是成熟保证。 -- `AddToCache()` 会同时写入 `m_resourceCache` 和 `m_cache` -- `Unload()` / `UnloadGroup()` 只移除 `m_resourceCache`,不会同步移除 `m_cache` -- `FlushCache()` 只调用 `m_cache.Flush()`,不会清理 `m_resourceCache` 和 `m_memoryUsage` -- `ResourceCache::Evict()` 会直接 `Release()` 资源,但不会回写 `m_resourceCache` -- `SetMemoryBudget()` 只改 `ResourceManager` 自己的预算值,没有把预算同步给 `m_cache` +## 测试锚点 -这意味着当前版本里,“缓存是否存在”“管理器是否还持有指针”“内存统计是否准确”并不总是完全一致。文档和调用方都不应把这里理解成已经严密打通的商业级缓存系统。 +`tests/core/Asset/test_resource_manager.cpp` 当前直接覆盖了几条关键语义: -## 查询与路径语义 - -- `Find(path)` / `Find(guid)` 只查当前缓存,不查磁盘。 -- `Exists(path)` / `Exists(guid)` 也只代表“当前是否在缓存里”,不代表文件存在。 -- `ResolvePath(relativePath)` 只是简单返回 `m_resourceRoot + "/" + relativePath`。 -- `GetResourcePaths()` 读取的是 `m_guidToPath`,但当前默认加载流程没有填这个表,所以它通常不会返回你期待的完整已加载资源路径列表。 -- `LoadGroup()` 会对每个路径分别发起 `LoadAsync()`,并逐个回调,不会做“整组完成后再统一通知”的聚合。 - -## 线程语义 - -- `AddRef()`、`Release()`、缓存写入和 loader 注册等路径使用了 `m_mutex`。 -- 并不是所有只读接口都加了锁,例如 `GetRefCount()`、`GetMemoryUsage()`、`GetMemoryBudget()`、`GetLoader()`。 -- loader 本身是否线程安全、回调在哪个线程执行,当前并没有统一契约。 -- 结合 `AsyncLoader` 的现状,默认仍应按主线程驱动资源系统理解。 +- `ConcurrentAsyncLoadsCoalesceSameMeshPath` + - 验证相同 `guid + type` 的并发请求会共享一次实际加载 +- `AssetLookupFallbackRefreshesSnapshotForNewProjectAsset` + - 验证 `TryGetAssetRef()` 会在 cache miss 时刷新 snapshot 再重试 +- `ResourceManager_Test.RebuildProjectAssetCacheRefreshesLookupState` + - 验证重建 `Library` 缓存后,查询状态仍能恢复 ## 当前实现限制 -- 当前自动注册的 loader 只有 `Material` 和 `Shader`。 -- `Load()` 不会自动使用 `SetResourceRoot()`。 -- `UnloadUnused()` 是空实现。 -- `UnloadAll()` 当前只是清空容器和内存计数,没有逐个释放所有资源对象。 -- `ReloadResource()` 只有路径表检查,没有真正重载逻辑。 -- `GetResourcePaths()` 依赖的 `m_guidToPath` 当前默认不会在同步加载时维护。 -- `ResourceManager` 和 `ResourceCache` 的状态同步并不完整,存在悬空指针和统计不一致风险。 -- `LoadAsync()` / `LoadGroup()` 目前不适合当成生产可用的异步加载通路。 +- 自动注册的 builtin loader 只有 `Material`、`Mesh`、`Shader`、`Texture` +- `UnloadUnused()` 是空实现 +- `LoadAsync()` / `LoadGroup()` 仍要求主线程稳定调用 `UpdateAsyncLoads()` +- 项目资产 snapshot 的自动刷新只发生在少数显式入口,不是文件监听驱动的全局自动同步系统 ## 相关方法 @@ -141,6 +222,9 @@ - [GetResourceRoot](GetResourceRoot.md) - [Load](Load.md) - [LoadAsync](LoadAsync.md) +- [UpdateAsyncLoads](UpdateAsyncLoads.md) +- [IsAsyncLoading](IsAsyncLoading.md) +- [GetAsyncPendingCount](GetAsyncPendingCount.md) - [Unload](Unload.md) - [UnloadUnused](UnloadUnused.md) - [UnloadAll](UnloadAll.md) @@ -160,6 +244,15 @@ - [LoadGroup](LoadGroup.md) - [GetResourcePaths](GetResourcePaths.md) - [UnloadGroup](UnloadGroup.md) +- [RefreshProjectAssets](RefreshProjectAssets.md) +- [RebuildProjectAssetCache](RebuildProjectAssetCache.md) +- [GetProjectLibraryRoot](GetProjectLibraryRoot.md) +- [TryGetAssetRef](TryGetAssetRef.md) +- [TryResolveAssetPath](TryResolveAssetPath.md) +- [ScopedDeferredSceneLoad](ScopedDeferredSceneLoad.md) +- [BeginDeferredSceneLoad](BeginDeferredSceneLoad.md) +- [EndDeferredSceneLoad](EndDeferredSceneLoad.md) +- [IsDeferredSceneLoadEnabled](IsDeferredSceneLoadEnabled.md) ## 相关指南 @@ -168,6 +261,9 @@ ## 相关文档 - [当前模块](../Asset.md) +- [AssetImportService](../AssetImportService/AssetImportService.md) +- [ProjectAssetIndex](../ProjectAssetIndex/ProjectAssetIndex.md) +- [AssetDatabase](../AssetDatabase/AssetDatabase.md) - [ResourceHandle](../ResourceHandle/ResourceHandle.md) - [ResourceCache](../ResourceCache/ResourceCache.md) - [AsyncLoader](../AsyncLoader/AsyncLoader.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/ScopedDeferredSceneLoad.md b/docs/api/XCEngine/Core/Asset/ResourceManager/ScopedDeferredSceneLoad.md new file mode 100644 index 00000000..5bcd85b4 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/ScopedDeferredSceneLoad.md @@ -0,0 +1,34 @@ +# ResourceManager::ScopedDeferredSceneLoad + +**命名空间**: `XCEngine::Resources` + +**类型**: `nested class` + +**头文件**: `XCEngine/Core/Asset/ResourceManager.h` + +## 作用 + +一个 RAII helper,用于在作用域内临时启用 deferred scene load 模式。 + +## 当前实现行为 + +- 构造函数默认绑定 `ResourceManager::Get()`,也可以显式传入某个 `ResourceManager&`。 +- 构造时立即调用 [BeginDeferredSceneLoad](BeginDeferredSceneLoad.md)。 +- 析构时如果 `m_manager` 非空,则调用 [EndDeferredSceneLoad](EndDeferredSceneLoad.md)。 +- 拷贝构造和拷贝赋值被显式删除,避免同一作用域令牌被意外复制。 + +## 当前语义 + +它的价值不是提供新状态,而是保证: + +- 进入作用域时深度计数 `+1` +- 离开作用域时深度计数 `-1` + +这样即便提前 `return` 或异常退出,也不容易漏掉结束调用。 + +## 相关文档 + +- [ResourceManager](ResourceManager.md) +- [BeginDeferredSceneLoad](BeginDeferredSceneLoad.md) +- [EndDeferredSceneLoad](EndDeferredSceneLoad.md) +- [IsDeferredSceneLoadEnabled](IsDeferredSceneLoadEnabled.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/SetMemoryBudget.md b/docs/api/XCEngine/Core/Asset/ResourceManager/SetMemoryBudget.md index 1fd83c21..422b87db 100644 --- a/docs/api/XCEngine/Core/Asset/ResourceManager/SetMemoryBudget.md +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/SetMemoryBudget.md @@ -1,31 +1,22 @@ # ResourceManager::SetMemoryBudget -设置相关状态或配置。 +设置运行时资源缓存使用的目标内存预算。 ```cpp void SetMemoryBudget(size_t bytes); ``` -该方法声明于 `XCEngine/Core/Asset/ResourceManager.h`,当前页面用于固定 `ResourceManager` 类目录下的方法级 canonical 路径。 +## 当前行为 -**参数:** -- `bytes` - 参数语义详见头文件声明。 +- 直接把 `bytes` 写入 `m_memoryBudget` -**返回:** `void` - 无返回值。 +## 当前语义 -**示例:** - -```cpp -#include - -void Example() { - XCEngine::Resources::ResourceManager object; - // 根据上下文补齐参数后调用 ResourceManager::SetMemoryBudget(...)。 - (void)object; -} -``` +- 这不会立刻触发清理。 +- 当前内存压力处理主要发生在后续资源进入缓存时,由 `AddToCache()` 在 `m_memoryUsage > m_memoryBudget` 时通知 `m_cache.OnMemoryPressure(...)`。 ## 相关文档 -- [返回类总览](ResourceManager.md) -- [返回模块目录](../Asset.md) +- [GetMemoryBudget](GetMemoryBudget.md) +- [GetMemoryUsage](GetMemoryUsage.md) +- [FlushCache](FlushCache.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/SetResourceRoot.md b/docs/api/XCEngine/Core/Asset/ResourceManager/SetResourceRoot.md index aa5c2559..9acdf4da 100644 --- a/docs/api/XCEngine/Core/Asset/ResourceManager/SetResourceRoot.md +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/SetResourceRoot.md @@ -1,31 +1,48 @@ # ResourceManager::SetResourceRoot -设置相关状态或配置。 +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/ResourceManager.h` + +## 签名 ```cpp void SetResourceRoot(const Containers::String& rootPath); ``` -该方法声明于 `XCEngine/Core/Asset/ResourceManager.h`,当前页面用于固定 `ResourceManager` 类目录下的方法级 canonical 路径。 +## 作用 -**参数:** -- `rootPath` - 参数语义详见头文件声明。 +切换资源根目录,并决定 `ResourceManager` 是否进入“项目资产模式”。 -**返回:** `void` - 无返回值。 +## 当前实现行为 -**示例:** +- 总是先把 `m_resourceRoot` 设为传入值。 +- 当 `rootPath` 非空时: + - 初始化 `ResourceFileSystem` + - 调用 `m_assetImportService.SetProjectRoot(rootPath)` + - 调用 `m_projectAssetIndex.RefreshFrom(m_assetImportService)` +- 当 `rootPath` 为空时: + - 清空 `AssetImportService` 的项目根 + - 关闭 `ResourceFileSystem` + - 重置 `ProjectAssetIndex` -```cpp -#include +## 为什么这很重要 -void Example() { - XCEngine::Resources::ResourceManager object; - // 根据上下文补齐参数后调用 ResourceManager::SetResourceRoot(...)。 - (void)object; -} -``` +这不是一个单纯的“路径前缀 setter”。 + +设置资源根之后,`ResourceManager` 才会真正接入: + +- 项目资产到 artifact 的转换 +- `AssetRef` 与 `AssetGUID` 查询 +- `ProjectAssetIndex` 的初始 lookup snapshot + +清空资源根则会把这整条项目资产服务链一并断开。 ## 相关文档 -- [返回类总览](ResourceManager.md) -- [返回模块目录](../Asset.md) +- [ResourceManager](ResourceManager.md) +- [GetResourceRoot](GetResourceRoot.md) +- [RefreshProjectAssets](RefreshProjectAssets.md) +- [AssetImportService](../AssetImportService/AssetImportService.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/Shutdown.md b/docs/api/XCEngine/Core/Asset/ResourceManager/Shutdown.md index a7e68617..779e90ea 100644 --- a/docs/api/XCEngine/Core/Asset/ResourceManager/Shutdown.md +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/Shutdown.md @@ -1,30 +1,35 @@ # ResourceManager::Shutdown -关闭并清理内部状态。 +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/ResourceManager.h` + +## 签名 ```cpp void Shutdown(); ``` -该方法声明于 `XCEngine/Core/Asset/ResourceManager.h`,当前页面用于固定 `ResourceManager` 类目录下的方法级 canonical 路径。 +## 作用 -**参数:** 无。 +关闭资源管理器并清理当前会话级状态。 -**返回:** `void` - 无返回值。 +## 当前实现行为 -**示例:** +- 会先调用 [UnloadAll](UnloadAll.md)。 +- 如果 `m_asyncLoader` 存在,再调用其 `Shutdown()` 并清空 unique ptr。 +- 随后关闭 `m_assetImportService`、关闭 `ResourceFileSystem`,并把 `ProjectAssetIndex` 重置为空项目根状态。 +- 还会清空 `m_inFlightLoads`。 -```cpp -#include +## 当前实现边界 -void Example() { - XCEngine::Resources::ResourceManager object; - // 根据上下文补齐参数后调用 ResourceManager::Shutdown(...)。 - (void)object; -} -``` +- 这不是严格幂等的“随时安全调用”接口;当前实现仍假设资源管理器已经经历过正常初始化。 +- 它不会保留项目资产 snapshot,也不会保留挂起加载状态。 ## 相关文档 -- [返回类总览](ResourceManager.md) -- [返回模块目录](../Asset.md) +- [ResourceManager](ResourceManager.md) +- [Initialize](Initialize.md) +- [UnloadAll](UnloadAll.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/TryGetAssetRef.md b/docs/api/XCEngine/Core/Asset/ResourceManager/TryGetAssetRef.md new file mode 100644 index 00000000..21644600 --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/TryGetAssetRef.md @@ -0,0 +1,44 @@ +# ResourceManager::TryGetAssetRef + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/ResourceManager.h` + +## 签名 + +```cpp +bool TryGetAssetRef( + const Containers::String& path, + ResourceType resourceType, + AssetRef& outRef) const; +``` + +## 作用 + +把一条项目资产路径解析成主资产 `AssetRef`。 + +## 当前实现行为 + +- 当前直接委托给 `m_projectAssetIndex.TryGetAssetRef(m_assetImportService, path, resourceType, outRef)`。 +- `ProjectAssetIndex` 会优先查本地 `path -> guid` snapshot。 +- snapshot miss 时,会回退到 `AssetImportService::TryGetAssetRef(...)`。 +- 如果第一次回退仍失败,而且路径看起来像当前项目下的 `Assets/...`,它会进一步触发: + - `importService.Refresh()` + - `ProjectAssetIndex::RefreshFrom(importService)` + - 然后再重试一次 +- 成功后还会通过 `TryGetPrimaryAssetPath()` 回填本地 snapshot。 + +## 当前语义 + +- 这是当前项目资产查询接口,不适用于 `builtin://...` 这类虚拟路径。 +- `resourceType` 由调用方指定,并会写入成功返回的 `AssetRef`;这里不会重新校验它是否与 importer 真实匹配。 +- 真正的类型匹配要到 `AssetImportService::EnsureArtifact()` / `LoadResource()` 阶段才会检查。 + +## 相关文档 + +- [ResourceManager](ResourceManager.md) +- [TryResolveAssetPath](TryResolveAssetPath.md) +- [ProjectAssetIndex](../ProjectAssetIndex/ProjectAssetIndex.md) +- [AssetDatabase::TryGetAssetRef](../AssetDatabase/TryGetAssetRef.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/TryResolveAssetPath.md b/docs/api/XCEngine/Core/Asset/ResourceManager/TryResolveAssetPath.md new file mode 100644 index 00000000..33abbcba --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/TryResolveAssetPath.md @@ -0,0 +1,42 @@ +# ResourceManager::TryResolveAssetPath + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/ResourceManager.h` + +## 签名 + +```cpp +bool TryResolveAssetPath( + const AssetRef& assetRef, + Containers::String& outPath) const; +``` + +## 作用 + +把一个主资产 `AssetRef` 反查回其 source 资产路径,通常是 `Assets/...`。 + +## 当前实现行为 + +- 当前直接委托给 `m_projectAssetIndex.TryResolveAssetPath(m_assetImportService, assetRef, outPath)`。 +- `ProjectAssetIndex` 会优先使用本地 `guid -> path` snapshot。 +- snapshot miss 时,会回退到 `AssetImportService::TryGetPrimaryAssetPath(...)`。 +- 如果回退成功,会把结果通过 `RememberResolvedPath()` 回写到本地 snapshot。 + +## 和 `TryGetAssetRef()` 的差异 + +这条路径和 `TryGetAssetRef()` 并不完全对称: + +- `TryGetAssetRef()` 在 cache miss 时可能触发一次 `Refresh() + RefreshFrom()`。 +- `TryResolveAssetPath()` 当前不会主动刷新整份 snapshot;它只做一次数据库回退查询。 + +因此它更像“guid -> path 的轻量回填路径”,而不是带自动刷新能力的发现入口。 + +## 相关文档 + +- [ResourceManager](ResourceManager.md) +- [TryGetAssetRef](TryGetAssetRef.md) +- [ProjectAssetIndex](../ProjectAssetIndex/ProjectAssetIndex.md) +- [AssetImportService](../AssetImportService/AssetImportService.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/Unload.md b/docs/api/XCEngine/Core/Asset/ResourceManager/Unload.md index 7c2c36d4..e379df63 100644 --- a/docs/api/XCEngine/Core/Asset/ResourceManager/Unload.md +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/Unload.md @@ -1,44 +1,37 @@ # ResourceManager::Unload -卸载资源或释放缓存。 - -该方法在 `XCEngine/Core/Asset/ResourceManager.h` 中提供了 2 个重载,当前页面统一汇总这些公开声明。 - -## 重载 1: 声明 +把一个资源从当前运行时缓存中移除,并释放资源对象引用。 ```cpp void Unload(const Containers::String& path); -``` - -**参数:** -- `path` - 参数语义详见头文件声明。 - -**返回:** `void` - 无返回值。 - -## 重载 2: 声明 - -```cpp void Unload(ResourceGUID guid); ``` -**参数:** -- `guid` - 参数语义详见头文件声明。 +## 当前行为 -**返回:** `void` - 无返回值。 +### `Unload(path)` -**示例:** +- 先把路径转成 `ResourceGUID::Generate(path)` +- 再转发到 `Unload(guid)` -```cpp -#include +### `Unload(guid)` -void Example() { - XCEngine::Resources::ResourceManager object; - // 根据上下文补齐参数后调用 ResourceManager::Unload(...)。 - (void)object; -} -``` +按当前实现: + +1. 在锁内从 `m_resourceCache` 里查找目标资源 +2. 若找到: + - 从 `m_resourceCache` 移除 + - 从 `m_guidToPath` 移除 + - 从 `m_memoryUsage` 中扣除该资源大小 +3. 解锁后对资源对象执行 `resource->Release()` + +## 当前实现边界 + +- 当前 `Unload(guid)` 不会同步清掉 `m_refCounts`。 +- 因此它更像“强制从缓存摘掉对象”,而不是一次完整的引用计数收口。 ## 相关文档 -- [返回类总览](ResourceManager.md) -- [返回模块目录](../Asset.md) +- [UnloadAll](UnloadAll.md) +- [UnloadGroup](UnloadGroup.md) +- [Release](Release.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/UnloadAll.md b/docs/api/XCEngine/Core/Asset/ResourceManager/UnloadAll.md index bd2408a5..03311f0b 100644 --- a/docs/api/XCEngine/Core/Asset/ResourceManager/UnloadAll.md +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/UnloadAll.md @@ -1,30 +1,30 @@ # ResourceManager::UnloadAll -卸载资源或释放缓存。 +清空当前运行时缓存中的全部资源。 ```cpp void UnloadAll(); ``` -该方法声明于 `XCEngine/Core/Asset/ResourceManager.h`,当前页面用于固定 `ResourceManager` 类目录下的方法级 canonical 路径。 +## 当前行为 -**参数:** 无。 +按当前实现: -**返回:** `void` - 无返回值。 +1. 在锁内收集当前 `m_resourceCache` 中所有非空资源指针 +2. 清空: + - `m_resourceCache` + - `m_refCounts` + - `m_guidToPath` +3. 把 `m_memoryUsage` 归零 +4. 解锁后逐个对收集到的资源执行 `Release()` -**示例:** +## 当前语义 -```cpp -#include - -void Example() { - XCEngine::Resources::ResourceManager object; - // 根据上下文补齐参数后调用 ResourceManager::UnloadAll(...)。 - (void)object; -} -``` +- 这是 `Shutdown()` 关闭阶段使用的主清理入口。 +- 它会直接丢弃当前所有缓存与计数状态,不保留增量信息。 ## 相关文档 -- [返回类总览](ResourceManager.md) -- [返回模块目录](../Asset.md) +- [Unload](Unload.md) +- [UnloadGroup](UnloadGroup.md) +- [Shutdown](Shutdown.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/UnloadGroup.md b/docs/api/XCEngine/Core/Asset/ResourceManager/UnloadGroup.md index 0793a21a..41a7ad67 100644 --- a/docs/api/XCEngine/Core/Asset/ResourceManager/UnloadGroup.md +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/UnloadGroup.md @@ -1,31 +1,30 @@ # ResourceManager::UnloadGroup -卸载资源或释放缓存。 +按 GUID 列表批量把资源从运行时缓存中移除。 ```cpp void UnloadGroup(const Containers::Array& guids); ``` -该方法声明于 `XCEngine/Core/Asset/ResourceManager.h`,当前页面用于固定 `ResourceManager` 类目录下的方法级 canonical 路径。 +## 当前行为 -**参数:** -- `guids` - 参数语义详见头文件声明。 +按当前实现: -**返回:** `void` - 无返回值。 +1. 在锁内遍历 `guids` +2. 对每个命中的缓存项: + - 从 `m_resourceCache` 移除 + - 从 `m_guidToPath` 移除 + - 从 `m_memoryUsage` 中扣除资源大小 + - 把资源指针加入待释放数组 +3. 解锁后逐个对这些资源执行 `Release()` -**示例:** +## 当前实现边界 -```cpp -#include - -void Example() { - XCEngine::Resources::ResourceManager object; - // 根据上下文补齐参数后调用 ResourceManager::UnloadGroup(...)。 - (void)object; -} -``` +- 和 [Unload](Unload.md) 一样,它当前不会同步清掉 `m_refCounts`。 +- 这是“缓存批量摘除”接口,不是完整的引用计数批量回收接口。 ## 相关文档 -- [返回类总览](ResourceManager.md) -- [返回模块目录](../Asset.md) +- [Unload](Unload.md) +- [UnloadAll](UnloadAll.md) +- [GetResourcePaths](GetResourcePaths.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/UnloadUnused.md b/docs/api/XCEngine/Core/Asset/ResourceManager/UnloadUnused.md index 38909b27..9f207703 100644 --- a/docs/api/XCEngine/Core/Asset/ResourceManager/UnloadUnused.md +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/UnloadUnused.md @@ -1,30 +1,22 @@ # ResourceManager::UnloadUnused -卸载资源或释放缓存。 +尝试卸载未使用资源。 ```cpp void UnloadUnused(); ``` -该方法声明于 `XCEngine/Core/Asset/ResourceManager.h`,当前页面用于固定 `ResourceManager` 类目录下的方法级 canonical 路径。 +## 当前行为 -**参数:** 无。 +当前实现为空函数,不执行任何操作。 -**返回:** `void` - 无返回值。 +## 当前语义 -**示例:** - -```cpp -#include - -void Example() { - XCEngine::Resources::ResourceManager object; - // 根据上下文补齐参数后调用 ResourceManager::UnloadUnused(...)。 - (void)object; -} -``` +- 这个 API 形状已经存在,但“按引用计数或缓存策略自动清理未使用资源”的逻辑目前还没有落地。 +- 如果调用方需要确定性释放,当前仍应使用 [Unload](Unload.md)、[UnloadGroup](UnloadGroup.md) 或 [UnloadAll](UnloadAll.md)。 ## 相关文档 -- [返回类总览](ResourceManager.md) -- [返回模块目录](../Asset.md) +- [Unload](Unload.md) +- [UnloadAll](UnloadAll.md) +- [ResourceManager](ResourceManager.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/UnregisterLoader.md b/docs/api/XCEngine/Core/Asset/ResourceManager/UnregisterLoader.md index bb739177..0cd958f1 100644 --- a/docs/api/XCEngine/Core/Asset/ResourceManager/UnregisterLoader.md +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/UnregisterLoader.md @@ -1,31 +1,22 @@ # ResourceManager::UnregisterLoader -取消注册对象、回调或映射。 +移除某个资源类型对应的 loader 注册。 ```cpp void UnregisterLoader(ResourceType type); ``` -该方法声明于 `XCEngine/Core/Asset/ResourceManager.h`,当前页面用于固定 `ResourceManager` 类目录下的方法级 canonical 路径。 +## 当前行为 -**参数:** -- `type` - 参数语义详见头文件声明。 +- 直接从 `m_loaders` 映射里删除指定 `ResourceType` -**返回:** `void` - 无返回值。 +## 当前语义 -**示例:** - -```cpp -#include - -void Example() { - XCEngine::Resources::ResourceManager object; - // 根据上下文补齐参数后调用 ResourceManager::UnregisterLoader(...)。 - (void)object; -} -``` +- 这里只移除 `ResourceManager` 的类型到指针映射。 +- 不会销毁 loader 对象本身。 ## 相关文档 -- [返回类总览](ResourceManager.md) -- [返回模块目录](../Asset.md) +- [RegisterLoader](RegisterLoader.md) +- [GetLoader](GetLoader.md) +- [ResourceManager](ResourceManager.md) diff --git a/docs/api/XCEngine/Core/Asset/ResourceManager/UpdateAsyncLoads.md b/docs/api/XCEngine/Core/Asset/ResourceManager/UpdateAsyncLoads.md new file mode 100644 index 00000000..078c6b1d --- /dev/null +++ b/docs/api/XCEngine/Core/Asset/ResourceManager/UpdateAsyncLoads.md @@ -0,0 +1,28 @@ +# ResourceManager::UpdateAsyncLoads + +**命名空间**: `XCEngine::Resources` + +**类型**: `method` + +**头文件**: `XCEngine/Core/Asset/ResourceManager.h` + +## 签名 + +```cpp +void UpdateAsyncLoads(); +``` + +## 作用 + +推进一次异步加载器的更新循环。 + +## 当前实现行为 + +- 若 `m_asyncLoader` 存在,则调用 `m_asyncLoader->Update()`。 +- 若异步加载器尚未初始化,则安全地什么都不做。 + +## 相关文档 + +- [ResourceManager](ResourceManager.md) +- [LoadAsync](LoadAsync.md) +- [IsAsyncLoading](IsAsyncLoading.md) diff --git a/docs/api/XCEngine/Resources/Material/Material.md b/docs/api/XCEngine/Resources/Material/Material.md index 7dcee09d..4aa56cc9 100644 --- a/docs/api/XCEngine/Resources/Material/Material.md +++ b/docs/api/XCEngine/Resources/Material/Material.md @@ -1,14 +1,34 @@ # Material -**命名空间**: `XCEngine::Resources::Material` +**命名空间**: `XCEngine::Resources` **类型**: `submodule` -**描述**: 材质资源与材质加载器。 +**描述**: 材质运行时对象、材质 source 文件解析,以及 `.xcmat` artifact 回读链路所在子模块。 ## 概览 -该目录与 `XCEngine/Resources/Material` 对应的 public headers 保持平行,用于承载唯一的 canonical API 文档入口。 +`Resources/Material` 当前已经把三层语义收在同一目录里: + +- [Material](Material/Material.md) + 运行时材质对象,负责 shader schema、render metadata、数值属性、texture binding 元数据和 packed constant buffer。 +- [MaterialLoader](MaterialLoader/MaterialLoader.md) + 负责 source 材质与 `.xcmat` artifact 的加载。 +- 与项目资产链路的接缝 + 通过 `AssetRef`、texture path 和 `ResourceManager` 的异步加载,把材质纹理绑定从“稳定身份”兑现成运行时句柄。 + +当前材质链路的关键点是: + +- source 材质里的 texture path 会进入 `AssetDatabase` 的依赖快照 +- `.xcmat` v2 会同时保存编码 `AssetRef` 与可选 path +- `Material::GetTexture(...)` / `GetTextureBindingTexture(...)` 会在首次访问时启动异步纹理兑现 + +## 当前主链路 + +1. `MaterialLoader` 读取 `.mat` / `.material` / `.json` 或 `.xcmat`。 +2. `Material` 保存 render metadata、数值属性和 texture binding 元数据。 +3. 若 texture binding 只有 path / `AssetRef`,首次读取时才通过 `ResourceManager` 启动异步加载。 +4. `AssetDatabase` 写出新的 `.xcmat` 时,会把这套元数据重新编码进 artifact。 ## 头文件 @@ -17,5 +37,8 @@ ## 相关文档 +- [ArtifactFormats](../../Core/Asset/ArtifactFormats/ArtifactFormats.md) +- [AssetDatabase](../../Core/Asset/AssetDatabase/AssetDatabase.md) +- [Asset](../../Core/Asset/Asset.md) - [上级目录](../Resources.md) - [API 总索引](../../../main.md) diff --git a/docs/api/XCEngine/Resources/Material/Material/ClearAllProperties.md b/docs/api/XCEngine/Resources/Material/Material/ClearAllProperties.md index ea0918e6..2b8d105c 100644 --- a/docs/api/XCEngine/Resources/Material/Material/ClearAllProperties.md +++ b/docs/api/XCEngine/Resources/Material/Material/ClearAllProperties.md @@ -1,30 +1,39 @@ # Material::ClearAllProperties -清空内部数据。 - ```cpp void ClearAllProperties(); ``` -该方法声明于 `XCEngine/Resources/Material/Material.h`,当前页面用于固定 `Material` 类目录下的方法级 canonical 路径。 +## 作用 -**参数:** 无。 +清空当前材质持有的属性、texture binding 和常量缓冲数据,然后按当前 shader schema 重新同步默认值。 -**返回:** `void` - 无返回值。 +## 当前行为 -**示例:** +1. 清空 `m_properties` +2. 清空 `m_textureBindings` +3. 清空 `m_constantBufferData` +4. 调用 `SyncShaderSchemaProperties(false)` +5. 调用 `MarkChanged(true)` -```cpp -#include +如果当前绑定了 shader,`SyncShaderSchemaProperties(false)` 会把 shader schema 中声明的属性重新补回默认值;如果当前没有 shader,则清空后的空状态会被直接保留。 -void Example() { - XCEngine::Resources::Material object; - // 根据上下文补齐参数后调用 Material::ClearAllProperties(...)。 - (void)object; -} -``` +## 关键语义 + +- 它不是简单的“全部删除后什么都不剩” +- 对带 shader schema 的材质来说,它更接近“恢复到当前 shader 默认材质状态” +- 现有 texture binding 会全部被丢弃;即使 shader schema 里仍声明 texture property,也只会恢复属性默认值,不会保留旧的 loaded texture / `AssetRef` / path +- 因为最终走 `MarkChanged(true)`,所以它也会触发常量缓冲重打包和 `changeVersion` 递增 + +## 测试覆盖 + +`tests/Resources/Material/test_material.cpp` 当前覆盖了两类语义: + +- 无 shader 时,`ClearAllProperties()` 会让原有数值属性全部消失 +- 有 shader schema 时,`ClearAllProperties()` 会恢复 schema 默认值,而不是保持完全空白 ## 相关文档 -- [返回类总览](Material.md) -- [返回模块目录](../Material.md) +- [Material](Material.md) +- [SetShader](SetShader.md) +- [RemoveProperty](RemoveProperty.md) diff --git a/docs/api/XCEngine/Resources/Material/Material/GetTexture.md b/docs/api/XCEngine/Resources/Material/Material/GetTexture.md index 258cf58c..231f28ef 100644 --- a/docs/api/XCEngine/Resources/Material/Material/GetTexture.md +++ b/docs/api/XCEngine/Resources/Material/Material/GetTexture.md @@ -1,31 +1,49 @@ # Material::GetTexture -获取相关状态或对象。 - ```cpp ResourceHandle GetTexture(const Containers::String& name) const; ``` -该方法声明于 `XCEngine/Resources/Material/Material.h`,当前页面用于固定 `Material` 类目录下的方法级 canonical 路径。 +## 作用 -**参数:** -- `name` - 参数语义详见头文件声明。 +按 texture property 名称读取纹理句柄,并在需要时启动一次懒加载。 -**返回:** `ResourceHandle` - 返回值语义详见头文件声明。 +## 当前行为 -**示例:** +1. 先调用 `ResolvePendingTextureBindings()`,把已完成的异步加载结果并回 binding。 +2. 查找同名 texture binding。 +3. 如果当前还没有 loaded handle,且: + - `pendingLoad == nullptr` + - `texturePath` 非空,或 `textureRef` 有效 + 则调用 `BeginAsyncTextureLoad(...)` 启动异步加载。 +4. 返回当前 `binding.texture`。 -```cpp -#include +## 关键语义 -void Example() { - XCEngine::Resources::Material object; - // 根据上下文补齐参数后调用 Material::GetTexture(...)。 - (void)object; -} -``` +- 第一次调用时,如果 binding 只有 path / `AssetRef`,通常会返回空句柄,同时把异步加载排进 `ResourceManager`。 +- 如果 binding 只有 `AssetRef` 没有 path,`BeginAsyncTextureLoad(...)` 会先通过 `ResourceManager::TryResolveAssetPath(...)` 反查 path。 +- 要让异步加载真正完成,调用方仍需要驱动 `ResourceManager::UpdateAsyncLoads()`。 +- 想只看“当前是否已经有 loaded handle”,而不触发新的加载,应改用 [GetTextureBindingLoadedTexture](GetTextureBindingLoadedTexture.md)。 + +## 参数 + +- `name` - 目标 texture property 名称。 + +## 返回值 + +- 返回当前 binding 的 loaded texture。 +- 若尚未加载完成,可能返回空句柄。 + +## 测试覆盖 + +`tests/Resources/Material/test_material_loader.cpp` 当前验证了: + +- source 材质与 `.xcmat` artifact 中的 texture binding 都可以先以空句柄状态加载 +- 首次访问会把纹理异步加载排进 `ResourceManager` +- 完成 `UpdateAsyncLoads()` 后再次访问能拿到真实纹理 ## 相关文档 -- [返回类总览](Material.md) -- [返回模块目录](../Material.md) +- [Material](Material.md) +- [GetTextureBindingLoadedTexture](GetTextureBindingLoadedTexture.md) +- [GetTextureBindingTexture](GetTextureBindingTexture.md) diff --git a/docs/api/XCEngine/Resources/Material/Material/GetTextureBindingAssetRef.md b/docs/api/XCEngine/Resources/Material/Material/GetTextureBindingAssetRef.md new file mode 100644 index 00000000..7b7b1854 --- /dev/null +++ b/docs/api/XCEngine/Resources/Material/Material/GetTextureBindingAssetRef.md @@ -0,0 +1,22 @@ +# Material::GetTextureBindingAssetRef + +```cpp +AssetRef GetTextureBindingAssetRef(Core::uint32 index) const; +``` + +## 作用 + +读取指定 binding 当前保存的稳定 `AssetRef`。 + +## 当前语义 + +- `index` 有效时,返回该 binding 的 `textureRef` +- 越界时返回默认构造的无效 `AssetRef` + +这个访问器不会触发纹理加载,也不会解析 path。 + +## 相关文档 + +- [Material](Material.md) +- [SetTextureAssetRef](SetTextureAssetRef.md) +- [GetTextureBindingPath](GetTextureBindingPath.md) diff --git a/docs/api/XCEngine/Resources/Material/Material/GetTextureBindingCount.md b/docs/api/XCEngine/Resources/Material/Material/GetTextureBindingCount.md index c93023f5..b4f89101 100644 --- a/docs/api/XCEngine/Resources/Material/Material/GetTextureBindingCount.md +++ b/docs/api/XCEngine/Resources/Material/Material/GetTextureBindingCount.md @@ -1,30 +1,32 @@ # Material::GetTextureBindingCount -获取相关状态或对象。 - ```cpp Core::uint32 GetTextureBindingCount() const; ``` -该方法声明于 `XCEngine/Resources/Material/Material.h`,当前页面用于固定 `Material` 类目录下的方法级 canonical 路径。 +## 作用 -**参数:** 无。 +返回当前材质持有的 texture binding 元数据条目数。 -**返回:** `Core::uint32` - 返回值语义详见头文件声明。 +## 当前语义 -**示例:** +这里统计的是 `m_textureBindings` 的数量,而不是 shader schema 中声明的 texture property 总数。 -```cpp -#include +因此它反映的是“当前真的有 binding 元数据”的槽位数,包括: -void Example() { - XCEngine::Resources::Material object; - // 根据上下文补齐参数后调用 Material::GetTextureBindingCount(...)。 - (void)object; -} -``` +- 已加载纹理的 binding +- 只有 `AssetRef` 的 binding +- 只有 path 的 binding + +如果某个 texture property 被删除、被标量属性替换,或被空 `AssetRef + emptyPath` 清空,对应数量也会减少。 + +## 返回值 + +- 当前 texture binding 元数据条目数。 ## 相关文档 -- [返回类总览](Material.md) -- [返回模块目录](../Material.md) +- [Material](Material.md) +- [GetTextureBindingAssetRef](GetTextureBindingAssetRef.md) +- [GetTextureBindingPath](GetTextureBindingPath.md) +- [GetTextureBindingLoadedTexture](GetTextureBindingLoadedTexture.md) diff --git a/docs/api/XCEngine/Resources/Material/Material/GetTextureBindingLoadedTexture.md b/docs/api/XCEngine/Resources/Material/Material/GetTextureBindingLoadedTexture.md new file mode 100644 index 00000000..91bc70df --- /dev/null +++ b/docs/api/XCEngine/Resources/Material/Material/GetTextureBindingLoadedTexture.md @@ -0,0 +1,26 @@ +# Material::GetTextureBindingLoadedTexture + +```cpp +ResourceHandle GetTextureBindingLoadedTexture(Core::uint32 index) const; +``` + +## 作用 + +只读取指定 binding 当前已经兑现完成的纹理句柄,不触发新的加载。 + +## 当前语义 + +- `index` 有效时,直接返回该 binding 的 `texture` +- 越界时返回空句柄 + +它和 [GetTextureBindingTexture](GetTextureBindingTexture.md) 的区别是: + +- 这个方法只看当前已加载状态 +- 不会去 resolve pending load +- 不会去启动新的异步加载 + +## 相关文档 + +- [Material](Material.md) +- [GetTexture](GetTexture.md) +- [GetTextureBindingTexture](GetTextureBindingTexture.md) diff --git a/docs/api/XCEngine/Resources/Material/Material/GetTextureBindingName.md b/docs/api/XCEngine/Resources/Material/Material/GetTextureBindingName.md new file mode 100644 index 00000000..99c06864 --- /dev/null +++ b/docs/api/XCEngine/Resources/Material/Material/GetTextureBindingName.md @@ -0,0 +1,32 @@ +# Material::GetTextureBindingName + +```cpp +Containers::String GetTextureBindingName(Core::uint32 index) const; +``` + +## 作用 + +读取指定 texture binding 当前保存的名字。 + +## 当前语义 + +- `index` 有效时,返回该 binding 的 `name` +- 越界时返回默认构造的空 `Containers::String` + +这个访问器只读取 `m_textureBindings[index].name`,不会触发纹理加载,也不会解析 `AssetRef` 或 path。 + +## 测试覆盖 + +当前至少有两条直接调用链: + +- `tests/Resources/Material/test_material.cpp` + 在 `SetTextureAssetRefStoresStableBindingMetadata` 里验证写入后名字保持为 `"uDiffuse"` +- `tests/Resources/Material/test_material_loader.cpp` + 在材质 artifact 回读后验证首个 binding 名字为 `"baseColorTexture"` + +## 相关文档 + +- [Material](Material.md) +- [GetTextureBindingCount](GetTextureBindingCount.md) +- [GetTextureBindingAssetRef](GetTextureBindingAssetRef.md) +- [GetTextureBindingPath](GetTextureBindingPath.md) diff --git a/docs/api/XCEngine/Resources/Material/Material/GetTextureBindingPath.md b/docs/api/XCEngine/Resources/Material/Material/GetTextureBindingPath.md new file mode 100644 index 00000000..7425a269 --- /dev/null +++ b/docs/api/XCEngine/Resources/Material/Material/GetTextureBindingPath.md @@ -0,0 +1,25 @@ +# Material::GetTextureBindingPath + +```cpp +Containers::String GetTextureBindingPath(Core::uint32 index) const; +``` + +## 作用 + +读取指定 binding 当前保存的 path 元数据。 + +## 当前语义 + +- `index` 有效时,返回该 binding 的 `texturePath` +- 越界时返回空字符串 + +注意它返回的是当前 `Material` 持有的 path 字段: + +- 可能是 source 材质解析出的路径 +- 也可能是 `.xcmat` 回读或后续懒解析 `AssetRef` 后补回的路径 + +## 相关文档 + +- [Material](Material.md) +- [SetTexturePath](SetTexturePath.md) +- [GetTextureBindingAssetRef](GetTextureBindingAssetRef.md) diff --git a/docs/api/XCEngine/Resources/Material/Material/GetTextureBindingTexture.md b/docs/api/XCEngine/Resources/Material/Material/GetTextureBindingTexture.md new file mode 100644 index 00000000..54af0388 --- /dev/null +++ b/docs/api/XCEngine/Resources/Material/Material/GetTextureBindingTexture.md @@ -0,0 +1,29 @@ +# Material::GetTextureBindingTexture + +```cpp +ResourceHandle GetTextureBindingTexture(Core::uint32 index) const; +``` + +## 作用 + +按 binding index 读取纹理句柄,并在需要时启动一次懒加载。 + +## 当前行为 + +1. 先对该 index 执行 `ResolvePendingTextureBinding(index)`。 +2. 若 binding 仍未持有 loaded texture,且: + - `pendingLoad == nullptr` + - `texturePath` 非空,或 `textureRef` 有效 + 则调用 `BeginAsyncTextureLoad(index)`。 +3. 返回当前 `binding.texture`。 + +## 关键语义 + +- 这是 [GetTexture](GetTexture.md) 的 index 版入口。 +- 第一次调用时可能只会把加载任务排进 `ResourceManager`,并返回空句柄。 + +## 相关文档 + +- [Material](Material.md) +- [GetTexture](GetTexture.md) +- [GetTextureBindingLoadedTexture](GetTextureBindingLoadedTexture.md) diff --git a/docs/api/XCEngine/Resources/Material/Material/Material.md b/docs/api/XCEngine/Resources/Material/Material/Material.md index e4c735e2..4f32f176 100644 --- a/docs/api/XCEngine/Resources/Material/Material/Material.md +++ b/docs/api/XCEngine/Resources/Material/Material/Material.md @@ -6,56 +6,140 @@ **头文件**: `XCEngine/Resources/Material/Material.h` -**描述**: 定义 `XCEngine/Resources/Material` 子目录中的 `Material` public API。 +**描述**: 运行时材质资源,统一承载 shader schema、render metadata、数值属性、texture binding 元数据以及延迟纹理兑现逻辑。 -## 概述 +## 概览 -`Material.h` 是 `XCEngine/Resources/Material` 子目录 下的 public header,当前页面作为平行目录中的 canonical 总览,用于汇总该头文件暴露的主要声明。 +`Material` 当前同时维护五类状态: -## 声明概览 +1. 资源身份 + `name`、`path`、`guid`、`isValid`、`memorySize` +2. 渲染元数据 + `shader`、`renderQueue`、`renderState`、`shaderPass`、`tags` +3. 数值属性 + `Float` / `Float2` / `Float3` / `Float4` / `Int` / `Bool` +4. texture binding 元数据 + `name`、`slot`、已加载 `Texture` 句柄、稳定 `AssetRef`、path、pending async load 状态 +5. packed constant buffer + `m_constantBufferData` -| 声明 | 类型 | 说明 | -|------|------|------| -| `MaterialPropertyType` | `enum class` | 头文件中的公开声明。 | -| `MaterialProperty` | `struct` | 头文件中的公开声明。 | -| `Material` | `class` | 继承自 `IResource` 的公开声明。 | +## shader schema 与默认值 -## 公共方法 +当 `SetShader(...)` 绑定 shader 后,`Material` 会按 shader property schema 做一次同步: -| 方法 | 描述 | +- 为 schema 中缺失的属性补默认值 +- 清理不再存在的旧属性 +- 如果现有属性类型和新 schema 不兼容,则重置回 shader 默认值 +- texture / cubemap 属性保留在 texture binding 通道,不写进 numeric property packing + +`tests/Resources/Material/test_material.cpp` 当前明确覆盖了: + +- `SetShader()` 会 seed shader 默认值 +- `RemoveProperty()` 会把 schema 内属性恢复到 shader 默认值 +- `ClearAllProperties()` 在有 shader schema 时会恢复整套默认值 +- 切换 shader 时会清理旧 schema 属性并补齐新 schema 默认值 + +## texture binding 的当前模型 + +### 两层状态 + +每个 texture binding 现在同时有两层语义: + +- 稳定元数据 + `AssetRef` + path +- 已加载句柄 + `ResourceHandle` + +### 三种写入入口 + +- [SetTexture](SetTexture.md) + 直接写入已加载纹理,并尽量补齐 `AssetRef` +- [SetTextureAssetRef](SetTextureAssetRef.md) + 直接写入稳定 `AssetRef` 与可选 path,同时清空 loaded handle +- [SetTexturePath](SetTexturePath.md) + 只保留 path 元数据,同时清空 `AssetRef` 和 loaded handle + +### 两种读取入口 + +- [GetTexture](GetTexture.md) + 按名称读纹理,并在需要时启动异步加载 +- [GetTextureBindingTexture](GetTextureBindingTexture.md) + 按 binding index 做同样的懒加载入口 + +如果只想看“当前是否已经加载完成”,而不想触发新的加载,应改用: + +- [GetTextureBindingLoadedTexture](GetTextureBindingLoadedTexture.md) + +### `AssetRef` 与 path 的边界 + +- `AssetRef` 是更稳定的项目资产身份 +- path 是可选的辅助定位信息 +- `.xcmat` v2 当前会尽量把两者都保存进 artifact +- 如果只有 `AssetRef` 没有 path,首次访问纹理时会先通过 `ResourceManager::TryResolveAssetPath(...)` 反查 path,再启动异步加载 +- source 材质解析出来的 binding path 可能是项目内绝对路径;而只带 `AssetRef` 的 `.xcmat` 绑定在首次兑现后,通常会补回 `Assets/...` 形式的主 source 路径 + +## 常量缓冲打包语义 + +[UpdateConstantBuffer](UpdateConstantBuffer.md) 当前只打包数值属性: + +- `Float*` +- `Int*` +- `Bool` + +texture / cubemap 属性不会写进 `m_constantBufferData`。 + +当前打包顺序分两种路径: + +- 如果当前绑定了 shader 且 shader property 列表非空: + - 只按 shader schema 中声明顺序收集兼容的数值属性 +- 如果当前没有 shader schema: + - 按属性名字典序排序 + +两种路径下都遵守: + +- 每个属性固定占 `16` 字节槽位 +- `Bool` 写成 `uint32 0/1` + +## 当前实现边界 + +- `GetTexture(...)` / `GetTextureBindingTexture(...)` 只会启动异步加载,不会自己 pump `ResourceManager` 完成队列 +- `SetTexture(...)` 只有在纹理路径非空且不是虚拟路径时,才会尝试反查 `AssetRef` +- `GetTextureBindingLoadedTexture(...)` 只返回当前已兑现的句柄,不会触发新的加载 +- constant buffer 在“有 shader schema”和“无 shader schema”两种情况下采用不同排序规则;不要把无 shader 时的字典序行为误解成通用规则 + +## 关键声明 + +| 声明 | 角色 | |------|------| -| [Material()](Constructor.md) | 构造对象。 | -| [~Material()](Destructor.md) | 销毁对象并释放相关资源。 | -| [GetType](GetType.md) | 获取相关状态或对象。 | -| [GetName](GetName.md) | 获取相关状态或对象。 | -| [GetPath](GetPath.md) | 获取相关状态或对象。 | -| [GetGUID](GetGUID.md) | 获取相关状态或对象。 | -| [IsValid](IsValid.md) | 查询当前状态。 | -| [GetMemorySize](GetMemorySize.md) | 获取相关状态或对象。 | -| [Release](Release.md) | 释放引用或底层资源。 | -| [SetShader](SetShader.md) | 设置相关状态或配置。 | -| [SetFloat](SetFloat.md) | 设置相关状态或配置。 | -| [SetFloat2](SetFloat2.md) | 设置相关状态或配置。 | -| [SetFloat3](SetFloat3.md) | 设置相关状态或配置。 | -| [SetFloat4](SetFloat4.md) | 设置相关状态或配置。 | -| [SetInt](SetInt.md) | 设置相关状态或配置。 | -| [SetBool](SetBool.md) | 设置相关状态或配置。 | -| [SetTexture](SetTexture.md) | 设置相关状态或配置。 | -| [GetFloat](GetFloat.md) | 获取相关状态或对象。 | -| [GetFloat2](GetFloat2.md) | 获取相关状态或对象。 | -| [GetFloat3](GetFloat3.md) | 获取相关状态或对象。 | -| [GetFloat4](GetFloat4.md) | 获取相关状态或对象。 | -| [GetInt](GetInt.md) | 获取相关状态或对象。 | -| [GetBool](GetBool.md) | 获取相关状态或对象。 | -| [GetTexture](GetTexture.md) | 获取相关状态或对象。 | -| [GetTextureBindingCount](GetTextureBindingCount.md) | 获取相关状态或对象。 | -| [GetConstantBufferData](GetConstantBufferData.md) | 获取相关状态或对象。 | -| [UpdateConstantBuffer](UpdateConstantBuffer.md) | 更新运行时状态。 | -| [HasProperty](HasProperty.md) | 判断是否具备指定状态或能力。 | -| [RemoveProperty](RemoveProperty.md) | 移除元素或解除关联。 | -| [ClearAllProperties](ClearAllProperties.md) | 清空内部数据。 | +| `MaterialRenderQueue` | 内建 render queue 常量 | +| `MaterialRenderState` | blend / depth / cull 等 runtime render state | +| `MaterialPropertyType` | 数值属性与 texture/cubemap 属性的区分枚举 | +| `MaterialProperty` | 单个属性的名称、类型和值 | +| `MaterialTextureBinding` | 单个 texture binding 的 loaded handle、`AssetRef`、path 与 pending load 状态 | + +## 重点方法 + +| 方法 | 说明 | +|------|------| +| [SetShader](SetShader.md) | 绑定 shader,并按 shader schema 同步属性集。 | +| [SetTexture](SetTexture.md) | 直接写入已加载纹理,并尽量补 `AssetRef` 元数据。 | +| [SetTextureAssetRef](SetTextureAssetRef.md) | 只写 texture 资产身份与可选 path。 | +| [SetTexturePath](SetTexturePath.md) | 只写 path 元数据。 | +| [GetTexture](GetTexture.md) | 按名称读纹理,并在需要时启动懒加载。 | +| [GetTextureBindingCount](GetTextureBindingCount.md) | 返回当前 texture binding 元数据数量。 | +| [GetTextureBindingName](GetTextureBindingName.md) | 读取某个 binding 的名字。 | +| [GetTextureBindingAssetRef](GetTextureBindingAssetRef.md) | 读取某个 binding 的稳定 `AssetRef`。 | +| [GetTextureBindingPath](GetTextureBindingPath.md) | 读取某个 binding 当前保存的 path。 | +| [GetTextureBindingLoadedTexture](GetTextureBindingLoadedTexture.md) | 只看已加载句柄,不触发新的加载。 | +| [GetTextureBindingTexture](GetTextureBindingTexture.md) | 按 index 读取纹理,并在需要时启动懒加载。 | +| [ClearAllProperties](ClearAllProperties.md) | 清空当前属性集,再按 shader schema 回填默认值。 | +| [UpdateConstantBuffer](UpdateConstantBuffer.md) | 重新打包数值属性常量缓冲。 | ## 相关文档 -- [当前目录](../Material.md) - 返回 `Material` 平行目录 -- [API 总索引](../../../../main.md) - 返回顶层索引 +- [当前目录](../Material.md) +- [MaterialLoader](../MaterialLoader/MaterialLoader.md) +- [ArtifactFormats](../../../Core/Asset/ArtifactFormats/ArtifactFormats.md) +- [AssetRef](../../../Core/Asset/AssetRef/AssetRef.md) +- [ResourceManager](../../../Core/Asset/ResourceManager/ResourceManager.md) +- [API 总索引](../../../../main.md) diff --git a/docs/api/XCEngine/Resources/Material/Material/SetShader.md b/docs/api/XCEngine/Resources/Material/Material/SetShader.md index a55cabf3..6fd16cb3 100644 --- a/docs/api/XCEngine/Resources/Material/Material/SetShader.md +++ b/docs/api/XCEngine/Resources/Material/Material/SetShader.md @@ -1,31 +1,43 @@ # Material::SetShader -设置相关状态或配置。 - ```cpp void SetShader(const ResourceHandle& shader); ``` -该方法声明于 `XCEngine/Resources/Material/Material.h`,当前页面用于固定 `Material` 类目录下的方法级 canonical 路径。 +## 作用 -**参数:** -- `shader` - 参数语义详见头文件声明。 +为材质绑定 shader,并让当前属性集与新的 shader property schema 重新对齐。 -**返回:** `void` - 无返回值。 +## 当前行为 -**示例:** +1. 把 `m_shader` 设为传入句柄 +2. 调用 `SyncShaderSchemaProperties(true)` +3. 调用 `MarkChanged(true)` -```cpp -#include +`SyncShaderSchemaProperties(true)` 的当前行为包括: -void Example() { - XCEngine::Resources::Material object; - // 根据上下文补齐参数后调用 Material::SetShader(...)。 - (void)object; -} -``` +- 删除新 shader schema 里不存在的旧属性 +- 对缺失的 schema 属性补默认值 +- 对类型与 schema 不兼容的属性重置为 shader 默认值 +- 对数值属性移除残留的 texture binding + +## 关键语义 + +- 这不是单纯的指针替换;它会主动重塑材质当前的属性布局 +- 切换 shader 后,旧 schema 的遗留属性不会被继续保留 +- 如果某个属性名在新旧 shader 中都存在且类型兼容,当前值会尽量被保留 +- 因为最终走 `MarkChanged(true)`,所以它会触发常量缓冲重打包,并递增 `changeVersion` + +## 测试覆盖 + +`tests/Resources/Material/test_material.cpp` 当前验证了: + +- 首次绑定 shader 会补齐 shader 默认属性 +- `RemoveProperty()` 在有 shader schema 时会恢复默认值 +- 切换到新 shader 时,旧 schema 属性会被剔除,新 schema 属性会被补齐 ## 相关文档 -- [返回类总览](Material.md) -- [返回模块目录](../Material.md) +- [Material](Material.md) +- [ClearAllProperties](ClearAllProperties.md) +- [UpdateConstantBuffer](UpdateConstantBuffer.md) diff --git a/docs/api/XCEngine/Resources/Material/Material/SetTexture.md b/docs/api/XCEngine/Resources/Material/Material/SetTexture.md index 9cccdb6c..3a7ba2dc 100644 --- a/docs/api/XCEngine/Resources/Material/Material/SetTexture.md +++ b/docs/api/XCEngine/Resources/Material/Material/SetTexture.md @@ -1,32 +1,42 @@ # Material::SetTexture -设置相关状态或配置。 - ```cpp void SetTexture(const Containers::String& name, const ResourceHandle& texture); ``` -该方法声明于 `XCEngine/Resources/Material/Material.h`,当前页面用于固定 `Material` 类目录下的方法级 canonical 路径。 +## 作用 -**参数:** -- `name` - 参数语义详见头文件声明。 -- `texture` - 参数语义详见头文件声明。 +直接把一个已加载 `Texture` 句柄挂到指定 texture property 上,并同步更新 binding 的稳定元数据。 -**返回:** `void` - 无返回值。 +## 当前行为 -**示例:** +1. 先根据当前 shader schema 判断 `name` 是否允许写成 `Texture` / `Cubemap` 属性。 +2. 确保 `m_properties` 中存在对应的 texture property。 +3. 如果传入纹理带有非空、非虚拟路径,会尝试通过 `ResourceManager::TryGetAssetRef(...)` 反查稳定 `AssetRef`。 +4. 若同名 binding 已存在,则原地替换 `texture`、`textureRef`、`texturePath` 和 `pendingLoad`。 +5. 若不存在,则在 `m_textureBindings` 末尾追加一个新的 binding。 -```cpp -#include +## 关键语义 -void Example() { - XCEngine::Resources::Material object; - // 根据上下文补齐参数后调用 Material::SetTexture(...)。 - (void)object; -} -``` +- 这是“直接注入已加载纹理”的入口。 +- 如果纹理路径能映射到项目资产,当前实现会尽量补齐 `AssetRef`,这样后续写 `.xcmat` 时能保留更稳定的引用。 +- 如果后续对同名属性写入标量值,旧 texture binding 会被移除。 + +## 参数 + +- `name` - 目标 texture property 名称。 +- `texture` - 已加载纹理句柄。 + +## 测试覆盖 + +`tests/Resources/Material/test_material.cpp` 当前覆盖了: + +- 设置后可通过 [GetTexture](GetTexture.md) 取回同一纹理 +- 同名 `SetTexture(...)` 会替换旧 binding +- 同名标量属性写入会移除旧 texture binding ## 相关文档 -- [返回类总览](Material.md) -- [返回模块目录](../Material.md) +- [Material](Material.md) +- [SetTextureAssetRef](SetTextureAssetRef.md) +- [GetTexture](GetTexture.md) diff --git a/docs/api/XCEngine/Resources/Material/Material/SetTextureAssetRef.md b/docs/api/XCEngine/Resources/Material/Material/SetTextureAssetRef.md new file mode 100644 index 00000000..5528e29c --- /dev/null +++ b/docs/api/XCEngine/Resources/Material/Material/SetTextureAssetRef.md @@ -0,0 +1,42 @@ +# Material::SetTextureAssetRef + +```cpp +void SetTextureAssetRef( + const Containers::String& name, + const AssetRef& textureRef, + const Containers::String& texturePath = Containers::String()); +``` + +## 作用 + +为指定 texture property 写入稳定 `AssetRef` 与可选 path,而不立即持有 loaded texture 句柄。 + +## 当前行为 + +- 如果 `textureRef` 无效且 `texturePath` 为空,直接退化成删除该属性。 +- 否则会确保该属性在 `m_properties` 中以 texture / cubemap 形式存在。 +- 如果同名 binding 已存在,会: + - 清空 `texture` + - 写入新的 `textureRef` + - 写入新的 `texturePath` + - 清空 `pendingLoad` +- 如果 binding 不存在,则创建新的 binding。 + +## 关键语义 + +- 这是当前 `.xcmat` v2 回读最重要的 texture binding 写入口。 +- 它保存的是“稳定身份 + 可选定位信息”,不是即时可用的纹理句柄。 +- 后续第一次访问纹理时,`Material` 才会再根据 `AssetRef` / path 启动异步加载。 + +## 测试覆盖 + +`tests/Resources/Material/test_material.cpp` 当前验证了: + +- `AssetRef`、path 和 binding 名称都会被稳定保存 +- 初始时 [GetTextureBindingLoadedTexture](GetTextureBindingLoadedTexture.md) 仍为空句柄 + +## 相关文档 + +- [Material](Material.md) +- [SetTexturePath](SetTexturePath.md) +- [GetTextureBindingAssetRef](GetTextureBindingAssetRef.md) diff --git a/docs/api/XCEngine/Resources/Material/Material/SetTexturePath.md b/docs/api/XCEngine/Resources/Material/Material/SetTexturePath.md new file mode 100644 index 00000000..de661c5c --- /dev/null +++ b/docs/api/XCEngine/Resources/Material/Material/SetTexturePath.md @@ -0,0 +1,31 @@ +# Material::SetTexturePath + +```cpp +void SetTexturePath(const Containers::String& name, const Containers::String& texturePath); +``` + +## 作用 + +只为指定 texture property 保存 path 元数据,不保留稳定 `AssetRef` 或 loaded texture 句柄。 + +## 当前行为 + +- 若 `texturePath` 为空,则退化成删除该属性。 +- 否则会确保该属性以 texture / cubemap 形式存在。 +- 同名 binding 已存在时,会: + - 清空 `texture` + - `textureRef.Reset()` + - 写入新的 `texturePath` + - 清空 `pendingLoad` +- 若不存在,则创建新的 binding。 + +## 关键语义 + +- 这是没有稳定 `AssetRef` 时的退化路径。 +- 后续首次访问纹理时,仍可按 path 启动异步加载。 + +## 相关文档 + +- [Material](Material.md) +- [SetTextureAssetRef](SetTextureAssetRef.md) +- [GetTextureBindingPath](GetTextureBindingPath.md) diff --git a/docs/api/XCEngine/Resources/Material/Material/UpdateConstantBuffer.md b/docs/api/XCEngine/Resources/Material/Material/UpdateConstantBuffer.md index 0a72d41b..bdbd323e 100644 --- a/docs/api/XCEngine/Resources/Material/Material/UpdateConstantBuffer.md +++ b/docs/api/XCEngine/Resources/Material/Material/UpdateConstantBuffer.md @@ -1,30 +1,59 @@ # Material::UpdateConstantBuffer -更新运行时状态。 - ```cpp void UpdateConstantBuffer(); ``` -该方法声明于 `XCEngine/Resources/Material/Material.h`,当前页面用于固定 `Material` 类目录下的方法级 canonical 路径。 +## 作用 -**参数:** 无。 +重新根据当前数值属性生成 `m_constantBufferData`。 -**返回:** `void` - 无返回值。 +## 当前打包规则 -**示例:** +当前实现会先收集“可打包”的数值属性: -```cpp -#include +- `Float` +- `Float2` +- `Float3` +- `Float4` +- `Int` +- `Int2` +- `Int3` +- `Int4` +- `Bool` -void Example() { - XCEngine::Resources::Material object; - // 根据上下文补齐参数后调用 Material::UpdateConstantBuffer(...)。 - (void)object; -} -``` +随后分两条路径打包: + +1. 如果当前绑定了 shader,且 shader property 列表非空 + - 按 shader property 声明顺序遍历 + - 只收集在 `m_properties` 中存在、类型可打包、并且与 shader property 兼容的属性 +2. 如果当前没有 shader schema + - 从 `m_properties` 里收集所有可打包属性 + - 再按属性名字典序排序 + +最后会: + +3. 为每个属性分配固定 `16` 字节槽位 +4. 依类型把值写进槽位前部,其余字节补零 + +texture / cubemap 属性不会写入这个常量缓冲镜像。 + +## 关键语义 + +- 常量缓冲布局不取决于属性写入顺序 +- 但它也不总是按名字典序决定: 有 shader schema 时,以 shader schema 顺序为准 +- `Bool` 会被写成 `uint32 0/1` +- 方法结束时还会更新 `memorySize` + +## 测试覆盖 + +`tests/Resources/Material/test_material.cpp` 当前验证了: + +- 在无 shader schema 时,`alpha` / `beta` / `gamma` 会按名字典序落位 +- 每个属性固定占一个 `16` 字节槽位 +- 在有 shader schema 时,buffer 顺序跟随 schema 声明顺序,而不是名字典序 ## 相关文档 -- [返回类总览](Material.md) -- [返回模块目录](../Material.md) +- [Material](Material.md) +- [GetConstantBufferData](GetConstantBufferData.md) diff --git a/docs/api/XCEngine/Resources/Material/MaterialLoader/CanLoad.md b/docs/api/XCEngine/Resources/Material/MaterialLoader/CanLoad.md index a9f47912..3948afdc 100644 --- a/docs/api/XCEngine/Resources/Material/MaterialLoader/CanLoad.md +++ b/docs/api/XCEngine/Resources/Material/MaterialLoader/CanLoad.md @@ -1,31 +1,39 @@ # MaterialLoader::CanLoad -判断当前条件下是否可执行。 - ```cpp bool CanLoad(const Containers::String& path) const override; ``` -该方法声明于 `XCEngine/Resources/Material/MaterialLoader.h`,当前页面用于固定 `MaterialLoader` 类目录下的方法级 canonical 路径。 +## 作用 -**参数:** -- `path` - 参数语义详见头文件声明。 +判断给定路径是否应交给当前材质加载器处理。 -**返回:** `bool` - 返回值语义详见头文件声明。 +## 当前规则 -**示例:** +- `builtin://...` 材质路径直接返回 `true` +- 扩展名为以下任一值时返回 `true` + - `.mat` + - `.material` + - `.json` + - `.xcmat` +- 其他扩展名返回 `false` -```cpp -#include +## 关键语义 -void Example() { - XCEngine::Resources::MaterialLoader object; - // 根据上下文补齐参数后调用 MaterialLoader::CanLoad(...)。 - (void)object; -} -``` +- `.xcmat` 当前代表材质 artifact,而不是普通 source 文件。 +- `CanLoad()` 只按路径协议和扩展名判断,不提前校验文件内容是否真能成功解析。 + +## 测试覆盖 + +`tests/Resources/Material/test_material_loader.cpp` 当前验证了: + +- `test.mat` +- `test.json` +- `test.xcmat` + +都能被接受,而 `.txt` / `.png` 会被拒绝。 ## 相关文档 -- [返回类总览](MaterialLoader.md) -- [返回模块目录](../Material.md) +- [MaterialLoader](MaterialLoader.md) +- [Load](Load.md) diff --git a/docs/api/XCEngine/Resources/Material/MaterialLoader/Load.md b/docs/api/XCEngine/Resources/Material/MaterialLoader/Load.md index be29d0c4..d4b682c3 100644 --- a/docs/api/XCEngine/Resources/Material/MaterialLoader/Load.md +++ b/docs/api/XCEngine/Resources/Material/MaterialLoader/Load.md @@ -1,32 +1,87 @@ # MaterialLoader::Load -加载资源或数据。 - ```cpp LoadResult Load(const Containers::String& path, const ImportSettings* settings = nullptr) override; ``` -该方法声明于 `XCEngine/Resources/Material/MaterialLoader.h`,当前页面用于固定 `MaterialLoader` 类目录下的方法级 canonical 路径。 +## 作用 -**参数:** -- `path` - 参数语义详见头文件声明。 -- `settings` - 参数语义详见头文件声明。 +把 builtin 材质路径、source 材质文件或 `.xcmat` artifact 加载成 [Material](../Material/Material.md)。 -**返回:** `LoadResult` - 返回值语义详见头文件声明。 +## 当前分派逻辑 -**示例:** +1. 如果 `path` 是 builtin 材质路径,直接转到内建材质资源工厂。 +2. 否则按扩展名分支: + - `.xcmat` -> 走 artifact 回读 + - 其他支持扩展 -> 读 source 文件并做轻量解析 +3. 成功后补 `m_path`、`m_name`、`m_guid`、`m_isValid`,再返回 `LoadResult(material)`。 -```cpp -#include +## source 材质分支 -void Example() { - XCEngine::Resources::MaterialLoader object; - // 根据上下文补齐参数后调用 MaterialLoader::Load(...)。 - (void)object; -} -``` +对 `.mat` / `.material` / `.json`,当前会解析: + +- `shader` +- `shaderPass` 或 `pass` +- `renderQueue` + 支持整数或名字 +- `tags` +- `renderState` +- texture 路径 + +遇到这些情况会失败: + +- 文件不存在 +- `renderQueue` 名字非法 +- `renderState` 枚举值非法 +- 结构化对象提取失败 + +## `.xcmat` artifact 分支 + +当前 `.xcmat` 回读顺序是: + +1. 读取 `MaterialArtifactFileHeader` +2. 校验 magic 必须是 `XCMAT02` +3. 读取材质名、source path、shader path、shader pass +4. 读取 `MaterialArtifactHeader` +5. 恢复 tags +6. 恢复非 texture 属性 +7. 恢复每个 texture binding 的: + - binding name + - 编码 `AssetRef` 字符串 + - 可选 texture path + +texture binding 恢复时: + +- 有效 `AssetRef` 优先走 `SetTextureAssetRef(...)` +- 否则有 path 时走 `SetTexturePath(...)` + +因此 artifact 加载完成后的材质,texture binding 通常先只有元数据,没有立即兑现的纹理句柄。 + +## 与延迟纹理加载的关系 + +`Load()` 本身不会等待纹理依赖同步完成。 + +后续真正访问: + +- [Material::GetTexture](../Material/GetTexture.md) +- [Material::GetTextureBindingTexture](../Material/GetTextureBindingTexture.md) + +时,`Material` 才会根据 path / `AssetRef` 启动异步纹理加载。 + +## 测试覆盖 + +`tests/Resources/Material/test_material_loader.cpp` 当前覆盖了: + +- source 材质的 render metadata 解析 +- shader manifest + `shaderPass` 解析 +- 非法 `renderQueue` / render state 拒绝 +- `resourceRoot` 下相对材质加载 +- `AssetDatabase` 产出的 `main.xcmat` 能成功回读 +- `.xcmat` 中仅保存 texture `AssetRef` 时,首次访问才触发异步纹理加载 ## 相关文档 -- [返回类总览](MaterialLoader.md) -- [返回模块目录](../Material.md) +- [MaterialLoader](MaterialLoader.md) +- [CanLoad](CanLoad.md) +- [Material](../Material/Material.md) +- [ArtifactFormats](../../../Core/Asset/ArtifactFormats/ArtifactFormats.md) diff --git a/docs/api/XCEngine/Resources/Material/MaterialLoader/MaterialLoader.md b/docs/api/XCEngine/Resources/Material/MaterialLoader/MaterialLoader.md index 88d54129..4e65fb6f 100644 --- a/docs/api/XCEngine/Resources/Material/MaterialLoader/MaterialLoader.md +++ b/docs/api/XCEngine/Resources/Material/MaterialLoader/MaterialLoader.md @@ -6,31 +6,56 @@ **头文件**: `XCEngine/Resources/Material/MaterialLoader.h` -**描述**: 定义 `XCEngine/Resources/Material` 子目录中的 `MaterialLoader` public API。 +**描述**: 材质 source 文件与 `.xcmat` artifact 的统一加载器。 -## 概述 +## 概览 -`MaterialLoader.h` 是 `XCEngine/Resources/Material` 子目录 下的 public header,当前页面作为平行目录中的 canonical 总览,用于汇总该头文件暴露的主要声明。 +`MaterialLoader` 当前要处理三类输入: -## 声明概览 +1. `builtin://` 材质路径 +2. source 材质文件 + `.mat` / `.material` / `.json` +3. 材质 artifact + `.xcmat` -| 声明 | 类型 | 说明 | -|------|------|------| -| `MaterialLoader` | `class` | 继承自 `IResourceLoader` 的公开声明。 | +其中 `.xcmat` 已经是当前材质 artifact v2: -## 公共方法 +- magic `XCMAT02` +- texture binding 保存的是“binding name + 编码 `AssetRef` + 可选 path” +- 回读后默认只恢复绑定元数据,不会立即同步加载全部纹理 payload -| 方法 | 描述 | +## source 材质解析能力 + +对 source 文件,loader 当前支持解析: + +- `shader` +- `shaderPass` / `pass` +- `renderQueue` + 支持整数或名字 +- `tags` +- `renderState` +- texture 声明 + 包括 known key 和 `textures { ... }` 字典 + +相对 texture 路径会按材质文件目录或当前 `resourceRoot` 解析。 + +## 当前实现边界 + +- source 解析仍是轻量手写 JSON-like parser,不是完整 JSON AST。 +- `.xcmat` 分支显式检查的是 magic,不是 `schemaVersion` 分支分发。 +- 异步纹理兑现由 `Material` 在后续访问时触发,而不是在 `Load()` 内一次性完成。 + +## 重点方法 + +| 方法 | 说明 | |------|------| -| [MaterialLoader()](Constructor.md) | 构造对象。 | -| [~MaterialLoader()](Destructor.md) | 销毁对象并释放相关资源。 | -| [GetResourceType](GetResourceType.md) | 获取相关状态或对象。 | -| [GetSupportedExtensions](GetSupportedExtensions.md) | 获取相关状态或对象。 | -| [CanLoad](CanLoad.md) | 判断当前条件下是否可执行。 | -| [Load](Load.md) | 加载资源或数据。 | -| [GetDefaultSettings](GetDefaultSettings.md) | 获取相关状态或对象。 | +| [CanLoad](CanLoad.md) | 判断路径是否属于 builtin 材质、source 材质或 `.xcmat` artifact。 | +| [Load](Load.md) | 按输入类型分派到 builtin 材质、source 解析或 `.xcmat` artifact 回读。 | ## 相关文档 -- [当前目录](../Material.md) - 返回 `Material` 平行目录 -- [API 总索引](../../../../main.md) - 返回顶层索引 +- [Material](../Material/Material.md) +- [ArtifactFormats](../../../Core/Asset/ArtifactFormats/ArtifactFormats.md) +- [AssetDatabase](../../../Core/Asset/AssetDatabase/AssetDatabase.md) +- [当前目录](../Material.md) +- [API 总索引](../../../../main.md)