553 lines
15 KiB
C++
553 lines
15 KiB
C++
#pragma once
|
||
|
||
#include <assets/asset_loader.h>
|
||
#include <assets/asset_storage.h>
|
||
#include <assets/handle.h>
|
||
#include <atomic>
|
||
#include <condition_variable>
|
||
#include <event/events.h>
|
||
#include <filesystem>
|
||
#include <functional>
|
||
#include <memory>
|
||
#include <module/module.h>
|
||
#include <module/module_registry.h>
|
||
#include <mutex>
|
||
#include <renderer/material.h>
|
||
#include <renderer/mesh.h>
|
||
#include <renderer/shader.h>
|
||
#include <renderer/texture.h>
|
||
#include <shared_mutex>
|
||
#include <string>
|
||
#include <thread>
|
||
#include <unordered_map>
|
||
#include <vector>
|
||
|
||
namespace extra2d {
|
||
|
||
/**
|
||
* @brief 资源管理模块(主流 ECS 风格)
|
||
*
|
||
* 设计参考 Bevy/Amethyst:
|
||
* - AssetStorage: 密集存储资源
|
||
* - load<T>(): 统一加载接口
|
||
* - Handle<T>: 轻量级句柄
|
||
*/
|
||
class AssetsModule : public Module {
|
||
// 优先级为 3,在 FileModule (优先级 2) 之后初始化
|
||
E2D_REGISTER_MODULE(AssetsModule, "Assets", 3)
|
||
|
||
public:
|
||
AssetsModule();
|
||
~AssetsModule() override;
|
||
|
||
bool init() override;
|
||
void shutdown() override;
|
||
|
||
/**
|
||
* @brief 在 OpenGL 上下文就绪后创建默认资源
|
||
*/
|
||
void onGLContextReady();
|
||
|
||
//===========================================================================
|
||
// 核心加载接口(模板方法,主流设计)
|
||
//===========================================================================
|
||
|
||
/**
|
||
* @brief 加载资源(自动缓存,重复加载返回已有)
|
||
*
|
||
* 示例:
|
||
* @code
|
||
* Handle<Texture> tex = assets->load<Texture>("player.png");
|
||
* Handle<Shader> shader = assets->load<Shader>("sprite.vert", "sprite.frag");
|
||
* @endcode
|
||
*
|
||
* @tparam T 资源类型
|
||
* @param path 文件路径
|
||
* @return 资源句柄
|
||
*/
|
||
template <typename T, typename... Args>
|
||
Handle<T> load(const std::string &path, Args &&...args);
|
||
|
||
/**
|
||
* @brief 从内存加载资源
|
||
* @tparam T 资源类型
|
||
* @param key 缓存键名
|
||
* @param data 数据指针
|
||
* @param size 数据大小
|
||
* @return 资源句柄
|
||
*/
|
||
template <typename T>
|
||
Handle<T> loadFromMemory(const std::string &key, const uint8_t *data,
|
||
size_t size);
|
||
|
||
/**
|
||
* @brief 获取资源(返回指针,可能为 nullptr)
|
||
*
|
||
* 示例:
|
||
* @code
|
||
* Texture* tex = assets->get(handle);
|
||
* if (tex) {
|
||
* tex->bind();
|
||
* }
|
||
* @endcode
|
||
*/
|
||
template <typename T> T *get(Handle<T> handle);
|
||
|
||
/**
|
||
* @brief 获取资源(返回智能指针)
|
||
*/
|
||
template <typename T> Ptr<T> getPtr(Handle<T> handle);
|
||
|
||
/**
|
||
* @brief 检查句柄是否有效
|
||
*/
|
||
template <typename T> bool isValid(Handle<T> handle) const;
|
||
|
||
/**
|
||
* @brief 移除资源
|
||
*/
|
||
template <typename T> void remove(Handle<T> handle);
|
||
|
||
//===========================================================================
|
||
// 批量加载(目录/包)
|
||
//===========================================================================
|
||
|
||
/**
|
||
* @brief 加载整个目录
|
||
* @tparam T 资源类型
|
||
* @param directory 目录路径
|
||
* @param pattern 文件模式(如 "*.png")
|
||
* @param recursive 是否递归
|
||
* @return 句柄列表
|
||
*/
|
||
template <typename T>
|
||
std::vector<Handle<T>> loadDir(const std::string &directory,
|
||
const std::string &pattern = "*",
|
||
bool recursive = true);
|
||
|
||
//===========================================================================
|
||
// 异步加载
|
||
//===========================================================================
|
||
|
||
/**
|
||
* @brief 异步加载资源
|
||
* @tparam T 资源类型
|
||
* @param path 文件路径
|
||
* @param callback 加载完成回调
|
||
*/
|
||
template <typename T>
|
||
void loadAsync(const std::string &path,
|
||
std::function<void(Handle<T>)> callback);
|
||
|
||
//===========================================================================
|
||
// 注册加载器(扩展点)
|
||
//===========================================================================
|
||
|
||
/**
|
||
* @brief 注册资源加载器
|
||
* @tparam T 资源类型
|
||
* @param loader 加载器实例
|
||
*/
|
||
template <typename T>
|
||
void registerLoader(std::unique_ptr<AssetLoader<T>> loader);
|
||
|
||
//===========================================================================
|
||
// 默认资源
|
||
//===========================================================================
|
||
|
||
/**
|
||
* @brief 创建默认资源
|
||
* @return 是否成功
|
||
*/
|
||
bool createDefaultResources();
|
||
|
||
/**
|
||
* @brief 销毁默认资源
|
||
*/
|
||
void destroyDefaultResources();
|
||
|
||
Handle<Texture> getDefaultTexture();
|
||
Handle<Shader> getDefaultShader();
|
||
Handle<Material> getDefaultMaterial();
|
||
Handle<Mesh> getDefaultQuad();
|
||
|
||
Texture *getDefaultTexturePtr();
|
||
Shader *getDefaultShaderPtr();
|
||
Material *getDefaultMaterialPtr();
|
||
Mesh *getDefaultQuadPtr();
|
||
|
||
//===========================================================================
|
||
// 实例化渲染资源
|
||
//===========================================================================
|
||
|
||
/**
|
||
* @brief 获取实例化渲染着色器
|
||
* @return 实例化着色器句柄
|
||
*/
|
||
Handle<Shader> getInstancedShader();
|
||
|
||
/**
|
||
* @brief 获取实例化渲染材质
|
||
* @return 实例化材质句柄
|
||
*/
|
||
Handle<Material> getInstancedMaterial();
|
||
|
||
Shader *getInstancedShaderPtr();
|
||
Material *getInstancedMaterialPtr();
|
||
|
||
/**
|
||
* @brief 创建实例化渲染资源
|
||
* @return 是否成功
|
||
*/
|
||
bool createInstancedResources();
|
||
|
||
//===========================================================================
|
||
// 热重载
|
||
//===========================================================================
|
||
|
||
/**
|
||
* @brief 启用/禁用热重载
|
||
* @param enable 是否启用
|
||
*/
|
||
void enableHotReload(bool enable);
|
||
|
||
/**
|
||
* @brief 检查文件变更并重新加载
|
||
*/
|
||
void checkForChanges();
|
||
|
||
/**
|
||
* @brief 设置热重载检查间隔(秒)
|
||
* @param interval 检查间隔,默认 1.0 秒
|
||
*/
|
||
void setHotReloadInterval(float interval);
|
||
|
||
private:
|
||
/**
|
||
* @brief 资源文件监控信息
|
||
*/
|
||
struct FileWatchInfo {
|
||
std::string path;
|
||
std::filesystem::file_time_type lastWriteTime;
|
||
Handle<Texture> textureHandle;
|
||
Handle<Shader> shaderHandle;
|
||
bool isTexture = false;
|
||
};
|
||
|
||
/**
|
||
* @brief 添加文件监控
|
||
*/
|
||
void addFileWatch(const std::string &path, Handle<Texture> handle);
|
||
void addFileWatch(const std::string &path, Handle<Shader> handle);
|
||
|
||
/**
|
||
* @brief 重新加载纹理
|
||
*/
|
||
void reloadTexture(const FileWatchInfo &info);
|
||
|
||
/**
|
||
* @brief 重新加载着色器
|
||
*/
|
||
void reloadShader(const FileWatchInfo &info);
|
||
|
||
public:
|
||
//===========================================================================
|
||
// 统计
|
||
//===========================================================================
|
||
|
||
struct Stats {
|
||
size_t textureCount = 0;
|
||
size_t shaderCount = 0;
|
||
size_t materialCount = 0;
|
||
size_t meshCount = 0;
|
||
};
|
||
|
||
Stats getStats() const;
|
||
|
||
private:
|
||
// 资源存储
|
||
AssetStorage<Texture> textures_;
|
||
AssetStorage<Shader> shaders_;
|
||
AssetStorage<Material> materials_;
|
||
AssetStorage<Mesh> meshes_;
|
||
|
||
// 加载器
|
||
std::unique_ptr<AssetLoader<Texture>> textureLoader_;
|
||
std::unique_ptr<AssetLoader<Shader>> shaderLoader_;
|
||
|
||
// 路径缓存(避免重复加载)
|
||
std::unordered_map<std::string, Handle<Texture>> texturePathCache_;
|
||
std::unordered_map<std::string, Handle<Shader>> shaderPathCache_;
|
||
|
||
// 默认资源
|
||
Handle<Texture> defaultTexture_;
|
||
Handle<Shader> defaultShader_;
|
||
Handle<Material> defaultMaterial_;
|
||
Handle<Mesh> defaultQuad_;
|
||
|
||
// 实例化渲染资源
|
||
Handle<Shader> instancedShader_;
|
||
Handle<Material> instancedMaterial_;
|
||
|
||
// 热重载
|
||
bool hotReloadEnabled_ = false;
|
||
float hotReloadInterval_ = 1.0f;
|
||
float hotReloadTimer_ = 0.0f;
|
||
std::vector<FileWatchInfo> fileWatchList_;
|
||
|
||
// 线程安全
|
||
mutable std::shared_mutex mutex_;
|
||
|
||
// 事件监听器
|
||
std::unique_ptr<events::OnShow::Listener> onShowListener_;
|
||
|
||
// 标记默认资源是否已创建
|
||
bool defaultResourcesCreated_ = false;
|
||
|
||
//===========================================================================
|
||
// 异步加载系统
|
||
//===========================================================================
|
||
public:
|
||
/**
|
||
* @brief 异步加载任务
|
||
*/
|
||
struct LoadTask {
|
||
enum class Type { Texture, Shader };
|
||
enum class Priority { Low = 0, Normal = 1, High = 2 };
|
||
|
||
Type type;
|
||
Priority priority;
|
||
std::string path;
|
||
std::string secondaryPath; // 用于着色器的片段着色器路径
|
||
std::function<void(Handle<Texture>)> textureCallback;
|
||
std::function<void(Handle<Shader>)> shaderCallback;
|
||
};
|
||
|
||
/**
|
||
* @brief 初始化异步加载系统
|
||
* @param threadCount 工作线程数,默认使用硬件并发数
|
||
*/
|
||
void initAsyncLoader(uint32_t threadCount = 0);
|
||
|
||
/**
|
||
* @brief 关闭异步加载系统
|
||
*/
|
||
void shutdownAsyncLoader();
|
||
|
||
/**
|
||
* @brief 提交异步加载任务
|
||
*/
|
||
void submitLoadTask(const LoadTask &task);
|
||
|
||
/**
|
||
* @brief 处理异步加载队列(在主线程调用)
|
||
*/
|
||
void processAsyncCallbacks();
|
||
|
||
private:
|
||
// 异步加载队列
|
||
std::vector<LoadTask> loadQueue_;
|
||
std::vector<std::thread> workerThreads_;
|
||
std::mutex queueMutex_;
|
||
std::condition_variable queueCV_;
|
||
std::atomic<bool> asyncLoaderRunning_{false};
|
||
|
||
// 完成的回调队列(主线程处理)
|
||
std::vector<std::function<void()>> completedCallbacks_;
|
||
std::mutex callbackMutex_;
|
||
|
||
void workerThreadLoop();
|
||
|
||
//===========================================================================
|
||
// 资源依赖跟踪
|
||
//===========================================================================
|
||
public:
|
||
/**
|
||
* @brief 资源依赖关系
|
||
*/
|
||
struct DependencyInfo {
|
||
Handle<Texture> texture;
|
||
Handle<Shader> shader;
|
||
std::vector<Handle<Material>> dependentMaterials;
|
||
};
|
||
|
||
/**
|
||
* @brief 注册材质对纹理的依赖
|
||
*/
|
||
void registerMaterialDependency(Handle<Material> material,
|
||
Handle<Texture> texture);
|
||
|
||
/**
|
||
* @brief 注册材质对着色器的依赖
|
||
*/
|
||
void registerMaterialDependency(Handle<Material> material,
|
||
Handle<Shader> shader);
|
||
|
||
/**
|
||
* @brief 当纹理重新加载时通知依赖的材质
|
||
*/
|
||
void notifyTextureReloaded(Handle<Texture> texture);
|
||
|
||
/**
|
||
* @brief 当着色器重新加载时通知依赖的材质
|
||
*/
|
||
void notifyShaderReloaded(Handle<Shader> shader);
|
||
|
||
private:
|
||
// 资源依赖映射
|
||
std::unordered_map<uint32_t, DependencyInfo> textureDependencies_;
|
||
std::unordered_map<uint32_t, DependencyInfo> shaderDependencies_;
|
||
std::shared_mutex dependencyMutex_;
|
||
};
|
||
|
||
// 全局访问
|
||
AssetsModule *getAssets();
|
||
|
||
//===========================================================================
|
||
// 模板实现
|
||
//===========================================================================
|
||
|
||
template <typename T, typename... Args>
|
||
Handle<T> AssetsModule::load(const std::string &path, Args &&...args) {
|
||
static_assert(sizeof...(Args) == 0 || sizeof...(Args) == 1,
|
||
"load() accepts 0 or 1 additional arguments");
|
||
return Handle<T>::invalid();
|
||
}
|
||
|
||
template <>
|
||
Handle<Texture> AssetsModule::load<Texture>(const std::string &path);
|
||
|
||
template <> Handle<Shader> AssetsModule::load<Shader>(const std::string &path);
|
||
|
||
template <>
|
||
Handle<Shader> AssetsModule::load<Shader>(const std::string &vertPath,
|
||
const std::string &fragPath);
|
||
|
||
template <typename T>
|
||
Handle<T> AssetsModule::loadFromMemory(const std::string &key,
|
||
const uint8_t *data, size_t size) {
|
||
return Handle<T>::invalid();
|
||
}
|
||
|
||
template <>
|
||
Handle<Texture> AssetsModule::loadFromMemory<Texture>(const std::string &key,
|
||
const uint8_t *data,
|
||
size_t size);
|
||
|
||
template <typename T> T *AssetsModule::get(Handle<T> handle) { return nullptr; }
|
||
|
||
template <> inline Texture *AssetsModule::get<Texture>(Handle<Texture> handle) {
|
||
return textures_.get(handle);
|
||
}
|
||
|
||
template <> inline Shader *AssetsModule::get<Shader>(Handle<Shader> handle) {
|
||
return shaders_.get(handle);
|
||
}
|
||
|
||
template <>
|
||
inline Material *AssetsModule::get<Material>(Handle<Material> handle) {
|
||
return materials_.get(handle);
|
||
}
|
||
|
||
template <> inline Mesh *AssetsModule::get<Mesh>(Handle<Mesh> handle) {
|
||
return meshes_.get(handle);
|
||
}
|
||
|
||
template <typename T> Ptr<T> AssetsModule::getPtr(Handle<T> handle) {
|
||
return Ptr<T>();
|
||
}
|
||
|
||
template <>
|
||
inline Ptr<Texture> AssetsModule::getPtr<Texture>(Handle<Texture> handle) {
|
||
return textures_.getPtr(handle);
|
||
}
|
||
|
||
template <>
|
||
inline Ptr<Shader> AssetsModule::getPtr<Shader>(Handle<Shader> handle) {
|
||
return shaders_.getPtr(handle);
|
||
}
|
||
|
||
template <>
|
||
inline Ptr<Material> AssetsModule::getPtr<Material>(Handle<Material> handle) {
|
||
return materials_.getPtr(handle);
|
||
}
|
||
|
||
template <> inline Ptr<Mesh> AssetsModule::getPtr<Mesh>(Handle<Mesh> handle) {
|
||
return meshes_.getPtr(handle);
|
||
}
|
||
|
||
template <typename T> bool AssetsModule::isValid(Handle<T> handle) const {
|
||
return false;
|
||
}
|
||
|
||
template <>
|
||
inline bool AssetsModule::isValid<Texture>(Handle<Texture> handle) const {
|
||
return textures_.isValid(handle);
|
||
}
|
||
|
||
template <>
|
||
inline bool AssetsModule::isValid<Shader>(Handle<Shader> handle) const {
|
||
return shaders_.isValid(handle);
|
||
}
|
||
|
||
template <>
|
||
inline bool AssetsModule::isValid<Material>(Handle<Material> handle) const {
|
||
return materials_.isValid(handle);
|
||
}
|
||
|
||
template <> inline bool AssetsModule::isValid<Mesh>(Handle<Mesh> handle) const {
|
||
return meshes_.isValid(handle);
|
||
}
|
||
|
||
template <typename T> void AssetsModule::remove(Handle<T> handle) {}
|
||
|
||
template <> inline void AssetsModule::remove<Texture>(Handle<Texture> handle) {
|
||
textures_.remove(handle);
|
||
}
|
||
|
||
template <> inline void AssetsModule::remove<Shader>(Handle<Shader> handle) {
|
||
shaders_.remove(handle);
|
||
}
|
||
|
||
template <>
|
||
inline void AssetsModule::remove<Material>(Handle<Material> handle) {
|
||
materials_.remove(handle);
|
||
}
|
||
|
||
template <> inline void AssetsModule::remove<Mesh>(Handle<Mesh> handle) {
|
||
meshes_.remove(handle);
|
||
}
|
||
|
||
template <typename T>
|
||
std::vector<Handle<T>> AssetsModule::loadDir(const std::string &directory,
|
||
const std::string &pattern,
|
||
bool recursive) {
|
||
return {};
|
||
}
|
||
|
||
template <>
|
||
std::vector<Handle<Texture>>
|
||
AssetsModule::loadDir<Texture>(const std::string &directory,
|
||
const std::string &pattern, bool recursive);
|
||
|
||
template <typename T>
|
||
void AssetsModule::loadAsync(const std::string &path,
|
||
std::function<void(Handle<T>)> callback) {}
|
||
|
||
template <>
|
||
void AssetsModule::loadAsync<Texture>(
|
||
const std::string &path, std::function<void(Handle<Texture>)> callback);
|
||
|
||
template <typename T>
|
||
void AssetsModule::registerLoader(std::unique_ptr<AssetLoader<T>> loader) {}
|
||
|
||
template <>
|
||
void AssetsModule::registerLoader<Texture>(
|
||
std::unique_ptr<AssetLoader<Texture>> loader);
|
||
|
||
template <>
|
||
void AssetsModule::registerLoader<Shader>(
|
||
std::unique_ptr<AssetLoader<Shader>> loader);
|
||
|
||
} // namespace extra2d
|