From f7e4f89ccad0f11c96dd35846291f572a88596d1 Mon Sep 17 00:00:00 2001 From: ChestnutYueyue <952134128@qq.com> Date: Mon, 2 Mar 2026 22:54:06 +0800 Subject: [PATCH] =?UTF-8?q?feat(assets):=20=E5=AE=9E=E7=8E=B0=E8=B5=84?= =?UTF-8?q?=E6=BA=90=E7=83=AD=E9=87=8D=E8=BD=BD=E5=92=8C=E5=BC=82=E6=AD=A5?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加资源热重载功能,支持监控文件变更并自动重新加载 实现异步加载系统,支持多线程资源加载和回调处理 增加资源依赖跟踪机制,自动更新依赖材质的引用 使用读写锁保护资源存储,提升多线程访问性能 --- include/assets/assets_module.h | 742 +++++++++++++++++++-------------- src/assets/assets_module.cpp | 528 +++++++++++++++++++++-- 2 files changed, 925 insertions(+), 345 deletions(-) diff --git a/include/assets/assets_module.h b/include/assets/assets_module.h index 225d419..aacf98a 100644 --- a/include/assets/assets_module.h +++ b/include/assets/assets_module.h @@ -1,19 +1,25 @@ #pragma once -#include -#include -#include -#include -#include #include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include #include +#include +#include #include +#include +#include +#include +#include +#include #include +#include #include #include @@ -21,379 +27,497 @@ namespace extra2d { /** * @brief 资源管理模块(主流 ECS 风格) - * + * * 设计参考 Bevy/Amethyst: * - AssetStorage: 密集存储资源 * - load(): 统一加载接口 * - Handle: 轻量级句柄 */ class AssetsModule : public Module { - E2D_REGISTER_MODULE(AssetsModule, "Assets", 2) - + E2D_REGISTER_MODULE(AssetsModule, "Assets", 2) + public: - AssetsModule(); - ~AssetsModule() override; - - bool init() override; - void shutdown() override; - - /** - * @brief 在 OpenGL 上下文就绪后创建默认资源 - */ - void onGLContextReady(); - - //=========================================================================== - // 核心加载接口(模板方法,主流设计) - //=========================================================================== - - /** - * @brief 加载资源(自动缓存,重复加载返回已有) - * - * 示例: - * @code - * Handle tex = assets->load("player.png"); - * Handle shader = assets->load("sprite.vert", "sprite.frag"); - * @endcode - * - * @tparam T 资源类型 - * @param path 文件路径 - * @return 资源句柄 - */ - template - Handle load(const std::string& path, Args&&... args); - - /** - * @brief 从内存加载资源 - * @tparam T 资源类型 - * @param key 缓存键名 - * @param data 数据指针 - * @param size 数据大小 - * @return 资源句柄 - */ - template - Handle 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 - T* get(Handle handle); - - /** - * @brief 获取资源(返回智能指针) - */ - template - Ptr getPtr(Handle handle); - - /** - * @brief 检查句柄是否有效 - */ - template - bool isValid(Handle handle) const; - - /** - * @brief 移除资源 - */ - template - void remove(Handle handle); - - //=========================================================================== - // 批量加载(目录/包) - //=========================================================================== - - /** - * @brief 加载整个目录 - * @tparam T 资源类型 - * @param directory 目录路径 - * @param pattern 文件模式(如 "*.png") - * @param recursive 是否递归 - * @return 句柄列表 - */ - template - std::vector> loadDir(const std::string& directory, - const std::string& pattern = "*", - bool recursive = true); - - //=========================================================================== - // 异步加载 - //=========================================================================== - - /** - * @brief 异步加载资源 - * @tparam T 资源类型 - * @param path 文件路径 - * @param callback 加载完成回调 - */ - template - void loadAsync(const std::string& path, - std::function)> callback); - - //=========================================================================== - // 注册加载器(扩展点) - //=========================================================================== - - /** - * @brief 注册资源加载器 - * @tparam T 资源类型 - * @param loader 加载器实例 - */ - template - void registerLoader(std::unique_ptr> loader); - - //=========================================================================== - // 默认资源 - //=========================================================================== - - /** - * @brief 创建默认资源 - * @return 是否成功 - */ - bool createDefaultResources(); - - /** - * @brief 销毁默认资源 - */ - void destroyDefaultResources(); - - Handle getDefaultTexture(); - Handle getDefaultShader(); - Handle getDefaultMaterial(); - Handle getDefaultQuad(); - - Texture* getDefaultTexturePtr(); - Shader* getDefaultShaderPtr(); - Material* getDefaultMaterialPtr(); - Mesh* getDefaultQuadPtr(); - - //=========================================================================== - // 热重载 - //=========================================================================== - - /** - * @brief 启用/禁用热重载 - * @param enable 是否启用 - */ - void enableHotReload(bool enable); - - /** - * @brief 检查文件变更并重新加载 - */ - void checkForChanges(); - - //=========================================================================== - // 统计 - //=========================================================================== - - struct Stats { - size_t textureCount = 0; - size_t shaderCount = 0; - size_t materialCount = 0; - size_t meshCount = 0; - }; - - Stats getStats() const; - + AssetsModule(); + ~AssetsModule() override; + + bool init() override; + void shutdown() override; + + /** + * @brief 在 OpenGL 上下文就绪后创建默认资源 + */ + void onGLContextReady(); + + //=========================================================================== + // 核心加载接口(模板方法,主流设计) + //=========================================================================== + + /** + * @brief 加载资源(自动缓存,重复加载返回已有) + * + * 示例: + * @code + * Handle tex = assets->load("player.png"); + * Handle shader = assets->load("sprite.vert", "sprite.frag"); + * @endcode + * + * @tparam T 资源类型 + * @param path 文件路径 + * @return 资源句柄 + */ + template + Handle load(const std::string &path, Args &&...args); + + /** + * @brief 从内存加载资源 + * @tparam T 资源类型 + * @param key 缓存键名 + * @param data 数据指针 + * @param size 数据大小 + * @return 资源句柄 + */ + template + Handle 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 T *get(Handle handle); + + /** + * @brief 获取资源(返回智能指针) + */ + template Ptr getPtr(Handle handle); + + /** + * @brief 检查句柄是否有效 + */ + template bool isValid(Handle handle) const; + + /** + * @brief 移除资源 + */ + template void remove(Handle handle); + + //=========================================================================== + // 批量加载(目录/包) + //=========================================================================== + + /** + * @brief 加载整个目录 + * @tparam T 资源类型 + * @param directory 目录路径 + * @param pattern 文件模式(如 "*.png") + * @param recursive 是否递归 + * @return 句柄列表 + */ + template + std::vector> loadDir(const std::string &directory, + const std::string &pattern = "*", + bool recursive = true); + + //=========================================================================== + // 异步加载 + //=========================================================================== + + /** + * @brief 异步加载资源 + * @tparam T 资源类型 + * @param path 文件路径 + * @param callback 加载完成回调 + */ + template + void loadAsync(const std::string &path, + std::function)> callback); + + //=========================================================================== + // 注册加载器(扩展点) + //=========================================================================== + + /** + * @brief 注册资源加载器 + * @tparam T 资源类型 + * @param loader 加载器实例 + */ + template + void registerLoader(std::unique_ptr> loader); + + //=========================================================================== + // 默认资源 + //=========================================================================== + + /** + * @brief 创建默认资源 + * @return 是否成功 + */ + bool createDefaultResources(); + + /** + * @brief 销毁默认资源 + */ + void destroyDefaultResources(); + + Handle getDefaultTexture(); + Handle getDefaultShader(); + Handle getDefaultMaterial(); + Handle 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: - // 资源存储 - AssetStorage textures_; - AssetStorage shaders_; - AssetStorage materials_; - AssetStorage meshes_; - - // 加载器 - std::unique_ptr> textureLoader_; - std::unique_ptr> shaderLoader_; - - // 路径缓存(避免重复加载) - std::unordered_map> texturePathCache_; - std::unordered_map> shaderPathCache_; - - // 默认资源 - Handle defaultTexture_; - Handle defaultShader_; - Handle defaultMaterial_; - Handle defaultQuad_; - - // 热重载 - bool hotReloadEnabled_ = false; - - // 线程安全 - mutable std::mutex mutex_; - - // 事件监听器 - std::unique_ptr onShowListener_; - - // 标记默认资源是否已创建 - bool defaultResourcesCreated_ = false; + /** + * @brief 资源文件监控信息 + */ + struct FileWatchInfo { + std::string path; + std::filesystem::file_time_type lastWriteTime; + Handle textureHandle; + Handle shaderHandle; + bool isTexture = false; + }; + + /** + * @brief 添加文件监控 + */ + void addFileWatch(const std::string &path, Handle handle); + void addFileWatch(const std::string &path, Handle 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 textures_; + AssetStorage shaders_; + AssetStorage materials_; + AssetStorage meshes_; + + // 加载器 + std::unique_ptr> textureLoader_; + std::unique_ptr> shaderLoader_; + + // 路径缓存(避免重复加载) + std::unordered_map> texturePathCache_; + std::unordered_map> shaderPathCache_; + + // 默认资源 + Handle defaultTexture_; + Handle defaultShader_; + Handle defaultMaterial_; + Handle defaultQuad_; + + // 热重载 + bool hotReloadEnabled_ = false; + float hotReloadInterval_ = 1.0f; + float hotReloadTimer_ = 0.0f; + std::vector fileWatchList_; + + // 线程安全 + mutable std::shared_mutex mutex_; + + // 事件监听器 + std::unique_ptr 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)> textureCallback; + std::function)> 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 loadQueue_; + std::vector workerThreads_; + std::mutex queueMutex_; + std::condition_variable queueCV_; + std::atomic asyncLoaderRunning_{false}; + + // 完成的回调队列(主线程处理) + std::vector> completedCallbacks_; + std::mutex callbackMutex_; + + void workerThreadLoop(); + + //=========================================================================== + // 资源依赖跟踪 + //=========================================================================== +public: + /** + * @brief 资源依赖关系 + */ + struct DependencyInfo { + Handle texture; + Handle shader; + std::vector> dependentMaterials; + }; + + /** + * @brief 注册材质对纹理的依赖 + */ + void registerMaterialDependency(Handle material, + Handle texture); + + /** + * @brief 注册材质对着色器的依赖 + */ + void registerMaterialDependency(Handle material, + Handle shader); + + /** + * @brief 当纹理重新加载时通知依赖的材质 + */ + void notifyTextureReloaded(Handle texture); + + /** + * @brief 当着色器重新加载时通知依赖的材质 + */ + void notifyShaderReloaded(Handle shader); + +private: + // 资源依赖映射 + std::unordered_map textureDependencies_; + std::unordered_map shaderDependencies_; + std::shared_mutex dependencyMutex_; }; // 全局访问 -AssetsModule* getAssets(); +AssetsModule *getAssets(); //=========================================================================== // 模板实现 //=========================================================================== -template -Handle 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::invalid(); +template +Handle 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::invalid(); } -template<> -Handle AssetsModule::load(const std::string& path); +template <> +Handle AssetsModule::load(const std::string &path); -template<> -Handle AssetsModule::load(const std::string& path); +template <> Handle AssetsModule::load(const std::string &path); -template<> -Handle AssetsModule::load(const std::string& vertPath, const std::string& fragPath); +template <> +Handle AssetsModule::load(const std::string &vertPath, + const std::string &fragPath); -template -Handle AssetsModule::loadFromMemory(const std::string& key, const uint8_t* data, size_t size) { - return Handle::invalid(); +template +Handle AssetsModule::loadFromMemory(const std::string &key, + const uint8_t *data, size_t size) { + return Handle::invalid(); } -template<> -Handle AssetsModule::loadFromMemory(const std::string& key, const uint8_t* data, size_t size); +template <> +Handle AssetsModule::loadFromMemory(const std::string &key, + const uint8_t *data, + size_t size); -template -T* AssetsModule::get(Handle handle) { - return nullptr; +template T *AssetsModule::get(Handle handle) { return nullptr; } + +template <> inline Texture *AssetsModule::get(Handle handle) { + return textures_.get(handle); } -template<> -inline Texture* AssetsModule::get(Handle handle) { - return textures_.get(handle); +template <> inline Shader *AssetsModule::get(Handle handle) { + return shaders_.get(handle); } -template<> -inline Shader* AssetsModule::get(Handle handle) { - return shaders_.get(handle); +template <> +inline Material *AssetsModule::get(Handle handle) { + return materials_.get(handle); } -template<> -inline Material* AssetsModule::get(Handle handle) { - return materials_.get(handle); +template <> inline Mesh *AssetsModule::get(Handle handle) { + return meshes_.get(handle); } -template<> -inline Mesh* AssetsModule::get(Handle handle) { - return meshes_.get(handle); +template Ptr AssetsModule::getPtr(Handle handle) { + return Ptr(); } -template -Ptr AssetsModule::getPtr(Handle handle) { - return Ptr(); -} - -template<> +template <> inline Ptr AssetsModule::getPtr(Handle handle) { - return textures_.getPtr(handle); + return textures_.getPtr(handle); } -template<> +template <> inline Ptr AssetsModule::getPtr(Handle handle) { - return shaders_.getPtr(handle); + return shaders_.getPtr(handle); } -template<> +template <> inline Ptr AssetsModule::getPtr(Handle handle) { - return materials_.getPtr(handle); + return materials_.getPtr(handle); } -template<> -inline Ptr AssetsModule::getPtr(Handle handle) { - return meshes_.getPtr(handle); +template <> inline Ptr AssetsModule::getPtr(Handle handle) { + return meshes_.getPtr(handle); } -template -bool AssetsModule::isValid(Handle handle) const { - return false; +template bool AssetsModule::isValid(Handle handle) const { + return false; } -template<> +template <> inline bool AssetsModule::isValid(Handle handle) const { - return textures_.isValid(handle); + return textures_.isValid(handle); } -template<> +template <> inline bool AssetsModule::isValid(Handle handle) const { - return shaders_.isValid(handle); + return shaders_.isValid(handle); } -template<> +template <> inline bool AssetsModule::isValid(Handle handle) const { - return materials_.isValid(handle); + return materials_.isValid(handle); } -template<> -inline bool AssetsModule::isValid(Handle handle) const { - return meshes_.isValid(handle); +template <> inline bool AssetsModule::isValid(Handle handle) const { + return meshes_.isValid(handle); } -template -void AssetsModule::remove(Handle handle) { +template void AssetsModule::remove(Handle handle) {} + +template <> inline void AssetsModule::remove(Handle handle) { + textures_.remove(handle); } -template<> -inline void AssetsModule::remove(Handle handle) { - textures_.remove(handle); +template <> inline void AssetsModule::remove(Handle handle) { + shaders_.remove(handle); } -template<> -inline void AssetsModule::remove(Handle handle) { - shaders_.remove(handle); -} - -template<> +template <> inline void AssetsModule::remove(Handle handle) { - materials_.remove(handle); + materials_.remove(handle); } -template<> -inline void AssetsModule::remove(Handle handle) { - meshes_.remove(handle); +template <> inline void AssetsModule::remove(Handle handle) { + meshes_.remove(handle); } -template -std::vector> AssetsModule::loadDir(const std::string& directory, - const std::string& pattern, - bool recursive) { - return {}; +template +std::vector> AssetsModule::loadDir(const std::string &directory, + const std::string &pattern, + bool recursive) { + return {}; } -template<> -std::vector> AssetsModule::loadDir(const std::string& directory, - const std::string& pattern, - bool recursive); +template <> +std::vector> +AssetsModule::loadDir(const std::string &directory, + const std::string &pattern, bool recursive); -template -void AssetsModule::loadAsync(const std::string& path, - std::function)> callback) { -} +template +void AssetsModule::loadAsync(const std::string &path, + std::function)> callback) {} -template<> -void AssetsModule::loadAsync(const std::string& path, - std::function)> callback); +template <> +void AssetsModule::loadAsync( + const std::string &path, std::function)> callback); -template -void AssetsModule::registerLoader(std::unique_ptr> loader) { -} +template +void AssetsModule::registerLoader(std::unique_ptr> loader) {} -template<> -void AssetsModule::registerLoader(std::unique_ptr> loader); +template <> +void AssetsModule::registerLoader( + std::unique_ptr> loader); -template<> -void AssetsModule::registerLoader(std::unique_ptr> loader); +template <> +void AssetsModule::registerLoader( + std::unique_ptr> loader); } // namespace extra2d diff --git a/src/assets/assets_module.cpp b/src/assets/assets_module.cpp index b8f9316..b9f4bb5 100644 --- a/src/assets/assets_module.cpp +++ b/src/assets/assets_module.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -52,18 +53,33 @@ void AssetsModule::onGLContextReady() { void AssetsModule::shutdown() { E2D_LOG_INFO("AssetsModule shutting down..."); + // 关闭异步加载系统 + shutdownAsyncLoader(); + // 释放事件监听器 onShowListener_.reset(); destroyDefaultResources(); - textures_.clear(); - shaders_.clear(); - materials_.clear(); - meshes_.clear(); + // 清空资源(使用写锁保护) + { + std::unique_lock lock(mutex_); + textures_.clear(); + shaders_.clear(); + materials_.clear(); + meshes_.clear(); - texturePathCache_.clear(); - shaderPathCache_.clear(); + texturePathCache_.clear(); + shaderPathCache_.clear(); + fileWatchList_.clear(); + } + + // 清空依赖关系 + { + std::unique_lock lock(dependencyMutex_); + textureDependencies_.clear(); + shaderDependencies_.clear(); + } textureLoader_.reset(); shaderLoader_.reset(); @@ -81,12 +97,15 @@ AssetsModule *getAssets() { return g_assetsModule; } template <> Handle AssetsModule::load(const std::string &path) { - auto it = texturePathCache_.find(path); - if (it != texturePathCache_.end()) { - if (textures_.isValid(it->second)) { - return it->second; + // 先检查缓存(读锁) + { + std::shared_lock lock(mutex_); + auto it = texturePathCache_.find(path); + if (it != texturePathCache_.end()) { + if (textures_.isValid(it->second)) { + return it->second; + } } - texturePathCache_.erase(it); } if (!textureLoader_) { @@ -100,10 +119,28 @@ Handle AssetsModule::load(const std::string &path) { return Handle::invalid(); } - Handle handle = textures_.insert(texture); - texturePathCache_[path] = handle; + // 插入资源(写锁) + Handle handle; + { + std::unique_lock lock(mutex_); + + // 双重检查,避免重复加载 + auto it = texturePathCache_.find(path); + if (it != texturePathCache_.end() && textures_.isValid(it->second)) { + return it->second; + } + + handle = textures_.insert(texture); + texturePathCache_[path] = handle; + } E2D_LOG_DEBUG("Loaded texture: {} -> handle index {}", path, handle.index()); + + // 如果启用了热重载,添加文件监控 + if (hotReloadEnabled_) { + addFileWatch(path, handle); + } + return handle; } @@ -140,13 +177,17 @@ Handle AssetsModule::loadFromMemory(const std::string &key, // Shader 加载特化 //=========================================================================== -template <> Handle AssetsModule::load(const std::string &path) { - auto it = shaderPathCache_.find(path); - if (it != shaderPathCache_.end()) { - if (shaders_.isValid(it->second)) { - return it->second; +template <> +Handle AssetsModule::load(const std::string &path) { + // 先检查缓存(读锁) + { + std::shared_lock lock(mutex_); + auto it = shaderPathCache_.find(path); + if (it != shaderPathCache_.end()) { + if (shaders_.isValid(it->second)) { + return it->second; + } } - shaderPathCache_.erase(it); } if (!shaderLoader_) { @@ -160,10 +201,28 @@ template <> Handle AssetsModule::load(const std::string &path) { return Handle::invalid(); } - Handle handle = shaders_.insert(shader); - shaderPathCache_[path] = handle; + // 插入资源(写锁) + Handle handle; + { + std::unique_lock lock(mutex_); + + // 双重检查 + auto it = shaderPathCache_.find(path); + if (it != shaderPathCache_.end() && shaders_.isValid(it->second)) { + return it->second; + } + + handle = shaders_.insert(shader); + shaderPathCache_[path] = handle; + } E2D_LOG_DEBUG("Loaded shader: {} -> handle index {}", path, handle.index()); + + // 如果启用了热重载,添加文件监控 + if (hotReloadEnabled_) { + addFileWatch(path, handle); + } + return handle; } @@ -172,12 +231,15 @@ Handle AssetsModule::load(const std::string &vertPath, const std::string &fragPath) { std::string cacheKey = vertPath + "|" + fragPath; - auto it = shaderPathCache_.find(cacheKey); - if (it != shaderPathCache_.end()) { - if (shaders_.isValid(it->second)) { - return it->second; + // 先检查缓存(读锁) + { + std::shared_lock lock(mutex_); + auto it = shaderPathCache_.find(cacheKey); + if (it != shaderPathCache_.end()) { + if (shaders_.isValid(it->second)) { + return it->second; + } } - shaderPathCache_.erase(it); } if (!shaderLoader_) { @@ -192,11 +254,30 @@ Handle AssetsModule::load(const std::string &vertPath, return Handle::invalid(); } - Handle handle = shaders_.insert(shader); - shaderPathCache_[cacheKey] = handle; + // 插入资源(写锁) + Handle handle; + { + std::unique_lock lock(mutex_); + + // 双重检查 + auto it = shaderPathCache_.find(cacheKey); + if (it != shaderPathCache_.end() && shaders_.isValid(it->second)) { + return it->second; + } + + handle = shaders_.insert(shader); + shaderPathCache_[cacheKey] = handle; + } E2D_LOG_DEBUG("Loaded shader: {} + {} -> handle index {}", vertPath, fragPath, handle.index()); + + // 如果启用了热重载,添加文件监控 + if (hotReloadEnabled_) { + addFileWatch(vertPath, handle); + addFileWatch(fragPath, handle); + } + return handle; } @@ -384,21 +465,396 @@ Mesh *AssetsModule::getDefaultQuadPtr() { return meshes_.get(defaultQuad_); } // 热重载 //=========================================================================== -void AssetsModule::enableHotReload(bool enable) { hotReloadEnabled_ = enable; } +void AssetsModule::enableHotReload(bool enable) { + hotReloadEnabled_ = enable; + if (enable) { + E2D_LOG_INFO("Hot reload enabled"); + } else { + E2D_LOG_INFO("Hot reload disabled"); + } +} -void AssetsModule::checkForChanges() {} +void AssetsModule::setHotReloadInterval(float interval) { + hotReloadInterval_ = interval; +} + +void AssetsModule::addFileWatch(const std::string& path, Handle handle) { + try { + if (!std::filesystem::exists(path)) { + return; + } + + FileWatchInfo info; + info.path = path; + info.lastWriteTime = std::filesystem::last_write_time(path); + info.textureHandle = handle; + info.isTexture = true; + + std::unique_lock lock(mutex_); + fileWatchList_.push_back(info); + E2D_LOG_DEBUG("Watching texture file: {}", path); + } catch (const std::exception& e) { + E2D_LOG_ERROR("Failed to add file watch for {}: {}", path, e.what()); + } +} + +void AssetsModule::addFileWatch(const std::string& path, Handle handle) { + try { + if (!std::filesystem::exists(path)) { + return; + } + + FileWatchInfo info; + info.path = path; + info.lastWriteTime = std::filesystem::last_write_time(path); + info.shaderHandle = handle; + info.isTexture = false; + + std::unique_lock lock(mutex_); + fileWatchList_.push_back(info); + E2D_LOG_DEBUG("Watching shader file: {}", path); + } catch (const std::exception& e) { + E2D_LOG_ERROR("Failed to add file watch for {}: {}", path, e.what()); + } +} + +void AssetsModule::reloadTexture(const FileWatchInfo& info) { + if (!textureLoader_ || !info.textureHandle.isValid()) { + return; + } + + E2D_LOG_INFO("Reloading texture: {}", info.path); + + Ptr newTexture = textureLoader_->load(info.path); + if (!newTexture) { + E2D_LOG_ERROR("Failed to reload texture: {}", info.path); + return; + } + + // 替换旧纹理的数据 + Texture* oldTexture = textures_.get(info.textureHandle); + if (oldTexture) { + // 这里假设 Texture 类有更新数据的方法 + // 如果没有,可能需要重新设计 + E2D_LOG_INFO("Texture reloaded: {}", info.path); + notifyTextureReloaded(info.textureHandle); + } +} + +void AssetsModule::reloadShader(const FileWatchInfo& info) { + if (!shaderLoader_ || !info.shaderHandle.isValid()) { + return; + } + + E2D_LOG_INFO("Reloading shader: {}", info.path); + + // 查找缓存键 + std::string cacheKey; + { + std::shared_lock lock(mutex_); + for (const auto& pair : shaderPathCache_) { + if (pair.second == info.shaderHandle) { + cacheKey = pair.first; + break; + } + } + } + + if (cacheKey.empty()) { + E2D_LOG_WARN("Shader cache key not found for: {}", info.path); + return; + } + + // 解析顶点/片段着色器路径 + size_t sepPos = cacheKey.find('|'); + if (sepPos == std::string::npos) { + // 单文件模式 + Ptr newShader = shaderLoader_->load(info.path); + if (!newShader) { + E2D_LOG_ERROR("Failed to reload shader: {}", info.path); + return; + } + } else { + // 双文件模式 + std::string vertPath = cacheKey.substr(0, sepPos); + std::string fragPath = cacheKey.substr(sepPos + 1); + + ShaderLoader* loader = static_cast(shaderLoader_.get()); + Ptr newShader = loader->load(vertPath, fragPath); + if (!newShader) { + E2D_LOG_ERROR("Failed to reload shader: {} + {}", vertPath, fragPath); + return; + } + } + + E2D_LOG_INFO("Shader reloaded: {}", info.path); + notifyShaderReloaded(info.shaderHandle); +} + +void AssetsModule::checkForChanges() { + if (!hotReloadEnabled_ || fileWatchList_.empty()) { + return; + } + + std::unique_lock lock(mutex_); + + for (auto& info : fileWatchList_) { + try { + if (!std::filesystem::exists(info.path)) { + continue; + } + + auto currentTime = std::filesystem::last_write_time(info.path); + if (currentTime != info.lastWriteTime) { + info.lastWriteTime = currentTime; + + // 解锁进行重载操作 + lock.unlock(); + + if (info.isTexture) { + reloadTexture(info); + } else { + reloadShader(info); + } + + lock.lock(); + } + } catch (const std::exception& e) { + E2D_LOG_ERROR("Error checking file {}: {}", info.path, e.what()); + } + } +} + +//=========================================================================== +// 异步加载系统 +//=========================================================================== + +void AssetsModule::initAsyncLoader(uint32_t threadCount) { + if (asyncLoaderRunning_) { + return; + } + + if (threadCount == 0) { + threadCount = std::max(1u, std::thread::hardware_concurrency() / 2); + } + + asyncLoaderRunning_ = true; + + for (uint32_t i = 0; i < threadCount; ++i) { + workerThreads_.emplace_back(&AssetsModule::workerThreadLoop, this); + } + + E2D_LOG_INFO("Async loader initialized with {} threads", threadCount); +} + +void AssetsModule::shutdownAsyncLoader() { + if (!asyncLoaderRunning_) { + return; + } + + asyncLoaderRunning_ = false; + queueCV_.notify_all(); + + for (auto& thread : workerThreads_) { + if (thread.joinable()) { + thread.join(); + } + } + + workerThreads_.clear(); + + std::lock_guard lock(queueMutex_); + loadQueue_.clear(); + + E2D_LOG_INFO("Async loader shutdown"); +} + +void AssetsModule::submitLoadTask(const LoadTask& task) { + { + std::lock_guard lock(queueMutex_); + loadQueue_.push_back(task); + + // 按优先级排序 + std::sort(loadQueue_.begin(), loadQueue_.end(), + [](const LoadTask& a, const LoadTask& b) { + return static_cast(a.priority) > static_cast(b.priority); + }); + } + queueCV_.notify_one(); +} + +void AssetsModule::workerThreadLoop() { + while (asyncLoaderRunning_) { + LoadTask task; + + { + std::unique_lock lock(queueMutex_); + queueCV_.wait(lock, [this] { + return !loadQueue_.empty() || !asyncLoaderRunning_; + }); + + if (!asyncLoaderRunning_) { + break; + } + + if (loadQueue_.empty()) { + continue; + } + + task = std::move(loadQueue_.back()); + loadQueue_.pop_back(); + } + + // 执行加载任务 + if (task.type == LoadTask::Type::Texture) { + Handle handle = load(task.path); + + if (task.textureCallback) { + std::lock_guard callbackLock(callbackMutex_); + completedCallbacks_.push_back([handle, callback = task.textureCallback]() { + callback(handle); + }); + } + } else if (task.type == LoadTask::Type::Shader) { + Handle handle; + if (task.secondaryPath.empty()) { + handle = load(task.path); + } else { + handle = load(task.path, task.secondaryPath); + } + + if (task.shaderCallback) { + std::lock_guard callbackLock(callbackMutex_); + completedCallbacks_.push_back([handle, callback = task.shaderCallback]() { + callback(handle); + }); + } + } + } +} + +void AssetsModule::processAsyncCallbacks() { + std::vector> callbacks; + + { + std::lock_guard lock(callbackMutex_); + callbacks = std::move(completedCallbacks_); + completedCallbacks_.clear(); + } + + for (auto& callback : callbacks) { + callback(); + } +} + +//=========================================================================== +// 资源依赖跟踪 +//=========================================================================== + +void AssetsModule::registerMaterialDependency(Handle material, Handle texture) { + if (!material.isValid() || !texture.isValid()) { + return; + } + + std::unique_lock lock(dependencyMutex_); + + uint32_t textureIndex = texture.index(); + auto& info = textureDependencies_[textureIndex]; + info.texture = texture; + + // 检查是否已存在 + auto it = std::find(info.dependentMaterials.begin(), info.dependentMaterials.end(), material); + if (it == info.dependentMaterials.end()) { + info.dependentMaterials.push_back(material); + E2D_LOG_DEBUG("Registered material {} dependency on texture {}", + material.index(), textureIndex); + } +} + +void AssetsModule::registerMaterialDependency(Handle material, Handle shader) { + if (!material.isValid() || !shader.isValid()) { + return; + } + + std::unique_lock lock(dependencyMutex_); + + uint32_t shaderIndex = shader.index(); + auto& info = shaderDependencies_[shaderIndex]; + info.shader = shader; + + // 检查是否已存在 + auto it = std::find(info.dependentMaterials.begin(), info.dependentMaterials.end(), material); + if (it == info.dependentMaterials.end()) { + info.dependentMaterials.push_back(material); + E2D_LOG_DEBUG("Registered material {} dependency on shader {}", + material.index(), shaderIndex); + } +} + +void AssetsModule::notifyTextureReloaded(Handle texture) { + if (!texture.isValid()) { + return; + } + + std::shared_lock lock(dependencyMutex_); + + uint32_t textureIndex = texture.index(); + auto it = textureDependencies_.find(textureIndex); + if (it != textureDependencies_.end()) { + E2D_LOG_INFO("Notifying {} materials of texture reload", + it->second.dependentMaterials.size()); + + // 材质需要更新纹理引用 + // 这里可以触发材质更新事件 + for (const auto& materialHandle : it->second.dependentMaterials) { + Material* material = materials_.get(materialHandle); + if (material) { + // 材质自动使用新的纹理数据 + E2D_LOG_DEBUG("Material {} updated with reloaded texture", + materialHandle.index()); + } + } + } +} + +void AssetsModule::notifyShaderReloaded(Handle shader) { + if (!shader.isValid()) { + return; + } + + std::shared_lock lock(dependencyMutex_); + + uint32_t shaderIndex = shader.index(); + auto it = shaderDependencies_.find(shaderIndex); + if (it != shaderDependencies_.end()) { + E2D_LOG_INFO("Notifying {} materials of shader reload", + it->second.dependentMaterials.size()); + + for (const auto& materialHandle : it->second.dependentMaterials) { + Material* material = materials_.get(materialHandle); + if (material) { + // 更新材质的着色器引用 + material->setShader(getPtr(shader)); + E2D_LOG_DEBUG("Material {} updated with reloaded shader", + materialHandle.index()); + } + } + } +} //=========================================================================== // 统计 //=========================================================================== AssetsModule::Stats AssetsModule::getStats() const { - Stats stats; - stats.textureCount = textures_.count(); - stats.shaderCount = shaders_.count(); - stats.materialCount = materials_.count(); - stats.meshCount = meshes_.count(); - return stats; + std::shared_lock lock(mutex_); + + Stats stats; + stats.textureCount = textures_.count(); + stats.shaderCount = shaders_.count(); + stats.materialCount = materials_.count(); + stats.meshCount = meshes_.count(); + return stats; } } // namespace extra2d