feat(assets): 添加资源热重载和智能卸载功能

实现纹理和着色器的热重载功能,支持文件修改后自动重新加载
添加资源引用计数和LRU缓存机制,支持智能卸载长时间未使用的资源
重构文件监控系统,减少重复代码
扩展AssetStorage功能,增加加载状态管理和访问时间追踪
This commit is contained in:
ChestnutYueyue 2026-03-03 19:32:23 +08:00
parent e0b0a7883d
commit 44e0d65f10
7 changed files with 677 additions and 54 deletions

View File

@ -4,24 +4,42 @@
#include <types/ptr/intrusive_ptr.h> #include <types/ptr/intrusive_ptr.h>
#include <vector> #include <vector>
#include <cstdint> #include <cstdint>
#include <chrono>
#include <algorithm>
#include <unordered_map>
namespace extra2d { namespace extra2d {
/**
* @brief
*/
enum class AssetLoadState {
Unloaded, ///< 未加载
Loading, ///< 加载中
Loaded, ///< 已加载
Failed ///< 加载失败
};
/** /**
* @brief * @brief
* *
* 使 Dense Vec ECS * 使 Dense Vec ECS
* O(1) 访 * O(1) 访
* LRU使
* *
* @tparam T * @tparam T
*/ */
template<typename T> template<typename T>
class AssetStorage { class AssetStorage {
public: public:
using TimePoint = std::chrono::steady_clock::time_point;
struct Slot { struct Slot {
Ptr<T> asset; Ptr<T> asset;
typename Handle<T>::Generation generation = 0; typename Handle<T>::Generation generation = 0;
bool active = false; bool active = false;
AssetLoadState loadState = AssetLoadState::Unloaded;
TimePoint lastAccessTime; ///< 最后访问时间(用于 LRU
}; };
AssetStorage() = default; AssetStorage() = default;
@ -37,18 +55,23 @@ public:
*/ */
Handle<T> insert(Ptr<T> asset) { Handle<T> insert(Ptr<T> asset) {
uint32_t index; uint32_t index;
auto now = std::chrono::steady_clock::now();
if (!freeIndices_.empty()) { if (!freeIndices_.empty()) {
index = freeIndices_.back(); index = freeIndices_.back();
freeIndices_.pop_back(); freeIndices_.pop_back();
slots_[index].asset = std::move(asset); slots_[index].asset = std::move(asset);
slots_[index].active = true; slots_[index].active = true;
slots_[index].loadState = AssetLoadState::Loaded;
slots_[index].lastAccessTime = now;
} else { } else {
index = static_cast<uint32_t>(slots_.size()); index = static_cast<uint32_t>(slots_.size());
Slot slot; Slot slot;
slot.asset = std::move(asset); slot.asset = std::move(asset);
slot.generation = nextGeneration_++; slot.generation = nextGeneration_++;
slot.active = true; slot.active = true;
slot.loadState = AssetLoadState::Loaded;
slot.lastAccessTime = now;
slots_.push_back(std::move(slot)); slots_.push_back(std::move(slot));
} }
@ -76,7 +99,18 @@ public:
* @param handle * @param handle
* @return * @return
*/ */
T* get(Handle<T> handle) const { T* get(Handle<T> handle) {
if (!isValid(handle)) return nullptr;
updateAccessTime(handle);
return slots_[handle.index()].asset.get();
}
/**
* @brief nullptr - 访
* @param handle
* @return
*/
const T* get(Handle<T> handle) const {
if (!isValid(handle)) return nullptr; if (!isValid(handle)) return nullptr;
return slots_[handle.index()].asset.get(); return slots_[handle.index()].asset.get();
} }
@ -86,6 +120,17 @@ public:
* @param handle * @param handle
* @return * @return
*/ */
Ptr<T> getPtr(Handle<T> handle) {
if (!isValid(handle)) return Ptr<T>();
updateAccessTime(handle);
return slots_[handle.index()].asset;
}
/**
* @brief - 访
* @param handle
* @return
*/
Ptr<T> getPtr(Handle<T> handle) const { Ptr<T> getPtr(Handle<T> handle) const {
if (!isValid(handle)) return Ptr<T>(); if (!isValid(handle)) return Ptr<T>();
return slots_[handle.index()].asset; return slots_[handle.index()].asset;
@ -104,6 +149,44 @@ public:
return slot.active && slot.generation == handle.generation(); return slot.active && slot.generation == handle.generation();
} }
/**
* @brief
* @param handle
* @return
*/
AssetLoadState getLoadState(Handle<T> handle) const {
if (!isValid(handle)) return AssetLoadState::Unloaded;
return slots_[handle.index()].loadState;
}
/**
* @brief
* @param handle
* @param state
*/
void setLoadState(Handle<T> handle, AssetLoadState state) {
if (!isValid(handle)) return;
slots_[handle.index()].loadState = state;
}
/**
* @brief
* @param handle
* @return
*/
bool isLoading(Handle<T> handle) const {
return getLoadState(handle) == AssetLoadState::Loading;
}
/**
* @brief
* @param handle
* @return
*/
bool isLoaded(Handle<T> handle) const {
return getLoadState(handle) == AssetLoadState::Loaded;
}
/** /**
* @brief * @brief
* @param func void(Handle<T>, T*) * @param func void(Handle<T>, T*)
@ -137,6 +220,111 @@ public:
*/ */
size_t capacity() const { return slots_.size(); } size_t capacity() const { return slots_.size(); }
/**
* @brief
* @param handle
* @return
*/
uint32_t getRefCount(Handle<T> handle) const {
if (!isValid(handle)) return 0;
const auto& slot = slots_[handle.index()];
return slot.asset ? slot.asset.refCount() : 0;
}
/**
* @brief 访
* @param handle
* @return 访
*/
TimePoint getLastAccessTime(Handle<T> handle) const {
if (!isValid(handle)) return TimePoint();
return slots_[handle.index()].lastAccessTime;
}
/**
* @brief 访
* @param handle
*/
void updateAccessTime(Handle<T> handle) {
if (!isValid(handle)) return;
slots_[handle.index()].lastAccessTime = std::chrono::steady_clock::now();
}
/**
* @brief 1AssetStorage自己持有引用
* @param maxAgeSeconds 300
* @return
*/
std::vector<Handle<T>> findUnloadableResources(int maxAgeSeconds = 300) const {
std::vector<Handle<T>> result;
auto now = std::chrono::steady_clock::now();
for (size_t i = 0; i < slots_.size(); ++i) {
const Slot& slot = slots_[i];
if (slot.active && slot.asset) {
// 引用计数为1表示只有 AssetStorage 自己持有引用
if (slot.asset.refCount() == 1) {
auto age = std::chrono::duration_cast<std::chrono::seconds>(
now - slot.lastAccessTime).count();
if (age >= maxAgeSeconds) {
result.emplace_back(static_cast<uint32_t>(i), slot.generation);
}
}
}
}
// 按访问时间排序,最久未使用的排在前面
std::sort(result.begin(), result.end(),
[this](const Handle<T>& a, const Handle<T>& b) {
return slots_[a.index()].lastAccessTime < slots_[b.index()].lastAccessTime;
});
return result;
}
/**
* @brief 1
* @param handle
* @return
*/
bool unloadResource(Handle<T> handle) {
if (!isValid(handle)) return false;
auto& slot = slots_[handle.index()];
if (!slot.asset) return false;
// 只有引用计数为1时才可以卸载只有AssetStorage自己持有
if (slot.asset.refCount() > 1) {
return false;
}
slot.asset.reset();
slot.loadState = AssetLoadState::Unloaded;
return true;
}
/**
* @brief
* @param maxCount -1
* @param maxAgeSeconds
* @return
*/
size_t unloadOldResources(int maxCount = -1, int maxAgeSeconds = 300) {
auto unloadable = findUnloadableResources(maxAgeSeconds);
size_t unloaded = 0;
for (const auto& handle : unloadable) {
if (maxCount >= 0 && unloaded >= static_cast<size_t>(maxCount)) {
break;
}
if (unloadResource(handle)) {
++unloaded;
}
}
return unloaded;
}
private: private:
std::vector<Slot> slots_; std::vector<Slot> slots_;
std::vector<uint32_t> freeIndices_; std::vector<uint32_t> freeIndices_;

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <assets/asset_loader.h>
#include <assets/asset_storage.h> #include <assets/asset_storage.h>
#include <assets/asset_loader.h>
#include <assets/handle.h> #include <assets/handle.h>
#include <atomic> #include <atomic>
#include <condition_variable> #include <condition_variable>
@ -103,11 +103,52 @@ public:
*/ */
template <typename T> bool isValid(Handle<T> handle) const; template <typename T> bool isValid(Handle<T> handle) const;
/**
* @brief
*/
template <typename T> bool isLoading(Handle<T> handle) const;
/**
* @brief
*/
template <typename T> bool isLoaded(Handle<T> handle) const;
/**
* @brief
*/
template <typename T> AssetLoadState getLoadState(Handle<T> handle) const;
/** /**
* @brief * @brief
*/ */
template <typename T> void remove(Handle<T> handle); template <typename T> void remove(Handle<T> handle);
//===========================================================================
// 资源引用统计与智能卸载
//===========================================================================
/**
* @brief
*/
template <typename T> uint32_t getRefCount(Handle<T> handle) const;
/**
* @brief
*/
template <typename T>
std::vector<Handle<T>> findUnloadableResources(int maxAgeSeconds = 300) const;
/**
* @brief
*/
template <typename T> bool unloadResource(Handle<T> handle);
/**
* @brief
*/
template <typename T>
size_t unloadOldResources(int maxCount = -1, int maxAgeSeconds = 300);
//=========================================================================== //===========================================================================
// 批量加载(目录/包) // 批量加载(目录/包)
//=========================================================================== //===========================================================================
@ -215,6 +256,12 @@ private:
void addFileWatch(const std::string &path, Handle<Texture> handle); void addFileWatch(const std::string &path, Handle<Texture> handle);
void addFileWatch(const std::string &path, Handle<Shader> handle); void addFileWatch(const std::string &path, Handle<Shader> handle);
private:
/**
* @brief
*/
void addFileWatchInternal(const std::string &path, FileWatchInfo &&info);
/** /**
* @brief * @brief
*/ */
@ -470,6 +517,75 @@ template <> inline bool AssetsModule::isValid<Mesh>(Handle<Mesh> handle) const {
return meshes_.isValid(handle); return meshes_.isValid(handle);
} }
template <typename T> bool AssetsModule::isLoading(Handle<T> handle) const {
return false;
}
template <>
inline bool AssetsModule::isLoading<Texture>(Handle<Texture> handle) const {
return textures_.isLoading(handle);
}
template <>
inline bool AssetsModule::isLoading<Shader>(Handle<Shader> handle) const {
return shaders_.isLoading(handle);
}
template <>
inline bool AssetsModule::isLoading<Material>(Handle<Material> handle) const {
return materials_.isLoading(handle);
}
template <> inline bool AssetsModule::isLoading<Mesh>(Handle<Mesh> handle) const {
return meshes_.isLoading(handle);
}
template <typename T> bool AssetsModule::isLoaded(Handle<T> handle) const {
return false;
}
template <>
inline bool AssetsModule::isLoaded<Texture>(Handle<Texture> handle) const {
return textures_.isLoaded(handle);
}
template <>
inline bool AssetsModule::isLoaded<Shader>(Handle<Shader> handle) const {
return shaders_.isLoaded(handle);
}
template <>
inline bool AssetsModule::isLoaded<Material>(Handle<Material> handle) const {
return materials_.isLoaded(handle);
}
template <> inline bool AssetsModule::isLoaded<Mesh>(Handle<Mesh> handle) const {
return meshes_.isLoaded(handle);
}
template <typename T> AssetLoadState AssetsModule::getLoadState(Handle<T> handle) const {
return AssetLoadState::Unloaded;
}
template <>
inline AssetLoadState AssetsModule::getLoadState<Texture>(Handle<Texture> handle) const {
return textures_.getLoadState(handle);
}
template <>
inline AssetLoadState AssetsModule::getLoadState<Shader>(Handle<Shader> handle) const {
return shaders_.getLoadState(handle);
}
template <>
inline AssetLoadState AssetsModule::getLoadState<Material>(Handle<Material> handle) const {
return materials_.getLoadState(handle);
}
template <> inline AssetLoadState AssetsModule::getLoadState<Mesh>(Handle<Mesh> handle) const {
return meshes_.getLoadState(handle);
}
template <typename T> void AssetsModule::remove(Handle<T> handle) {} template <typename T> void AssetsModule::remove(Handle<T> handle) {}
template <> inline void AssetsModule::remove<Texture>(Handle<Texture> handle) { template <> inline void AssetsModule::remove<Texture>(Handle<Texture> handle) {
@ -489,6 +605,108 @@ template <> inline void AssetsModule::remove<Mesh>(Handle<Mesh> handle) {
meshes_.remove(handle); meshes_.remove(handle);
} }
template <typename T> uint32_t AssetsModule::getRefCount(Handle<T> handle) const {
return 0;
}
template <>
inline uint32_t AssetsModule::getRefCount<Texture>(Handle<Texture> handle) const {
return textures_.getRefCount(handle);
}
template <>
inline uint32_t AssetsModule::getRefCount<Shader>(Handle<Shader> handle) const {
return shaders_.getRefCount(handle);
}
template <>
inline uint32_t AssetsModule::getRefCount<Material>(Handle<Material> handle) const {
return materials_.getRefCount(handle);
}
template <>
inline uint32_t AssetsModule::getRefCount<Mesh>(Handle<Mesh> handle) const {
return meshes_.getRefCount(handle);
}
template <typename T>
std::vector<Handle<T>> AssetsModule::findUnloadableResources(int maxAgeSeconds) const {
return {};
}
template <>
inline std::vector<Handle<Texture>>
AssetsModule::findUnloadableResources<Texture>(int maxAgeSeconds) const {
return textures_.findUnloadableResources(maxAgeSeconds);
}
template <>
inline std::vector<Handle<Shader>>
AssetsModule::findUnloadableResources<Shader>(int maxAgeSeconds) const {
return shaders_.findUnloadableResources(maxAgeSeconds);
}
template <>
inline std::vector<Handle<Material>>
AssetsModule::findUnloadableResources<Material>(int maxAgeSeconds) const {
return materials_.findUnloadableResources(maxAgeSeconds);
}
template <>
inline std::vector<Handle<Mesh>>
AssetsModule::findUnloadableResources<Mesh>(int maxAgeSeconds) const {
return meshes_.findUnloadableResources(maxAgeSeconds);
}
template <typename T> bool AssetsModule::unloadResource(Handle<T> handle) {
return false;
}
template <>
inline bool AssetsModule::unloadResource<Texture>(Handle<Texture> handle) {
return textures_.unloadResource(handle);
}
template <>
inline bool AssetsModule::unloadResource<Shader>(Handle<Shader> handle) {
return shaders_.unloadResource(handle);
}
template <>
inline bool AssetsModule::unloadResource<Material>(Handle<Material> handle) {
return materials_.unloadResource(handle);
}
template <>
inline bool AssetsModule::unloadResource<Mesh>(Handle<Mesh> handle) {
return meshes_.unloadResource(handle);
}
template <typename T>
size_t AssetsModule::unloadOldResources(int maxCount, int maxAgeSeconds) {
return 0;
}
template <>
inline size_t AssetsModule::unloadOldResources<Texture>(int maxCount, int maxAgeSeconds) {
return textures_.unloadOldResources(maxCount, maxAgeSeconds);
}
template <>
inline size_t AssetsModule::unloadOldResources<Shader>(int maxCount, int maxAgeSeconds) {
return shaders_.unloadOldResources(maxCount, maxAgeSeconds);
}
template <>
inline size_t AssetsModule::unloadOldResources<Material>(int maxCount, int maxAgeSeconds) {
return materials_.unloadOldResources(maxCount, maxAgeSeconds);
}
template <>
inline size_t AssetsModule::unloadOldResources<Mesh>(int maxCount, int maxAgeSeconds) {
return meshes_.unloadOldResources(maxCount, maxAgeSeconds);
}
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,
@ -509,6 +727,10 @@ template <>
void AssetsModule::loadAsync<Texture>( void AssetsModule::loadAsync<Texture>(
const std::string &path, std::function<void(Handle<Texture>)> callback); const std::string &path, std::function<void(Handle<Texture>)> callback);
template <>
void AssetsModule::loadAsync<Shader>(
const std::string &path, std::function<void(Handle<Shader>)> 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) {}

View File

@ -78,6 +78,25 @@ public:
*/ */
uint32_t getUniformBlockBinding(const std::string& name) const; uint32_t getUniformBlockBinding(const std::string& name) const;
/**
* @brief
* @param vsSource
* @param fsSource
* @return
*/
bool reloadFromSource(const std::string &vsSource, const std::string &fsSource);
/**
* @brief 使
* @param vsSource
* @param fsSource
* @param vertexLayout
* @return
*/
bool reloadFromSourceWithLayout(const std::string &vsSource,
const std::string &fsSource,
const VertexLayout &vertexLayout);
private: private:
/** /**
* @brief * @brief

View File

@ -82,6 +82,23 @@ public:
*/ */
bool isLoaded() const { return handle_.isValid(); } bool isLoaded() const { return handle_.isValid(); }
/**
* @brief
* @param path
* @return
*/
bool reloadFromFile(const std::string& path);
/**
* @brief
* @param data
* @param width
* @param height
* @param format
* @return
*/
bool reloadFromMemory(const uint8_t* data, int width, int height, TextureFormat format);
private: private:
/** /**
* @brief * @brief

View File

@ -396,12 +396,38 @@ AssetsModule::loadDir<Texture>(const std::string &directory,
template <> template <>
void AssetsModule::loadAsync<Texture>( void AssetsModule::loadAsync<Texture>(
const std::string &path, std::function<void(Handle<Texture>)> callback) { const std::string &path, std::function<void(Handle<Texture>)> callback) {
std::thread([this, path, callback]() { // 确保异步加载系统已初始化
Handle<Texture> handle = load<Texture>(path); if (!asyncLoaderRunning_) {
if (callback) { initAsyncLoader();
callback(handle); }
}
}).detach(); // 创建并提交加载任务
LoadTask task;
task.type = LoadTask::Type::Texture;
task.priority = LoadTask::Priority::Normal;
task.path = path;
task.textureCallback = callback;
submitLoadTask(task);
}
template <>
void AssetsModule::loadAsync<Shader>(
const std::string &path, std::function<void(Handle<Shader>)> callback) {
// 确保异步加载系统已初始化
if (!asyncLoaderRunning_) {
initAsyncLoader();
}
// 创建并提交加载任务
LoadTask task;
task.type = LoadTask::Type::Shader;
task.priority = LoadTask::Priority::Normal;
task.path = path;
task.secondaryPath = "";
task.shaderCallback = callback;
submitLoadTask(task);
} }
//=========================================================================== //===========================================================================
@ -568,46 +594,36 @@ void AssetsModule::setHotReloadInterval(float interval) {
hotReloadInterval_ = interval; hotReloadInterval_ = interval;
} }
void AssetsModule::addFileWatch(const std::string &path, void AssetsModule::addFileWatchInternal(const std::string &path, FileWatchInfo &&info) {
Handle<Texture> handle) {
try { try {
if (!fileExists(path)) { if (!fileExists(path)) {
return; return;
} }
FileWatchInfo info;
info.path = path; info.path = path;
info.lastWriteTime = getLastWriteTime(path); info.lastWriteTime = getLastWriteTime(path);
info.textureHandle = handle;
info.isTexture = true;
std::unique_lock<std::shared_mutex> lock(mutex_); std::unique_lock<std::shared_mutex> lock(mutex_);
fileWatchList_.push_back(info); fileWatchList_.push_back(std::move(info));
E2D_LOG_DEBUG("Watching texture file: {}", path); const char *type = fileWatchList_.back().isTexture ? "texture" : "shader";
E2D_LOG_DEBUG("Watching {} file: {}", type, path);
} catch (const std::exception &e) { } catch (const std::exception &e) {
E2D_LOG_ERROR("Failed to add file watch for {}: {}", path, e.what()); E2D_LOG_ERROR("Failed to add file watch for {}: {}", path, e.what());
} }
} }
void AssetsModule::addFileWatch(const std::string &path, void AssetsModule::addFileWatch(const std::string &path, Handle<Texture> handle) {
Handle<Shader> handle) { FileWatchInfo info;
try { info.textureHandle = handle;
if (!fileExists(path)) { info.isTexture = true;
return; addFileWatchInternal(path, std::move(info));
} }
FileWatchInfo info; void AssetsModule::addFileWatch(const std::string &path, Handle<Shader> handle) {
info.path = path; FileWatchInfo info;
info.lastWriteTime = getLastWriteTime(path); info.shaderHandle = handle;
info.shaderHandle = handle; info.isTexture = false;
info.isTexture = false; addFileWatchInternal(path, std::move(info));
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) { void AssetsModule::reloadTexture(const FileWatchInfo &info) {
@ -617,20 +633,21 @@ void AssetsModule::reloadTexture(const FileWatchInfo &info) {
E2D_LOG_INFO("Reloading texture: {}", info.path); E2D_LOG_INFO("Reloading texture: {}", info.path);
Ptr<Texture> newTexture = textureLoader_->load(info.path); // 获取旧纹理指针
if (!newTexture) { Texture *oldTexture = textures_.get(info.textureHandle);
if (!oldTexture) {
E2D_LOG_ERROR("Old texture not found for reload: {}", info.path);
return;
}
// 直接在原有纹理对象上重新加载
if (!oldTexture->reloadFromFile(info.path)) {
E2D_LOG_ERROR("Failed to reload texture: {}", info.path); E2D_LOG_ERROR("Failed to reload texture: {}", info.path);
return; return;
} }
// 替换旧纹理的数据 E2D_LOG_INFO("Texture reloaded successfully: {}", info.path);
Texture *oldTexture = textures_.get(info.textureHandle); notifyTextureReloaded(info.textureHandle);
if (oldTexture) {
// 这里假设 Texture 类有更新数据的方法
// 如果没有,可能需要重新设计
E2D_LOG_INFO("Texture reloaded: {}", info.path);
notifyTextureReloaded(info.textureHandle);
}
} }
void AssetsModule::reloadShader(const FileWatchInfo &info) { void AssetsModule::reloadShader(const FileWatchInfo &info) {
@ -640,6 +657,13 @@ void AssetsModule::reloadShader(const FileWatchInfo &info) {
E2D_LOG_INFO("Reloading shader: {}", info.path); E2D_LOG_INFO("Reloading shader: {}", info.path);
// 获取旧着色器指针
Shader *oldShader = shaders_.get(info.shaderHandle);
if (!oldShader) {
E2D_LOG_ERROR("Old shader not found for reload: {}", info.path);
return;
}
// 查找缓存键 // 查找缓存键
std::string cacheKey; std::string cacheKey;
{ {
@ -657,29 +681,32 @@ void AssetsModule::reloadShader(const FileWatchInfo &info) {
return; return;
} }
// 解析顶点/片段着色器路径 // 解析顶点/片段着色器路径并重新加载
size_t sepPos = cacheKey.find('|'); size_t sepPos = cacheKey.find('|');
if (sepPos == std::string::npos) { if (sepPos == std::string::npos) {
// 单文件模式 // 单文件模式 - 这里暂不支持,因为 ShaderLoader 目前只支持双文件
Ptr<Shader> newShader = shaderLoader_->load(info.path); E2D_LOG_WARN("Single-file shader reload not supported: {}", info.path);
if (!newShader) {
E2D_LOG_ERROR("Failed to reload shader: {}", info.path);
return;
}
} else { } else {
// 双文件模式 // 双文件模式
std::string vertPath = cacheKey.substr(0, sepPos); std::string vertPath = cacheKey.substr(0, sepPos);
std::string fragPath = cacheKey.substr(sepPos + 1); std::string fragPath = cacheKey.substr(sepPos + 1);
ShaderLoader *loader = static_cast<ShaderLoader *>(shaderLoader_.get()); // 读取着色器文件内容
Ptr<Shader> newShader = loader->load(vertPath, fragPath); std::string vsSource = readFileToString(vertPath);
if (!newShader) { std::string fsSource = readFileToString(fragPath);
if (vsSource.empty() || fsSource.empty()) {
E2D_LOG_ERROR("Failed to read shader files for reload: {}, {}", vertPath, fragPath);
return;
}
// 直接在原有着色器对象上重新加载
if (!oldShader->reloadFromSource(vsSource, fsSource)) {
E2D_LOG_ERROR("Failed to reload shader: {} + {}", vertPath, fragPath); E2D_LOG_ERROR("Failed to reload shader: {} + {}", vertPath, fragPath);
return; return;
} }
} }
E2D_LOG_INFO("Shader reloaded: {}", info.path); E2D_LOG_INFO("Shader reloaded successfully: {}", info.path);
notifyShaderReloaded(info.shaderHandle); notifyShaderReloaded(info.shaderHandle);
} }

View File

@ -105,6 +105,81 @@ uint32_t Shader::getUniformBlockBinding(const std::string &name) const {
return UINT32_MAX; // 未找到 return UINT32_MAX; // 未找到
} }
bool Shader::reloadFromSource(const std::string &vsSource,
const std::string &fsSource) {
// 创建标准2D顶点布局位置 + 纹理坐标 + 颜色)
VertexLayout vertexLayout;
vertexLayout.stride = sizeof(float) * 8; // 2 (pos) + 2 (uv) + 4 (color)
vertexLayout.addAttribute(0, VertexFormat::Float2, 0); // 位置
vertexLayout.addAttribute(1, VertexFormat::Float2, 8); // 纹理坐标
vertexLayout.addAttribute(2, VertexFormat::Float4, 16); // 颜色
return reloadFromSourceWithLayout(vsSource, fsSource, vertexLayout);
}
bool Shader::reloadFromSourceWithLayout(const std::string &vsSource,
const std::string &fsSource,
const VertexLayout &vertexLayout) {
// 释放旧资源
pipeline_ = PipelineHandle();
handle_ = ShaderHandle();
// 获取 RHI 设备
auto *rhiModule = RHIModule::get();
if (!rhiModule) {
E2D_LOG_ERROR("RHIModule not available");
return false;
}
auto *device = rhiModule->getDevice();
if (!device) {
E2D_LOG_ERROR("RHIDevice not available");
return false;
}
// 处理源码(添加版本声明)
std::string processedVS = addVersionIfNeeded(vsSource, true);
std::string processedFS = addVersionIfNeeded(fsSource, false);
// 创建着色器描述
ShaderDesc shaderDesc;
shaderDesc.vertexSource = processedVS;
shaderDesc.fragmentSource = processedFS;
// 创建着色器
auto shader = device->createShader(shaderDesc);
if (!shader) {
E2D_LOG_ERROR("Failed to create shader during reload");
return false;
}
// 获取着色器句柄
handle_ = ShaderHandle(shader.release());
// 创建管线描述
PipelineDesc pipelineDesc;
pipelineDesc.vertexShader = handle_;
pipelineDesc.fragmentShader = handle_;
pipelineDesc.vertexLayout = vertexLayout;
pipelineDesc.blendState = BlendState::alphaBlend();
pipelineDesc.depthStencilState = DepthStencilState::noDepthTest();
pipelineDesc.rasterizerState = RasterizerState::noCull();
// 创建管线
auto pipeline = device->createPipeline(pipelineDesc);
if (!pipeline) {
E2D_LOG_ERROR("Failed to create pipeline during reload");
handle_ = ShaderHandle();
return false;
}
// 获取管线句柄
pipeline_ = PipelineHandle(pipeline.release());
E2D_LOG_INFO("Shader reloaded successfully");
return true;
}
std::string Shader::addVersionIfNeeded(const std::string &source, std::string Shader::addVersionIfNeeded(const std::string &source,
bool isVertex) { bool isVertex) {
// 如果已经包含版本声明,直接返回 // 如果已经包含版本声明,直接返回

View File

@ -136,6 +136,81 @@ bool Texture::create(int width, int height, TextureFormat format) {
return true; return true;
} }
bool Texture::reloadFromFile(const std::string& path) {
// 加载图片
int channels;
stbi_set_flip_vertically_on_load(true);
unsigned char* data = stbi_load(path.c_str(), &width_, &height_, &channels, 0);
if (!data) {
E2D_LOG_ERROR("Failed to reload texture: {}", path);
return false;
}
// 根据通道数确定格式
TextureFormat format;
switch (channels) {
case 1: format = TextureFormat::R8; break;
case 2: format = TextureFormat::RG8; break;
case 3: format = TextureFormat::RGB8; break;
case 4: format = TextureFormat::RGBA8; break;
default: format = TextureFormat::RGBA8; break;
}
bool result = reloadFromMemory(data, width_, height_, format);
stbi_image_free(data);
if (result) {
E2D_LOG_INFO("Texture reloaded: {} ({}x{})", path, width_, height_);
}
return result;
}
bool Texture::reloadFromMemory(const uint8_t* data, int width, int height, TextureFormat format) {
// 更新尺寸和格式
width_ = width;
height_ = height;
format_ = format;
// 获取 RHI 设备
auto* rhiModule = RHIModule::get();
if (!rhiModule) {
E2D_LOG_ERROR("RHIModule not available");
return false;
}
auto* device = rhiModule->getDevice();
if (!device) {
E2D_LOG_ERROR("RHIDevice not available");
return false;
}
// 创建新的纹理描述
TextureDesc desc;
desc.width = static_cast<uint32_t>(width);
desc.height = static_cast<uint32_t>(height);
desc.format = format;
desc.mipLevels = 1;
// 创建新的 RHI 纹理
auto texture = device->createTexture(desc);
if (!texture) {
E2D_LOG_ERROR("Failed to create RHI texture during reload");
return false;
}
// 上传纹理数据
if (data) {
texture->update(data, static_cast<size_t>(width * height * getBytesPerPixel(format)));
}
// 替换旧的纹理句柄
handle_ = TextureHandle(texture.release());
return true;
}
// 辅助函数:获取每个像素的字节数 // 辅助函数:获取每个像素的字节数
uint32_t Texture::getBytesPerPixel(TextureFormat format) { uint32_t Texture::getBytesPerPixel(TextureFormat format) {
switch (format) { switch (format) {