From 44e0d65f10360829eafdbde6a581b87e823a339f Mon Sep 17 00:00:00 2001 From: ChestnutYueyue <952134128@qq.com> Date: Tue, 3 Mar 2026 19:32:23 +0800 Subject: [PATCH] =?UTF-8?q?feat(assets):=20=E6=B7=BB=E5=8A=A0=E8=B5=84?= =?UTF-8?q?=E6=BA=90=E7=83=AD=E9=87=8D=E8=BD=BD=E5=92=8C=E6=99=BA=E8=83=BD?= =?UTF-8?q?=E5=8D=B8=E8=BD=BD=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 实现纹理和着色器的热重载功能,支持文件修改后自动重新加载 添加资源引用计数和LRU缓存机制,支持智能卸载长时间未使用的资源 重构文件监控系统,减少重复代码 扩展AssetStorage功能,增加加载状态管理和访问时间追踪 --- include/assets/asset_storage.h | 190 +++++++++++++++++++++++++++- include/assets/assets_module.h | 224 ++++++++++++++++++++++++++++++++- include/renderer/shader.h | 19 +++ include/renderer/texture.h | 17 +++ src/assets/assets_module.cpp | 131 +++++++++++-------- src/renderer/shader.cpp | 75 +++++++++++ src/renderer/texture.cpp | 75 +++++++++++ 7 files changed, 677 insertions(+), 54 deletions(-) diff --git a/include/assets/asset_storage.h b/include/assets/asset_storage.h index 0f4aeac..c0f5d13 100644 --- a/include/assets/asset_storage.h +++ b/include/assets/asset_storage.h @@ -4,24 +4,42 @@ #include #include #include +#include +#include +#include namespace extra2d { +/** + * @brief 资源加载状态 + */ +enum class AssetLoadState { + Unloaded, ///< 未加载 + Loading, ///< 加载中 + Loaded, ///< 已加载 + Failed ///< 加载失败 +}; + /** * @brief 密集存储的资源仓库 * * 使用 Dense Vec 存储,类似 ECS 的组件存储。 * 提供 O(1) 的插入、删除、访问。 + * 支持引用统计和 LRU(最近最少使用)缓存策略。 * * @tparam T 资源类型 */ template class AssetStorage { public: + using TimePoint = std::chrono::steady_clock::time_point; + struct Slot { Ptr asset; typename Handle::Generation generation = 0; bool active = false; + AssetLoadState loadState = AssetLoadState::Unloaded; + TimePoint lastAccessTime; ///< 最后访问时间(用于 LRU) }; AssetStorage() = default; @@ -37,18 +55,23 @@ public: */ Handle insert(Ptr asset) { uint32_t index; + auto now = std::chrono::steady_clock::now(); if (!freeIndices_.empty()) { index = freeIndices_.back(); freeIndices_.pop_back(); slots_[index].asset = std::move(asset); slots_[index].active = true; + slots_[index].loadState = AssetLoadState::Loaded; + slots_[index].lastAccessTime = now; } else { index = static_cast(slots_.size()); Slot slot; slot.asset = std::move(asset); slot.generation = nextGeneration_++; slot.active = true; + slot.loadState = AssetLoadState::Loaded; + slot.lastAccessTime = now; slots_.push_back(std::move(slot)); } @@ -76,7 +99,18 @@ public: * @param handle 资源句柄 * @return 资源指针 */ - T* get(Handle handle) const { + T* get(Handle handle) { + if (!isValid(handle)) return nullptr; + updateAccessTime(handle); + return slots_[handle.index()].asset.get(); + } + + /** + * @brief 获取资源(返回指针,可能为 nullptr) - 常量版本,不更新访问时间 + * @param handle 资源句柄 + * @return 资源指针 + */ + const T* get(Handle handle) const { if (!isValid(handle)) return nullptr; return slots_[handle.index()].asset.get(); } @@ -86,6 +120,17 @@ public: * @param handle 资源句柄 * @return 资源智能指针 */ + Ptr getPtr(Handle handle) { + if (!isValid(handle)) return Ptr(); + updateAccessTime(handle); + return slots_[handle.index()].asset; + } + + /** + * @brief 获取资源(返回智能指针) - 常量版本,不更新访问时间 + * @param handle 资源句柄 + * @return 资源智能指针 + */ Ptr getPtr(Handle handle) const { if (!isValid(handle)) return Ptr(); return slots_[handle.index()].asset; @@ -104,6 +149,44 @@ public: return slot.active && slot.generation == handle.generation(); } + /** + * @brief 获取资源加载状态 + * @param handle 资源句柄 + * @return 加载状态 + */ + AssetLoadState getLoadState(Handle handle) const { + if (!isValid(handle)) return AssetLoadState::Unloaded; + return slots_[handle.index()].loadState; + } + + /** + * @brief 设置资源加载状态 + * @param handle 资源句柄 + * @param state 加载状态 + */ + void setLoadState(Handle handle, AssetLoadState state) { + if (!isValid(handle)) return; + slots_[handle.index()].loadState = state; + } + + /** + * @brief 检查资源是否正在加载 + * @param handle 资源句柄 + * @return 是否正在加载 + */ + bool isLoading(Handle handle) const { + return getLoadState(handle) == AssetLoadState::Loading; + } + + /** + * @brief 检查资源是否已加载 + * @param handle 资源句柄 + * @return 是否已加载 + */ + bool isLoaded(Handle handle) const { + return getLoadState(handle) == AssetLoadState::Loaded; + } + /** * @brief 遍历所有资源 * @param func 回调函数,签名 void(Handle, T*) @@ -137,6 +220,111 @@ public: */ size_t capacity() const { return slots_.size(); } + /** + * @brief 获取资源引用计数 + * @param handle 资源句柄 + * @return 引用计数 + */ + uint32_t getRefCount(Handle 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 handle) const { + if (!isValid(handle)) return TimePoint(); + return slots_[handle.index()].lastAccessTime; + } + + /** + * @brief 更新资源访问时间 + * @param handle 资源句柄 + */ + void updateAccessTime(Handle handle) { + if (!isValid(handle)) return; + slots_[handle.index()].lastAccessTime = std::chrono::steady_clock::now(); + } + + /** + * @brief 查找可以卸载的资源(引用计数为1的资源,只有AssetStorage自己持有引用) + * @param maxAgeSeconds 最大空闲时间(秒),默认300秒 + * @return 可以卸载的资源句柄列表 + */ + std::vector> findUnloadableResources(int maxAgeSeconds = 300) const { + std::vector> 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( + now - slot.lastAccessTime).count(); + if (age >= maxAgeSeconds) { + result.emplace_back(static_cast(i), slot.generation); + } + } + } + } + + // 按访问时间排序,最久未使用的排在前面 + std::sort(result.begin(), result.end(), + [this](const Handle& a, const Handle& b) { + return slots_[a.index()].lastAccessTime < slots_[b.index()].lastAccessTime; + }); + + return result; + } + + /** + * @brief 卸载指定资源(只有引用计数为1时才会真正卸载) + * @param handle 资源句柄 + * @return 是否成功卸载 + */ + bool unloadResource(Handle 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(maxCount)) { + break; + } + if (unloadResource(handle)) { + ++unloaded; + } + } + + return unloaded; + } + private: std::vector slots_; std::vector freeIndices_; diff --git a/include/assets/assets_module.h b/include/assets/assets_module.h index 9f43e26..7248a75 100644 --- a/include/assets/assets_module.h +++ b/include/assets/assets_module.h @@ -1,7 +1,7 @@ #pragma once -#include #include +#include #include #include #include @@ -103,11 +103,52 @@ public: */ template bool isValid(Handle handle) const; + /** + * @brief 检查资源是否正在加载 + */ + template bool isLoading(Handle handle) const; + + /** + * @brief 检查资源是否已加载 + */ + template bool isLoaded(Handle handle) const; + + /** + * @brief 获取资源加载状态 + */ + template AssetLoadState getLoadState(Handle handle) const; + /** * @brief 移除资源 */ template void remove(Handle handle); + //=========================================================================== + // 资源引用统计与智能卸载 + //=========================================================================== + + /** + * @brief 获取资源引用计数 + */ + template uint32_t getRefCount(Handle handle) const; + + /** + * @brief 查找可以卸载的资源 + */ + template + std::vector> findUnloadableResources(int maxAgeSeconds = 300) const; + + /** + * @brief 卸载指定资源 + */ + template bool unloadResource(Handle handle); + + /** + * @brief 批量卸载可卸载的资源 + */ + template + size_t unloadOldResources(int maxCount = -1, int maxAgeSeconds = 300); + //=========================================================================== // 批量加载(目录/包) //=========================================================================== @@ -215,6 +256,12 @@ private: void addFileWatch(const std::string &path, Handle handle); void addFileWatch(const std::string &path, Handle handle); +private: + /** + * @brief 内部实现:添加文件监控(减少重复代码) + */ + void addFileWatchInternal(const std::string &path, FileWatchInfo &&info); + /** * @brief 重新加载纹理 */ @@ -470,6 +517,75 @@ template <> inline bool AssetsModule::isValid(Handle handle) const { return meshes_.isValid(handle); } +template bool AssetsModule::isLoading(Handle handle) const { + return false; +} + +template <> +inline bool AssetsModule::isLoading(Handle handle) const { + return textures_.isLoading(handle); +} + +template <> +inline bool AssetsModule::isLoading(Handle handle) const { + return shaders_.isLoading(handle); +} + +template <> +inline bool AssetsModule::isLoading(Handle handle) const { + return materials_.isLoading(handle); +} + +template <> inline bool AssetsModule::isLoading(Handle handle) const { + return meshes_.isLoading(handle); +} + +template bool AssetsModule::isLoaded(Handle handle) const { + return false; +} + +template <> +inline bool AssetsModule::isLoaded(Handle handle) const { + return textures_.isLoaded(handle); +} + +template <> +inline bool AssetsModule::isLoaded(Handle handle) const { + return shaders_.isLoaded(handle); +} + +template <> +inline bool AssetsModule::isLoaded(Handle handle) const { + return materials_.isLoaded(handle); +} + +template <> inline bool AssetsModule::isLoaded(Handle handle) const { + return meshes_.isLoaded(handle); +} + +template AssetLoadState AssetsModule::getLoadState(Handle handle) const { + return AssetLoadState::Unloaded; +} + +template <> +inline AssetLoadState AssetsModule::getLoadState(Handle handle) const { + return textures_.getLoadState(handle); +} + +template <> +inline AssetLoadState AssetsModule::getLoadState(Handle handle) const { + return shaders_.getLoadState(handle); +} + +template <> +inline AssetLoadState AssetsModule::getLoadState(Handle handle) const { + return materials_.getLoadState(handle); +} + +template <> inline AssetLoadState AssetsModule::getLoadState(Handle handle) const { + return meshes_.getLoadState(handle); +} + template void AssetsModule::remove(Handle handle) {} template <> inline void AssetsModule::remove(Handle handle) { @@ -489,6 +605,108 @@ template <> inline void AssetsModule::remove(Handle handle) { meshes_.remove(handle); } +template uint32_t AssetsModule::getRefCount(Handle handle) const { + return 0; +} + +template <> +inline uint32_t AssetsModule::getRefCount(Handle handle) const { + return textures_.getRefCount(handle); +} + +template <> +inline uint32_t AssetsModule::getRefCount(Handle handle) const { + return shaders_.getRefCount(handle); +} + +template <> +inline uint32_t AssetsModule::getRefCount(Handle handle) const { + return materials_.getRefCount(handle); +} + +template <> +inline uint32_t AssetsModule::getRefCount(Handle handle) const { + return meshes_.getRefCount(handle); +} + +template +std::vector> AssetsModule::findUnloadableResources(int maxAgeSeconds) const { + return {}; +} + +template <> +inline std::vector> +AssetsModule::findUnloadableResources(int maxAgeSeconds) const { + return textures_.findUnloadableResources(maxAgeSeconds); +} + +template <> +inline std::vector> +AssetsModule::findUnloadableResources(int maxAgeSeconds) const { + return shaders_.findUnloadableResources(maxAgeSeconds); +} + +template <> +inline std::vector> +AssetsModule::findUnloadableResources(int maxAgeSeconds) const { + return materials_.findUnloadableResources(maxAgeSeconds); +} + +template <> +inline std::vector> +AssetsModule::findUnloadableResources(int maxAgeSeconds) const { + return meshes_.findUnloadableResources(maxAgeSeconds); +} + +template bool AssetsModule::unloadResource(Handle handle) { + return false; +} + +template <> +inline bool AssetsModule::unloadResource(Handle handle) { + return textures_.unloadResource(handle); +} + +template <> +inline bool AssetsModule::unloadResource(Handle handle) { + return shaders_.unloadResource(handle); +} + +template <> +inline bool AssetsModule::unloadResource(Handle handle) { + return materials_.unloadResource(handle); +} + +template <> +inline bool AssetsModule::unloadResource(Handle handle) { + return meshes_.unloadResource(handle); +} + +template +size_t AssetsModule::unloadOldResources(int maxCount, int maxAgeSeconds) { + return 0; +} + +template <> +inline size_t AssetsModule::unloadOldResources(int maxCount, int maxAgeSeconds) { + return textures_.unloadOldResources(maxCount, maxAgeSeconds); +} + +template <> +inline size_t AssetsModule::unloadOldResources(int maxCount, int maxAgeSeconds) { + return shaders_.unloadOldResources(maxCount, maxAgeSeconds); +} + +template <> +inline size_t AssetsModule::unloadOldResources(int maxCount, int maxAgeSeconds) { + return materials_.unloadOldResources(maxCount, maxAgeSeconds); +} + +template <> +inline size_t AssetsModule::unloadOldResources(int maxCount, int maxAgeSeconds) { + return meshes_.unloadOldResources(maxCount, maxAgeSeconds); +} + template std::vector> AssetsModule::loadDir(const std::string &directory, const std::string &pattern, @@ -509,6 +727,10 @@ 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) {} diff --git a/include/renderer/shader.h b/include/renderer/shader.h index a20f62a..e59e195 100644 --- a/include/renderer/shader.h +++ b/include/renderer/shader.h @@ -78,6 +78,25 @@ public: */ 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: /** * @brief 添加版本声明(如果不存在) diff --git a/include/renderer/texture.h b/include/renderer/texture.h index e61daa0..1c1f759 100644 --- a/include/renderer/texture.h +++ b/include/renderer/texture.h @@ -82,6 +82,23 @@ public: */ 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: /** * @brief 获取每个像素的字节数 diff --git a/src/assets/assets_module.cpp b/src/assets/assets_module.cpp index 9b6db45..7da68bc 100644 --- a/src/assets/assets_module.cpp +++ b/src/assets/assets_module.cpp @@ -396,12 +396,38 @@ AssetsModule::loadDir(const std::string &directory, template <> void AssetsModule::loadAsync( const std::string &path, std::function)> callback) { - std::thread([this, path, callback]() { - Handle handle = load(path); - if (callback) { - callback(handle); - } - }).detach(); + // 确保异步加载系统已初始化 + if (!asyncLoaderRunning_) { + initAsyncLoader(); + } + + // 创建并提交加载任务 + LoadTask task; + task.type = LoadTask::Type::Texture; + task.priority = LoadTask::Priority::Normal; + task.path = path; + task.textureCallback = callback; + + submitLoadTask(task); +} + +template <> +void AssetsModule::loadAsync( + const std::string &path, std::function)> 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; } -void AssetsModule::addFileWatch(const std::string &path, - Handle handle) { +void AssetsModule::addFileWatchInternal(const std::string &path, FileWatchInfo &&info) { try { if (!fileExists(path)) { return; } - FileWatchInfo info; info.path = path; info.lastWriteTime = getLastWriteTime(path); - info.textureHandle = handle; - info.isTexture = true; std::unique_lock lock(mutex_); - fileWatchList_.push_back(info); - E2D_LOG_DEBUG("Watching texture file: {}", path); + fileWatchList_.push_back(std::move(info)); + const char *type = fileWatchList_.back().isTexture ? "texture" : "shader"; + E2D_LOG_DEBUG("Watching {} file: {}", type, 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 (!fileExists(path)) { - return; - } +void AssetsModule::addFileWatch(const std::string &path, Handle handle) { + FileWatchInfo info; + info.textureHandle = handle; + info.isTexture = true; + addFileWatchInternal(path, std::move(info)); +} - FileWatchInfo info; - info.path = path; - info.lastWriteTime = getLastWriteTime(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::addFileWatch(const std::string &path, Handle handle) { + FileWatchInfo info; + info.shaderHandle = handle; + info.isTexture = false; + addFileWatchInternal(path, std::move(info)); } void AssetsModule::reloadTexture(const FileWatchInfo &info) { @@ -617,20 +633,21 @@ void AssetsModule::reloadTexture(const FileWatchInfo &info) { E2D_LOG_INFO("Reloading texture: {}", info.path); - Ptr 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); return; } - // 替换旧纹理的数据 - Texture *oldTexture = textures_.get(info.textureHandle); - if (oldTexture) { - // 这里假设 Texture 类有更新数据的方法 - // 如果没有,可能需要重新设计 - E2D_LOG_INFO("Texture reloaded: {}", info.path); - notifyTextureReloaded(info.textureHandle); - } + E2D_LOG_INFO("Texture reloaded successfully: {}", info.path); + notifyTextureReloaded(info.textureHandle); } void AssetsModule::reloadShader(const FileWatchInfo &info) { @@ -640,6 +657,13 @@ void AssetsModule::reloadShader(const FileWatchInfo &info) { 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; { @@ -657,29 +681,32 @@ void AssetsModule::reloadShader(const FileWatchInfo &info) { 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; - } + // 单文件模式 - 这里暂不支持,因为 ShaderLoader 目前只支持双文件 + E2D_LOG_WARN("Single-file shader reload not supported: {}", info.path); } 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) { + // 读取着色器文件内容 + std::string vsSource = readFileToString(vertPath); + 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); return; } } - E2D_LOG_INFO("Shader reloaded: {}", info.path); + E2D_LOG_INFO("Shader reloaded successfully: {}", info.path); notifyShaderReloaded(info.shaderHandle); } diff --git a/src/renderer/shader.cpp b/src/renderer/shader.cpp index 763a852..fdaf93c 100644 --- a/src/renderer/shader.cpp +++ b/src/renderer/shader.cpp @@ -105,6 +105,81 @@ uint32_t Shader::getUniformBlockBinding(const std::string &name) const { 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, bool isVertex) { // 如果已经包含版本声明,直接返回 diff --git a/src/renderer/texture.cpp b/src/renderer/texture.cpp index fd60edc..ddfbba5 100644 --- a/src/renderer/texture.cpp +++ b/src/renderer/texture.cpp @@ -136,6 +136,81 @@ bool Texture::create(int width, int height, TextureFormat format) { 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(width); + desc.height = static_cast(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(width * height * getBytesPerPixel(format))); + } + + // 替换旧的纹理句柄 + handle_ = TextureHandle(texture.release()); + + return true; +} + // 辅助函数:获取每个像素的字节数 uint32_t Texture::getBytesPerPixel(TextureFormat format) { switch (format) {