feat(assets): 实现资源热重载和异步加载系统

添加资源热重载功能,支持监控文件变更并自动重新加载
实现异步加载系统,支持多线程资源加载和回调处理
增加资源依赖跟踪机制,自动更新依赖材质的引用
使用读写锁保护资源存储,提升多线程访问性能
This commit is contained in:
ChestnutYueyue 2026-03-02 22:54:06 +08:00
parent 46ec1c665f
commit f7e4f89cca
2 changed files with 925 additions and 345 deletions

View File

@ -1,19 +1,25 @@
#pragma once #pragma once
#include <event/events.h>
#include <module/module.h>
#include <module/module_registry.h>
#include <assets/handle.h>
#include <assets/asset_storage.h>
#include <assets/asset_loader.h> #include <assets/asset_loader.h>
#include <renderer/texture.h> #include <assets/asset_storage.h>
#include <renderer/shader.h> #include <assets/handle.h>
#include <renderer/material.h> #include <atomic>
#include <renderer/mesh.h> #include <chrono>
#include <condition_variable>
#include <event/events.h>
#include <filesystem>
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <module/module.h>
#include <module/module_registry.h>
#include <mutex> #include <mutex>
#include <renderer/material.h>
#include <renderer/mesh.h>
#include <renderer/shader.h>
#include <renderer/texture.h>
#include <shared_mutex>
#include <string> #include <string>
#include <thread>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
@ -59,8 +65,8 @@ public:
* @param path * @param path
* @return * @return
*/ */
template<typename T, typename... Args> template <typename T, typename... Args>
Handle<T> load(const std::string& path, Args&&... args); Handle<T> load(const std::string &path, Args &&...args);
/** /**
* @brief * @brief
@ -70,8 +76,9 @@ public:
* @param size * @param size
* @return * @return
*/ */
template<typename T> template <typename T>
Handle<T> loadFromMemory(const std::string& key, const uint8_t* data, size_t size); Handle<T> loadFromMemory(const std::string &key, const uint8_t *data,
size_t size);
/** /**
* @brief nullptr * @brief nullptr
@ -84,26 +91,22 @@ public:
* } * }
* @endcode * @endcode
*/ */
template<typename T> template <typename T> T *get(Handle<T> handle);
T* get(Handle<T> handle);
/** /**
* @brief * @brief
*/ */
template<typename T> template <typename T> Ptr<T> getPtr(Handle<T> handle);
Ptr<T> getPtr(Handle<T> handle);
/** /**
* @brief * @brief
*/ */
template<typename T> template <typename T> bool isValid(Handle<T> handle) const;
bool isValid(Handle<T> handle) const;
/** /**
* @brief * @brief
*/ */
template<typename T> template <typename T> void remove(Handle<T> handle);
void remove(Handle<T> handle);
//=========================================================================== //===========================================================================
// 批量加载(目录/包) // 批量加载(目录/包)
@ -117,9 +120,9 @@ public:
* @param recursive * @param recursive
* @return * @return
*/ */
template<typename T> template <typename T>
std::vector<Handle<T>> loadDir(const std::string& directory, std::vector<Handle<T>> loadDir(const std::string &directory,
const std::string& pattern = "*", const std::string &pattern = "*",
bool recursive = true); bool recursive = true);
//=========================================================================== //===========================================================================
@ -132,8 +135,8 @@ public:
* @param path * @param path
* @param callback * @param callback
*/ */
template<typename T> template <typename T>
void loadAsync(const std::string& path, void loadAsync(const std::string &path,
std::function<void(Handle<T>)> callback); std::function<void(Handle<T>)> callback);
//=========================================================================== //===========================================================================
@ -145,7 +148,7 @@ public:
* @tparam T * @tparam T
* @param loader * @param loader
*/ */
template<typename T> template <typename T>
void registerLoader(std::unique_ptr<AssetLoader<T>> loader); void registerLoader(std::unique_ptr<AssetLoader<T>> loader);
//=========================================================================== //===========================================================================
@ -168,10 +171,10 @@ public:
Handle<Material> getDefaultMaterial(); Handle<Material> getDefaultMaterial();
Handle<Mesh> getDefaultQuad(); Handle<Mesh> getDefaultQuad();
Texture* getDefaultTexturePtr(); Texture *getDefaultTexturePtr();
Shader* getDefaultShaderPtr(); Shader *getDefaultShaderPtr();
Material* getDefaultMaterialPtr(); Material *getDefaultMaterialPtr();
Mesh* getDefaultQuadPtr(); Mesh *getDefaultQuadPtr();
//=========================================================================== //===========================================================================
// 热重载 // 热重载
@ -188,6 +191,41 @@ public:
*/ */
void checkForChanges(); 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:
//=========================================================================== //===========================================================================
// 统计 // 统计
//=========================================================================== //===========================================================================
@ -224,176 +262,262 @@ private:
// 热重载 // 热重载
bool hotReloadEnabled_ = false; bool hotReloadEnabled_ = false;
float hotReloadInterval_ = 1.0f;
float hotReloadTimer_ = 0.0f;
std::vector<FileWatchInfo> fileWatchList_;
// 线程安全 // 线程安全
mutable std::mutex mutex_; mutable std::shared_mutex mutex_;
// 事件监听器 // 事件监听器
std::unique_ptr<events::OnShow::Listener> onShowListener_; std::unique_ptr<events::OnShow::Listener> onShowListener_;
// 标记默认资源是否已创建 // 标记默认资源是否已创建
bool defaultResourcesCreated_ = false; 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(); AssetsModule *getAssets();
//=========================================================================== //===========================================================================
// 模板实现 // 模板实现
//=========================================================================== //===========================================================================
template<typename T, typename... Args> template <typename T, typename... Args>
Handle<T> AssetsModule::load(const std::string& path, Args&&... args) { Handle<T> AssetsModule::load(const std::string &path, Args &&...args) {
static_assert(sizeof...(Args) == 0 || sizeof...(Args) == 1, static_assert(sizeof...(Args) == 0 || sizeof...(Args) == 1,
"load() accepts 0 or 1 additional arguments"); "load() accepts 0 or 1 additional arguments");
return Handle<T>::invalid(); return Handle<T>::invalid();
} }
template<> template <>
Handle<Texture> AssetsModule::load<Texture>(const std::string& path); Handle<Texture> AssetsModule::load<Texture>(const std::string &path);
template<> template <> Handle<Shader> AssetsModule::load<Shader>(const std::string &path);
Handle<Shader> AssetsModule::load<Shader>(const std::string& path);
template<> template <>
Handle<Shader> AssetsModule::load<Shader>(const std::string& vertPath, const std::string& fragPath); Handle<Shader> AssetsModule::load<Shader>(const std::string &vertPath,
const std::string &fragPath);
template<typename T> template <typename T>
Handle<T> AssetsModule::loadFromMemory(const std::string& key, const uint8_t* data, size_t size) { Handle<T> AssetsModule::loadFromMemory(const std::string &key,
const uint8_t *data, size_t size) {
return Handle<T>::invalid(); return Handle<T>::invalid();
} }
template<> template <>
Handle<Texture> AssetsModule::loadFromMemory<Texture>(const std::string& key, const uint8_t* data, size_t size); Handle<Texture> AssetsModule::loadFromMemory<Texture>(const std::string &key,
const uint8_t *data,
size_t size);
template<typename T> template <typename T> T *AssetsModule::get(Handle<T> handle) { return nullptr; }
T* AssetsModule::get(Handle<T> handle) {
return nullptr;
}
template<> template <> inline Texture *AssetsModule::get<Texture>(Handle<Texture> handle) {
inline Texture* AssetsModule::get<Texture>(Handle<Texture> handle) {
return textures_.get(handle); return textures_.get(handle);
} }
template<> template <> inline Shader *AssetsModule::get<Shader>(Handle<Shader> handle) {
inline Shader* AssetsModule::get<Shader>(Handle<Shader> handle) {
return shaders_.get(handle); return shaders_.get(handle);
} }
template<> template <>
inline Material* AssetsModule::get<Material>(Handle<Material> handle) { inline Material *AssetsModule::get<Material>(Handle<Material> handle) {
return materials_.get(handle); return materials_.get(handle);
} }
template<> template <> inline Mesh *AssetsModule::get<Mesh>(Handle<Mesh> handle) {
inline Mesh* AssetsModule::get<Mesh>(Handle<Mesh> handle) {
return meshes_.get(handle); return meshes_.get(handle);
} }
template<typename T> template <typename T> Ptr<T> AssetsModule::getPtr(Handle<T> handle) {
Ptr<T> AssetsModule::getPtr(Handle<T> handle) {
return Ptr<T>(); return Ptr<T>();
} }
template<> template <>
inline Ptr<Texture> AssetsModule::getPtr<Texture>(Handle<Texture> handle) { inline Ptr<Texture> AssetsModule::getPtr<Texture>(Handle<Texture> handle) {
return textures_.getPtr(handle); return textures_.getPtr(handle);
} }
template<> template <>
inline Ptr<Shader> AssetsModule::getPtr<Shader>(Handle<Shader> handle) { inline Ptr<Shader> AssetsModule::getPtr<Shader>(Handle<Shader> handle) {
return shaders_.getPtr(handle); return shaders_.getPtr(handle);
} }
template<> template <>
inline Ptr<Material> AssetsModule::getPtr<Material>(Handle<Material> handle) { inline Ptr<Material> AssetsModule::getPtr<Material>(Handle<Material> handle) {
return materials_.getPtr(handle); return materials_.getPtr(handle);
} }
template<> template <> inline Ptr<Mesh> AssetsModule::getPtr<Mesh>(Handle<Mesh> handle) {
inline Ptr<Mesh> AssetsModule::getPtr<Mesh>(Handle<Mesh> handle) {
return meshes_.getPtr(handle); return meshes_.getPtr(handle);
} }
template<typename T> template <typename T> bool AssetsModule::isValid(Handle<T> handle) const {
bool AssetsModule::isValid(Handle<T> handle) const {
return false; return false;
} }
template<> template <>
inline bool AssetsModule::isValid<Texture>(Handle<Texture> handle) const { inline bool AssetsModule::isValid<Texture>(Handle<Texture> handle) const {
return textures_.isValid(handle); return textures_.isValid(handle);
} }
template<> template <>
inline bool AssetsModule::isValid<Shader>(Handle<Shader> handle) const { inline bool AssetsModule::isValid<Shader>(Handle<Shader> handle) const {
return shaders_.isValid(handle); return shaders_.isValid(handle);
} }
template<> template <>
inline bool AssetsModule::isValid<Material>(Handle<Material> handle) const { inline bool AssetsModule::isValid<Material>(Handle<Material> handle) const {
return materials_.isValid(handle); return materials_.isValid(handle);
} }
template<> template <> inline bool AssetsModule::isValid<Mesh>(Handle<Mesh> handle) const {
inline bool AssetsModule::isValid<Mesh>(Handle<Mesh> handle) const {
return meshes_.isValid(handle); return meshes_.isValid(handle);
} }
template<typename T> template <typename T> void AssetsModule::remove(Handle<T> handle) {}
void AssetsModule::remove(Handle<T> handle) {
}
template<> template <> inline void AssetsModule::remove<Texture>(Handle<Texture> handle) {
inline void AssetsModule::remove<Texture>(Handle<Texture> handle) {
textures_.remove(handle); textures_.remove(handle);
} }
template<> template <> inline void AssetsModule::remove<Shader>(Handle<Shader> handle) {
inline void AssetsModule::remove<Shader>(Handle<Shader> handle) {
shaders_.remove(handle); shaders_.remove(handle);
} }
template<> template <>
inline void AssetsModule::remove<Material>(Handle<Material> handle) { inline void AssetsModule::remove<Material>(Handle<Material> handle) {
materials_.remove(handle); materials_.remove(handle);
} }
template<> template <> inline void AssetsModule::remove<Mesh>(Handle<Mesh> handle) {
inline void AssetsModule::remove<Mesh>(Handle<Mesh> handle) {
meshes_.remove(handle); meshes_.remove(handle);
} }
template<typename T> template <typename T>
std::vector<Handle<T>> AssetsModule::loadDir(const std::string& directory, std::vector<Handle<T>> AssetsModule::loadDir(const std::string &directory,
const std::string& pattern, const std::string &pattern,
bool recursive) { bool recursive) {
return {}; return {};
} }
template<> template <>
std::vector<Handle<Texture>> AssetsModule::loadDir<Texture>(const std::string& directory, std::vector<Handle<Texture>>
const std::string& pattern, AssetsModule::loadDir<Texture>(const std::string &directory,
bool recursive); const std::string &pattern, bool recursive);
template<typename T> template <typename T>
void AssetsModule::loadAsync(const std::string& path, void AssetsModule::loadAsync(const std::string &path,
std::function<void(Handle<T>)> callback) { std::function<void(Handle<T>)> callback) {}
}
template<> template <>
void AssetsModule::loadAsync<Texture>(const std::string& path, void AssetsModule::loadAsync<Texture>(
std::function<void(Handle<Texture>)> callback); const std::string &path, std::function<void(Handle<Texture>)> callback);
template<typename T> template <typename T>
void AssetsModule::registerLoader(std::unique_ptr<AssetLoader<T>> loader) { void AssetsModule::registerLoader(std::unique_ptr<AssetLoader<T>> loader) {}
}
template<> template <>
void AssetsModule::registerLoader<Texture>(std::unique_ptr<AssetLoader<Texture>> loader); void AssetsModule::registerLoader<Texture>(
std::unique_ptr<AssetLoader<Texture>> loader);
template<> template <>
void AssetsModule::registerLoader<Shader>(std::unique_ptr<AssetLoader<Shader>> loader); void AssetsModule::registerLoader<Shader>(
std::unique_ptr<AssetLoader<Shader>> loader);
} // namespace extra2d } // namespace extra2d

View File

@ -2,6 +2,7 @@
#include <assets/loaders/shader_loader.h> #include <assets/loaders/shader_loader.h>
#include <assets/loaders/texture_loader.h> #include <assets/loaders/texture_loader.h>
#include <event/events.h> #include <event/events.h>
#include <algorithm>
#include <filesystem> #include <filesystem>
#include <thread> #include <thread>
#include <utils/logger.h> #include <utils/logger.h>
@ -52,11 +53,17 @@ void AssetsModule::onGLContextReady() {
void AssetsModule::shutdown() { void AssetsModule::shutdown() {
E2D_LOG_INFO("AssetsModule shutting down..."); E2D_LOG_INFO("AssetsModule shutting down...");
// 关闭异步加载系统
shutdownAsyncLoader();
// 释放事件监听器 // 释放事件监听器
onShowListener_.reset(); onShowListener_.reset();
destroyDefaultResources(); destroyDefaultResources();
// 清空资源(使用写锁保护)
{
std::unique_lock<std::shared_mutex> lock(mutex_);
textures_.clear(); textures_.clear();
shaders_.clear(); shaders_.clear();
materials_.clear(); materials_.clear();
@ -64,6 +71,15 @@ void AssetsModule::shutdown() {
texturePathCache_.clear(); texturePathCache_.clear();
shaderPathCache_.clear(); shaderPathCache_.clear();
fileWatchList_.clear();
}
// 清空依赖关系
{
std::unique_lock<std::shared_mutex> lock(dependencyMutex_);
textureDependencies_.clear();
shaderDependencies_.clear();
}
textureLoader_.reset(); textureLoader_.reset();
shaderLoader_.reset(); shaderLoader_.reset();
@ -81,12 +97,15 @@ AssetsModule *getAssets() { return g_assetsModule; }
template <> template <>
Handle<Texture> AssetsModule::load<Texture>(const std::string &path) { Handle<Texture> AssetsModule::load<Texture>(const std::string &path) {
// 先检查缓存(读锁)
{
std::shared_lock<std::shared_mutex> lock(mutex_);
auto it = texturePathCache_.find(path); auto it = texturePathCache_.find(path);
if (it != texturePathCache_.end()) { if (it != texturePathCache_.end()) {
if (textures_.isValid(it->second)) { if (textures_.isValid(it->second)) {
return it->second; return it->second;
} }
texturePathCache_.erase(it); }
} }
if (!textureLoader_) { if (!textureLoader_) {
@ -100,10 +119,28 @@ Handle<Texture> AssetsModule::load<Texture>(const std::string &path) {
return Handle<Texture>::invalid(); return Handle<Texture>::invalid();
} }
Handle<Texture> handle = textures_.insert(texture); // 插入资源(写锁)
Handle<Texture> handle;
{
std::unique_lock<std::shared_mutex> 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; texturePathCache_[path] = handle;
}
E2D_LOG_DEBUG("Loaded texture: {} -> handle index {}", path, handle.index()); E2D_LOG_DEBUG("Loaded texture: {} -> handle index {}", path, handle.index());
// 如果启用了热重载,添加文件监控
if (hotReloadEnabled_) {
addFileWatch(path, handle);
}
return handle; return handle;
} }
@ -140,13 +177,17 @@ Handle<Texture> AssetsModule::loadFromMemory<Texture>(const std::string &key,
// Shader 加载特化 // Shader 加载特化
//=========================================================================== //===========================================================================
template <> Handle<Shader> AssetsModule::load<Shader>(const std::string &path) { template <>
Handle<Shader> AssetsModule::load<Shader>(const std::string &path) {
// 先检查缓存(读锁)
{
std::shared_lock<std::shared_mutex> lock(mutex_);
auto it = shaderPathCache_.find(path); auto it = shaderPathCache_.find(path);
if (it != shaderPathCache_.end()) { if (it != shaderPathCache_.end()) {
if (shaders_.isValid(it->second)) { if (shaders_.isValid(it->second)) {
return it->second; return it->second;
} }
shaderPathCache_.erase(it); }
} }
if (!shaderLoader_) { if (!shaderLoader_) {
@ -160,10 +201,28 @@ template <> Handle<Shader> AssetsModule::load<Shader>(const std::string &path) {
return Handle<Shader>::invalid(); return Handle<Shader>::invalid();
} }
Handle<Shader> handle = shaders_.insert(shader); // 插入资源(写锁)
Handle<Shader> handle;
{
std::unique_lock<std::shared_mutex> 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; shaderPathCache_[path] = handle;
}
E2D_LOG_DEBUG("Loaded shader: {} -> handle index {}", path, handle.index()); E2D_LOG_DEBUG("Loaded shader: {} -> handle index {}", path, handle.index());
// 如果启用了热重载,添加文件监控
if (hotReloadEnabled_) {
addFileWatch(path, handle);
}
return handle; return handle;
} }
@ -172,12 +231,15 @@ Handle<Shader> AssetsModule::load<Shader>(const std::string &vertPath,
const std::string &fragPath) { const std::string &fragPath) {
std::string cacheKey = vertPath + "|" + fragPath; std::string cacheKey = vertPath + "|" + fragPath;
// 先检查缓存(读锁)
{
std::shared_lock<std::shared_mutex> lock(mutex_);
auto it = shaderPathCache_.find(cacheKey); auto it = shaderPathCache_.find(cacheKey);
if (it != shaderPathCache_.end()) { if (it != shaderPathCache_.end()) {
if (shaders_.isValid(it->second)) { if (shaders_.isValid(it->second)) {
return it->second; return it->second;
} }
shaderPathCache_.erase(it); }
} }
if (!shaderLoader_) { if (!shaderLoader_) {
@ -192,11 +254,30 @@ Handle<Shader> AssetsModule::load<Shader>(const std::string &vertPath,
return Handle<Shader>::invalid(); return Handle<Shader>::invalid();
} }
Handle<Shader> handle = shaders_.insert(shader); // 插入资源(写锁)
Handle<Shader> handle;
{
std::unique_lock<std::shared_mutex> 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; shaderPathCache_[cacheKey] = handle;
}
E2D_LOG_DEBUG("Loaded shader: {} + {} -> handle index {}", vertPath, fragPath, E2D_LOG_DEBUG("Loaded shader: {} + {} -> handle index {}", vertPath, fragPath,
handle.index()); handle.index());
// 如果启用了热重载,添加文件监控
if (hotReloadEnabled_) {
addFileWatch(vertPath, handle);
addFileWatch(fragPath, handle);
}
return handle; return handle;
} }
@ -384,15 +465,390 @@ 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<Texture> 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<std::shared_mutex> 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<Shader> 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<std::shared_mutex> 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<Texture> 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<std::shared_mutex> 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<Shader> 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*>(shaderLoader_.get());
Ptr<Shader> 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<std::shared_mutex> 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<std::mutex> lock(queueMutex_);
loadQueue_.clear();
E2D_LOG_INFO("Async loader shutdown");
}
void AssetsModule::submitLoadTask(const LoadTask& task) {
{
std::lock_guard<std::mutex> lock(queueMutex_);
loadQueue_.push_back(task);
// 按优先级排序
std::sort(loadQueue_.begin(), loadQueue_.end(),
[](const LoadTask& a, const LoadTask& b) {
return static_cast<int>(a.priority) > static_cast<int>(b.priority);
});
}
queueCV_.notify_one();
}
void AssetsModule::workerThreadLoop() {
while (asyncLoaderRunning_) {
LoadTask task;
{
std::unique_lock<std::mutex> 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<Texture> handle = load<Texture>(task.path);
if (task.textureCallback) {
std::lock_guard<std::mutex> callbackLock(callbackMutex_);
completedCallbacks_.push_back([handle, callback = task.textureCallback]() {
callback(handle);
});
}
} else if (task.type == LoadTask::Type::Shader) {
Handle<Shader> handle;
if (task.secondaryPath.empty()) {
handle = load<Shader>(task.path);
} else {
handle = load<Shader>(task.path, task.secondaryPath);
}
if (task.shaderCallback) {
std::lock_guard<std::mutex> callbackLock(callbackMutex_);
completedCallbacks_.push_back([handle, callback = task.shaderCallback]() {
callback(handle);
});
}
}
}
}
void AssetsModule::processAsyncCallbacks() {
std::vector<std::function<void()>> callbacks;
{
std::lock_guard<std::mutex> lock(callbackMutex_);
callbacks = std::move(completedCallbacks_);
completedCallbacks_.clear();
}
for (auto& callback : callbacks) {
callback();
}
}
//===========================================================================
// 资源依赖跟踪
//===========================================================================
void AssetsModule::registerMaterialDependency(Handle<Material> material, Handle<Texture> texture) {
if (!material.isValid() || !texture.isValid()) {
return;
}
std::unique_lock<std::shared_mutex> 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> material, Handle<Shader> shader) {
if (!material.isValid() || !shader.isValid()) {
return;
}
std::unique_lock<std::shared_mutex> 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> texture) {
if (!texture.isValid()) {
return;
}
std::shared_lock<std::shared_mutex> 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> shader) {
if (!shader.isValid()) {
return;
}
std::shared_lock<std::shared_mutex> 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 { AssetsModule::Stats AssetsModule::getStats() const {
std::shared_lock<std::shared_mutex> lock(mutex_);
Stats stats; Stats stats;
stats.textureCount = textures_.count(); stats.textureCount = textures_.count();
stats.shaderCount = shaders_.count(); stats.shaderCount = shaders_.count();