Files
XCEngine/docs/used/第四阶段计划_资源系统.md

5200 lines
155 KiB
Markdown
Raw Normal View History

# 资源系统设计与实现
> **目标**:构建游戏引擎资源管理系统,支持多种资源类型的加载、缓存、异步加载
> **阶段**第四阶段与RHI并行开发
> **前置依赖**:第一阶段核心基础层(容器、内存管理、线程系统)
---
## 0. 类型声明(文档内统一使用)
本文档中使用的类型说明:
- `String` = `XCEngine::Containers::String`
- `Array<T>` = `XCEngine::Containers::Array<T>`
- `HashMap<K,V>` = `XCEngine::Containers::HashMap<K,V>`
- `uint8/uint32/uint64` = `XCEngine::Core::uint8/uint32/uint64`
- `UniquePtr<T>` = `XCEngine::Core::UniqueRef<T>``std::unique_ptr<T>`
- `SharedPtr<T>` = `XCEngine::Core::Ref<T>``std::shared_ptr<T>`
- `Mutex` = `XCEngine::Threading::Mutex`
- `ITask` = `XCEngine::Threading::ITask`
---
## 1. 概述
### 1.1 设计目标
资源系统是游戏引擎的核心基础设施之一,负责管理游戏运行时所需的所有数据资产。一个优秀的资源系统需要满足以下目标:
#### 1.1.1 功能性目标
- **统一资源管理**:提供统一的接口来管理不同类型的资源(纹理、网格、材质、音频等)
- **异步加载支持**:支持后台线程异步加载资源,避免阻塞主线程
- **资源缓存机制**:实现智能的资源缓存和淘汰策略,优化内存使用
- **引用计数管理**:通过引用计数自动管理资源生命周期
- **资源依赖管理**:跟踪资源之间的依赖关系,支持资源组加载和批量卸载
- **可扩展架构**:易于添加新的资源类型和加载器
#### 1.1.2 性能目标
- **低内存占用**通过LRU缓存和按需加载控制内存使用
- **快速加载**:异步加载 + 缓存命中 = 零加载时间
- **线程安全**:多线程环境下安全访问资源
- **零拷贝优化**:尽可能减少不必要的数据拷贝
#### 1.1.3 开发效率目标
- **简单易用的API**:提供清晰的同步/异步加载接口
- **灵活的配置**:支持自定义导入设置和加载选项
- **完善的错误处理**:清晰的错误信息和调试支持
---
## 2. 系统架构
### 2.1 整体架构图
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ ResourceSystem │
├─────────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────────────────────────┐ │
│ │ 应用层 (Application) │ │
│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────────────┐ │ │
│ │ │ Game Code │ │ Renderer │ │ Scene │ │ Editor │ │ │
│ │ └─────┬──────┘ └─────┬──────┘ └─────┬──────┘ └─────────┬──────────┘ │ │
│ └────────┼────────────────┼────────────────┼───────────────────┼────────────┘ │
│ │ │ │ │ │
│ ┌────────▼────────────────▼────────────────▼───────────────────▼────────────┐ │
│ │ ResourceManager (资源管理器) │ │
│ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │
│ │ │ • Load<T>(path) - 同步加载 │ │ │
│ │ │ • LoadAsync<T>(path, callback) - 异步加载 │ │ │
│ │ │ • Unload(path/guid) - 卸载资源 │ │ │
│ │ │ • RegisterLoader() - 注册加载器 │ │ │
│ │ │ • AddRef/Release - 引用计数管理 │ │ │
│ │ └─────────────────────────────────────────────────────────────────────┘ │ │
│ └────────┬───────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌────────▼───────────────────────────────────────────────────────────────────┐ │
│ │ ResourceLoader (加载器层) │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌─────────────┐ │ │
│ │ │TextureLoader │ │ MeshLoader │ │ AudioLoader │ │ShaderLoader │ │ │
│ │ │ (纹理加载) │ │ (模型加载) │ │ (音频加载) │ │ (着色器加载) │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ └─────────────┘ │ │
│ └────────┬───────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌────────▼───────────────────────────────────────────────────────────────────┐ │
│ │ ResourceCache (缓存层) │ │
│ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │
│ │ │ • LRU淘汰策略 │ │ │
│ │ │ • 内存压力响应 │ │ │
│ │ │ • 手动刷新接口 │ │ │
│ │ └─────────────────────────────────────────────────────────────────────┘ │ │
│ └────────┬───────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌────────▼───────────────────────────────────────────────────────────────────┐ │
│ │ AsyncLoader (异步加载层) │ │
│ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │
│ │ │ • 后台工作线程 │ │ │
│ │ │ • 任务队列管理 │ │ │
│ │ │ • 完成回调分发 │ │ │
│ │ └─────────────────────────────────────────────────────────────────────┘ │ │
│ └────────┬───────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌────────▼───────────────────────────────────────────────────────────────────┐ │
│ │ FileSystem (文件系统层) │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ Stream │ │ Path.h │ │ Archive │ │ Compression │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ └────────────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
```
### 2.2 模块职责
| 模块 | 职责 | 关键类 |
|------|------|--------|
| **ResourceManager** | 资源总入口,引用计数管理 | ResourceManager |
| **ResourceLoader** | 具体资源类型的加载逻辑 | IResourceLoader, TextureLoader, MeshLoader |
| **ResourceCache** | 内存缓存LRU淘汰 | ResourceCache, CacheEntry |
| **AsyncLoader** | 异步任务调度 | AsyncLoader, LoadRequest |
| **FileSystem** | 文件读写,路径管理 | FileSystem, Path, FileReader |
### 2.3 数据流
```
同步加载流程:
Load(path) → CheckCache → Found? → Return Handle
Not Found
GetLoader(type) → LoadResource → Create Handle → Add to Cache → Return
异步加载流程:
LoadAsync(path, callback) → Create Request → Submit to Queue
Worker Thread: Get Request → GetLoader → LoadResource
Main Thread: Update() → Process Completed → Invoke Callback → Create Handle
```
---
## 3. 目录结构
### 3.1 设计目录结构
```
engine/include/XCEngine/Resources/
├── Resources.h # 主头文件,导出所有资源系统类
├── IResource.h # 资源基类接口
├── ResourceHandle.h # 资源句柄(模板智能指针)
├── ResourceManager.h # 资源管理器
├── IResourceLoader.h # 加载器基类接口
├── ResourceCache.h # 资源缓存
├── AsyncLoader.h # 异步加载器
├── ImportSettings.h # 导入设置基类
├── Texture.h # 纹理资源
├── TextureLoader.h # 纹理加载器
├── TextureImportSettings.h # 纹理导入设置
├── Mesh.h # 网格资源
├── MeshLoader.h # 模型加载器
├── MeshImportSettings.h # 模型导入设置
├── Material.h # 材质资源
├── MaterialLoader.h # 材质加载器
├── Shader.h # 着色器资源
├── ShaderLoader.h # 着色器加载器
├── AudioClip.h # 音频资源
├── AudioLoader.h # 音频加载器
├── BinaryResource.h # 二进制资源
└── ResourceTypes.h # 资源类型枚举
engine/src/Resources/
├── Resources.cpp # 资源系统初始化
├── ResourceManager.cpp
├── ResourceCache.cpp
├── AsyncLoader.cpp
├── TextureLoader.cpp
├── MeshLoader.cpp
├── MaterialLoader.cpp
├── ShaderLoader.cpp
└── AudioLoader.cpp
```
### 3.2 与现有项目整合
资源系统需要整合到现有项目中,需要创建或更新的文件:
```
engine/
├── include/XCEngine/
│ └── XCEngine.h # 添加 #include "Resources/Resources.h"
└── src/
└── Resources/ # 新建目录
└── (上述实现文件)
```
---
## 4. 核心类型定义
### 4.1 资源类型枚举
```cpp
// ResourceTypes.h
namespace XCEngine {
namespace Resources {
// 资源类型
enum class ResourceType : Core::uint8 {
Unknown = 0,
Texture,
Mesh,
Material,
Shader,
AudioClip,
Binary,
AnimationClip,
Skeleton,
Font,
ParticleSystem,
Scene,
Prefab
};
constexpr const char* GetResourceTypeName(ResourceType type) {
switch (type) {
case ResourceType::Texture: return "Texture";
case ResourceType::Mesh: return "Mesh";
case ResourceType::Material: return "Material";
case ResourceType::Shader: return "Shader";
case ResourceType::AudioClip: return "AudioClip";
case ResourceType::Binary: return "Binary";
case ResourceType::AnimationClip: return "AnimationClip";
case ResourceType::Skeleton: return "Skeleton";
case ResourceType::Font: return "Font";
case ResourceType::ParticleSystem:return "ParticleSystem";
case ResourceType::Scene: return "Scene";
case ResourceType::Prefab: return "Prefab";
default: return "Unknown";
}
}
// 资源使用64位GUID作为唯一标识符
// GUID生成规则MD5(path) 的前64位
// 这样可以保证相同路径生成相同的GUID
struct ResourceGUID {
Core::uint64 value;
ResourceGUID() : value(0) {}
explicit ResourceGUID(Core::uint64 v) : value(v) {}
bool IsValid() const { return value != 0; }
bool operator==(const ResourceGUID& other) const { return value == other.value; }
bool operator!=(const ResourceGUID& other) const { return value != other.value; }
// 从路径生成GUID
static ResourceGUID Generate(const char* path);
static ResourceGUID Generate(const String& path);
String ToString() const;
};
// 全局函数
inline ResourceGUID MakeResourceGUID(const char* path) {
return ResourceGUID::Generate(path);
}
// 从模板类型获取ResourceType的辅助函数
template<typename T>
ResourceType GetResourceType();
// 特化模板
template<> inline ResourceType GetResourceType<Texture>() { return ResourceType::Texture; }
template<> inline ResourceType GetResourceType<Mesh>() { return ResourceType::Mesh; }
template<> inline ResourceType GetResourceType<Material>() { return ResourceType::Material; }
template<> inline ResourceType GetResourceType<Shader>() { return ResourceType::Shader; }
template<> inline ResourceType GetResourceType<AudioClip>() { return ResourceType::AudioClip; }
template<> inline ResourceType GetResourceType<BinaryResource>() { return ResourceType::Binary; }
// 导入设置基类
class ImportSettings {
public:
virtual ~ImportSettings() = default;
// 克隆设置
virtual UniquePtr<ImportSettings> Clone() const = 0;
// 从JSON加载
virtual bool LoadFromJSON(const String& json) { return false; }
// 保存为JSON
virtual String SaveToJSON() const { return String(); }
};
} // namespace Resources
} // namespace XCEngine
}
} // namespace Resources
} // namespace XCEngine
```
### 4.2 资源GUID
```cpp
// 资源使用64位GUID作为唯一标识符
// GUID生成规则MD5(path) 的前64位
// 这样可以保证相同路径生成相同的GUID
struct ResourceGUID {
uint64 value;
ResourceGUID() : value(0) {}
explicit ResourceGUID(uint64 v) : value(v) {}
bool IsValid() const { return value != 0; }
bool operator==(const ResourceGUID& other) const { return value == other.value; }
bool operator!=(const ResourceGUID& other) const { return value != other.value; }
// 从路径生成GUID
static ResourceGUID Generate(const char* path);
static ResourceGUID Generate(const String& path);
String ToString() const;
};
// 全局函数
inline ResourceGUID MakeResourceGUID(const char* path) {
return ResourceGUID::Generate(path);
}
```
### 4.3 资源路径
```cpp
// 资源路径使用相对于资源根目录的路径
// 例如textures/player.png, models/player.fbx
// 资源系统会自动添加资源根目录前缀
class ResourcePath {
public:
ResourcePath() = default;
ResourcePath(const char* path);
ResourcePath(const String& path);
// 获取扩展名
String GetExtension() const;
// 获取文件名(不含扩展名)
String GetStem() const;
// 获取完整路径
String GetFullPath() const;
// 获取相对于资源根的路径
String GetRelativePath() const;
// 转换为资源GUID
ResourceGUID ToGUID() const;
// 检查扩展名
bool HasExtension(const char* ext) const;
bool HasAnyExtension(const char* const* extensions, Core::uint32 count) const;
private:
String m_path;
};
```
---
## 5. 核心接口设计
### 5.1 资源基类接口 (IResource)
```cpp
// IResource.h
namespace XCEngine {
namespace Resources {
class IResource {
public:
virtual ~IResource() = default;
// 获取资源类型
virtual ResourceType GetType() const = 0;
// 获取资源名称
virtual const String& GetName() const = 0;
// 获取资源路径
virtual const String& GetPath() const = 0;
// 获取资源GUID
virtual ResourceGUID GetGUID() const = 0;
// 资源是否有效
virtual bool IsValid() const = 0;
// 获取资源内存大小(用于缓存统计)
virtual size_t GetMemorySize() const = 0;
// 释放资源(返回到对象池或真正删除)
virtual void Release() = 0;
protected:
// 资源初始化(供子类调用)
struct ConstructParams {
String name;
String path;
ResourceGUID guid;
size_t memorySize = 0;
};
void Initialize(const ConstructParams& params) {
m_name = params.name;
m_path = params.path;
m_guid = params.guid;
m_memorySize = params.memorySize;
m_isValid = true;
}
void SetInvalid() { m_isValid = false; }
String m_name;
String m_path;
ResourceGUID m_guid;
bool m_isValid = false;
size_t m_memorySize = 0;
};
} // namespace Resources
} // namespace XCEngine
```
### 5.2 资源句柄 (ResourceHandle)
```cpp
// ResourceHandle.h
#include <type_traits>
#include <functional>
namespace XCEngine {
namespace Resources {
// 资源句柄模板类
// 类似智能指针,但专门用于资源管理
template<typename T>
class ResourceHandle {
static_assert(std::is_base_of_v<IResource, T>, "T must derive from IResource");
public:
ResourceHandle() = default;
explicit ResourceHandle(T* resource)
: m_resource(resource) {
if (m_resource) {
ResourceManager::Get().AddRef(m_resource->GetGUID());
}
}
ResourceHandle(const ResourceHandle& other)
: m_resource(other.m_resource) {
if (m_resource) {
ResourceManager::Get().AddRef(m_resource->GetGUID());
}
}
ResourceHandle(ResourceHandle&& other) noexcept
: m_resource(other.m_resource) {
// 移动语义不转移引用计数所有权
// 原句柄被掏空,不减少引用计数
other.m_resource = nullptr;
}
~ResourceHandle() {
Reset();
}
ResourceHandle& operator=(const ResourceHandle& other) {
if (this != &other) {
Reset();
m_resource = other.m_resource;
if (m_resource) {
ResourceManager::Get().AddRef(m_resource->GetGUID());
}
}
return *this;
}
ResourceHandle& operator=(ResourceHandle&& other) noexcept {
if (this != &other) {
// 先释放当前持有的资源
Reset();
// 移动所有权
m_resource = other.m_resource;
// 原句柄被掏空,不减少引用计数
other.m_resource = nullptr;
}
return *this;
}
// 访问资源
T* Get() const { return m_resource; }
T* operator->() const { return m_resource; }
T& operator*() const { return *m_resource; }
// 检查有效性
bool IsValid() const { return m_resource != nullptr && m_resource->IsValid(); }
explicit operator bool() const { return IsValid(); }
// 获取GUID
ResourceGUID GetGUID() const {
return m_resource ? m_resource->GetGUID() : ResourceGUID(0);
}
// 获取资源类型
ResourceType GetResourceType() const {
return m_resource ? m_resource->GetType() : ResourceType::Unknown;
}
// 重置句柄
void Reset() {
if (m_resource) {
ResourceManager::Get().Release(m_resource->GetGUID());
m_resource = nullptr;
}
}
// 交换
void Swap(ResourceHandle& other) {
std::swap(m_resource, other.m_resource);
}
private:
T* m_resource = nullptr;
};
// 比较操作符
template<typename T>
bool operator==(const ResourceHandle<T>& lhs, const ResourceHandle<T>& rhs) {
return lhs.GetGUID() == rhs.GetGUID();
}
template<typename T>
bool operator!=(const ResourceHandle<T>& lhs, const ResourceHandle<T>& rhs) {
return !(lhs == rhs);
}
} // namespace Resources
} // namespace XCEngine
```
### 5.3 加载器基类接口 (IResourceLoader)
```cpp
// IResourceLoader.h
namespace XCEngine {
namespace Resources {
// 加载结果
struct LoadResult {
IResource* resource = nullptr;
bool success = false;
String errorMessage;
LoadResult() = default;
explicit LoadResult(IResource* res)
: resource(res), success(res != nullptr) {}
explicit LoadResult(const String& error)
: success(false), errorMessage(error) {}
explicit LoadResult(bool success, const String& error = "")
: success(success), errorMessage(error) {}
operator bool() const { return success && resource != nullptr; }
};
// 加载器接口
class IResourceLoader {
public:
virtual ~IResourceLoader() = default;
// 获取支持的资源类型
virtual ResourceType GetResourceType() const = 0;
// 获取支持的文件扩展名
virtual Array<String> GetSupportedExtensions() const = 0;
// 检查是否可以加载该文件
virtual bool CanLoad(const String& path) const = 0;
// 同步加载
virtual LoadResult Load(const String& path, const ImportSettings* settings = nullptr) = 0;
// 异步加载(默认实现:同步加载然后在主线程回调)
virtual void LoadAsync(const String& path,
const ImportSettings* settings,
std::function<void(LoadResult)> callback) {
LoadResult result = Load(path, settings);
if (callback) {
callback(result);
}
}
// 获取默认导入设置
virtual ImportSettings* GetDefaultSettings() const = 0;
protected:
// 辅助方法:从文件读取数据
static Array<Core::uint8> ReadFileData(const String& path);
// 辅助方法:解析路径获取扩展名
static String GetExtension(const String& path);
};
// 加载器工厂宏(方便注册)
#define REGISTER_RESOURCE_LOADER(loaderType) \
namespace { \
struct loaderType##Registrar { \
loaderType##Registrar() { \
ResourceManager::Get().RegisterLoader(new loaderType()); \
} \
} g_##loaderType##Registrar; \
}
} // namespace Resources
} // namespace XCEngine
```
### 5.4 资源管理器 (ResourceManager)
```cpp
// ResourceManager.h
namespace XCEngine {
namespace Resources {
class ResourceManager {
public:
static ResourceManager& Get();
// ==================== 初始化 ====================
void Initialize();
void Shutdown();
// 设置资源根目录
void SetResourceRoot(const String& rootPath);
const String& GetResourceRoot() const;
// ==================== 同步加载 ====================
// 通过路径加载
template<typename T>
ResourceHandle<T> Load(const String& path, ImportSettings* settings = nullptr) {
static_assert(std::is_base_of_v<IResource, T>, "T must derive from IResource");
ResourcePath resourcePath(path);
ResourceGUID guid = resourcePath.ToGUID();
// 1. 检查缓存
IResource* cached = FindInCache(guid);
if (cached) {
return ResourceHandle<T>(static_cast<T*>(cached));
}
// 2. 查找加载器
IResourceLoader* loader = FindLoader(GetResourceType<T>());
if (!loader) {
Debug::Logger::Get().Warning(Debug::LogCategory::FileSystem,
"No loader found for resource type: " + String(GetResourceTypeName(GetResourceType<T>())));
return ResourceHandle<T>();
}
// 3. 加载资源
LoadResult result = loader->Load(resourcePath.GetFullPath(), settings);
if (!result) {
Debug::Logger::Get().Error(Debug::LogCategory::FileSystem,
"Failed to load resource: " + path + " - " + result.errorMessage);
return ResourceHandle<T>();
}
// 4. 添加到缓存
AddToCache(guid, result.resource);
return ResourceHandle<T>(static_cast<T*>(result.resource));
}
// 通过GUID加载
template<typename T>
ResourceHandle<T> Load(ResourceGUID guid) {
// 从GUID反向查找路径需要维护guid到path的映射
// 这里简化处理,实际实现需要从数据库查询
return ResourceHandle<T>();
}
// ==================== 异步加载 ====================
// 异步加载资源(需要显式指定类型)
void LoadAsync(const String& path, ResourceType type,
std::function<void(LoadResult)> callback);
// 异步加载资源(带自定义设置)
void LoadAsync(const String& path, ResourceType type, ImportSettings* settings,
std::function<void(LoadResult)> callback);
// ==================== 卸载 ====================
// 通过路径卸载
void Unload(const String& path);
// 通过GUID卸载
void Unload(ResourceGUID guid);
// 卸载所有未使用的资源
void UnloadUnused();
// 卸载所有资源
void UnloadAll();
// ==================== 引用计数 ====================
// 增加引用计数
void AddRef(ResourceGUID guid);
// 减少引用计数引用计数为0时可能卸载资源
void Release(ResourceGUID guid);
// 获取引用计数
Core::uint32 GetRefCount(ResourceGUID guid) const;
// ==================== 加载器管理 ====================
// 注册加载器ResourceManager拥有加载器
void RegisterLoader(IResourceLoader* loader);
// 移除加载器
void UnregisterLoader(ResourceType type);
// 获取加载器
IResourceLoader* GetLoader(ResourceType type) const;
// ==================== 缓存管理 ====================
// 设置内存预算(字节)
void SetMemoryBudget(size_t bytes);
// 获取当前内存使用
size_t GetMemoryUsage() const;
// 获取内存预算
size_t GetMemoryBudget() const;
// 刷新缓存(强制淘汰最少使用的资源)
void FlushCache();
// ==================== 查询 ====================
// 查找资源(不增加引用计数)
IResource* Find(const String& path);
IResource* Find(ResourceGUID guid);
// 检查资源是否存在
bool Exists(const String& path) const;
bool Exists(ResourceGUID guid) const;
// ==================== 资源根目录相关 ====================
// 解析资源路径(添加资源根目录)
String ResolvePath(const String& relativePath) const;
private:
ResourceManager() = default;
~ResourceManager() = default;
// 内部方法
IResource* FindInCache(ResourceGUID guid);
void AddToCache(ResourceGUID guid, IResource* resource);
IResourceLoader* FindLoader(ResourceType type);
void ReloadResource(ResourceGUID guid);
// 数据成员
String m_resourceRoot;
// 资源缓存
HashMap<ResourceGUID, IResource*> m_resourceCache;
// 引用计数
HashMap<ResourceGUID, Core::uint32> m_refCounts;
// GUID到路径的映射用于从guid加载
HashMap<ResourceGUID, String> m_guidToPath;
// 加载器
HashMap<ResourceType, UniquePtr<IResourceLoader>> m_loaders;
// 缓存统计
size_t m_memoryUsage = 0;
size_t m_memoryBudget = 512 * 1024 * 1024; // 默认512MB
// 异步加载器
UniquePtr<AsyncLoader> m_asyncLoader;
// 互斥锁
Threading::Mutex m_mutex;
friend class ResourceHandleBase;
};
} // namespace Resources
} // namespace XCEngine
```
### 5.5 资源缓存 (ResourceCache)
```cpp
// ResourceCache.h
#include <algorithm>
namespace XCEngine {
namespace Resources {
// 缓存条目
struct CacheEntry {
IResource* resource;
ResourceGUID guid;
size_t memorySize;
Core::uint64 lastAccessTime; // 使用Profiler::Get().GetTickCount()
Core::uint32 accessCount;
CacheEntry()
: resource(nullptr), memorySize(0), lastAccessTime(0), accessCount(0) {}
CacheEntry(IResource* res, size_t size)
: resource(res), guid(res->GetGUID()), memorySize(size),
lastAccessTime(GetCurrentTick()), accessCount(1) {}
private:
static Core::uint64 GetCurrentTick();
};
// LRU缓存实现
class ResourceCache {
public:
ResourceCache();
~ResourceCache();
// 添加资源到缓存
void Add(ResourceGUID guid, IResource* resource);
// 从缓存移除
void Remove(ResourceGUID guid);
// 查找资源(不移动到头部)
IResource* Find(ResourceGUID guid) const;
// 访问资源更新LRU信息
void Touch(ResourceGUID guid);
// 获取缓存大小
size_t GetSize() const { return m_cache.Size(); }
// 获取内存使用
size_t GetMemoryUsage() const { return m_memoryUsage; }
// 设置内存预算
void SetMemoryBudget(size_t bytes);
size_t GetMemoryBudget() const { return m_memoryBudget; }
// 内存压力响应(淘汰资源直到满足要求)
void OnMemoryPressure(size_t requiredBytes);
// 引用计数为0时的通知供ResourceManager调用
void OnZeroRefCount(ResourceGUID guid);
// 刷新缓存(淘汰所有未使用的资源)
void Flush();
// 清空缓存
void Clear();
// 获取最少使用的资源(用于调试)
Array<ResourceGUID> GetLRUList(size_t count) const;
private:
// 淘汰最少使用的资源
void Evict(size_t requiredBytes);
// 更新内存统计
void UpdateMemoryStats();
// 数据成员
HashMap<ResourceGUID, CacheEntry> m_cache;
Array<ResourceGUID> m_lruOrder; // 按访问时间排序,最少使用的在前面
size_t m_memoryUsage = 0;
size_t m_memoryBudget = 512 * 1024 * 1024; // 默认512MB
Mutex m_mutex;
};
} // namespace Resources
} // namespace XCEngine
```
### 5.6 异步加载器 (AsyncLoader)
```cpp
// AsyncLoader.h
#include <atomic>
#include <functional>
namespace XCEngine {
namespace Resources {
// 加载请求
struct LoadRequest {
String path;
ResourceType type;
std::function<void(LoadResult)> callback;
UniquePtr<ImportSettings> settings;
Core::uint64 requestId;
LoadRequest()
: requestId(0) {}
LoadRequest(const String& p, ResourceType t,
std::function<void(LoadResult)> cb,
ImportSettings* s = nullptr)
: path(p), type(t), callback(std::move(cb)),
settings(s), requestId(GenerateRequestId()) {}
private:
static Core::uint64 GenerateRequestId();
};
// 加载任务(工作线程执行)
class LoadTask : public Threading::ITask {
public:
LoadTask(LoadRequest request, IResourceLoader* loader);
void Execute() override;
LoadResult& GetResult() { return m_result; }
private:
LoadRequest m_request;
IResourceLoader* m_loader;
LoadResult m_result;
};
// 异步加载器
class AsyncLoader {
public:
static AsyncLoader& Get();
// 初始化
void Initialize(Core::uint32 workerThreadCount = 2);
void Shutdown();
// 提交加载请求(需要显式指定类型)
void Submit(const String& path, ResourceType type,
std::function<void(LoadResult)> callback);
// 提交加载请求(带自定义设置)
void Submit(const String& path, ResourceType type, ImportSettings* settings,
std::function<void(LoadResult)> callback);
// 更新(每帧调用,处理完成的加载)
void Update();
// 检查是否正在加载
bool IsLoading() const { return m_pendingCount > 0; }
// 获取待处理数量
Core::uint32 GetPendingCount() const { return m_pendingCount; }
// 获取加载进度0.0 - 1.0
float GetProgress() const;
// 取消所有待处理的请求
void CancelAll();
// 取消特定请求
void Cancel(Core::uint64 requestId);
private:
AsyncLoader() = default;
~AsyncLoader() = default;
void SubmitInternal(LoadRequest& request);
// 待处理的请求队列
Threading::Mutex m_queueMutex;
Array<LoadRequest> m_pendingQueue;
// 完成的请求队列(主线程处理)
Threading::Mutex m_completedMutex;
Array<LoadRequest> m_completedQueue;
// 统计
std::atomic<Core::uint32> m_pendingCount{0};
std::atomic<Core::uint32> m_completedCount{0};
Core::uint32 m_totalRequested = 0;
// 加载器查找
IResourceLoader* FindLoader(ResourceType type) const;
};
} // namespace Resources
} // namespace XCEngine
```
---
## 6. 具体资源类型
### 6.1 纹理资源 (Texture)
```cpp
// Texture.h
namespace XCEngine {
namespace Resources {
// 纹理类型
enum class TextureType {
Texture2D,
Texture3D,
TextureCube,
Texture2DArray,
TextureCubeArray
};
// 纹理格式
enum class TextureFormat {
Unknown,
R8_UNORM,
RG8_UNORM,
RGBA8_UNORM,
RGBA8_SRGB,
R16_FLOAT,
RG16_FLOAT,
RGBA16_FLOAT,
R32_FLOAT,
RG32_FLOAT,
RGBA32_FLOAT,
D16_UNORM,
D24_UNORM_S8_UINT,
D32_FLOAT,
D32_FLOAT_S8_X24_UINT,
BC1_UNORM,
BC1_UNORM_SRGB,
BC2_UNORM,
BC2_UNORM_SRGB,
BC3_UNORM,
BC3_UNORM_SRGB,
BC4_UNORM,
BC5_UNORM,
BC6H_UF16,
BC7_UNORM,
BC7_UNORM_SRGB
};
// 纹理使用flags
enum class TextureUsage : Core::uint8 {
None = 0,
ShaderResource = 1 << 0,
RenderTarget = 1 << 1,
DepthStencil = 1 << 2,
UnorderedAccess = 1 << 3,
TransferSrc = 1 << 4,
TransferDst = 1 << 5
};
// 纹理资源
class Texture : public IResource {
public:
Texture();
virtual ~Texture() override;
// IResource 接口
ResourceType GetType() const override { return ResourceType::Texture; }
const String& GetName() const override { return m_name; }
const String& GetPath() const override { return m_path; }
ResourceGUID GetGUID() const override { return m_guid; }
bool IsValid() const override { return m_isValid; }
size_t GetMemorySize() const override { return m_memorySize; }
void Release() override;
// 纹理属性
Core::uint32 GetWidth() const { return m_width; }
Core::uint32 GetHeight() const { return m_height; }
Core::uint32 GetDepth() const { return m_depth; }
Core::uint32 GetMipLevels() const { return m_mipLevels; }
Core::uint32 GetArraySize() const { return m_arraySize; }
TextureType GetTextureType() const { return m_textureType; }
TextureFormat GetFormat() const { return m_format; }
TextureUsage GetUsage() const { return m_usage; }
// RHI资源
class IRHIResource* GetRHIResource() const { return m_rhiResource; }
void SetRHIResource(class IRHIResource* resource);
// 创建纹理(从原始数据)
bool Create(Core::uint32 width, Core::uint32 height, Core::uint32 depth,
Core::uint32 mipLevels, TextureType type, TextureFormat format,
const void* data, size_t dataSize);
// 生成Mipmap
bool GenerateMipmaps();
private:
// 成员变量
String m_name;
String m_path;
ResourceGUID m_guid;
bool m_isValid = false;
size_t m_memorySize = 0;
Core::uint32 m_width = 0;
Core::uint32 m_height = 0;
Core::uint32 m_depth = 1;
Core::uint32 m_mipLevels = 1;
Core::uint32 m_arraySize = 1;
TextureType m_textureType = TextureType::Texture2D;
TextureFormat m_format = TextureFormat::RGBA8_UNORM;
TextureUsage m_usage = TextureUsage::ShaderResource;
class IRHIResource* m_rhiResource = nullptr;
};
} // namespace Resources
} // namespace XCEngine
```
### 6.2 纹理导入设置
```cpp
// TextureImportSettings.h
namespace XCEngine {
namespace Resources {
class TextureImportSettings : public ImportSettings {
public:
TextureImportSettings();
virtual ~TextureImportSettings() override;
// ==================== 纹理设置 ====================
// 纹理类型
void SetTextureType(TextureType type) { m_textureType = type; }
TextureType GetTextureType() const { return m_textureType; }
// 目标格式(用于压缩)
void SetTargetFormat(TextureFormat format) { m_targetFormat = format; }
TextureFormat GetTargetFormat() const { return m_targetFormat; }
// 是否生成Mipmap
void SetGenerateMipmaps(bool generate) { m_generateMipmaps = generate; }
bool GetGenerateMipmaps() const { return m_generateMipmaps; }
// Mipmap过滤方式
void SetMipmapFilter(MipmapFilter filter) { m_mipmapFilter = filter; }
MipmapFilter GetMipmapFilter() const { return m_mipmapFilter; }
// 各向异性过滤
void SetMaxAnisotropy(Core::uint32 anisotropy) { m_maxAnisotropy = anisotropy; }
Core::uint32 GetMaxAnisotropy() const { return m_maxAnisotropy; }
// 是否sRGB影响着色器中的采样
void SetSRGB(bool srgb) { m_sRGB = srgb; }
bool GetSRGB() const { return m_sRGB; }
// 是否启用纹理数组
void SetTextureArray(bool array) { m_textureArray = array; }
bool GetTextureArray() const { return m_textureArray; }
// 垂直翻转
void SetFlipVertical(bool flip) { m_flipVertical = flip; }
bool GetFlipVertical() const { return m_flipVertical; }
// 水平翻转
void SetFlipHorizontal(bool flip) { m_flipHorizontal = flip; }
bool GetFlipHorizontal() const { return m_flipHorizontal; }
// 边框颜色(对于透明纹理)
void SetBorderColor(const Math::Color& color) { m_borderColor = color; }
const Math::Color& GetBorderColor() const { return m_borderColor; }
// ==================== 压缩设置 ====================
// 压缩质量
void SetCompressionQuality(CompressionQuality quality) { m_compressionQuality = quality; }
CompressionQuality GetCompressionQuality() const { return m_compressionQuality; }
// 是否使用硬件压缩
void SetUseHardwareCompression(bool use) { m_useHardwareCompression = use; }
bool GetUseHardwareCompression() const { return m_useHardwareCompression; }
// ==================== 实用方法 ====================
// 克隆设置
UniquePtr<ImportSettings> Clone() const override;
// 从JSON加载
bool LoadFromJSON(const String& json);
// 保存为JSON
String SaveToJSON() const;
private:
// 成员变量
TextureType m_textureType = TextureType::Texture2D;
TextureFormat m_targetFormat = TextureFormat::Unknown; // 自动选择
bool m_generateMipmaps = true;
MipmapFilter m_mipmapFilter = MipmapFilter::Box;
Core::uint32 m_maxAnisotropy = 16;
bool m_sRGB = false;
bool m_textureArray = false;
bool m_flipVertical = false;
bool m_flipHorizontal = false;
Math::Color m_borderColor = Math::Color::Black;
CompressionQuality m_compressionQuality = CompressionQuality::High;
bool m_useHardwareCompression = true;
};
// Mipmap过滤方式
enum class MipmapFilter {
Box,
Kaiser
};
// 压缩质量
enum class CompressionQuality {
Low,
Medium,
High,
Ultra
};
} // namespace Resources
} // namespace XCEngine
```
### 6.3 纹理加载器
```cpp
// TextureLoader.h
namespace XCEngine {
namespace Resources {
class TextureLoader : public IResourceLoader {
public:
TextureLoader();
virtual ~TextureLoader() override;
// IResourceLoader 接口
ResourceType GetResourceType() const override {
return ResourceType::Texture;
}
Array<String> GetSupportedExtensions() const override {
return Array<String>{".png", ".jpg", ".jpeg", ".tga", ".bmp",
".dds", ".hdr", ".exr", ".psd"};
}
bool CanLoad(const String& path) const override {
String ext = GetExtension(path);
return ext == ".png" || ext == ".jpg" || ext == ".jpeg" ||
ext == ".tga" || ext == ".bmp" || ext == ".dds" ||
ext == ".hdr" || ext == ".exr" || ext == ".psd";
}
LoadResult Load(const String& path, const ImportSettings* settings = nullptr) override;
ImportSettings* GetDefaultSettings() const override {
return new TextureImportSettings();
}
private:
// 内部加载方法
LoadResult LoadPNG(const String& path, const TextureImportSettings* settings);
LoadResult LoadJPG(const String& path, const TextureImportSettings* settings);
LoadResult LoadTGA(const String& path, const TextureImportSettings* settings);
LoadResult LoadDDS(const String& path, const TextureImportSettings* settings);
LoadResult LoadHDR(const String& path, const TextureImportSettings* settings);
// 辅助方法
TextureFormat DetectFormat(const String& path) const;
bool ConvertFormat(const Core::uint8* srcData, Core::uint32 width, Core::uint32 height,
TextureFormat srcFormat, TextureFormat dstFormat,
Core::uint8*& outData, size_t& outDataSize);
};
// 注册加载器
REGISTER_RESOURCE_LOADER(TextureLoader);
} // namespace Resources
} // namespace XCEngine
```
### 6.4 网格资源 (Mesh)
```cpp
// Mesh.h
namespace XCEngine {
namespace Resources {
// 顶点属性
enum class VertexAttribute : Core::uint32 {
Position = 1 << 0,
Normal = 1 << 1,
Tangent = 1 << 2,
Color = 1 << 3,
UV0 = 1 << 4,
UV1 = 1 << 5,
UV2 = 1 << 6,
UV3 = 1 << 7,
BoneWeights = 1 << 8,
BoneIndices = 1 << 9
};
// 网格子资源(用于多材质)
struct MeshSection {
Core::uint32 baseVertex;
Core::uint32 vertexCount;
Core::uint32 startIndex;
Core::uint32 indexCount;
Core::uint32 materialID;
Math::AABB boundingBox;
};
// 网格资源
class Mesh : public IResource {
public:
Mesh();
virtual ~Mesh() override;
// IResource 接口
ResourceType GetType() const override { return ResourceType::Mesh; }
const String& GetName() const override { return m_name; }
const String& GetPath() const override { return m_path; }
ResourceGUID GetGUID() const override { return m_guid; }
bool IsValid() const override { return m_isValid; }
size_t GetMemorySize() const override { return m_memorySize; }
void Release() override;
// ==================== 顶点数据 ====================
// 设置顶点数据
void SetVertexData(const void* data, size_t size, Core::uint32 vertexCount,
Core::uint32 vertexStride, VertexAttribute attributes);
// 获取顶点数据
const void* GetVertexData() const { return m_vertexData.Data(); }
size_t GetVertexDataSize() const { return m_vertexData.Size(); }
Core::uint32 GetVertexCount() const { return m_vertexCount; }
Core::uint32 GetVertexStride() const { return m_vertexStride; }
VertexAttribute GetVertexAttributes() const { return m_attributes; }
// ==================== 索引数据 ====================
// 设置索引数据
void SetIndexData(const void* data, size_t size, Core::uint32 indexCount, bool use32Bit);
// 获取索引数据
const void* GetIndexData() const { return m_indexData.Data(); }
size_t GetIndexDataSize() const { return m_indexData.Size(); }
Core::uint32 GetIndexCount() const { return m_indexCount; }
bool IsUse32BitIndex() const { return m_use32BitIndex; }
// ==================== 子网格 ====================
void AddSection(const MeshSection& section);
const Array<MeshSection>& GetSections() const { return m_sections; }
// ==================== 包围盒 ====================
void SetBoundingBox(const Math::AABB& box);
const Math::AABB& GetBoundingBox() const { return m_boundingBox; }
// ==================== 骨骼动画(可选)====================
void SetSkeleton(class Skeleton* skeleton);
class Skeleton* GetSkeleton() const { return m_skeleton; }
// ==================== RHI资源 ====================
class IRHIBuffer* GetVertexBuffer() const { return m_vertexBuffer; }
class IRHIBuffer* GetIndexBuffer() const { return m_indexBuffer; }
void SetRHIBuffers(class IRHIBuffer* vb, class IRHIBuffer* ib);
private:
// 成员变量
String m_name;
String m_path;
ResourceGUID m_guid;
bool m_isValid = false;
size_t m_memorySize = 0;
// 顶点数据
Array<Core::uint8> m_vertexData;
Core::uint32 m_vertexCount = 0;
Core::uint32 m_vertexStride = 0;
VertexAttribute m_attributes = VertexAttribute::Position;
// 索引数据
Array<Core::uint8> m_indexData;
Core::uint32 m_indexCount = 0;
bool m_use32BitIndex = false;
// 子网格
Array<MeshSection> m_sections;
// 包围盒
Math::AABB m_boundingBox;
// 骨骼
class Skeleton* m_skeleton = nullptr;
// RHI资源
class IRHIBuffer* m_vertexBuffer = nullptr;
class IRHIBuffer* m_indexBuffer = nullptr;
};
} // namespace Resources
} // namespace XCEngine
```
### 6.5 材质资源 (Material)
```cpp
// Material.h
namespace XCEngine {
namespace Resources {
// 材质属性类型
enum class MaterialPropertyType {
Float,
Float2,
Float3,
Float4,
Int,
Int2,
Int3,
Int4,
Bool,
Texture,
Cubemap
};
// 材质属性
struct MaterialProperty {
String name;
MaterialPropertyType type;
// 值联合体
union Value {
float floatValue[4];
Core::int32 intValue[4];
bool boolValue;
ResourceHandle<Texture> textureValue;
Value() { memset(this, 0, sizeof(Value)); }
} value;
// 引用计数(纹理资源需要特殊处理)
Core::uint32 refCount;
};
// 材质资源
class Material : public IResource {
public:
Material();
virtual ~Material() override;
// IResource 接口
ResourceType GetType() const override { return ResourceType::Material; }
const String& GetName() const override { return m_name; }
const String& GetPath() const override { return m_path; }
ResourceGUID GetGUID() const override { return m_guid; }
bool IsValid() const override { return m_isValid; }
size_t GetMemorySize() const override { return m_memorySize; }
void Release() override;
// ==================== 着色器 ====================
void SetShader(const ResourceHandle<class Shader>& shader);
class Shader* GetShader() const { return m_shader.Get(); }
// ==================== 属性设置 ====================
void SetFloat(const String& name, float value);
void SetFloat2(const String& name, const Math::Vector2& value);
void SetFloat3(const String& name, const Math::Vector3& value);
void SetFloat4(const String& name, const Math::Vector4& value);
void SetInt(const String& name, int32 value);
void SetInt2(const String& name, const Math::IntVector2& value);
void SetInt3(const String& name, const Math::IntVector3& value);
void SetInt4(const String& name, const Math::IntVector4& value);
void SetBool(const String& name, bool value);
void SetTexture(const String& name, const ResourceHandle<Texture>& texture);
// ==================== 属性获取 ====================
float GetFloat(const String& name) const;
Math::Vector2 GetFloat2(const String& name) const;
// ... 其他Get方法
ResourceHandle<Texture> GetTexture(const String& name) const;
// ==================== 属性遍历 ====================
using PropertyIterator = HashMap<String, MaterialProperty>::Iterator;
PropertyIterator BeginProperties() { return m_properties.Begin(); }
PropertyIterator EndProperties() { return m_properties.End(); }
// ==================== 批处理数据 ====================
// 获取用于GPU的常数缓冲区数据
const Array<Core::uint8>& GetConstantBufferData() const { return m_constantBufferData; }
// 更新常数缓冲区(当属性变化时调用)
void UpdateConstantBuffer();
private:
// 成员变量
String m_name;
String m_path;
ResourceGUID m_guid;
bool m_isValid = false;
size_t m_memorySize = 0;
// 着色器
ResourceHandle<class Shader> m_shader;
// 属性表
HashMap<String, MaterialProperty> m_properties;
// 常数缓冲区数据
Array<Core::uint8> m_constantBufferData;
// 纹理绑定信息用于设置到Pipeline
struct TextureBinding {
String name;
Core::uint32 slot;
ResourceHandle<Texture> texture;
};
Array<TextureBinding> m_textureBindings;
};
} // namespace Resources
} // namespace XCEngine
```
### 6.6 音频资源 (AudioClip)
```cpp
// AudioClip.h
namespace XCEngine {
namespace Resources {
// 音频格式
enum class AudioFormat {
Unknown,
WAV,
OGG,
MP3,
FLAC
};
// 音频类型
enum class AudioType {
SFX, // 音效
Music, // 背景音乐
Voice // 语音
};
// 音频资源
class AudioClip : public IResource {
public:
AudioClip();
virtual ~AudioClip() override;
// IResource 接口
ResourceType GetType() const override { return ResourceType::AudioClip; }
const String& GetName() const override { return m_name; }
const String& GetPath() const override { return m_path; }
ResourceGUID GetGUID() const override { return m_guid; }
bool IsValid() const override { return m_isValid; }
size_t GetMemorySize() const override { return m_memorySize; }
void Release() override;
// ==================== 音频属性 ====================
// 采样率
Core::uint32 GetSampleRate() const { return m_sampleRate; }
// 通道数
Core::uint32 GetChannels() const { return m_channels; }
// 位深度
Core::uint32 GetBitsPerSample() const { return m_bitsPerSample; }
// 持续时间(秒)
float GetDuration() const { return m_duration; }
// 数据大小
size_t GetDataSize() const { return m_data.Size(); }
// 格式
AudioFormat GetFormat() const { return m_format; }
// 类型
AudioType GetAudioType() const { return m_audioType; }
// ==================== 音频数据 ====================
// 获取PCM数据
const Core::uint8* GetData() const { return m_data.Data(); }
// 是否流式(不加载到内存)
bool IsStreaming() const { return m_isStreaming; }
// 设置流式
void SetStreaming(bool streaming) { m_isStreaming = streaming; }
// ==================== 播放控制 ====================
// 创建音频源(用于播放)
class IAudioSource* CreateSource() const;
private:
// 成员变量
String m_name;
String m_path;
ResourceGUID m_guid;
bool m_isValid = false;
size_t m_memorySize = 0;
// 音频属性
Core::uint32 m_sampleRate = 0;
Core::uint32 m_channels = 0;
Core::uint32 m_bitsPerSample = 0;
float m_duration = 0.0f;
AudioFormat m_format = AudioFormat::Unknown;
AudioType m_audioType = AudioType::SFX;
bool m_isStreaming = false;
// 音频数据
Array<Core::uint8> m_data;
};
} // namespace Resources
} // namespace XCEngine
```
---
## 7. 文件系统集成
### 7.1 文件系统接口
```cpp
// FileSystem.h (扩展现有IO模块)
namespace XCEngine {
namespace IO {
// 文件系统接口
class IFileSystem {
public:
virtual ~IFileSystem() = default;
// 文件操作
virtual bool FileExists(const String& path) const = 0;
virtual bool DirectoryExists(const String& path) const = 0;
virtual size_t GetFileSize(const String& path) const = 0;
virtual bool GetFileTime(const String& path, Core::uint64& outTime) const = 0;
// 读取
virtual Array<Core::uint8> ReadAllBytes(const String& path) const = 0;
virtual String ReadAllText(const String& path) const = 0;
virtual bool Read(const String& path, void* buffer, size_t size, size_t offset = 0) const = 0;
// 写入
virtual bool WriteAllBytes(const String& path, const void* data, size_t size) = 0;
virtual bool WriteAllText(const String& path, const String& text) = 0;
virtual bool Write(const String& path, const void* data, size_t size, size_t offset = 0) = 0;
// 目录操作
virtual bool CreateDirectory(const String& path) = 0;
virtual bool DeleteDirectory(const String& path, bool recursive = false) = 0;
virtual bool DeleteFile(const String& path) = 0;
// 遍历
virtual void EnumerateFiles(const String& path, const String& pattern,
Array<String>& outFiles) const = 0;
virtual void EnumerateDirectories(const String& path, const String& pattern,
Array<String>& outDirectories) const = 0;
// 获取路径信息
virtual String GetExtension(const String& path) const = 0;
virtual String GetFileName(const String& path) const = 0;
virtual String GetFileNameWithoutExtension(const String& path) const = 0;
virtual String GetDirectoryName(const String& path) const = 0;
virtual String GetFullPath(const String& path) const = 0;
virtual String Combine(const String& path1, const String& path2) const = 0;
};
// 获取文件系统单例
IFileSystem* GetFileSystem();
void SetFileSystem(IFileSystem* fs);
// Windows文件系统实现默认
class WindowsFileSystem : public IFileSystem {
public:
// 实现Windows API调用
};
} // namespace IO
} // namespace XCEngine
```
### 7.2 资源文件系统 (ResourceFileSystem)
```cpp
// ResourceFileSystem.h
namespace XCEngine {
namespace Resources {
// 资源文件系统 - 管理资源包/归档
class ResourceFileSystem {
public:
ResourceFileSystem();
~ResourceFileSystem();
// 初始化
void Initialize(const String& rootPath);
void Shutdown();
// 添加资源包
bool AddArchive(const String& archivePath);
bool AddDirectory(const String& directoryPath);
// 移除资源包
void RemoveArchive(const String& archivePath);
// 查找资源
bool FindResource(const String& relativePath, String& outAbsolutePath) const;
// 读取资源数据
Array<Core::uint8> ReadResource(const String& relativePath) const;
// 检查资源是否存在
bool Exists(const String& relativePath) const;
// 获取资源信息
struct ResourceInfo {
String path;
size_t size;
Core::uint64 modifiedTime;
bool inArchive;
String archivePath;
};
bool GetResourceInfo(const String& relativePath, ResourceInfo& outInfo) const;
// 枚举资源
void EnumerateResources(const String& pattern, Array<ResourceInfo>& outResources) const;
private:
// 资源包接口
class IArchive {
public:
virtual ~IArchive() = default;
virtual bool Open(const String& path) = 0;
virtual void Close() = 0;
virtual bool Read(const String& fileName, void* buffer, size_t size, size_t offset) const = 0;
virtual size_t GetSize(const String& fileName) const = 0;
virtual bool Exists(const String& fileName) const = 0;
virtual void Enumerate(const String& pattern, Array<String>& outFiles) const = 0;
};
// 数据成员
String m_rootPath;
Array<UniquePtr<IArchive>> m_archives;
Array<String> m_directories;
// 缓存
mutable HashMap<String, ResourceInfo> m_infoCache;
};
} // namespace Resources
} // namespace XCEngine
```
---
## 8. 资源打包系统
### 8.1 资源包格式
```
┌─────────────────────────────────────────────────────┐
│ ResourcePackage │
├─────────────────────────────────────────────────────┤
│ Header (固定大小) │
│ ├── Magic: "XCRP" (4 bytes) │
│ ├── Version: uint16 │
│ ├── Manifest Size: uint32 │
│ ├── Asset Count: uint32 │
│ └── Data Offset: uint64 │
├─────────────────────────────────────────────────────┤
│ Manifest (JSON/二进制) │
│ ├── File 1: { path, offset, size, checksum } │
│ ├── File 2: { path, offset, size, checksum } │
│ └── ... │
├─────────────────────────────────────────────────────┤
│ Data Section │
│ ├── File 1 Data (对齐到16字节) │
│ ├── File 2 Data │
│ └── ... │
└─────────────────────────────────────────────────────┘
```
### 8.2 资源包构建器
```cpp
// ResourcePackageBuilder.h
namespace XCEngine {
namespace Resources {
class ResourcePackageBuilder {
public:
ResourcePackageBuilder();
~ResourcePackageBuilder();
// 添加文件
bool AddFile(const String& sourcePath, const String& relativePath);
// 添加目录
bool AddDirectory(const String& sourceDir, const String& relativeBase = "");
// 设置输出路径
void SetOutputPath(const String& path) { m_outputPath = path; }
// 构建包
bool Build();
// 获取进度
float GetProgress() const { return m_progress; }
// 获取错误信息
const String& GetError() const { return m_error; }
private:
// 文件条目
struct FileEntry {
String sourcePath;
String relativePath;
size_t size;
uint64 checksum;
};
// 成员变量
String m_outputPath;
Array<FileEntry> m_files;
float m_progress = 0.0f;
String m_error;
// 辅助方法
Core::uint64 CalculateChecksum(const void* data, size_t size);
bool WriteHeader(FileWriter& writer, size_t dataOffset);
bool WriteManifest(FileWriter& writer);
bool WriteData(FileWriter& writer);
};
} // namespace Resources
} // namespace XCEngine
```
---
## 9. 资源依赖管理
### 9.1 依赖图
```cpp
// ResourceDependencyGraph.h
namespace XCEngine {
namespace Resources {
// 资源依赖节点
struct DependencyNode {
ResourceGUID guid;
ResourceType type;
Array<ResourceGUID> dependencies; // 当前资源依赖的资源
Array<ResourceGUID> dependents; // 依赖当前资源的资源
Core::uint32 refCount = 0;
};
// 依赖图
class ResourceDependencyGraph {
public:
ResourceDependencyGraph();
~ResourceDependencyGraph();
// 添加节点
void AddNode(ResourceGUID guid, ResourceType type);
// 添加依赖关系
void AddDependency(ResourceGUID owner, ResourceGUID dependency);
// 移除节点
void RemoveNode(ResourceGUID guid);
// 移除依赖关系
void RemoveDependency(ResourceGUID owner, ResourceGUID dependency);
// 获取依赖(当前资源依赖的资源)
Array<ResourceGUID> GetDependencies(ResourceGUID guid) const;
// 获取依赖者(依赖当前资源的资源)
Array<ResourceGUID> GetDependents(ResourceGUID guid) const;
// 获取传递依赖(所有层级的依赖)
Array<ResourceGUID> GetAllDependencies(ResourceGUID guid) const;
// 引用计数管理
void IncrementRefCount(ResourceGUID guid);
void DecrementRefCount(ResourceGUID guid);
Core::uint32 GetRefCount(ResourceGUID guid) const;
// 查找循环依赖
bool HasCircularDependency(ResourceGUID guid, Array<ResourceGUID>& outCycle) const;
// 拓扑排序(用于加载顺序)
Array<ResourceGUID> TopologicalSort() const;
// 加载资源组及其所有依赖
Array<ResourceGUID> LoadWithDependencies(const String& entryPath);
// 卸载资源组(仅当没有依赖者时)
bool Unload(ResourceGUID guid);
private:
HashMap<ResourceGUID, DependencyNode> m_nodes;
};
} // namespace Resources
} // namespace XCEngine
```
---
## 10. 实现顺序
### 10.1 阶段划分
```
阶段 4.1: 基础框架 (3天)
├── 4.1.1 ResourceTypes.h - 资源类型枚举
├── 4.1.2 IResource.h - 资源基类接口
├── 4.1.3 ResourceHandle.h - 资源句柄
├── 4.1.4 IResourceLoader.h - 加载器接口
└── 4.1.5 ResourceManager.h/cpp - 资源管理器基础
阶段 4.2: 缓存系统 (2天)
├── 4.2.1 ResourceCache.h/cpp - LRU缓存实现
└── 4.2.2 与ResourceManager集成
阶段 4.3: 异步加载 (2天)
├── 4.3.1 AsyncLoader.h/cpp - 异步加载器
├── 4.3.2 与TaskSystem集成
└── 4.3.3 完成回调机制
阶段 4.4: 具体资源类型 (4天)
├── 4.4.1 Texture + TextureLoader - 纹理资源
├── 4.4.2 Mesh + MeshLoader - 网格资源
├── 4.4.3 Material + MaterialLoader - 材质资源
└── 4.4.4 AudioClip + AudioLoader - 音频资源
阶段 4.5: 文件系统集成 (2天)
├── 4.5.1 ResourceFileSystem - 资源文件系统
└── 4.5.2 资源包支持
阶段 4.6: 依赖管理 (2天)
├── 4.6.1 ResourceDependencyGraph - 依赖图
└── 4.6.2 资源组加载/卸载
阶段 4.7: 测试与集成 (2天)
├── 4.7.1 单元测试
└── 4.7.2 与现有mvs集成
```
### 10.2 详细任务列表
```
任务 4.1.1: ResourceTypes.h
实现:
- ResourceType 枚举Texture, Mesh, Material, Shader, AudioClip等
- GetResourceTypeName() 函数
- ResourceGUID 结构体
任务 4.1.2: IResource.h
实现:
- IResource 抽象基类
- 纯虚方法GetType(), GetName(), GetPath(), GetGUID(), IsValid(), GetMemorySize(), Release()
- 虚析构函数
任务 4.1.3: ResourceHandle.h
实现:
- ResourceHandle<T> 模板类
- 构造/析构(自动引用计数)
- operator->(), Get(), IsValid()
- 移动语义支持
任务 4.1.4: IResourceLoader.h
实现:
- IResourceLoader 抽象基类
- LoadResult 结构体
- GetResourceType(), GetSupportedExtensions(), CanLoad(), Load()
- REGISTER_RESOURCE_LOADER 宏
任务 4.1.5: ResourceManager.h/cpp
实现:
- ResourceManager 单例
- Initialize()/Shutdown()
- Load<T>(path) 模板方法
- Unload(path/guid)
- AddRef()/Release()
- RegisterLoader()
- SetMemoryBudget()
任务 4.2.1: ResourceCache.h/cpp
实现:
- CacheEntry 结构体
- ResourceCache 类
- Add(), Remove(), Find(), Touch()
- SetMemoryBudget(), OnMemoryPressure()
- LRU淘汰逻辑
任务 4.3.1: AsyncLoader.h/cpp
实现:
- LoadRequest 结构体
- AsyncLoader 单例
- Submit<T>(path, callback)
- Update() - 处理完成队列
- IsLoading(), GetProgress()
任务 4.4.1: Texture + TextureLoader
实现:
- Texture 类继承IResource
- TextureImportSettings 类
- TextureLoader 类继承IResourceLoader
- 使用stb_image加载PNG/JPG/TGA
- REGISTER_RESOURCE_LOADER(TextureLoader)
任务 4.4.2: Mesh + MeshLoader
实现:
- Mesh 类继承IResource
- MeshSection 结构体
- MeshImportSettings 类
- MeshLoader 类使用Assimp
- 支持OBJ/glTF格式
- REGISTER_RESOURCE_LOADER(MeshLoader)
任务 4.4.3: Material + MaterialLoader
实现:
- Material 类
- MaterialProperty 结构体
- MaterialLoader 类解析JSON格式
- REGISTER_RESOURCE_LOADER(MaterialLoader)
任务 4.4.4: AudioClip + AudioLoader
实现:
- AudioClip 类
- AudioLoader 类
- 支持WAV格式
- REGISTER_RESOURCE_LOADER(AudioLoader)
任务 4.5.1: ResourceFileSystem
实现:
- ResourceFileSystem 类
- IArchive 接口
- AddArchive(), AddDirectory()
- ReadResource(), Exists()
任务 4.6.1: ResourceDependencyGraph
实现:
- DependencyNode 结构体
- ResourceDependencyGraph 类
- AddDependency(), RemoveDependency()
- GetDependencies(), GetDependents()
- TopologicalSort()
```
---
## 11. 详细执行计划
> 本节详细说明如何逐步实现资源系统,包括每个文件的具体内容、实现步骤、测试方法和集成要点。
### 11.1 文件创建清单
首先,需要创建以下目录结构和文件:
```
engine/include/XCEngine/Resources/
├── Resources.h # 主头文件
├── ResourceTypes.h # 资源类型枚举
├── ImportSettings.h # 导入设置基类
├── IResource.h # 资源基类接口
├── ResourceHandle.h # 资源句柄
├── ResourceManager.h # 资源管理器
├── IResourceLoader.h # 加载器基类
├── ResourceCache.h # 资源缓存
├── AsyncLoader.h # 异步加载器
├── Texture.h # 纹理资源
├── TextureLoader.h # 纹理加载器
├── TextureImportSettings.h # 纹理导入设置
├── Mesh.h # 网格资源
├── MeshLoader.h # 网格加载器
├── MeshImportSettings.h # 网格导入设置
├── Material.h # 材质资源
├── MaterialLoader.h # 材质加载器
├── Shader.h # 着色器资源
├── ShaderLoader.h # 着色器加载器
├── AudioClip.h # 音频资源
├── AudioLoader.h # 音频加载器
└── BinaryResource.h # 二进制资源
engine/src/Resources/
├── Resources.cpp # 资源系统初始化
├── ResourceManager.cpp # 资源管理器实现
├── ResourceCache.cpp # 缓存实现
├── AsyncLoader.cpp # 异步加载实现
├── TextureLoader.cpp # 纹理加载实现
├── MeshLoader.cpp # 网格加载实现
├── MaterialLoader.cpp # 材质加载实现
├── ShaderLoader.cpp # 着色器加载实现
└── AudioLoader.cpp # 音频加载实现
```
### 11.2 第一阶段基础框架天1-3
#### 步骤 11.2.1 创建主头文件 Resources.h
```cpp
// Resources.h
#pragma once
#include "ResourceTypes.h"
#include "ImportSettings.h"
#include "IResource.h"
#include "ResourceHandle.h"
#include "ResourceManager.h"
#include "IResourceLoader.h"
#include "ResourceCache.h"
#include "AsyncLoader.h"
// 具体资源类型
#include "Texture.h"
#include "TextureLoader.h"
#include "TextureImportSettings.h"
#include "Mesh.h"
#include "MeshLoader.h"
#include "MeshImportSettings.h"
#include "Material.h"
#include "MaterialLoader.h"
#include "Shader.h"
#include "ShaderLoader.h"
#include "AudioClip.h"
#include "AudioLoader.h"
#include "BinaryResource.h"
```
#### 步骤 11.2.2 实现 ResourceTypes.h
**文件**: `engine/include/XCEngine/Resources/ResourceTypes.h`
```cpp
#pragma once
#include "../Core/Types.h"
#include "../Containers/String.h"
#include "../Containers/Array.h"
#include <cstdint>
namespace XCEngine {
namespace Resources {
// 资源类型枚举
enum class ResourceType : Core::uint8 {
Unknown = 0,
Texture,
Mesh,
Material,
Shader,
AudioClip,
Binary,
AnimationClip,
Skeleton,
Font,
ParticleSystem,
Scene,
Prefab
};
constexpr const char* GetResourceTypeName(ResourceType type) {
switch (type) {
case ResourceType::Texture: return "Texture";
case ResourceType::Mesh: return "Mesh";
case ResourceType::Material: return "Material";
case ResourceType::Shader: return "Shader";
case ResourceType::AudioClip: return "AudioClip";
case ResourceType::Binary: return "Binary";
case ResourceType::AnimationClip: return "AnimationClip";
case ResourceType::Skeleton: return "Skeleton";
case ResourceType::Font: return "Font";
case ResourceType::ParticleSystem:return "ParticleSystem";
case ResourceType::Scene: return "Scene";
case ResourceType::Prefab: return "Prefab";
default: return "Unknown";
}
}
// ResourceGUID - 64位唯一标识符
struct ResourceGUID {
Core::uint64 value;
ResourceGUID() : value(0) {}
explicit ResourceGUID(Core::uint64 v) : value(v) {}
bool IsValid() const { return value != 0; }
bool operator==(const ResourceGUID& other) const { return value == other.value; }
bool operator!=(const ResourceGUID& other) const { return value != other.value; }
static ResourceGUID Generate(const char* path);
static ResourceGUID Generate(const Containers::String& path);
Containers::String ToString() const;
};
inline ResourceGUID MakeResourceGUID(const char* path) {
return ResourceGUID::Generate(path);
}
// 模板特化获取ResourceType
template<typename T>
ResourceType GetResourceType();
template<> inline ResourceType GetResourceType<class Texture>() { return ResourceType::Texture; }
template<> inline ResourceType GetResourceType<class Mesh>() { return ResourceType::Mesh; }
template<> inline ResourceType GetResourceType<class Material>() { return ResourceType::Material; }
template<> inline ResourceType GetResourceType<class Shader>() { return ResourceType::Shader; }
template<> inline ResourceType GetResourceType<class AudioClip>() { return ResourceType::AudioClip; }
template<> inline ResourceType GetResourceType<class BinaryResource>() { return ResourceType::Binary; }
// 导入设置基类
class ImportSettings {
public:
virtual ~ImportSettings() = default;
virtual UniquePtr<ImportSettings> Clone() const = 0;
virtual bool LoadFromJSON(const Containers::String& json) { return false; }
virtual Containers::String SaveToJSON() const { return Containers::String(); }
};
} // namespace Resources
} // namespace XCEngine
```
**实现文件** `engine/src/Resources/ResourceTypes.cpp`:
```cpp
#include "Resources/ResourceTypes.h"
#include <algorithm>
namespace XCEngine {
namespace Resources {
// 简单哈希实现实际项目中可使用MD5
ResourceGUID ResourceGUID::Generate(const char* path) {
Core::uint64 hash = 14695981039346656037ULL; // FNV offset basis
while (*path) {
hash ^= static_cast<Core::uint64>(*path);
hash *= 1099511628211ULL; // FNV prime
path++;
}
return ResourceGUID(hash);
}
ResourceGUID ResourceGUID::Generate(const Containers::String& path) {
return Generate(path.CStr());
}
Containers::String ResourceGUID::ToString() const {
char buffer[17];
snprintf(buffer, sizeof(buffer), "%016llx", value);
return Containers::String(buffer);
}
} // namespace Resources
} // namespace XCEngine
```
#### 步骤 11.2.3 实现 IResource.h
**文件**: `engine/include/XCEngine/Resources/IResource.h`
```cpp
#pragma once
#include "ResourceTypes.h"
#include "../Containers/String.h"
namespace XCEngine {
namespace Resources {
class IResource {
public:
virtual ~IResource() = default;
virtual ResourceType GetType() const = 0;
virtual const Containers::String& GetName() const = 0;
virtual const Containers::String& GetPath() const = 0;
virtual ResourceGUID GetGUID() const = 0;
virtual bool IsValid() const = 0;
virtual size_t GetMemorySize() const = 0;
virtual void Release() = 0;
protected:
struct ConstructParams {
Containers::String name;
Containers::String path;
ResourceGUID guid;
size_t memorySize = 0;
};
void Initialize(const ConstructParams& params) {
m_name = params.name;
m_path = params.path;
m_guid = params.guid;
m_memorySize = params.memorySize;
m_isValid = true;
}
void SetInvalid() { m_isValid = false; }
Containers::String m_name;
Containers::String m_path;
ResourceGUID m_guid;
bool m_isValid = false;
size_t m_memorySize = 0;
};
} // namespace Resources
} // namespace XCEngine
```
#### 步骤 11.2.4 实现 ResourceHandle.h
**文件**: `engine/include/XCEngine/Resources/ResourceHandle.h`
```cpp
#pragma once
#include "IResource.h"
#include <type_traits>
#include <functional>
namespace XCEngine {
namespace Resources {
template<typename T>
class ResourceHandle {
static_assert(std::is_base_of_v<IResource, T>, "T must derive from IResource");
public:
ResourceHandle() = default;
explicit ResourceHandle(T* resource)
: m_resource(resource) {
if (m_resource) {
ResourceManager::Get().AddRef(m_resource->GetGUID());
}
}
ResourceHandle(const ResourceHandle& other)
: m_resource(other.m_resource) {
if (m_resource) {
ResourceManager::Get().AddRef(m_resource->GetGUID());
}
}
ResourceHandle(ResourceHandle&& other) noexcept
: m_resource(other.m_resource) {
other.m_resource = nullptr;
}
~ResourceHandle() {
Reset();
}
ResourceHandle& operator=(const ResourceHandle& other) {
if (this != &other) {
Reset();
m_resource = other.m_resource;
if (m_resource) {
ResourceManager::Get().AddRef(m_resource->GetGUID());
}
}
return *this;
}
ResourceHandle& operator=(ResourceHandle&& other) noexcept {
if (this != &other) {
Reset();
m_resource = other.m_resource;
other.m_resource = nullptr;
}
return *this;
}
T* Get() const { return m_resource; }
T* operator->() const { return m_resource; }
T& operator*() const { return *m_resource; }
bool IsValid() const { return m_resource != nullptr && m_resource->IsValid(); }
explicit operator bool() const { return IsValid(); }
ResourceGUID GetGUID() const {
return m_resource ? m_resource->GetGUID() : ResourceGUID(0);
}
ResourceType GetResourceType() const {
return m_resource ? m_resource->GetType() : ResourceType::Unknown;
}
void Reset() {
if (m_resource) {
ResourceManager::Get().Release(m_resource->GetGUID());
m_resource = nullptr;
}
}
void Swap(ResourceHandle& other) {
std::swap(m_resource, other.m_resource);
}
private:
T* m_resource = nullptr;
};
template<typename T>
bool operator==(const ResourceHandle<T>& lhs, const ResourceHandle<T>& rhs) {
return lhs.GetGUID() == rhs.GetGUID();
}
template<typename T>
bool operator!=(const ResourceHandle<T>& lhs, const ResourceHandle<T>& rhs) {
return !(lhs == rhs);
}
} // namespace Resources
} // namespace XCEngine
```
#### 步骤 11.2.5 实现 IResourceLoader.h
**文件**: `engine/include/XCEngine/Resources/IResourceLoader.h`
```cpp
#pragma once
#include "IResource.h"
#include "ResourceTypes.h"
#include "../Containers/String.h"
#include "../Containers/Array.h"
#include <functional>
namespace XCEngine {
namespace Resources {
struct LoadResult {
IResource* resource = nullptr;
bool success = false;
Containers::String errorMessage;
LoadResult() = default;
explicit LoadResult(IResource* res) : resource(res), success(res != nullptr) {}
explicit LoadResult(const Containers::String& error) : success(false), errorMessage(error) {}
explicit LoadResult(bool success, const Containers::String& error = "")
: success(success), errorMessage(error) {}
operator bool() const { return success && resource != nullptr; }
};
class IResourceLoader {
public:
virtual ~IResourceLoader() = default;
virtual ResourceType GetResourceType() const = 0;
virtual Containers::Array<Containers::String> GetSupportedExtensions() const = 0;
virtual bool CanLoad(const Containers::String& path) const = 0;
virtual LoadResult Load(const Containers::String& path, const ImportSettings* settings = nullptr) = 0;
virtual void LoadAsync(const Containers::String& path,
const ImportSettings* settings,
std::function<void(LoadResult)> callback) {
LoadResult result = Load(path, settings);
if (callback) {
callback(result);
}
}
virtual ImportSettings* GetDefaultSettings() const = 0;
protected:
static Containers::Array<Core::uint8> ReadFileData(const Containers::String& path);
static Containers::String GetExtension(const Containers::String& path);
};
#define REGISTER_RESOURCE_LOADER(loaderType) \
namespace { \
struct loaderType##Registrar { \
loaderType##Registrar() { \
ResourceManager::Get().RegisterLoader(new loaderType()); \
} \
} g_##loaderType##Registrar; \
}
} // namespace Resources
} // namespace XCEngine
```
#### 步骤 11.2.6 实现 ResourceManager.h
**文件**: `engine/include/XCEngine/Resources/ResourceManager.h`
```cpp
#pragma once
#include "IResourceLoader.h"
#include "ResourceCache.h"
#include "AsyncLoader.h"
#include "../Containers/String.h"
#include "../Containers/Array.h"
#include "../Containers/HashMap.h"
#include "../Threading/Mutex.h"
#include "../Debug/Logger.h"
namespace XCEngine {
namespace Resources {
class ResourceManager {
public:
static ResourceManager& Get();
void Initialize();
void Shutdown();
void SetResourceRoot(const Containers::String& rootPath);
const Containers::String& GetResourceRoot() const;
template<typename T>
ResourceHandle<T> Load(const Containers::String& path, ImportSettings* settings = nullptr) {
static_assert(std::is_base_of_v<IResource, T>, "T must derive from IResource");
// 实现见文档Section 5.4
}
void LoadAsync(const Containers::String& path, ResourceType type,
std::function<void(LoadResult)> callback);
void LoadAsync(const Containers::String& path, ResourceType type, ImportSettings* settings,
std::function<void(LoadResult)> callback);
void Unload(const Containers::String& path);
void Unload(ResourceGUID guid);
void UnloadUnused();
void UnloadAll();
void AddRef(ResourceGUID guid);
void Release(ResourceGUID guid);
Core::uint32 GetRefCount(ResourceGUID guid) const;
void RegisterLoader(IResourceLoader* loader);
void UnregisterLoader(ResourceType type);
IResourceLoader* GetLoader(ResourceType type) const;
void SetMemoryBudget(size_t bytes);
size_t GetMemoryUsage() const;
size_t GetMemoryBudget() const;
void FlushCache();
IResource* Find(const Containers::String& path);
IResource* Find(ResourceGUID guid);
bool Exists(const Containers::String& path) const;
bool Exists(ResourceGUID guid) const;
Containers::String ResolvePath(const Containers::String& relativePath) const;
private:
ResourceManager() = default;
~ResourceManager() = default;
IResource* FindInCache(ResourceGUID guid);
void AddToCache(ResourceGUID guid, IResource* resource);
IResourceLoader* FindLoader(ResourceType type);
void ReloadResource(ResourceGUID guid);
Containers::String m_resourceRoot;
Containers::HashMap<ResourceGUID, IResource*> m_resourceCache;
Containers::HashMap<ResourceGUID, Core::uint32> m_refCounts;
Containers::HashMap<ResourceGUID, Containers::String> m_guidToPath;
Containers::HashMap<ResourceType, UniquePtr<IResourceLoader>> m_loaders;
size_t m_memoryUsage = 0;
size_t m_memoryBudget = 512 * 1024 * 1024;
UniquePtr<AsyncLoader> m_asyncLoader;
Threading::Mutex m_mutex;
friend class ResourceHandleBase;
};
} // namespace Resources
} // namespace XCEngine
```
#### 步骤 11.2.7 实现 ResourceManager.cpp
**文件**: `engine/src/Resources/ResourceManager.cpp`
```cpp
#include "Resources/ResourceManager.h"
#include "Resources/ResourceHandle.h"
namespace XCEngine {
namespace Resources {
ResourceManager& ResourceManager::Get() {
static ResourceManager instance;
return instance;
}
void ResourceManager::Initialize() {
m_asyncLoader = UniquePtr<AsyncLoader>(new AsyncLoader());
m_asyncLoader->Initialize(2); // 默认2个工作线程
}
void ResourceManager::Shutdown() {
UnloadAll();
m_asyncLoader->Shutdown();
m_asyncLoader.Reset();
}
void ResourceManager::SetResourceRoot(const Containers::String& rootPath) {
m_resourceRoot = rootPath;
}
const Containers::String& ResourceManager::GetResourceRoot() const {
return m_resourceRoot;
}
void ResourceManager::AddRef(ResourceGUID guid) {
std::lock_guard lock(m_mutex);
auto it = m_refCounts.Find(guid);
if (it == m_refCounts.End()) {
m_refCounts.Insert(guid, 1);
} else {
it->second++;
}
if (!m_resourceCache.Contains(guid)) {
ReloadResource(guid);
}
}
void ResourceManager::Release(ResourceGUID guid) {
std::lock_guard lock(m_mutex);
auto it = m_refCounts.Find(guid);
if (it != m_refCounts.End()) {
it->second--;
if (it->second == 0) {
m_refCounts.Erase(guid);
m_cache.OnZeroRefCount(guid);
}
}
}
Core::uint32 ResourceManager::GetRefCount(ResourceGUID guid) const {
std::lock_guard lock(m_mutex);
auto it = m_refCounts.Find(guid);
return it != m_refCounts.End() ? it->second : 0;
}
void ResourceManager::RegisterLoader(IResourceLoader* loader) {
std::lock_guard lock(m_mutex);
m_loaders.Insert(loader->GetResourceType(), UniquePtr<IResourceLoader>(loader));
}
IResourceLoader* ResourceManager::GetLoader(ResourceType type) const {
std::lock_guard lock(m_mutex);
auto it = m_loaders.Find(type);
return it != m_loaders.End() ? it->second.Get() : nullptr;
}
IResourceLoader* ResourceManager::FindLoader(ResourceType type) {
return GetLoader(type);
}
IResource* ResourceManager::FindInCache(ResourceGUID guid) {
std::lock_guard lock(m_mutex);
auto it = m_resourceCache.Find(guid);
return it != m_resourceCache.End() ? it->second : nullptr;
}
void ResourceManager::AddToCache(ResourceGUID guid, IResource* resource) {
std::lock_guard lock(m_mutex);
m_resourceCache.Insert(guid, resource);
m_memoryUsage += resource->GetMemorySize();
m_cache.Add(guid, resource);
if (m_memoryUsage > m_memoryBudget) {
m_cache.OnMemoryPressure(m_memoryUsage - m_memoryBudget);
}
}
void ResourceManager::Unload(ResourceGUID guid) {
std::lock_guard lock(m_mutex);
auto it = m_resourceCache.Find(guid);
if (it != m_resourceCache.End()) {
IResource* resource = it->second;
m_resourceCache.Erase(guid);
m_memoryUsage -= resource->GetMemorySize();
resource->Release();
}
}
void ResourceManager::UnloadAll() {
std::lock_guard lock(m_mutex);
for (auto& pair : m_resourceCache) {
pair.second->Release();
}
m_resourceCache.Clear();
m_refCounts.Clear();
m_memoryUsage = 0;
}
void ResourceManager::SetMemoryBudget(size_t bytes) {
m_memoryBudget = bytes;
}
size_t ResourceManager::GetMemoryUsage() const {
return m_memoryUsage;
}
size_t ResourceManager::GetMemoryBudget() const {
return m_memoryBudget;
}
void ResourceManager::FlushCache() {
m_cache.Flush();
}
IResource* ResourceManager::Find(const Containers::String& path) {
ResourceGUID guid = MakeResourceGUID(path.CStr());
return Find(guid);
}
IResource* ResourceManager::Find(ResourceGUID guid) {
return FindInCache(guid);
}
bool ResourceManager::Exists(const Containers::String& path) const {
return Find(MakeResourceGUID(path.CStr())) != nullptr;
}
bool ResourceManager::Exists(ResourceGUID guid) const {
return false; // 需要实现
}
Containers::String ResourceManager::ResolvePath(const Containers::String& relativePath) const {
return m_resourceRoot + "/" + relativePath;
}
void ResourceManager::LoadAsync(const Containers::String& path, ResourceType type,
std::function<void(LoadResult)> callback) {
LoadAsync(path, type, nullptr, callback);
}
void ResourceManager::LoadAsync(const Containers::String& path, ResourceType type,
ImportSettings* settings,
std::function<void(LoadResult>)> callback) {
m_asyncLoader->Submit(path, type, settings, callback);
}
void ResourceManager::Unload(const Containers::String& path) {
Unload(MakeResourceGUID(path.CStr()));
}
void ResourceManager::UnloadUnused() {
// 实现清理未使用资源
}
void ResourceManager::UnregisterLoader(ResourceType type) {
m_loaders.Erase(type);
}
void ResourceManager::ReloadResource(ResourceGUID guid) {
// 实现重新加载逻辑
}
} // namespace Resources
} // namespace XCEngine
```
### 11.3 第二阶段缓存系统天4-5
#### 步骤 11.3.1 实现 ResourceCache.h
**文件**: `engine/include/XCEngine/Resources/ResourceCache.h`
```cpp
#pragma once
#include "ResourceTypes.h"
#include "../Containers/HashMap.h"
#include "../Containers/Array.h"
#include "../Threading/Mutex.h"
#include <algorithm>
namespace XCEngine {
namespace Resources {
struct CacheEntry {
IResource* resource;
ResourceGUID guid;
size_t memorySize;
Core::uint64 lastAccessTime;
Core::uint32 accessCount;
CacheEntry() : resource(nullptr), memorySize(0), lastAccessTime(0), accessCount(0) {}
CacheEntry(IResource* res, size_t size)
: resource(res), guid(res->GetGUID()), memorySize(size),
lastAccessTime(GetCurrentTick()), accessCount(1) {}
private:
static Core::uint64 GetCurrentTick();
};
class ResourceCache {
public:
ResourceCache();
~ResourceCache();
void Add(ResourceGUID guid, IResource* resource);
void Remove(ResourceGUID guid);
IResource* Find(ResourceGUID guid) const;
void Touch(ResourceGUID guid);
size_t GetSize() const { return m_cache.Size(); }
size_t GetMemoryUsage() const { return m_memoryUsage; }
void SetMemoryBudget(size_t bytes);
size_t GetMemoryBudget() const { return m_memoryBudget; }
void OnMemoryPressure(size_t requiredBytes);
void OnZeroRefCount(ResourceGUID guid);
void Flush();
void Clear();
Containers::Array<ResourceGUID> GetLRUList(size_t count) const;
private:
void Evict(size_t requiredBytes);
void UpdateMemoryStats();
Containers::HashMap<ResourceGUID, CacheEntry> m_cache;
Containers::Array<ResourceGUID> m_lruOrder;
size_t m_memoryUsage = 0;
size_t m_memoryBudget = 512 * 1024 * 1024;
Threading::Mutex m_mutex;
};
} // namespace Resources
} // namespace XCEngine
```
#### 步骤 11.3.2 实现 ResourceCache.cpp
**文件**: `engine/src/Resources/ResourceCache.cpp`
```cpp
#include "Resources/ResourceCache.h"
namespace XCEngine {
namespace Resources {
// 简单的时间戳实现实际可使用Profiler::Get().GetTickCount()
static Core::uint64 g_currentTick = 0;
Core::uint64 CacheEntry::GetCurrentTick() {
return ++g_currentTick;
}
ResourceCache::ResourceCache() = default;
ResourceCache::~ResourceCache() = default;
void ResourceCache::Add(ResourceGUID guid, IResource* resource) {
std::lock_guard lock(m_mutex);
if (m_cache.Contains(guid)) {
return; // 已存在
}
CacheEntry entry(resource, resource->GetMemorySize());
m_cache.Insert(guid, entry);
m_lruOrder.Add(guid);
m_memoryUsage += entry.memorySize;
}
void ResourceCache::Remove(ResourceGUID guid) {
std::lock_guard lock(m_mutex);
auto it = m_cache.Find(guid);
if (it != m_cache.End()) {
m_memoryUsage -= it->second.memorySize;
m_cache.Erase(guid);
for (auto lit = m_lruOrder.Begin(); lit != m_lruOrder.End(); ++lit) {
if (*lit == guid) {
m_lruOrder.Erase(lit);
break;
}
}
}
}
IResource* ResourceCache::Find(ResourceGUID guid) const {
std::lock_guard lock(m_mutex);
auto it = m_cache.Find(guid);
return it != m_cache.End() ? it->second.resource : nullptr;
}
void ResourceCache::Touch(ResourceGUID guid) {
std::lock_guard lock(m_mutex);
auto it = m_cache.Find(guid);
if (it != m_cache.End()) {
it->second.lastAccessTime = CacheEntry::GetCurrentTick();
it->second.accessCount++;
}
}
void ResourceCache::SetMemoryBudget(size_t bytes) {
std::lock_guard lock(m_mutex);
m_memoryBudget = bytes;
}
void ResourceCache::OnMemoryPressure(size_t requiredBytes) {
std::lock_guard lock(m_mutex);
if (m_memoryUsage + requiredBytes <= m_memoryBudget) {
return;
}
size_t targetRelease = (m_memoryUsage + requiredBytes) - m_memoryBudget;
Evict(targetRelease);
}
void ResourceCache::OnZeroRefCount(ResourceGUID guid) {
// 引用计数为0时标记资源可以被淘汰
// 实际淘汰由LRU策略决定
}
void ResourceCache::Evict(size_t requiredBytes) {
size_t released = 0;
for (auto it = m_lruOrder.Begin(); it != m_lruOrder.End() && released < requiredBytes; ) {
ResourceGUID guid = *it;
auto cacheIt = m_cache.Find(guid);
if (cacheIt != m_cache.End()) {
CacheEntry& entry = cacheIt->second;
if (ResourceManager::Get().GetRefCount(guid) == 0) {
size_t entrySize = entry.memorySize;
m_cache.Erase(guid);
m_memoryUsage -= entrySize;
released += entrySize;
entry.resource->Release();
it = m_lruOrder.Erase(it);
} else {
++it;
}
} else {
++it;
}
}
}
void ResourceCache::Flush() {
std::lock_guard lock(m_mutex);
for (auto& pair : m_cache) {
if (ResourceManager::Get().GetRefCount(pair.first) == 0) {
pair.second.resource->Release();
}
}
m_cache.Clear();
m_lruOrder.Clear();
m_memoryUsage = 0;
}
void ResourceCache::Clear() {
std::lock_guard lock(m_mutex);
m_cache.Clear();
m_lruOrder.Clear();
m_memoryUsage = 0;
}
Containers::Array<ResourceGUID> ResourceCache::GetLRUList(size_t count) const {
std::lock_guard lock(m_mutex);
Containers::Array<ResourceGUID> result;
size_t n = std::min(count, static_cast<size_t>(m_lruOrder.Size()));
for (size_t i = 0; i < n; i++) {
result.Add(m_lruOrder[i]);
}
return result;
}
} // namespace Resources
} // namespace XCEngine
```
### 11.4 第三阶段异步加载天6-7
#### 步骤 11.4.1 实现 AsyncLoader.h
**文件**: `engine/include/XCEngine/Resources/AsyncLoader.h`
```cpp
#pragma once
#include "IResourceLoader.h"
#include "../Containers/Array.h"
#include "../Threading/Mutex.h"
#include <atomic>
#include <functional>
namespace XCEngine {
namespace Resources {
struct LoadRequest {
Containers::String path;
ResourceType type;
std::function<void(LoadResult)> callback;
UniquePtr<ImportSettings> settings;
Core::uint64 requestId;
LoadRequest() : requestId(0) {}
LoadRequest(const Containers::String& p, ResourceType t,
std::function<void(LoadResult)> cb, ImportSettings* s = nullptr)
: path(p), type(t), callback(std::move(cb)),
settings(s), requestId(GenerateRequestId()) {}
private:
static Core::uint64 GenerateRequestId();
};
class LoadTask : public Threading::ITask {
public:
LoadTask(LoadRequest request, IResourceLoader* loader);
void Execute() override;
LoadResult& GetResult() { return m_result; }
private:
LoadRequest m_request;
IResourceLoader* m_loader;
LoadResult m_result;
};
class AsyncLoader {
public:
static AsyncLoader& Get();
void Initialize(Core::uint32 workerThreadCount = 2);
void Shutdown();
void Submit(const Containers::String& path, ResourceType type,
std::function<void(LoadResult)> callback);
void Submit(const Containers::String& path, ResourceType type, ImportSettings* settings,
std::function<void(LoadResult)> callback);
void Update();
bool IsLoading() const { return m_pendingCount > 0; }
Core::uint32 GetPendingCount() const { return m_pendingCount; }
float GetProgress() const;
void CancelAll();
void Cancel(Core::uint64 requestId);
private:
AsyncLoader() = default;
~AsyncLoader() = default;
void SubmitInternal(LoadRequest& request);
IResourceLoader* FindLoader(ResourceType type) const;
Threading::Mutex m_queueMutex;
Containers::Array<LoadRequest> m_pendingQueue;
Threading::Mutex m_completedMutex;
Containers::Array<LoadRequest> m_completedQueue;
std::atomic<Core::uint32> m_pendingCount{0};
std::atomic<Core::uint32> m_completedCount{0};
Core::uint32 m_totalRequested = 0;
};
} // namespace Resources
} // namespace XCEngine
```
#### 步骤 11.4.2 实现 AsyncLoader.cpp
**文件**: `engine/src/Resources/AsyncLoader.cpp`
```cpp
#include "Resources/AsyncLoader.h"
#include "Resources/ResourceManager.h"
#include "../Threading/TaskSystem.h"
namespace XCEngine {
namespace Resources {
Core::uint64 LoadRequest::GenerateRequestId() {
static std::atomic<Core::uint64> s_requestId{0};
return ++s_requestId;
}
LoadTask::LoadTask(LoadRequest request, IResourceLoader* loader)
: m_request(std::move(request)), m_loader(loader) {}
void LoadTask::Execute() {
m_result = m_loader->Load(m_request.path, m_request.settings.Get());
// 放入完成队列
AsyncLoader::Get().QueueCompleted(std::move(m_request), std::move(m_result));
}
AsyncLoader& AsyncLoader::Get() {
static AsyncLoader instance;
return instance;
}
void AsyncLoader::Initialize(Core::uint32 workerThreadCount) {
// TaskSystem已初始化此处不需要额外操作
}
void AsyncLoader::Shutdown() {
CancelAll();
}
void AsyncLoader::Submit(const Containers::String& path, ResourceType type,
std::function<void(LoadResult)> callback) {
Submit(path, type, nullptr, callback);
}
void AsyncLoader::Submit(const Containers::String& path, ResourceType type,
ImportSettings* settings, std::function<void(LoadResult)> callback) {
LoadRequest request(path, type, callback, settings);
SubmitInternal(request);
}
void AsyncLoader::SubmitInternal(LoadRequest& request) {
IResourceLoader* loader = FindLoader(request.type);
if (!loader) {
if (request.callback) {
LoadResult result(Containers::String("No loader for type: ") +
GetResourceTypeName(request.type));
request.callback(result);
}
return;
}
{
std::lock_guard lock(m_queueMutex);
m_pendingQueue.Add(request);
m_pendingCount++;
m_totalRequested++;
}
// 提交到任务系统
auto task = new LoadTask(request, loader);
Threading::TaskSystem::Get().Submit(UniquePtr<ITask>(task));
}
void AsyncLoader::Update() {
Containers::Array<LoadRequest> completed;
{
std::lock_guard lock(m_completedMutex);
completed = std::move(m_completedQueue);
m_completedQueue.Clear();
}
for (auto& request : completed) {
m_pendingCount--;
if (request.callback) {
request.callback(m_completedResults[request.requestId]);
}
}
m_completedResults.Clear();
}
float AsyncLoader::GetProgress() const {
if (m_totalRequested == 0) return 1.0f;
return static_cast<float>(m_totalRequested - m_pendingCount) / m_totalRequested;
}
void AsyncLoader::CancelAll() {
std::lock_guard lock(m_queueMutex);
m_pendingQueue.Clear();
m_pendingCount = 0;
}
void AsyncLoader::Cancel(Core::uint64 requestId) {
std::lock_guard lock(m_queueMutex);
for (auto it = m_pendingQueue.Begin(); it != m_pendingQueue.End(); ++it) {
if (it->requestId == requestId) {
m_pendingQueue.Erase(it);
m_pendingCount--;
return;
}
}
}
IResourceLoader* AsyncLoader::FindLoader(ResourceType type) const {
return ResourceManager::Get().GetLoader(type);
}
} // namespace Resources
} // namespace XCEngine
```
### 11.5 第四阶段具体资源类型天8-11
#### 步骤 11.5.1 实现 Texture 及其加载器
**文件**: `engine/include/XCEngine/Resources/Texture.h`
```cpp
#pragma once
#include "IResource.h"
#include "../Core/Types.h"
namespace XCEngine {
namespace Resources {
enum class TextureType {
Texture2D, Texture3D, TextureCube, Texture2DArray, TextureCubeArray
};
enum class TextureFormat {
Unknown, R8_UNORM, RG8_UNORM, RGBA8_UNORM, RGBA8_SRGB,
R16_FLOAT, RG16_FLOAT, RGBA16_FLOAT,
R32_FLOAT, RG32_FLOAT, RGBA32_FLOAT,
D16_UNORM, D24_UNORM_S8_UINT, D32_FLOAT, D32_FLOAT_S8_X24_UINT,
BC1_UNORM, BC1_UNORM_SRGB, BC2_UNORM, BC2_UNORM_SRGB,
BC3_UNORM, BC3_UNORM_SRGB, BC4_UNORM, BC5_UNORM, BC6H_UF16,
BC7_UNORM, BC7_UNORM_SRGB
};
enum class TextureUsage : Core::uint8 {
None = 0, ShaderResource = 1 << 0, RenderTarget = 1 << 1,
DepthStencil = 1 << 2, UnorderedAccess = 1 << 3,
TransferSrc = 1 << 4, TransferDst = 1 << 5
};
class Texture : public IResource {
public:
Texture();
virtual ~Texture() override;
ResourceType GetType() const override { return ResourceType::Texture; }
const Containers::String& GetName() const override { return m_name; }
const Containers::String& GetPath() const override { return m_path; }
ResourceGUID GetGUID() const override { return m_guid; }
bool IsValid() const override { return m_isValid; }
size_t GetMemorySize() const override { return m_memorySize; }
void Release() override;
Core::uint32 GetWidth() const { return m_width; }
Core::uint32 GetHeight() const { return m_height; }
Core::uint32 GetDepth() const { return m_depth; }
Core::uint32 GetMipLevels() const { return m_mipLevels; }
Core::uint32 GetArraySize() const { return m_arraySize; }
TextureType GetTextureType() const { return m_textureType; }
TextureFormat GetFormat() const { return m_format; }
TextureUsage GetUsage() const { return m_usage; }
class IRHIResource* GetRHIResource() const { return m_rhiResource; }
void SetRHIResource(class IRHIResource* resource);
bool Create(Core::uint32 width, Core::uint32 height, Core::uint32 depth,
Core::uint32 mipLevels, TextureType type, TextureFormat format,
const void* data, size_t dataSize);
bool GenerateMipmaps();
private:
Core::uint32 m_width = 0;
Core::uint32 m_height = 0;
Core::uint32 m_depth = 1;
Core::uint32 m_mipLevels = 1;
Core::uint32 m_arraySize = 1;
TextureType m_textureType = TextureType::Texture2D;
TextureFormat m_format = TextureFormat::RGBA8_UNORM;
TextureUsage m_usage = TextureUsage::ShaderResource;
class IRHIResource* m_rhiResource = nullptr;
};
} // namespace Resources
} // namespace XCEngine
```
**实现要点**:
1. 使用stb_image库加载PNG/JPG/TGA
2. 支持生成Mipmap
3. 完成后创建RHI资源
#### 步骤 11.5.2 实现 Mesh 及其加载器
**文件**: `engine/include/XCEngine/Resources/Mesh.h`
```cpp
#pragma once
#include "IResource.h"
#include "../Containers/Array.h"
#include "../Math/AABB.h"
namespace XCEngine {
namespace Resources {
enum class VertexAttribute : Core::uint32 {
Position = 1 << 0, Normal = 1 << 1, Tangent = 1 << 2,
Color = 1 << 3, UV0 = 1 << 4, UV1 = 1 << 5,
UV2 = 1 << 6, UV3 = 1 << 7, BoneWeights = 1 << 8, BoneIndices = 1 << 9
};
struct MeshSection {
Core::uint32 baseVertex;
Core::uint32 vertexCount;
Core::uint32 startIndex;
Core::uint32 indexCount;
Core::uint32 materialID;
Math::AABB boundingBox;
};
class Mesh : public IResource {
public:
Mesh();
virtual ~Mesh() override;
ResourceType GetType() const override { return ResourceType::Mesh; }
const Containers::String& GetName() const override { return m_name; }
const Containers::String& GetPath() const override { return m_path; }
ResourceGUID GetGUID() const override { return m_guid; }
bool IsValid() const override { return m_isValid; }
size_t GetMemorySize() const override { return m_memorySize; }
void Release() override;
void SetVertexData(const void* data, size_t size, Core::uint32 vertexCount,
Core::uint32 vertexStride, VertexAttribute attributes);
const void* GetVertexData() const { return m_vertexData.Data(); }
size_t GetVertexDataSize() const { return m_vertexData.Size(); }
Core::uint32 GetVertexCount() const { return m_vertexCount; }
Core::uint32 GetVertexStride() const { return m_vertexStride; }
VertexAttribute GetVertexAttributes() const { return m_attributes; }
void SetIndexData(const void* data, size_t size, Core::uint32 indexCount, bool use32Bit);
const void* GetIndexData() const { return m_indexData.Data(); }
size_t GetIndexDataSize() const { return m_indexData.Size(); }
Core::uint32 GetIndexCount() const { return m_indexCount; }
bool IsUse32BitIndex() const { return m_use32BitIndex; }
void AddSection(const MeshSection& section);
const Containers::Array<MeshSection>& GetSections() const { return m_sections; }
void SetBoundingBox(const Math::AABB& box);
const Math::AABB& GetBoundingBox() const { return m_boundingBox; }
class IRHIBuffer* GetVertexBuffer() const { return m_vertexBuffer; }
class IRHIBuffer* GetIndexBuffer() const { return m_indexBuffer; }
void SetRHIBuffers(class IRHIBuffer* vb, class IRHIBuffer* ib);
private:
Containers::Array<Core::uint8> m_vertexData;
Core::uint32 m_vertexCount = 0;
Core::uint32 m_vertexStride = 0;
VertexAttribute m_attributes = VertexAttribute::Position;
Containers::Array<Core::uint8> m_indexData;
Core::uint32 m_indexCount = 0;
bool m_use32BitIndex = false;
Containers::Array<MeshSection> m_sections;
Math::AABB m_boundingBox;
class IRHIBuffer* m_vertexBuffer = nullptr;
class IRHIBuffer* m_indexBuffer = nullptr;
};
} // namespace Resources
} // namespace XCEngine
```
#### 步骤 11.5.3 实现 Material 及其加载器
**文件**: `engine/include/XCEngine/Resources/Material.h`
```cpp
#pragma once
#include "IResource.h"
#include "ResourceHandle.h"
#include "Texture.h"
#include "../Containers/HashMap.h"
#include "../Math/Vector2.h"
#include "../Math/Vector3.h"
#include "../Math/Vector4.h"
namespace XCEngine {
namespace Resources {
enum class MaterialPropertyType {
Float, Float2, Float3, Float4,
Int, Int2, Int3, Int4,
Bool, Texture, Cubemap
};
struct MaterialProperty {
Containers::String name;
MaterialPropertyType type;
union Value {
float floatValue[4];
Core::int32 intValue[4];
bool boolValue;
ResourceHandle<Texture> textureValue;
Value() { memset(this, 0, sizeof(Value)); }
} value;
Core::uint32 refCount;
};
class Material : public IResource {
public:
Material();
virtual ~Material() override;
ResourceType GetType() const override { return ResourceType::Material; }
const Containers::String& GetName() const override { return m_name; }
const Containers::String& GetPath() const override { return m_path; }
ResourceGUID GetGUID() const override { return m_guid; }
bool IsValid() const override { return m_isValid; }
size_t GetMemorySize() const override { return m_memorySize; }
void Release() override;
void SetShader(const ResourceHandle<class Shader>& shader);
class Shader* GetShader() const { return m_shader.Get(); }
void SetFloat(const Containers::String& name, float value);
void SetFloat2(const Containers::String& name, const Math::Vector2& value);
void SetFloat3(const Containers::String& name, const Math::Vector3& value);
void SetFloat4(const Containers::String& name, const Math::Vector4& value);
void SetInt(const Containers::String& name, int32 value);
void SetBool(const Containers::String& name, bool value);
void SetTexture(const Containers::String& name, const ResourceHandle<Texture>& texture);
float GetFloat(const Containers::String& name) const;
ResourceHandle<Texture> GetTexture(const Containers::String& name) const;
const Containers::Array<Core::uint8>& GetConstantBufferData() const { return m_constantBufferData; }
void UpdateConstantBuffer();
private:
ResourceHandle<class Shader> m_shader;
Containers::HashMap<Containers::String, MaterialProperty> m_properties;
Containers::Array<Core::uint8> m_constantBufferData;
struct TextureBinding {
Containers::String name;
Core::uint32 slot;
ResourceHandle<Texture> texture;
};
Containers::Array<TextureBinding> m_textureBindings;
};
} // namespace Resources
} // namespace XCEngine
```
#### 步骤 11.5.4 实现 AudioClip 及其加载器
类似实现支持WAV格式加载。
### 11.6 第五阶段文件系统集成天12-13
#### 步骤 11.6.1 实现 ResourceFileSystem
**文件**: `engine/include/XCEngine/Resources/ResourceFileSystem.h`
```cpp
#pragma once
#include "ResourceTypes.h"
#include "../Containers/String.h"
#include "../Containers/Array.h"
#include "../Containers/HashMap.h"
namespace XCEngine {
namespace Resources {
class ResourceFileSystem {
public:
ResourceFileSystem();
~ResourceFileSystem();
void Initialize(const Containers::String& rootPath);
void Shutdown();
bool AddArchive(const Containers::String& archivePath);
bool AddDirectory(const Containers::String& directoryPath);
void RemoveArchive(const Containers::String& archivePath);
bool FindResource(const Containers::String& relativePath,
Containers::String& outAbsolutePath) const;
Containers::Array<Core::uint8> ReadResource(const Containers::String& relativePath) const;
bool Exists(const Containers::String& relativePath) const;
struct ResourceInfo {
Containers::String path;
size_t size;
Core::uint64 modifiedTime;
bool inArchive;
Containers::String archivePath;
};
bool GetResourceInfo(const Containers::String& relativePath, ResourceInfo& outInfo) const;
void EnumerateResources(const Containers::String& pattern,
Containers::Array<ResourceInfo>& outResources) const;
private:
class IArchive {
public:
virtual ~IArchive() = default;
virtual bool Open(const Containers::String& path) = 0;
virtual void Close() = 0;
virtual bool Read(const Containers::String& fileName, void* buffer,
size_t size, size_t offset) const = 0;
virtual size_t GetSize(const Containers::String& fileName) const = 0;
virtual bool Exists(const Containers::String& fileName) const = 0;
virtual void Enumerate(const Containers::String& pattern,
Containers::Array<Containers::String>& outFiles) const = 0;
};
Containers::String m_rootPath;
Containers::Array<UniquePtr<IArchive>> m_archives;
Containers::Array<Containers::String> m_directories;
mutable Containers::HashMap<Containers::String, ResourceInfo> m_infoCache;
};
} // namespace Resources
} // namespace XCEngine
```
### 11.7 第六阶段依赖管理天14-15
#### 步骤 11.7.1 实现 ResourceDependencyGraph
```cpp
// 见文档Section 9
```
### 11.8 第七阶段测试与集成天16-17
#### 步骤 11.8.1 单元测试
创建测试用例:
1. 资源加载/卸载
2. 引用计数
3. 缓存淘汰
4. 异步加载
5. 多线程安全
#### 步骤 11.8.2 集成到现有mvs项目
在示例应用中使用资源系统加载纹理和模型。
### 11.9 每日任务清单
| 天数 | 任务 | 完成标准 |
|------|------|----------|
| 1 | 创建目录结构 + Resources.h + ResourceTypes.h | 编译通过 |
| 2 | IResource.h + ResourceHandle.h | 智能指针正常工作 |
| 3 | IResourceLoader.h + ResourceManager.h框架 | 加载器接口完成 |
| 4 | ResourceCache.h/cpp | LRU淘汰正常 |
| 5 | ResourceManager与Cache集成 | 引用计数正常 |
| 6 | AsyncLoader.h/cpp框架 | 任务提交正常 |
| 7 | 异步回调机制 | 主线程回调正常 |
| 8 | Texture.h + 基础加载 | PNG加载成功 |
| 9 | Texture RHI资源创建 | 渲染器能使用 |
| 10 | Mesh.h + 加载器 | OBJ加载成功 |
| 11 | Material.h + 加载器 | JSON材质加载 |
| 12 | ResourceFileSystem | 目录/归档支持 |
| 13 | 资源打包Builder | 打包成功 |
| 14 | DependencyGraph | 依赖关系正确 |
| 15 | 资源组加载 | 批量加载正常 |
| 16 | 单元测试 | 测试通过 |
| 17 | 集成测试 | 示例运行正常 |
---
## 12. 测试设计
> 本节详细说明资源系统的测试设计确保每个阶段完成后都能立即进行测试验收。测试采用Google Test框架与现有项目保持一致。
### 12.1 测试目录结构
```
tests/
├── Resources/ # 新增资源系统测试
│ ├── CMakeLists.txt # 构建配置
│ ├── fixtures/
│ │ └── ResourcesTestFixture.h # 测试夹具基类
│ ├── test_resource_types.cpp # 资源类型测试
│ ├── test_resource_guid.cpp # GUID生成测试
│ ├── test_iresource.cpp # 资源接口测试
│ ├── test_resource_handle.cpp # 资源句柄测试
│ ├── test_resource_cache.cpp # 缓存测试
│ ├── test_async_loader.cpp # 异步加载测试
│ ├── test_texture.cpp # 纹理资源测试
│ ├── test_mesh.cpp # 网格资源测试
│ ├── test_material.cpp # 材质资源测试
│ ├── test_audio_clip.cpp # 音频资源测试
│ ├── test_resource_manager.cpp # 资源管理器测试
│ ├── test_resource_filesystem.cpp # 文件系统测试
│ ├── test_resource_dependency.cpp # 依赖管理测试
│ └── test_data/
│ ├── test_texture.png # 测试用纹理
│ ├── test_mesh.obj # 测试用模型
│ └── test_material.json # 测试用材质
```
### 12.2 测试夹具基类
**文件**: `tests/Resources/fixtures/ResourcesTestFixture.h`
```cpp
#pragma once
#include <gtest/gtest.h>
#include <XCEngine/Resources/ResourceManager.h>
#include <XCEngine/Resources/AsyncLoader.h>
#include <XCEngine/Containers/String.h>
namespace XCEngine {
namespace Resources {
namespace Test {
class ResourcesTestFixture : public ::testing::Test {
protected:
static void SetUpTestSuite() {
// 初始化资源系统
ResourceManager::Get().Initialize();
ResourceManager::Get().SetResourceRoot("tests/Resources/test_data/");
AsyncLoader::Get().Initialize(2);
}
static void TearDownTestSuite() {
ResourceManager::Get().UnloadAll();
AsyncLoader::Get().Shutdown();
ResourceManager::Get().Shutdown();
}
void SetUp() override {
// 每个测试前清空缓存
ResourceManager::Get().UnloadAll();
ResourceManager::Get().FlushCache();
}
void TearDown() override {
// 每个测试后确保清空资源
ResourceManager::Get().UnloadAll();
}
// 辅助方法:获取测试数据目录
Containers::String GetTestDataPath(const Containers::String& filename) {
return Containers::String("tests/Resources/test_data/") + filename;
}
};
} // namespace Test
} // namespace Resources
} // namespace XCEngine
```
### 12.3 第一阶段测试基础框架天1-3完成后
#### 测试 12.3.1 资源类型枚举测试
**文件**: `tests/Resources/test_resource_types.cpp`
```cpp
#include <gtest/gtest.h>
#include <XCEngine/Resources/ResourceTypes.h>
using namespace XCEngine::Resources;
namespace {
TEST(Resources_Types, ResourceType_EnumValues) {
EXPECT_EQ(static_cast<Core::uint8>(ResourceType::Unknown), 0);
EXPECT_EQ(static_cast<Core::uint8>(ResourceType::Texture), 1);
EXPECT_EQ(static_cast<Core::uint8>(ResourceType::Mesh), 2);
EXPECT_EQ(static_cast<Core::uint8>(ResourceType::Material), 3);
EXPECT_EQ(static_cast<Core::uint8>(ResourceType::Shader), 4);
EXPECT_EQ(static_cast<Core::uint8>(ResourceType::AudioClip), 5);
EXPECT_EQ(static_cast<Core::uint8>(ResourceType::Binary), 6);
}
TEST(Resources_Types, GetResourceTypeName) {
EXPECT_STREQ(GetResourceTypeName(ResourceType::Texture), "Texture");
EXPECT_STREQ(GetResourceTypeName(ResourceType::Mesh), "Mesh");
EXPECT_STREQ(GetResourceTypeName(ResourceType::Material), "Material");
EXPECT_STREQ(GetResourceTypeName(ResourceType::Unknown), "Unknown");
}
TEST(Resources_Types, GetResourceType_TemplateSpecializations) {
EXPECT_EQ(GetResourceType<Texture>(), ResourceType::Texture);
EXPECT_EQ(GetResourceType<Mesh>(), ResourceType::Mesh);
EXPECT_EQ(GetResourceType<Material>(), ResourceType::Material);
EXPECT_EQ(GetResourceType<Shader>(), ResourceType::Shader);
EXPECT_EQ(GetResourceType<AudioClip>(), ResourceType::AudioClip);
EXPECT_EQ(GetResourceType<BinaryResource>(), ResourceType::Binary);
}
} // namespace
```
#### 测试 12.3.2 ResourceGUID测试
**文件**: `tests/Resources/test_resource_guid.cpp`
```cpp
#include <gtest/gtest.h>
#include <XCEngine/Resources/ResourceTypes.h>
using namespace XCEngine::Resources;
namespace {
TEST(Resources_GUID, DefaultConstructor) {
ResourceGUID guid;
EXPECT_FALSE(guid.IsValid());
EXPECT_EQ(guid.value, 0);
}
TEST(Resources_GUID, ValueConstructor) {
ResourceGUID guid(12345);
EXPECT_TRUE(guid.IsValid());
EXPECT_EQ(guid.value, 12345);
}
TEST(Resources_GUID, Generate_FromCString) {
ResourceGUID guid1 = ResourceGUID::Generate("textures/player.png");
ResourceGUID guid2 = ResourceGUID::Generate("textures/player.png");
// 相同路径应生成相同GUID
EXPECT_EQ(guid1, guid2);
EXPECT_TRUE(guid1.IsValid());
}
TEST(Resources_GUID, Generate_FromString) {
Containers::String path = "models/player.fbx";
ResourceGUID guid = ResourceGUID::Generate(path);
EXPECT_TRUE(guid.IsValid());
}
TEST(Resources_GUID, Generate_DifferentPaths) {
ResourceGUID guid1 = ResourceGUID::Generate("textures/a.png");
ResourceGUID guid2 = ResourceGUID::Generate("textures/b.png");
// 不同路径应生成不同GUID
EXPECT_NE(guid1, guid2);
}
TEST(Resources_GUID, ToString) {
ResourceGUID guid(0x1234567890ABCDEF);
Containers::String str = guid.ToString();
EXPECT_EQ(str.Size(), 16u);
EXPECT_STREQ(str.CStr(), "1234567890abcdef");
}
TEST(Resources_GUID, MakeResourceGUID_Helper) {
ResourceGUID guid = MakeResourceGUID("test/path");
EXPECT_TRUE(guid.IsValid());
}
TEST(Resources_GUID, EqualityOperators) {
ResourceGUID guid1(100);
ResourceGUID guid2(100);
ResourceGUID guid3(200);
EXPECT_TRUE(guid1 == guid2);
EXPECT_TRUE(guid1 != guid3);
}
} // namespace
```
#### 测试 12.3.3 资源句柄测试
**文件**: `tests/Resources/test_resource_handle.cpp`
```cpp
#include <gtest/gtest.h>
#include <XCEngine/Resources/ResourceHandle.h>
#include <XCEngine/Resources/ResourceManager.h>
using namespace XCEngine::Resources;
// 测试用伪资源
class TestResource : public IResource {
public:
TestResource(const Containers::String& name) {
ConstructParams params;
params.name = name;
params.path = name;
params.guid = ResourceGUID::Generate(name.CStr());
params.memorySize = 1024;
Initialize(params);
}
ResourceType GetType() const override { return ResourceType::Binary; }
const Containers::String& GetName() const override { return m_name; }
const Containers::String& GetPath() const override { return m_path; }
ResourceGUID GetGUID() const override { return m_guid; }
bool IsValid() const override { return m_isValid; }
size_t GetMemorySize() const override { return m_memorySize; }
void Release() override { delete this; }
};
namespace {
TEST(Resources_Handle, DefaultConstructor) {
ResourceHandle<TestResource> handle;
EXPECT_FALSE(handle.IsValid());
EXPECT_EQ(handle.Get(), nullptr);
}
TEST(Resources_Handle, ConstructorWithValidResource) {
TestResource* resource = new TestResource("test_resource");
ResourceHandle<TestResource> handle(resource);
EXPECT_TRUE(handle.IsValid());
EXPECT_EQ(handle.Get(), resource);
EXPECT_EQ(handle->GetName().CStr(), Containers::String("test_resource"));
}
TEST(Resources_Handle, CopyConstructor_AddsRef) {
TestResource* resource = new TestResource("test_resource");
ResourceHandle<TestResource> handle1(resource);
Core::uint32 refCountBefore = ResourceManager::Get().GetRefCount(resource->GetGUID());
ResourceHandle<TestResource> handle2(handle1);
Core::uint32 refCountAfter = ResourceManager::Get().GetRefCount(resource->GetGUID());
EXPECT_EQ(refCountAfter, refCountBefore + 1);
}
TEST(Resources_Handle, MoveConstructor_DoesNotAddRef) {
TestResource* resource = new TestResource("test_resource");
ResourceHandle<TestResource> handle1(resource);
Core::uint32 refCountBefore = ResourceManager::Get().GetRefCount(resource->GetGUID());
ResourceHandle<TestResource> handle2(std::move(handle1));
// 移动语义不应增加引用计数
EXPECT_EQ(handle1.Get(), nullptr);
EXPECT_EQ(handle2.Get(), resource);
}
TEST(Resources_Handle, Destructor_ReleasesRef) {
TestResource* resource = new TestResource("test_resource");
{
ResourceHandle<TestResource> handle(resource);
EXPECT_TRUE(handle.IsValid());
}
// handle销毁后应释放引用
// 注意此时资源应该被卸载因为引用计数为0
}
TEST(Resources_Handle, Reset) {
TestResource* resource = new TestResource("test_resource");
ResourceHandle<TestResource> handle(resource);
EXPECT_TRUE(handle.IsValid());
handle.Reset();
EXPECT_FALSE(handle.IsValid());
EXPECT_EQ(handle.Get(), nullptr);
}
TEST(Resources_Handle, ArrowOperator) {
TestResource* resource = new TestResource("test_resource");
ResourceHandle<TestResource> handle(resource);
EXPECT_STREQ(handle->GetName().CStr(), "test_resource");
EXPECT_EQ(handle->GetGUID(), resource->GetGUID());
}
TEST(Resources_Handle, BoolOperator) {
ResourceHandle<TestResource> emptyHandle;
TestResource* resource = new TestResource("test");
ResourceHandle<TestResource> validHandle(resource);
EXPECT_FALSE(emptyHandle);
EXPECT_TRUE(validHandle);
}
TEST(Resources_Handle, GetGUID) {
TestResource* resource = new TestResource("test_resource");
ResourceHandle<TestResource> handle(resource);
EXPECT_EQ(handle.GetGUID(), resource->GetGUID());
EXPECT_TRUE(handle.GetGUID().IsValid());
}
TEST(Resources_Handle, GetResourceType) {
TestResource* resource = new TestResource("test_resource");
ResourceHandle<TestResource> handle(resource);
EXPECT_EQ(handle.GetResourceType(), ResourceType::Binary);
}
TEST(Resources_Handle, EqualityOperators) {
TestResource* resource = new TestResource("test");
ResourceHandle<TestResource> handle1(resource);
ResourceHandle<TestResource> handle2(resource);
EXPECT_TRUE(handle1 == handle2);
TestResource* resource2 = new TestResource("test2");
ResourceHandle<TestResource> handle3(resource2);
EXPECT_TRUE(handle1 != handle3);
}
} // namespace
```
### 12.4 第二阶段测试缓存系统天4-5完成后
#### 测试 12.4.1 资源缓存测试
**文件**: `tests/Resources/test_resource_cache.cpp`
```cpp
#include <gtest/gtest.h>
#include <XCEngine/Resources/ResourceCache.h>
#include "fixtures/ResourcesTestFixture.h"
using namespace XCEngine::Resources;
namespace {
class ResourceCache_Test : public ResourcesTestFixture {
protected:
void SetUp() override {
ResourcesTestFixture::SetUp();
m_cache = new ResourceCache();
}
void TearDown() override {
delete m_cache;
ResourcesTestFixture::TearDown();
}
ResourceCache* m_cache;
};
TEST_F(ResourceCache_Test, AddResource) {
TestResource* resource = new TestResource("test1");
ResourceGUID guid = resource->GetGUID();
m_cache->Add(guid, resource);
EXPECT_EQ(m_cache->GetSize(), 1u);
EXPECT_EQ(m_cache->GetMemoryUsage(), resource->GetMemorySize());
}
TEST_F(ResourceCache_Test, FindResource) {
TestResource* resource = new TestResource("test1");
ResourceGUID guid = resource->GetGUID();
m_cache->Add(guid, resource);
IResource* found = m_cache->Find(guid);
EXPECT_EQ(found, resource);
}
TEST_F(ResourceCache_Test, FindNonExistent) {
ResourceGUID guid(9999);
IResource* found = m_cache->Find(guid);
EXPECT_EQ(found, nullptr);
}
TEST_F(ResourceCache_Test, RemoveResource) {
TestResource* resource = new TestResource("test1");
ResourceGUID guid = resource->GetGUID();
m_cache->Add(guid, resource);
m_cache->Remove(guid);
EXPECT_EQ(m_cache->GetSize(), 0u);
EXPECT_EQ(m_cache->Find(guid), nullptr);
}
TEST_F(ResourceCache_Test, Touch_UpdatesAccessTime) {
TestResource* resource = new TestResource("test1");
ResourceGUID guid = resource->GetGUID();
m_cache->Add(guid, resource);
m_cache->Touch(guid);
// Touch后应能找到资源
EXPECT_EQ(m_cache->Find(guid), resource);
}
TEST_F(ResourceCache_Test, MemoryBudget) {
m_cache->SetMemoryBudget(1024); // 1KB
EXPECT_EQ(m_cache->GetMemoryBudget(), 1024u);
}
TEST_F(ResourceCache_Test, OnMemoryPressure_EvictUnreferenced) {
m_cache->SetMemoryBudget(512);
// 添加多个资源
for (int i = 0; i < 5; i++) {
TestResource* resource = new TestResource(Containers::String("test") + Containers::String::FromInt(i));
m_cache->Add(resource->GetGUID(), resource);
}
// 触发内存压力
m_cache->OnMemoryPressure(1024);
// 应该有资源被淘汰
// 具体数量取决于LRU策略
}
TEST_F(ResourceCache_Test, Clear) {
for (int i = 0; i < 3; i++) {
TestResource* resource = new TestResource(Containers::String("test") + Containers::String::FromInt(i));
m_cache->Add(resource->GetGUID(), resource);
}
m_cache->Clear();
EXPECT_EQ(m_cache->GetSize(), 0u);
EXPECT_EQ(m_cache->GetMemoryUsage(), 0u);
}
TEST_F(ResourceCache_Test, GetLRUList) {
// 添加资源并访问
TestResource* r1 = new TestResource("test1");
TestResource* r2 = new TestResource("test2");
TestResource* r3 = new TestResource("test3");
m_cache->Add(r1->GetGUID(), r1);
m_cache->Add(r2->GetGUID(), r2);
m_cache->Add(r3->GetGUID(), r3);
// 访问r1使其最新
m_cache->Touch(r1->GetGUID());
auto lru = m_cache->GetLRUList(2);
// r1应该是最新的不应在LRU列表前几位
EXPECT_GE(lru.Size(), 0u);
}
} // namespace
```
### 12.5 第三阶段测试异步加载天6-7完成后
#### 测试 12.5.1 异步加载器测试
**文件**: `tests/Resources/test_async_loader.cpp`
```cpp
#include <gtest/gtest.h>
#include <XCEngine/Resources/AsyncLoader.h>
#include "fixtures/ResourcesTestFixture.h"
#include <atomic>
using namespace XCEngine::Resources;
namespace {
class AsyncLoader_Test : public ResourcesTestFixture {
protected:
void SetUp() override {
ResourcesTestFixture::SetUp();
}
};
TEST_F(AsyncLoader_Test, Submit_IncrementsPendingCount) {
AsyncLoader& loader = AsyncLoader::Get();
Core::uint32 pendingBefore = loader.GetPendingCount();
// 注意:需要先注册加载器,否则会立即失败
// 这里测试API本身
EXPECT_GE(pendingBefore, 0u);
}
TEST_F(AsyncLoader_Test, Update_ProcessesCompletedRequests) {
AsyncLoader& loader = AsyncLoader::Get();
// 调用Update处理完成队列
loader.Update();
// 不应崩溃
EXPECT_TRUE(true);
}
TEST_F(AsyncLoader_Test, IsLoading) {
AsyncLoader& loader = AsyncLoader::Get();
bool isLoading = loader.IsLoading();
EXPECT_FALSE(isLoading); // 初始状态
}
TEST_F(AsyncLoader_Test, GetProgress) {
AsyncLoader& loader = AsyncLoader::Get();
float progress = loader.GetProgress();
// 0.0 - 1.0之间
EXPECT_GE(progress, 0.0f);
EXPECT_LE(progress, 1.0f);
}
TEST_F(AsyncLoader_Test, CancelAll) {
AsyncLoader& loader = AsyncLoader::Get();
loader.CancelAll();
EXPECT_EQ(loader.GetPendingCount(), 0u);
}
TEST_F(AsyncLoader_Test, CallbackInvocation) {
std::atomic<int> callbackCount{0};
// 模拟回调
auto callback = [&callbackCount](LoadResult result) {
callbackCount++;
};
// 调用Update确保没有pending请求
AsyncLoader::Get().Update();
EXPECT_EQ(callbackCount, 0);
}
} // namespace
```
### 12.6 第四阶段测试具体资源类型天8-11完成后
#### 测试 12.6.1 纹理资源测试
**文件**: `tests/Resources/test_texture.cpp`
```cpp
#include <gtest/gtest.h>
#include <XCEngine/Resources/Texture.h>
#include <XCEngine/Resources/TextureLoader.h>
#include "fixtures/ResourcesTestFixture.h"
using namespace XCEngine::Resources;
namespace {
class Texture_Test : public ResourcesTestFixture {
protected:
void SetUp() override {
ResourcesTestFixture::SetUp();
}
};
TEST_F(Texture_Test, DefaultConstructor) {
Texture texture;
EXPECT_FALSE(texture.IsValid());
EXPECT_EQ(texture.GetType(), ResourceType::Texture);
}
TEST_F(Texture_Test, GetDimensions_Default) {
Texture texture;
EXPECT_EQ(texture.GetWidth(), 0u);
EXPECT_EQ(texture.GetHeight(), 0u);
EXPECT_EQ(texture.GetDepth(), 1u);
EXPECT_EQ(texture.GetMipLevels(), 1u);
}
TEST_F(Texture_Test, GetTextureType_Default) {
Texture texture;
EXPECT_EQ(texture.GetTextureType(), TextureType::Texture2D);
}
TEST_F(Texture_Test, GetFormat_Default) {
Texture texture;
EXPECT_EQ(texture.GetFormat(), TextureFormat::RGBA8_UNORM);
}
TEST_F(Texture_Test, Create) {
Texture texture;
bool result = texture.Create(
256, 256, 1, 1,
TextureType::Texture2D,
TextureFormat::RGBA8_UNORM,
nullptr, 0
);
EXPECT_TRUE(result);
EXPECT_EQ(texture.GetWidth(), 256u);
EXPECT_EQ(texture.GetHeight(), 256u);
EXPECT_TRUE(texture.IsValid());
}
TEST_F(Texture_Test, CreateWithMipmaps) {
Texture texture;
bool result = texture.Create(
256, 256, 1, 4,
TextureType::Texture2D,
TextureFormat::RGBA8_UNORM,
nullptr, 0
);
EXPECT_TRUE(result);
EXPECT_EQ(texture.GetMipLevels(), 4u);
}
TEST_F(Texture_Test, GetMemorySize) {
Texture texture;
// 256x256 RGBA8 = 256*256*4 = 262144 bytes
texture.Create(256, 256, 1, 1, TextureType::Texture2D,
TextureFormat::RGBA8_UNORM, nullptr, 0);
size_t memorySize = texture.GetMemorySize();
EXPECT_GT(memorySize, 0u);
}
TEST_F(Texture_Test, GetName) {
Texture texture;
ConstructParams params;
params.name = "TestTexture";
params.path = "textures/test.png";
params.guid = ResourceGUID::Generate("test");
params.memorySize = 1024;
texture.Initialize(params);
EXPECT_STREQ(texture.GetName().CStr(), "TestTexture");
}
TEST_F(Texture_Test, GetPath) {
Texture texture;
ConstructParams params;
params.name = "TestTexture";
params.path = "textures/test.png";
params.guid = ResourceGUID::Generate("test");
params.memorySize = 1024;
texture.Initialize(params);
EXPECT_STREQ(texture.GetPath().CStr(), "textures/test.png");
}
TEST_F(Texture_Test, TextureLoader_CanLoad) {
TextureLoader loader;
EXPECT_TRUE(loader.CanLoad("textures/test.png"));
EXPECT_TRUE(loader.CanLoad("textures/test.jpg"));
EXPECT_TRUE(loader.CanLoad("textures/test.tga"));
EXPECT_FALSE(loader.CanLoad("models/test.obj"));
}
TEST_F(Texture_Test, TextureLoader_GetSupportedExtensions) {
TextureLoader loader;
auto extensions = loader.GetSupportedExtensions();
EXPECT_GE(extensions.Size(), 5u);
}
TEST_F(Texture_Test, TextureLoader_GetResourceType) {
TextureLoader loader;
EXPECT_EQ(loader.GetResourceType(), ResourceType::Texture);
}
TEST_F(Texture_Test, TextureLoader_GetDefaultSettings) {
TextureLoader loader;
ImportSettings* settings = loader.GetDefaultSettings();
EXPECT_NE(settings, nullptr);
EXPECT_TRUE(dynamic_cast<TextureImportSettings*>(settings) != nullptr);
delete settings;
}
TEST_F(Texture_Test, TextureImportSettings_Defaults) {
TextureImportSettings settings;
EXPECT_EQ(settings.GetTextureType(), TextureType::Texture2D);
EXPECT_TRUE(settings.GetGenerateMipmaps());
EXPECT_EQ(settings.GetMaxAnisotropy(), 16u);
}
TEST_F(Texture_Test, TextureImportSettings_Modify) {
TextureImportSettings settings;
settings.SetTextureType(TextureType::TextureCube);
settings.SetGenerateMipmaps(false);
settings.SetMaxAnisotropy(8);
settings.SetSRGB(true);
EXPECT_EQ(settings.GetTextureType(), TextureType::TextureCube);
EXPECT_FALSE(settings.GetGenerateMipmaps());
EXPECT_EQ(settings.GetMaxAnisotropy(), 8u);
EXPECT_TRUE(settings.GetSRGB());
}
} // namespace
```
#### 测试 12.6.2 网格资源测试
**文件**: `tests/Resources/test_mesh.cpp`
```cpp
#include <gtest/gtest.h>
#include <XCEngine/Resources/Mesh.h>
#include "fixtures/ResourcesTestFixture.h"
using namespace XCEngine::Resources;
namespace {
class Mesh_Test : public ResourcesTestFixture {};
TEST_F(Mesh_Test, DefaultConstructor) {
Mesh mesh;
EXPECT_FALSE(mesh.IsValid());
EXPECT_EQ(mesh.GetType(), ResourceType::Mesh);
}
TEST_F(Mesh_Test, GetVertexCount_Default) {
Mesh mesh;
EXPECT_EQ(mesh.GetVertexCount(), 0u);
}
TEST_F(Mesh_Test, GetIndexCount_Default) {
Mesh mesh;
EXPECT_EQ(mesh.GetIndexCount(), 0u);
}
TEST_F(Mesh_Test, SetVertexData) {
Mesh mesh;
// 简单顶点数据:位置
float vertices[] = {
0.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f
};
mesh.SetVertexData(vertices, sizeof(vertices), 3, 12, VertexAttribute::Position);
EXPECT_EQ(mesh.GetVertexCount(), 3u);
EXPECT_EQ(mesh.GetVertexStride(), 12u);
EXPECT_TRUE(mesh.GetVertexAttributes() & VertexAttribute::Position);
}
TEST_F(Mesh_Test, SetIndexData) {
Mesh mesh;
// 三角形索引
uint16_t indices[] = {0, 1, 2};
mesh.SetIndexData(indices, sizeof(indices), 3, false);
EXPECT_EQ(mesh.GetIndexCount(), 3u);
EXPECT_FALSE(mesh.IsUse32BitIndex());
}
TEST_F(Mesh_Test, SetIndexData_32Bit) {
Mesh mesh;
uint32_t indices[] = {0, 1, 2, 0, 2, 3};
mesh.SetIndexData(indices, sizeof(indices), 6, true);
EXPECT_EQ(mesh.GetIndexCount(), 6u);
EXPECT_TRUE(mesh.IsUse32BitIndex());
}
TEST_F(Mesh_Test, AddSection) {
Mesh mesh;
MeshSection section;
section.baseVertex = 0;
section.vertexCount = 3;
section.startIndex = 0;
section.indexCount = 3;
section.materialID = 0;
mesh.AddSection(section);
EXPECT_EQ(mesh.GetSections().Size(), 1u);
}
TEST_F(Mesh_Test, BoundingBox) {
Mesh mesh;
Math::AABB box;
box.Min = Math::Vector3(0, 0, 0);
box.Max = Math::Vector3(1, 1, 1);
mesh.SetBoundingBox(box);
EXPECT_EQ(mesh.GetBoundingBox().Min, Math::Vector3(0, 0, 0));
EXPECT_EQ(mesh.GetBoundingBox().Max, Math::Vector3(1, 1, 1));
}
TEST_F(Mesh_Test, VertexAttribute_Enum) {
// 测试顶点属性标志
VertexAttribute attrs = VertexAttribute::Position | VertexAttribute::Normal | VertexAttribute::UV0;
EXPECT_TRUE(attrs & VertexAttribute::Position);
EXPECT_TRUE(attrs & VertexAttribute::Normal);
EXPECT_TRUE(attrs & VertexAttribute::UV0);
EXPECT_FALSE(attrs & VertexAttribute::Tangent);
}
} // namespace
```
#### 测试 12.6.3 材质资源测试
**文件**: `tests/Resources/test_material.cpp`
```cpp
#include <gtest/gtest.h>
#include <XCEngine/Resources/Material.h>
#include "fixtures/ResourcesTestFixture.h"
using namespace XCEngine::Resources;
namespace {
class Material_Test : public ResourcesTestFixture {};
TEST_F(Material_Test, DefaultConstructor) {
Material material;
EXPECT_FALSE(material.IsValid());
EXPECT_EQ(material.GetType(), ResourceType::Material);
}
TEST_F(Material_Test, SetFloat) {
Material material;
material.SetFloat("Metallic", 0.5f);
EXPECT_FLOAT_EQ(material.GetFloat("Metallic"), 0.5f);
}
TEST_F(Material_Test, SetFloat2) {
Material material;
material.SetFloat2("UVScale", Math::Vector2(2.0f, 3.0f));
Math::Vector2 value = material.GetFloat2("UVScale");
EXPECT_FLOAT_EQ(value.x, 2.0f);
EXPECT_FLOAT_EQ(value.y, 3.0f);
}
TEST_F(Material_Test, SetFloat3) {
Material material;
material.SetFloat3("Albedo", Math::Vector3(1.0f, 0.0f, 0.0f));
Math::Vector3 value = material.GetFloat3("Albedo");
EXPECT_FLOAT_EQ(value.x, 1.0f);
EXPECT_FLOAT_EQ(value.y, 0.0f);
EXPECT_FLOAT_EQ(value.z, 0.0f);
}
TEST_F(Material_Test, SetFloat4) {
Material material;
material.SetFloat4("Color", Math::Vector4(1.0f, 1.0f, 1.0f, 0.5f));
Math::Vector4 value = material.GetFloat4("Color");
EXPECT_FLOAT_EQ(value.w, 0.5f);
}
TEST_F(Material_Test, SetInt) {
Material material;
material.SetInt("RenderQueue", 2000);
EXPECT_EQ(material.GetInt("RenderQueue"), 2000);
}
TEST_F(Material_Test, SetBool) {
Material material;
material.SetBool("EnableInstancing", true);
EXPECT_TRUE(material.GetBool("EnableInstancing"));
}
TEST_F(Material_Test, SetTexture) {
Material material;
// 创建测试纹理
Texture* tex = new Texture();
tex->Initialize({"TestTex", "test.png", ResourceGUID::Generate("test"), 1024});
ResourceHandle<Texture> handle(tex);
material.SetTexture("MainTex", handle);
// 验证纹理设置成功
// 注意需要实现GetTexture方法
}
TEST_F(Material_Test, PropertyNotFound) {
Material material;
// 查询不存在的属性应有默认值
EXPECT_FLOAT_EQ(material.GetFloat("NonExistent"), 0.0f);
}
} // namespace
```
### 12.7 第五阶段测试资源管理器天3-5完成后
#### 测试 12.7.1 资源管理器测试
**文件**: `tests/Resources/test_resource_manager.cpp`
```cpp
#include <gtest/gtest.h>
#include <XCEngine/Resources/ResourceManager.h>
#include "fixtures/ResourcesTestFixture.h"
using namespace XCEngine::Resources;
namespace {
class ResourceManager_Test : public ResourcesTestFixture {};
TEST_F(ResourceManager_Test, GetSingleton) {
ResourceManager& manager = ResourceManager::Get();
EXPECT_EQ(&manager, &ResourceManager::Get());
}
TEST_F(ResourceManager_Test, SetResourceRoot) {
ResourceManager& manager = ResourceManager::Get();
manager.SetResourceRoot("resources/");
EXPECT_STREQ(manager.GetResourceRoot().CStr(), "resources/");
}
TEST_F(ResourceManager_Test, SetMemoryBudget) {
ResourceManager& manager = ResourceManager::Get();
manager.SetMemoryBudget(1024 * 1024); // 1MB
EXPECT_EQ(manager.GetMemoryBudget(), 1024u * 1024u);
}
TEST_F(ResourceManager_Test, GetMemoryUsage_Initial) {
ResourceManager& manager = ResourceManager::Get();
EXPECT_EQ(manager.GetMemoryUsage(), 0u);
}
TEST_F(ResourceManager_Test, RegisterLoader) {
ResourceManager& manager = ResourceManager::Get();
// 纹理加载器应该已经注册
IResourceLoader* loader = manager.GetLoader(ResourceType::Texture);
EXPECT_NE(loader, nullptr);
}
TEST_F(ResourceManager_Test, GetLoader_NotFound) {
ResourceManager& manager = ResourceManager::Get();
IResourceLoader* loader = manager.GetLoader(ResourceType::Unknown);
EXPECT_EQ(loader, nullptr);
}
TEST_F(ResourceManager_Test, AddRef_Release) {
ResourceGUID guid(12345);
ResourceManager::Get().AddRef(guid);
EXPECT_EQ(ResourceManager::Get().GetRefCount(guid), 1u);
ResourceManager::Get().AddRef(guid);
EXPECT_EQ(ResourceManager::Get().GetRefCount(guid), 2u);
ResourceManager::Get().Release(guid);
EXPECT_EQ(ResourceManager::Get().GetRefCount(guid), 1u);
ResourceManager::Get().Release(guid);
// 引用计数为0时应返回0
}
TEST_F(ResourceManager_Test, UnloadAll) {
ResourceManager& manager = ResourceManager::Get();
// 添加一些引用
ResourceGUID guid1(100);
ResourceGUID guid2(200);
manager.AddRef(guid1);
manager.AddRef(guid2);
manager.UnloadAll();
// 所有引用计数应被清除
EXPECT_EQ(manager.GetRefCount(guid1), 0u);
EXPECT_EQ(manager.GetRefCount(guid2), 0u);
}
TEST_F(ResourceManager_Test, Exists) {
ResourceManager& manager = ResourceManager::Get();
// 未加载的资源应返回false
EXPECT_FALSE(manager.Exists("nonexistent.png"));
}
TEST_F(ResourceManager_Test, ResolvePath) {
ResourceManager& manager = ResourceManager::Get();
manager.SetResourceRoot("myresources/");
Containers::String resolved = manager.ResolvePath("textures/test.png");
EXPECT_STREQ(resolved.CStr(), "myresources//textures/test.png");
}
} // namespace
```
### 12.8 测试运行脚本
**文件**: `tests/Resources/run_tests.bat`
```bat
@echo off
echo ========================================
echo XCEngine Resources System Tests
echo ========================================
cd /d %~dp0\..\..\build
if exist "tests\Resources\xcengine_resources_tests.exe" (
echo Running Resources tests...
tests\Resources\xcengine_resources_tests.exe
if %ERRORLEVEL% EQU 0 (
echo.
echo [SUCCESS] All resource tests passed!
) else (
echo.
echo [FAILED] Some tests failed!
exit /b 1
)
) else (
echo Error: Test executable not found!
echo Please build the project first.
exit /b 1
)
```
### 12.9 测试CMakeLists.txt
**文件**: `tests/Resources/CMakeLists.txt`
```cmake
# ============================================================
# Resources Library Tests
# ============================================================
set(RESOURCES_TEST_SOURCES
test_resource_types.cpp
test_resource_guid.cpp
test_iresource.cpp
test_resource_handle.cpp
test_resource_cache.cpp
test_async_loader.cpp
test_texture.cpp
test_mesh.cpp
test_material.cpp
test_audio_clip.cpp
test_resource_manager.cpp
test_resource_filesystem.cpp
test_resource_dependency.cpp
)
add_executable(xcengine_resources_tests ${RESOURCES_TEST_SOURCES})
if(MSVC)
set_target_properties(xcengine_resources_tests PROPERTIES
LINK_FLAGS "/NODEFAULTLIB:libcpmt.lib /NODEFAULTLIB:libcmt.lib"
)
endif()
target_link_libraries(xcengine_resources_tests
PRIVATE
XCEngine
GTest::gtest
GTest::gtest_main
)
target_include_directories(xcengine_resources_tests PRIVATE
${CMAKE_SOURCE_DIR}/engine/include
${CMAKE_SOURCE_DIR}/tests/fixtures
${CMAKE_SOURCE_DIR}/tests/Resources/fixtures
)
# 复制测试数据到输出目录
file(COPY ${CMAKE_SOURCE_DIR}/tests/Resources/test_data
DESTINATION ${CMAKE_BINARY_DIR}/tests/Resources/)
add_test(NAME ResourcesTests COMMAND xcengine_resources_tests)
```
### 12.10 阶段测试验收清单
每个阶段完成后,运行对应测试验证功能正确性:
| 阶段 | 完成后运行测试 | 验收标准 |
|------|---------------|----------|
| **天1-3: 基础框架** | `test_resource_types.cpp`<br>`test_resource_guid.cpp`<br>`test_resource_handle.cpp` | ✅ ResourceType枚举正确<br>✅ GUID生成稳定<br>✅ 句柄引用计数正确 |
| **天4-5: 缓存系统** | `test_resource_cache.cpp` | ✅ LRU淘汰正常<br>✅ 内存预算生效<br>✅ 内存压力响应 |
| **天6-7: 异步加载** | `test_async_loader.cpp` | ✅ 任务提交正常<br>✅ 回调正常<br>✅ 进度计算正确 |
| **天8-9: 纹理** | `test_texture.cpp` | ✅ 创建/设置属性正常<br>✅ 加载器识别格式<br>✅ 导入设置生效 |
| **天10: 网格** | `test_mesh.cpp` | ✅ 顶点/索引数据正常<br>✅ 子网格正常<br>✅ 包围盒正常 |
| **天11: 材质** | `test_material.cpp` | ✅ 属性设置/获取正常<br>✅ 类型转换正确 |
| **天12-13: 文件系统** | `test_resource_filesystem.cpp` | ✅ 目录遍历正常<br>✅ 归档加载正常 |
| **天14-15: 依赖管理** | `test_resource_dependency.cpp` | ✅ 依赖关系正确<br>✅ 循环检测正常 |
| **天16-17: 集成** | 全部测试 | ✅ 所有测试通过<br>✅ 内存无泄漏 |
### 12.11 每日测试执行流程
```bash
# 每天完成代码后在build目录执行
cd build
# 编译测试
cmake --build . --target xcengine_resources_tests
# 运行当天相关测试
ctest -R "Resources_类型名" -V
# 例如:
# 第3天完成后运行
ctest -R "Resources_Types|Resources_GUID|Resources_Handle" -V
# 第5天完成后运行
ctest -R "Resources_Cache" -V
# 全部测试通过后进行下一阶段
```
### 12.12 测试数据准备
需要在 `tests/Resources/test_data/` 目录下准备以下测试文件:
```
test_data/
├── textures/
│ ├── test_256x256.png # 256x256 PNG测试纹理
│ ├── test_512x512.jpg # 512x512 JPG测试纹理
│ └── test_red.tga # 纯红色TGA
├── models/
│ ├── cube.obj # 简单立方体
│ ├── sphere.obj # 球体
│ └── plane.obj # 平面
├── materials/
│ ├── test_material.json # 测试材质
│ └── test_pbr.json # PBR材质
├── audio/
│ ├── test_mono.wav # 单声道WAV
│ └── test_stereo.wav # 立体声WAV
└── archives/
└── test.xcrap # 测试资源包
```
---
## 13. 关键技术点
### 13.1 引用计数与缓存淘汰的交互
```cpp
// 引用计数管理
void ResourceManager::AddRef(ResourceGUID guid) {
std::lock_guard lock(m_mutex);
// 增加引用计数
auto it = m_refCounts.Find(guid);
if (it == m_refCounts.End()) {
m_refCounts.Insert(guid, 1);
} else {
it->second++;
}
// 确保资源在缓存中
if (!m_resourceCache.Contains(guid)) {
// 重新加载资源
ReloadResource(guid);
}
}
void ResourceManager::Release(ResourceGUID guid) {
std::lock_guard lock(m_mutex);
auto it = m_refCounts.Find(guid);
if (it != m_refCounts.End()) {
it->second--;
// 引用计数为0时不立即卸载
// 由缓存系统根据LRU策略决定何时卸载
if (it->second == 0) {
m_refCounts.Erase(guid);
// 通知缓存系统资源可以淘汰
m_cache.OnZeroRefCount(guid);
}
}
}
```
### 13.2 异步加载线程安全
```cpp
// AsyncLoader::Update() 在主线程调用
void AsyncLoader::Update() {
// 1. 从完成队列取出结果(线程安全)
Array<LoadRequest> completed;
{
std::lock_guard lock(m_completedMutex);
completed = std::move(m_completedQueue);
m_completedQueue.Clear();
}
// 2. 在主线程处理完成回调
for (auto& request : completed) {
if (request.callback) {
// 回调中可能创建ResourceHandle增加引用计数
request.callback(m_result);
}
}
}
// 工作线程函数
void LoadTask::Execute() {
// 在工作线程执行实际加载
m_result = m_loader->Load(m_request.path, m_request.settings.get());
// 放入完成队列
AsyncLoader::Get().QueueCompleted(std::move(m_request), std::move(m_result));
}
```
### 13.3 内存压力响应
```cpp
void ResourceCache::OnMemoryPressure(size_t requiredBytes) {
std::lock_guard lock(m_mutex);
if (m_memoryUsage + requiredBytes <= m_memoryBudget) {
return; // 内存充足
}
// 需要释放内存
size_t targetRelease = (m_memoryUsage + requiredBytes) - m_memoryBudget;
// 按LRU顺序淘汰
for (auto it = m_lruOrder.Begin(); it != m_lruOrder.End() && targetRelease > 0; ) {
ResourceGUID guid = *it;
auto cacheIt = m_cache.Find(guid);
if (cacheIt != m_cache.End()) {
CacheEntry& entry = cacheIt->second;
// 检查是否有外部引用
if (ResourceManager::Get().GetRefCount(guid) == 0) {
size_t releasedSize = entry.memorySize;
// 从缓存移除
m_cache.Erase(guid);
m_memoryUsage -= releasedSize;
targetRelease -= releasedSize;
// 销毁资源
entry.resource->Release();
// 从LRU列表移除
it = m_lruOrder.Erase(it);
} else {
++it; // 有外部引用,跳过
}
} else {
++it;
}
}
}
```
---
## 14. 使用示例
### 14.1 基本使用
```cpp
// 初始化资源系统
void InitResourceSystem() {
// 设置资源根目录
ResourceManager::Get().SetResourceRoot("resources/");
// 初始化异步加载器2个工作线程
AsyncLoader::Get().Initialize(2);
// 设置内存预算1GB
ResourceManager::Get().SetMemoryBudget(1024 * 1024 * 1024);
}
// 同步加载纹理
void LoadTextureExample() {
// 加载纹理
ResourceHandle<Texture> tex = ResourceManager::Get().Load<Texture>("textures/player.png");
if (tex.IsValid()) {
// 使用纹理
Texture* texture = tex.Get();
printf("Loaded texture: %s, %dx%d\n",
tex->GetName().CStr(),
texture->GetWidth(),
texture->GetHeight());
// 传递给渲染器
renderer->SetTexture(0, texture->GetRHIResource());
}
// 离开作用域时自动释放(引用计数-1
}
// 异步加载网格
void LoadMeshAsyncExample() {
ResourceManager::Get().LoadAsync("models/player.fbx", ResourceType::Mesh,
[](LoadResult result) {
if (result && result.resource) {
// 在主线程回调中处理
// 注意:需要手动转换为正确的类型
Mesh* mesh = static_cast<Mesh*>(result.resource);
printf("Mesh loaded: %s\n", mesh->GetName().CStr());
// 添加到场景
// 注意这里需要创建ResourceHandle来管理生命周期
ResourceHandle<Mesh> meshHandle(mesh);
scene->AddMesh(meshHandle);
} else {
printf("Failed to load mesh: %s\n", result.errorMessage.CStr());
}
});
// 异步加载进行中...
// 主线程继续执行其他逻辑
}
```
### 14.2 资源组加载
```cpp
// 加载场景所需的所有资源
void LoadSceneResources(const String& scenePath) {
// 1. 加载场景配置文件
String sceneConfigPath = "scenes/" + scenePath + ".scene";
ResourceHandle<BinaryResource> sceneConfig =
ResourceManager::Get().Load<BinaryResource>(sceneConfigPath);
// 2. 解析配置文件获取依赖列表
Array<String> dependencies = ParseSceneDependencies(sceneConfig.Get());
// 3. 异步加载所有依赖
Core::uint32 totalResources = dependencies.Size();
Core::uint32 loadedCount = 0;
for (const auto& dep : dependencies) {
// 根据扩展名判断资源类型
String ext = Path::GetExtension(dep);
ResourceType type = ResourceType::Binary; // 默认
if (ext == ".png" || ext == ".jpg") {
type = ResourceType::Texture;
} else if (ext == ".fbx" || ext == ".obj") {
type = ResourceType::Mesh;
}
// ... 其他类型
ResourceManager::Get().LoadAsync(dep, type,
[&loadedCount, totalResources](LoadResult result) {
loadedCount++;
printf("Loading progress: %d/%d\n", loadedCount, totalResources);
});
}
}
```
### 14.3 自定义导入设置
```cpp
void LoadTextureWithSettings() {
// 创建导入设置
TextureImportSettings* settings = new TextureImportSettings();
settings->SetGenerateMipmaps(true);
settings->SetSRGB(true);
settings->SetTargetFormat(TextureFormat::BC7_UNORM);
settings->SetMaxAnisotropy(16);
// 加载时传入设置
ResourceHandle<Texture> tex = ResourceManager::Get().Load<Texture>(
"textures/diffuse.png", settings);
// settings由ResourceManager管理不需要手动delete
}
```
---
## 15. 验收标准
### 13.1 功能验收
- [ ] 能够通过路径加载Texture资源PNG, JPG, TGA
- [ ] 能够通过路径加载Mesh资源OBJ, glTF
- [ ] 能够通过路径加载Material资源JSON格式
- [ ] 能够通过路径加载AudioClip资源WAV格式
- [ ] 异步加载功能正常工作,回调在主线程执行
- [ ] 资源缓存机制正常工作LRU淘汰策略生效
- [ ] 引用计数正确管理,零引用时资源可被淘汰
- [ ] 内存预算控制正常工作,超出预算时自动淘汰资源
### 13.2 性能验收
- [ ] 缓存命中时资源加载时间为0
- [ ] 异步加载不阻塞主线程
- [ ] 多线程加载时无数据竞争
- [ ] 内存使用稳定,无内存泄漏
### 13.3 代码质量
- [ ] 编译通过,无警告
- [ ] 遵循项目编码规范
- [ ] 必要的注释和文档
- [ ] 错误处理完善
---
## 16. 后续扩展
### 14.1 可选的扩展功能
| 功能 | 说明 | 工作量 |
|------|------|--------|
| **资源热加载** | 编辑器中修改资源后实时更新 | 2天 |
| **资源版本控制** | 检测资源变化,自动重新加载 | 2天 |
| **资源加密** | 支持加密的资源包 | 1天 |
| **资源流式加载** | 大资源分块加载 | 3天 |
| **资源分析器** | 可视化资源使用情况 | 2天 |
### 14.2 资源格式扩展
| 格式 | 说明 | 优先级 |
|------|------|--------|
| glTF 2.0 | 现代3D模型格式高优先级 | P0 |
| DDS | 压缩纹理格式 | P0 |
| OGG/Vorbis | 压缩音频格式 | P1 |
| PSD | Photoshop源文件 | P2 |
| EXR | HDR图像 | P2 |
---
## 17. 依赖关系
```
资源系统依赖关系图:
第一阶段基础层
├── Math Library ─────┐
│ │
├── Containers ──────┼──► 资源系统
│ │
├── Memory ────────┤
│ │
└── Threading ─────────┘
┌─────────────────┐
│ AsyncLoader │ (依赖TaskSystem)
└────────┬────────┘
┌────────▼────────┐
│ ResourceManager │
└────────┬────────┘
┌────────▼────────┐ ┌─────────────┐
│ ResourceCache │────►│ 引用计数 │
└────────┬────────┘ │ 管理 │
│ └─────────────┘
┌────────▼────────┐
│ ResourceLoader │
└────────┬────────┘
┌────────▼────────┐
│ 具体资源类型 │
│ (Texture等) │
└─────────────────┘
```
---
*文档版本: 1.0*
*更新日期: 2026-03-17*