feat(assets): 添加资源热重载和智能卸载功能
实现纹理和着色器的热重载功能,支持文件修改后自动重新加载 添加资源引用计数和LRU缓存机制,支持智能卸载长时间未使用的资源 重构文件监控系统,减少重复代码 扩展AssetStorage功能,增加加载状态管理和访问时间追踪
This commit is contained in:
parent
e0b0a7883d
commit
44e0d65f10
|
|
@ -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 查找可以卸载的资源(引用计数为1的资源,只有AssetStorage自己持有引用)
|
||||||
|
* @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_;
|
||||||
|
|
|
||||||
|
|
@ -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) {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 添加版本声明(如果不存在)
|
||||||
|
|
|
||||||
|
|
@ -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 获取每个像素的字节数
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
// 如果已经包含版本声明,直接返回
|
// 如果已经包含版本声明,直接返回
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue