Extra2D/include/assets/assets_module.h

524 lines
14 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#pragma once
#include <assets/asset_loader.h>
#include <assets/asset_storage.h>
#include <assets/handle.h>
#include <atomic>
#include <chrono>
#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 {
E2D_REGISTER_MODULE(AssetsModule, "Assets", 2)
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 启用/禁用热重载
* @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_;
// 热重载
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