refactor(assets): 重构资源系统架构,提取核心逻辑到 AssetSystem

- 新增 AssetSystem 类,整合异步加载、热重载、依赖跟踪等核心功能
- 将 AssetAsyncRuntime、AssetHotReloadRuntime 等组件提取为独立运行时类
- 重命名 AssetDependencyTracker 为 AssetDependencyGraph,改进数据结构
- 新增 BuiltinAssetFactory 负责默认资源的创建与管理
- 新增 AssetCache 提供线程安全的资源缓存机制
- 简化 AssetsModule 实现,将其职责委托给 AssetSystem
- 保持原有 API 兼容性,仅重构内部实现
This commit is contained in:
ChestnutYueyue 2026-03-16 17:44:36 +08:00
parent 41817c9e8a
commit b672b16b83
14 changed files with 927 additions and 707 deletions

View File

@ -1,10 +1,7 @@
#pragma once #pragma once
#include <assets/asset_storage.h> #include <assets/asset_storage.h>
#include <assets/async/asset_async_loader.h> #include <assets/core/asset_system.h>
#include <assets/deps/asset_dependency_tracker.h>
#include <assets/hot_reload/asset_hot_reloader.h>
#include <assets/io/asset_file_system.h>
#include <assets/asset_loader.h> #include <assets/asset_loader.h>
#include <assets/handle.h> #include <assets/handle.h>
#include <atomic> #include <atomic>
@ -239,17 +236,6 @@ public:
*/ */
void setHotReloadInterval(float interval); void setHotReloadInterval(float interval);
private:
/**
* @brief
*/
void reloadTexture(const AssetHotReloader::FileWatchInfo &info);
/**
* @brief
*/
void reloadShader(const AssetHotReloader::FileWatchInfo &info);
public: public:
//=========================================================================== //===========================================================================
// 统计 // 统计
@ -285,9 +271,6 @@ private:
Handle<Material> defaultMaterial_; Handle<Material> defaultMaterial_;
Handle<Mesh> defaultQuad_; Handle<Mesh> defaultQuad_;
AssetFileSystem fileSystem_;
AssetHotReloader hotReloader_;
// 线程安全 // 线程安全
mutable std::shared_mutex mutex_; mutable std::shared_mutex mutex_;
@ -334,9 +317,6 @@ public:
*/ */
void processAsyncCallbacks(); void processAsyncCallbacks();
private:
AssetAsyncLoader asyncLoader_;
//=========================================================================== //===========================================================================
// 资源依赖跟踪 // 资源依赖跟踪
//=========================================================================== //===========================================================================
@ -373,7 +353,7 @@ public:
void notifyShaderReloaded(Handle<Shader> shader); void notifyShaderReloaded(Handle<Shader> shader);
private: private:
AssetDependencyTracker dependencyTracker_; std::unique_ptr<AssetSystem> system_;
}; };
// 全局访问 // 全局访问

View File

@ -0,0 +1,41 @@
#pragma once
#include <assets/asset_storage.h>
#include <assets/handle.h>
#include <assets/io/asset_file_system.h>
#include <renderer/material.h>
#include <renderer/mesh.h>
#include <renderer/shader.h>
#include <renderer/texture.h>
namespace extra2d {
class BuiltinAssetFactory {
public:
BuiltinAssetFactory(AssetStorage<Texture> &textures, AssetStorage<Shader> &shaders,
AssetStorage<Material> &materials, AssetStorage<Mesh> &meshes,
const AssetFileSystem &fileSystem);
bool create();
void destroy();
Handle<Texture> defaultTexture() const;
Handle<Shader> defaultShader() const;
Handle<Material> defaultMaterial() const;
Handle<Mesh> defaultQuad() const;
private:
AssetStorage<Texture> &textures_;
AssetStorage<Shader> &shaders_;
AssetStorage<Material> &materials_;
AssetStorage<Mesh> &meshes_;
const AssetFileSystem &fileSystem_;
Handle<Texture> defaultTexture_;
Handle<Shader> defaultShader_;
Handle<Material> defaultMaterial_;
Handle<Mesh> defaultQuad_;
};
} // namespace extra2d

52
include/assets/cache/asset_cache.h vendored Normal file
View File

@ -0,0 +1,52 @@
#pragma once
#include <assets/handle.h>
#include <mutex>
#include <shared_mutex>
#include <string>
#include <unordered_map>
namespace extra2d {
template <typename T> class AssetCache {
public:
Handle<T> find(const std::string &key) const {
std::shared_lock<std::shared_mutex> lock(mutex_);
auto it = map_.find(key);
if (it == map_.end()) {
return Handle<T>::invalid();
}
return it->second;
}
void set(const std::string &key, Handle<T> handle) {
std::unique_lock<std::shared_mutex> lock(mutex_);
map_[key] = handle;
}
void erase(const std::string &key) {
std::unique_lock<std::shared_mutex> lock(mutex_);
map_.erase(key);
}
void clear() {
std::unique_lock<std::shared_mutex> lock(mutex_);
map_.clear();
}
std::string findKeyByHandle(Handle<T> handle) const {
std::shared_lock<std::shared_mutex> lock(mutex_);
for (const auto &pair : map_) {
if (pair.second == handle) {
return pair.first;
}
}
return "";
}
private:
std::unordered_map<std::string, Handle<T>> map_;
mutable std::shared_mutex mutex_;
};
} // namespace extra2d

View File

@ -0,0 +1,95 @@
#pragma once
#include <assets/asset_loader.h>
#include <assets/asset_storage.h>
#include <assets/builtin/builtin_asset_factory.h>
#include <assets/cache/asset_cache.h>
#include <assets/dependency/asset_dependency_graph.h>
#include <assets/io/asset_file_system.h>
#include <assets/runtime/asset_async_runtime.h>
#include <assets/runtime/asset_hot_reload_runtime.h>
#include <functional>
#include <memory>
#include <string>
#include <vector>
namespace extra2d {
class ShaderLoader;
class AssetSystem {
public:
struct Stats {
size_t textureCount = 0;
size_t shaderCount = 0;
size_t materialCount = 0;
size_t meshCount = 0;
};
AssetSystem(AssetStorage<Texture> &textures, AssetStorage<Shader> &shaders,
AssetStorage<Material> &materials, AssetStorage<Mesh> &meshes,
std::unique_ptr<AssetLoader<Texture>> &textureLoader,
std::unique_ptr<AssetLoader<Shader>> &shaderLoader);
Handle<Texture> loadTexture(const std::string &path);
Handle<Texture> loadTextureFromMemory(const std::string &key,
const uint8_t *data, size_t size);
std::vector<Handle<Texture>>
loadTextureDir(const std::string &directory, bool recursive);
Handle<Shader> loadShader(const std::string &path);
Handle<Shader> loadShader(const std::string &vertPath,
const std::string &fragPath);
void loadTextureAsync(const std::string &path,
std::function<void(Handle<Texture>)> callback);
void loadShaderAsync(const std::string &path,
std::function<void(Handle<Shader>)> callback);
void initAsync(uint32_t threadCount = 0);
void shutdownAsync();
void processAsyncCallbacks();
void registerTextureLoader(std::unique_ptr<AssetLoader<Texture>> loader);
void registerShaderLoader(std::unique_ptr<AssetLoader<Shader>> loader);
bool createDefaults();
void destroyDefaults();
Handle<Texture> defaultTexture() const;
Handle<Shader> defaultShader() const;
Handle<Material> defaultMaterial() const;
Handle<Mesh> defaultQuad() const;
void enableHotReload(bool enable);
void setHotReloadInterval(float interval);
void checkForChanges();
void registerDependency(Handle<Material> material, Handle<Texture> texture);
void registerDependency(Handle<Material> material, Handle<Shader> shader);
void notifyTextureReloaded(Handle<Texture> texture);
void notifyShaderReloaded(Handle<Shader> shader);
void clear();
Stats stats() const;
private:
void reloadTexture(const AssetHotReloader::FileWatchInfo &info);
void reloadShader(const AssetHotReloader::FileWatchInfo &info);
AssetStorage<Texture> &textures_;
AssetStorage<Shader> &shaders_;
AssetStorage<Material> &materials_;
AssetStorage<Mesh> &meshes_;
std::unique_ptr<AssetLoader<Texture>> &textureLoader_;
std::unique_ptr<AssetLoader<Shader>> &shaderLoader_;
AssetCache<Texture> textureCache_;
AssetCache<Shader> shaderCache_;
AssetDependencyGraph dependencyGraph_;
AssetAsyncRuntime asyncRuntime_;
AssetHotReloadRuntime hotReloadRuntime_;
BuiltinAssetFactory builtinFactory_;
AssetFileSystem fileSystem_;
};
} // namespace extra2d

View File

@ -1,30 +1,22 @@
#pragma once #pragma once
#include <assets/handle.h> #include <assets/handle.h>
#include <functional>
#include <mutex>
#include <renderer/material.h> #include <renderer/material.h>
#include <renderer/shader.h> #include <renderer/shader.h>
#include <renderer/texture.h> #include <renderer/texture.h>
#include <functional>
#include <mutex>
#include <shared_mutex> #include <shared_mutex>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
namespace extra2d { namespace extra2d {
class AssetDependencyTracker { class AssetDependencyGraph {
public: public:
struct DependencyInfo {
Handle<Texture> texture;
Handle<Shader> shader;
std::vector<Handle<Material>> dependentMaterials;
};
void clear(); void clear();
void registerMaterialDependency(Handle<Material> material, void registerDependency(Handle<Material> material, Handle<Texture> texture);
Handle<Texture> texture); void registerDependency(Handle<Material> material, Handle<Shader> shader);
void registerMaterialDependency(Handle<Material> material,
Handle<Shader> shader);
void notifyTextureReloaded( void notifyTextureReloaded(
Handle<Texture> texture, Handle<Texture> texture,
const std::function<void(Handle<Material>)> &onMaterialUpdate) const; const std::function<void(Handle<Material>)> &onMaterialUpdate) const;
@ -33,8 +25,18 @@ public:
const std::function<void(Handle<Material>)> &onMaterialUpdate) const; const std::function<void(Handle<Material>)> &onMaterialUpdate) const;
private: private:
std::unordered_map<uint32_t, DependencyInfo> textureDependencies_; struct TextureDeps {
std::unordered_map<uint32_t, DependencyInfo> shaderDependencies_; Handle<Texture> texture;
std::vector<Handle<Material>> materials;
};
struct ShaderDeps {
Handle<Shader> shader;
std::vector<Handle<Material>> materials;
};
std::unordered_map<uint32_t, TextureDeps> textureDeps_;
std::unordered_map<uint32_t, ShaderDeps> shaderDeps_;
mutable std::shared_mutex mutex_; mutable std::shared_mutex mutex_;
}; };

View File

@ -0,0 +1,21 @@
#pragma once
#include <assets/async/asset_async_loader.h>
#include <cstdint>
namespace extra2d {
class AssetAsyncRuntime {
public:
void init(uint32_t threadCount = 0);
void shutdown();
bool running() const;
void submit(const AssetAsyncLoader::Task &task);
void processCallbacks();
private:
AssetAsyncLoader loader_;
};
} // namespace extra2d

View File

@ -0,0 +1,26 @@
#pragma once
#include <assets/hot_reload/asset_hot_reloader.h>
namespace extra2d {
class AssetHotReloadRuntime {
public:
explicit AssetHotReloadRuntime(const AssetFileSystem &fileSystem);
void enable(bool enable);
bool enabled() const;
void setInterval(float interval);
void addWatch(const std::string &path, Handle<Texture> handle);
void addWatch(const std::string &path, Handle<Shader> handle);
void clear();
void check(const std::function<void(const AssetHotReloader::FileWatchInfo &)>
&reloadTexture,
const std::function<void(const AssetHotReloader::FileWatchInfo &)>
&reloadShader);
private:
AssetHotReloader reloader_;
};
} // namespace extra2d

View File

@ -1,17 +1,14 @@
#include <algorithm>
#include <assets/assets_module.h> #include <assets/assets_module.h>
#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 <filesystem>
#include <thread>
#include <utils/logger.h> #include <utils/logger.h>
namespace extra2d { namespace extra2d {
AssetsModule *g_assetsModule = nullptr; AssetsModule *g_assetsModule = nullptr;
AssetsModule::AssetsModule() : hotReloader_(fileSystem_) { g_assetsModule = this; } AssetsModule::AssetsModule() { g_assetsModule = this; }
AssetsModule::~AssetsModule() { AssetsModule::~AssetsModule() {
if (g_assetsModule == this) { if (g_assetsModule == this) {
@ -21,712 +18,263 @@ AssetsModule::~AssetsModule() {
bool AssetsModule::init() { bool AssetsModule::init() {
E2D_INFO("资源模块正在初始化..."); E2D_INFO("资源模块正在初始化...");
textureLoader_ = std::make_unique<TextureLoader>(); textureLoader_ = std::make_unique<TextureLoader>();
shaderLoader_ = std::make_unique<ShaderLoader>(fileSystem_); shaderLoader_ = std::make_unique<ShaderLoader>();
system_ = std::make_unique<AssetSystem>(textures_, shaders_, materials_, meshes_,
// 监听窗口显示事件,在 OpenGL 上下文创建完成后创建默认资源 textureLoader_, shaderLoader_);
onShowListener_ = std::make_unique<events::OnShow::Listener>(); onShowListener_ = std::make_unique<events::OnShow::Listener>();
onShowListener_->bind([this]() { this->onGLContextReady(); }); onShowListener_->bind([this]() { this->onGLContextReady(); });
E2D_INFO("资源模块初始化成功 (V2)");
E2D_INFO("资源模块初始化成功 (等待 GL 上下文)");
return true; return true;
} }
void AssetsModule::onGLContextReady() {
if (defaultResourcesCreated_) {
return;
}
E2D_INFO("OpenGL 上下文就绪,正在创建默认资源...");
if (!createDefaultResources()) {
E2D_ERROR("创建默认资源失败");
return;
}
defaultResourcesCreated_ = true;
E2D_INFO("默认资源创建成功");
}
void AssetsModule::shutdown() { void AssetsModule::shutdown() {
E2D_INFO("资源模块正在关闭..."); E2D_INFO("资源模块正在关闭...");
// 关闭异步加载系统
shutdownAsyncLoader();
// 释放事件监听器
onShowListener_.reset(); onShowListener_.reset();
if (system_) {
destroyDefaultResources(); system_->clear();
system_.reset();
// 清空资源(使用写锁保护) }
{
std::unique_lock<std::shared_mutex> lock(mutex_);
textures_.clear(); textures_.clear();
shaders_.clear(); shaders_.clear();
materials_.clear(); materials_.clear();
meshes_.clear(); meshes_.clear();
texturePathCache_.clear();
shaderPathCache_.clear();
}
hotReloader_.clear();
dependencyTracker_.clear();
textureLoader_.reset(); textureLoader_.reset();
shaderLoader_.reset(); shaderLoader_.reset();
defaultResourcesCreated_ = false; defaultResourcesCreated_ = false;
E2D_INFO("资源模块关闭完成"); E2D_INFO("资源模块关闭完成");
} }
void AssetsModule::onGLContextReady() {
if (defaultResourcesCreated_ || !system_) {
return;
}
if (!system_->createDefaults()) {
E2D_ERROR("创建默认资源失败");
return;
}
defaultResourcesCreated_ = true;
}
AssetsModule *getAssets() { return g_assetsModule; } AssetsModule *getAssets() { return g_assetsModule; }
//=========================================================================== template <> Handle<Texture> AssetsModule::load<Texture>(const std::string &path) {
// Texture 加载特化 if (!system_) {
//===========================================================================
template <>
Handle<Texture> AssetsModule::load<Texture>(const std::string &path) {
// 先检查缓存(读锁)
{
std::shared_lock<std::shared_mutex> lock(mutex_);
auto it = texturePathCache_.find(path);
if (it != texturePathCache_.end()) {
if (textures_.isValid(it->second)) {
return it->second;
}
}
}
if (!textureLoader_) {
E2D_ERROR("纹理加载器未注册");
return Handle<Texture>::invalid(); return Handle<Texture>::invalid();
} }
return system_->loadTexture(path);
Ptr<Texture> texture = textureLoader_->load(path);
if (!texture) {
E2D_ERROR("加载纹理失败: {}", path);
return Handle<Texture>::invalid();
}
// 插入资源(写锁)
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;
}
E2D_DEBUG("已加载纹理: {} -> 句柄索引 {}", path, handle.index());
// 如果启用了热重载,添加文件监控
if (hotReloader_.enabled()) {
hotReloader_.addFileWatch(path, handle);
}
return handle;
} }
template <> template <>
Handle<Texture> AssetsModule::loadFromMemory<Texture>(const std::string &key, Handle<Texture> AssetsModule::loadFromMemory<Texture>(const std::string &key,
const uint8_t *data, const uint8_t *data,
size_t size) { size_t size) {
auto it = texturePathCache_.find(key); if (!system_) {
if (it != texturePathCache_.end()) {
if (textures_.isValid(it->second)) {
return it->second;
}
texturePathCache_.erase(it);
}
if (!textureLoader_) {
E2D_ERROR("纹理加载器未注册");
return Handle<Texture>::invalid(); return Handle<Texture>::invalid();
} }
return system_->loadTextureFromMemory(key, data, size);
Ptr<Texture> texture = textureLoader_->loadFromMemory(data, size);
if (!texture) {
E2D_ERROR("从内存加载纹理失败: {}", key);
return Handle<Texture>::invalid();
}
Handle<Texture> handle = textures_.insert(texture);
texturePathCache_[key] = handle;
return handle;
} }
//===========================================================================
// Shader 加载特化
//===========================================================================
template <> Handle<Shader> AssetsModule::load<Shader>(const std::string &path) { template <> Handle<Shader> AssetsModule::load<Shader>(const std::string &path) {
// 先检查缓存(读锁) if (!system_) {
{
std::shared_lock<std::shared_mutex> lock(mutex_);
auto it = shaderPathCache_.find(path);
if (it != shaderPathCache_.end()) {
if (shaders_.isValid(it->second)) {
return it->second;
}
}
}
if (!shaderLoader_) {
E2D_ERROR("着色器加载器未注册");
return Handle<Shader>::invalid(); return Handle<Shader>::invalid();
} }
return system_->loadShader(path);
Ptr<Shader> shader = shaderLoader_->load(path);
if (!shader) {
E2D_ERROR("加载着色器失败: {}", path);
return Handle<Shader>::invalid();
}
// 插入资源(写锁)
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;
}
E2D_DEBUG("已加载着色器: {} -> 句柄索引 {}", path, handle.index());
// 如果启用了热重载,添加文件监控
if (hotReloader_.enabled()) {
hotReloader_.addFileWatch(path, handle);
}
return handle;
} }
template <> template <>
Handle<Shader> AssetsModule::load<Shader>(const std::string &vertPath, Handle<Shader> AssetsModule::load<Shader>(const std::string &vertPath,
const std::string &fragPath) { const std::string &fragPath) {
std::string cacheKey = vertPath + "|" + fragPath; if (!system_) {
// 先检查缓存(读锁)
{
std::shared_lock<std::shared_mutex> lock(mutex_);
auto it = shaderPathCache_.find(cacheKey);
if (it != shaderPathCache_.end()) {
if (shaders_.isValid(it->second)) {
return it->second;
}
}
}
if (!shaderLoader_) {
E2D_ERROR("着色器加载器未注册");
return Handle<Shader>::invalid(); return Handle<Shader>::invalid();
} }
return system_->loadShader(vertPath, fragPath);
ShaderLoader *shaderLoader = static_cast<ShaderLoader *>(shaderLoader_.get());
Ptr<Shader> shader = shaderLoader->load(vertPath, fragPath);
if (!shader) {
E2D_ERROR("加载着色器失败: {} + {}", vertPath, fragPath);
return Handle<Shader>::invalid();
}
// 插入资源(写锁)
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;
}
E2D_DEBUG("已加载着色器: {} + {} -> 句柄索引 {}", vertPath, fragPath,
handle.index());
// 如果启用了热重载,添加文件监控
if (hotReloader_.enabled()) {
hotReloader_.addFileWatch(vertPath, handle);
hotReloader_.addFileWatch(fragPath, handle);
}
return handle;
} }
//===========================================================================
// 批量加载
//===========================================================================
template <> template <>
std::vector<Handle<Texture>> std::vector<Handle<Texture>>
AssetsModule::loadDir<Texture>(const std::string &directory, AssetsModule::loadDir<Texture>(const std::string &directory,
const std::string &pattern, bool recursive) { const std::string &pattern, bool recursive) {
std::vector<Handle<Texture>> handles; (void)pattern;
if (!system_) {
try { return {};
std::filesystem::path dirPath(directory);
if (!fileSystem_.exists(directory)) {
E2D_WARN("目录未找到: {}", directory);
return handles;
} }
return system_->loadTextureDir(directory, recursive);
auto loadFile = [this, &handles](const std::filesystem::path &filePath) {
std::string ext = filePath.extension().string();
std::string pathStr = filePath.string();
for (const auto &supportedExt : textureLoader_->getExtensions()) {
if (ext == supportedExt) {
Handle<Texture> handle = load<Texture>(pathStr);
if (handle.isValid()) {
handles.push_back(handle);
}
break;
}
}
};
if (recursive) {
for (const auto &entry :
std::filesystem::recursive_directory_iterator(dirPath)) {
if (entry.is_regular_file()) {
loadFile(entry.path());
}
}
} else {
for (const auto &entry : std::filesystem::directory_iterator(dirPath)) {
if (entry.is_regular_file()) {
loadFile(entry.path());
}
}
}
} catch (const std::exception &e) {
E2D_ERROR("加载目录 {} 时出错: {}", directory, e.what());
}
E2D_DEBUG("已从 {} 加载 {} 个纹理", directory, handles.size());
return handles;
} }
//===========================================================================
// 异步加载
//===========================================================================
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) {
// 确保异步加载系统已初始化 if (!system_) {
if (!asyncLoader_.isRunning()) { return;
initAsyncLoader();
} }
system_->loadTextureAsync(path, std::move(callback));
// 创建并提交加载任务
LoadTask task;
task.type = LoadTask::Type::Texture;
task.priority = LoadTask::Priority::Normal;
task.path = path;
task.textureCallback = callback;
submitLoadTask(task);
} }
template <> template <>
void AssetsModule::loadAsync<Shader>( void AssetsModule::loadAsync<Shader>(
const std::string &path, std::function<void(Handle<Shader>)> callback) { const std::string &path, std::function<void(Handle<Shader>)> callback) {
// 确保异步加载系统已初始化 if (!system_) {
if (!asyncLoader_.isRunning()) { return;
initAsyncLoader();
} }
system_->loadShaderAsync(path, std::move(callback));
// 创建并提交加载任务
LoadTask task;
task.type = LoadTask::Type::Shader;
task.priority = LoadTask::Priority::Normal;
task.path = path;
task.secondaryPath = "";
task.shaderCallback = callback;
submitLoadTask(task);
} }
//===========================================================================
// 加载器注册
//===========================================================================
template <> template <>
void AssetsModule::registerLoader<Texture>( void AssetsModule::registerLoader<Texture>(
std::unique_ptr<AssetLoader<Texture>> loader) { std::unique_ptr<AssetLoader<Texture>> loader) {
if (!system_) {
textureLoader_ = std::move(loader); textureLoader_ = std::move(loader);
return;
}
system_->registerTextureLoader(std::move(loader));
} }
template <> template <>
void AssetsModule::registerLoader<Shader>( void AssetsModule::registerLoader<Shader>(
std::unique_ptr<AssetLoader<Shader>> loader) { std::unique_ptr<AssetLoader<Shader>> loader) {
if (!system_) {
shaderLoader_ = std::move(loader); shaderLoader_ = std::move(loader);
return;
}
system_->registerShaderLoader(std::move(loader));
} }
//===========================================================================
// 默认资源
//===========================================================================
bool AssetsModule::createDefaultResources() { bool AssetsModule::createDefaultResources() {
{ if (!system_) {
Ptr<Texture> texture = makePtr<Texture>();
uint8_t whitePixel[] = {255, 255, 255, 255};
if (!texture->loadFromMemory(whitePixel, 1, 1, TextureFormat::RGBA8)) {
E2D_ERROR("创建默认纹理失败");
return false; return false;
} }
defaultTexture_ = textures_.insert(texture); return system_->createDefaults();
E2D_DEBUG("已创建默认纹理");
}
{
// 从文件加载默认着色器
// 使用 FileModule 的 assetPath 来获取正确的资源路径(支持 RomFS
std::string vertPath = fileSystem_.assetPath("shader/default.vert");
std::string fragPath = fileSystem_.assetPath("shader/default.frag");
if (!fileSystem_.exists(vertPath) || !fileSystem_.exists(fragPath)) {
E2D_ERROR("默认着色器文件未找到: {}, {}", vertPath, fragPath);
return false;
}
// 读取着色器文件内容
std::string vsSource = fileSystem_.readString(vertPath);
std::string fsSource = fileSystem_.readString(fragPath);
if (vsSource.empty() || fsSource.empty()) {
E2D_ERROR("读取默认着色器文件失败: {}, {}", vertPath, fragPath);
return false;
}
// 从源码加载着色器
Ptr<Shader> shader = makePtr<Shader>();
if (!shader->loadFromSource(vsSource, fsSource)) {
E2D_ERROR("编译默认着色器失败: {}, {}", vertPath, fragPath);
return false;
}
defaultShader_ = shaders_.insert(shader);
E2D_DEBUG("已从文件加载默认着色器: {}, {}", vertPath, fragPath);
}
{
// 创建材质布局(与着色器中的 MaterialUBO 匹配)
// layout(std140, binding = 1) uniform MaterialUBO {
// vec4 uColor; // 16 bytes
// vec4 uTintColor; // 16 bytes
// float uOpacity; // 4 bytes
// float uPadding[3]; // 12 bytes
// };
Ptr<MaterialLayout> layout = makePtr<MaterialLayout>();
layout->addParam("uColor", MaterialParamType::Color);
layout->addParam("uTintColor", MaterialParamType::Color);
layout->addParam("uOpacity", MaterialParamType::Float);
layout->finalize();
Ptr<Material> material = makePtr<Material>();
material->setShader(getPtr(defaultShader_));
material->setLayout(layout);
// 设置默认材质参数
material->setColor("uColor", Color::White);
material->setColor("uTintColor", Color::White);
material->setFloat("uOpacity", 1.0f);
// 添加默认纹理到材质
material->setTexture("uTexture", getPtr(defaultTexture_), 0);
defaultMaterial_ = materials_.insert(material);
E2D_DEBUG("已创建默认材质,使用默认纹理和布局");
}
{
Ptr<Mesh> mesh = Mesh::createQuad(Vec2(1.0f, 1.0f));
if (!mesh) {
E2D_ERROR("创建默认四边形网格失败");
return false;
}
defaultQuad_ = meshes_.insert(mesh);
E2D_DEBUG("已创建默认四边形网格");
}
return true;
} }
void AssetsModule::destroyDefaultResources() { void AssetsModule::destroyDefaultResources() {
materials_.remove(defaultMaterial_); if (!system_) {
meshes_.remove(defaultQuad_); return;
textures_.remove(defaultTexture_); }
shaders_.remove(defaultShader_); system_->destroyDefaults();
defaultMaterial_ = Handle<Material>::invalid();
defaultQuad_ = Handle<Mesh>::invalid();
defaultTexture_ = Handle<Texture>::invalid();
defaultShader_ = Handle<Shader>::invalid();
} }
Handle<Texture> AssetsModule::getDefaultTexture() { return defaultTexture_; } Handle<Texture> AssetsModule::getDefaultTexture() {
return system_ ? system_->defaultTexture() : Handle<Texture>::invalid();
Handle<Shader> AssetsModule::getDefaultShader() { return defaultShader_; }
Handle<Material> AssetsModule::getDefaultMaterial() { return defaultMaterial_; }
Handle<Mesh> AssetsModule::getDefaultQuad() { return defaultQuad_; }
Texture *AssetsModule::getDefaultTexturePtr() {
return textures_.get(defaultTexture_);
} }
Shader *AssetsModule::getDefaultShaderPtr() { Handle<Shader> AssetsModule::getDefaultShader() {
return shaders_.get(defaultShader_); return system_ ? system_->defaultShader() : Handle<Shader>::invalid();
} }
Handle<Material> AssetsModule::getDefaultMaterial() {
return system_ ? system_->defaultMaterial() : Handle<Material>::invalid();
}
Handle<Mesh> AssetsModule::getDefaultQuad() {
return system_ ? system_->defaultQuad() : Handle<Mesh>::invalid();
}
Texture *AssetsModule::getDefaultTexturePtr() { return textures_.get(getDefaultTexture()); }
Shader *AssetsModule::getDefaultShaderPtr() { return shaders_.get(getDefaultShader()); }
Material *AssetsModule::getDefaultMaterialPtr() { Material *AssetsModule::getDefaultMaterialPtr() {
return materials_.get(defaultMaterial_); return materials_.get(getDefaultMaterial());
} }
Mesh *AssetsModule::getDefaultQuadPtr() { return meshes_.get(defaultQuad_); } Mesh *AssetsModule::getDefaultQuadPtr() { return meshes_.get(getDefaultQuad()); }
//===========================================================================
// 热重载
//===========================================================================
void AssetsModule::enableHotReload(bool enable) { void AssetsModule::enableHotReload(bool enable) {
hotReloader_.enable(enable); if (!system_) {
if (enable) { return;
E2D_INFO("热重载已启用");
} else {
E2D_INFO("热重载已禁用");
} }
system_->enableHotReload(enable);
} }
void AssetsModule::setHotReloadInterval(float interval) { void AssetsModule::setHotReloadInterval(float interval) {
hotReloader_.setInterval(interval); if (!system_) {
}
void AssetsModule::reloadTexture(const AssetHotReloader::FileWatchInfo &info) {
if (!textureLoader_ || !info.textureHandle.isValid()) {
return; return;
} }
system_->setHotReloadInterval(interval);
E2D_INFO("正在重载纹理: {}", info.path);
// 获取旧纹理指针
Texture *oldTexture = textures_.get(info.textureHandle);
if (!oldTexture) {
E2D_ERROR("未找到要重载的旧纹理: {}", info.path);
return;
}
// 直接在原有纹理对象上重新加载
if (!oldTexture->reloadFromFile(info.path)) {
E2D_ERROR("重载纹理失败: {}", info.path);
return;
}
E2D_INFO("纹理重载成功: {}", info.path);
notifyTextureReloaded(info.textureHandle);
}
void AssetsModule::reloadShader(const AssetHotReloader::FileWatchInfo &info) {
if (!shaderLoader_ || !info.shaderHandle.isValid()) {
return;
}
E2D_INFO("正在重载着色器: {}", info.path);
// 获取旧着色器指针
Shader *oldShader = shaders_.get(info.shaderHandle);
if (!oldShader) {
E2D_ERROR("未找到要重载的旧着色器: {}", info.path);
return;
}
// 查找缓存键
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_WARN("未找到着色器缓存键: {}", info.path);
return;
}
// 解析顶点/片段着色器路径并重新加载
size_t sepPos = cacheKey.find('|');
if (sepPos == std::string::npos) {
// 单文件模式 - 这里暂不支持,因为 ShaderLoader 目前只支持双文件
E2D_WARN("不支持单文件着色器重载: {}", info.path);
} else {
// 双文件模式
std::string vertPath = cacheKey.substr(0, sepPos);
std::string fragPath = cacheKey.substr(sepPos + 1);
// 读取着色器文件内容
std::string vsSource = fileSystem_.readString(vertPath);
std::string fsSource = fileSystem_.readString(fragPath);
if (vsSource.empty() || fsSource.empty()) {
E2D_ERROR("读取着色器文件失败: {}, {}", vertPath, fragPath);
return;
}
// 直接在原有着色器对象上重新加载
if (!oldShader->reloadFromSource(vsSource, fsSource)) {
E2D_ERROR("重载着色器失败: {} + {}", vertPath, fragPath);
return;
}
}
E2D_INFO("着色器重载成功: {}", info.path);
notifyShaderReloaded(info.shaderHandle);
} }
void AssetsModule::checkForChanges() { void AssetsModule::checkForChanges() {
hotReloader_.checkForChanges( if (!system_) {
[this](const AssetHotReloader::FileWatchInfo &info) {
reloadTexture(info);
},
[this](const AssetHotReloader::FileWatchInfo &info) {
reloadShader(info);
});
}
//===========================================================================
// 异步加载系统
//===========================================================================
void AssetsModule::initAsyncLoader(uint32_t threadCount) {
if (asyncLoader_.isRunning()) {
return; return;
} }
asyncLoader_.init(threadCount); system_->checkForChanges();
E2D_INFO("异步加载器已初始化,使用 {} 个线程", threadCount); }
void AssetsModule::initAsyncLoader(uint32_t threadCount) {
if (!system_) {
return;
}
system_->initAsync(threadCount);
} }
void AssetsModule::shutdownAsyncLoader() { void AssetsModule::shutdownAsyncLoader() {
if (!asyncLoader_.isRunning()) { if (!system_) {
return; return;
} }
asyncLoader_.shutdown(); system_->shutdownAsync();
E2D_INFO("异步加载器已关闭");
} }
void AssetsModule::submitLoadTask(const LoadTask &task) { void AssetsModule::submitLoadTask(const LoadTask &task) {
AssetAsyncLoader::Task asyncTask; if (!system_) {
asyncTask.priority = static_cast<AssetAsyncLoader::Priority>(task.priority); return;
}
if (task.type == LoadTask::Type::Texture) { if (task.type == LoadTask::Type::Texture) {
auto handle = std::make_shared<Handle<Texture>>(Handle<Texture>::invalid()); system_->loadTextureAsync(task.path, task.textureCallback);
asyncTask.work = [this, path = task.path, handle]() {
*handle = load<Texture>(path);
};
if (task.textureCallback) {
asyncTask.onComplete = [handle, callback = task.textureCallback]() {
callback(*handle);
};
}
} else { } else {
auto handle = std::make_shared<Handle<Shader>>(Handle<Shader>::invalid()); system_->loadShaderAsync(task.path, task.shaderCallback);
asyncTask.work = [this, path = task.path, secondary = task.secondaryPath,
handle]() {
if (secondary.empty()) {
*handle = load<Shader>(path);
} else {
*handle = load<Shader>(path, secondary);
} }
};
if (task.shaderCallback) {
asyncTask.onComplete = [handle, callback = task.shaderCallback]() {
callback(*handle);
};
}
}
asyncLoader_.submit(asyncTask);
} }
void AssetsModule::processAsyncCallbacks() { void AssetsModule::processAsyncCallbacks() {
asyncLoader_.processCallbacks(); if (!system_) {
return;
}
system_->processAsyncCallbacks();
} }
//===========================================================================
// 资源依赖跟踪
//===========================================================================
void AssetsModule::registerMaterialDependency(Handle<Material> material, void AssetsModule::registerMaterialDependency(Handle<Material> material,
Handle<Texture> texture) { Handle<Texture> texture) {
dependencyTracker_.registerMaterialDependency(material, texture); if (!system_) {
E2D_DEBUG("已注册材质 {} 对纹理 {} 的依赖", material.index(), texture.index()); return;
}
system_->registerDependency(material, texture);
} }
void AssetsModule::registerMaterialDependency(Handle<Material> material, void AssetsModule::registerMaterialDependency(Handle<Material> material,
Handle<Shader> shader) { Handle<Shader> shader) {
dependencyTracker_.registerMaterialDependency(material, shader); if (!system_) {
E2D_DEBUG("已注册材质 {} 对着色器 {} 的依赖", material.index(), return;
shader.index()); }
system_->registerDependency(material, shader);
} }
void AssetsModule::notifyTextureReloaded(Handle<Texture> texture) { void AssetsModule::notifyTextureReloaded(Handle<Texture> texture) {
dependencyTracker_.notifyTextureReloaded( if (!system_) {
texture, [this](Handle<Material> materialHandle) { return;
Material *material = materials_.get(materialHandle);
if (material) {
E2D_DEBUG("材质 {} 已更新为重载后的纹理", materialHandle.index());
} }
}); system_->notifyTextureReloaded(texture);
} }
void AssetsModule::notifyShaderReloaded(Handle<Shader> shader) { void AssetsModule::notifyShaderReloaded(Handle<Shader> shader) {
dependencyTracker_.notifyShaderReloaded( if (!system_) {
shader, [this, shader](Handle<Material> materialHandle) { return;
Material *material = materials_.get(materialHandle);
if (material) {
material->setShader(getPtr(shader));
E2D_DEBUG("材质 {} 已更新为重载后的着色器", materialHandle.index());
} }
}); system_->notifyShaderReloaded(shader);
} }
//===========================================================================
// 统计
//===========================================================================
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(); if (!system_) {
stats.shaderCount = shaders_.count(); return stats;
stats.materialCount = materials_.count(); }
stats.meshCount = meshes_.count(); AssetSystem::Stats systemStats = system_->stats();
stats.textureCount = systemStats.textureCount;
stats.shaderCount = systemStats.shaderCount;
stats.materialCount = systemStats.materialCount;
stats.meshCount = systemStats.meshCount;
return stats; return stats;
} }
} // namespace extra2d } // namespace extra2d

View File

@ -0,0 +1,95 @@
#include <assets/builtin/builtin_asset_factory.h>
#include <utils/logger.h>
namespace extra2d {
BuiltinAssetFactory::BuiltinAssetFactory(AssetStorage<Texture> &textures,
AssetStorage<Shader> &shaders,
AssetStorage<Material> &materials,
AssetStorage<Mesh> &meshes,
const AssetFileSystem &fileSystem)
: textures_(textures), shaders_(shaders), materials_(materials), meshes_(meshes),
fileSystem_(fileSystem) {}
bool BuiltinAssetFactory::create() {
{
Ptr<Texture> texture = makePtr<Texture>();
uint8_t whitePixel[] = {255, 255, 255, 255};
if (!texture->loadFromMemory(whitePixel, 1, 1, TextureFormat::RGBA8)) {
return false;
}
defaultTexture_ = textures_.insert(texture);
}
{
std::string vertPath = fileSystem_.assetPath("shader/default.vert");
std::string fragPath = fileSystem_.assetPath("shader/default.frag");
if (!fileSystem_.exists(vertPath) || !fileSystem_.exists(fragPath)) {
return false;
}
std::string vsSource = fileSystem_.readString(vertPath);
std::string fsSource = fileSystem_.readString(fragPath);
if (vsSource.empty() || fsSource.empty()) {
return false;
}
Ptr<Shader> shader = makePtr<Shader>();
if (!shader->loadFromSource(vsSource, fsSource)) {
return false;
}
defaultShader_ = shaders_.insert(shader);
}
{
Ptr<MaterialLayout> layout = makePtr<MaterialLayout>();
layout->addParam("uColor", MaterialParamType::Color);
layout->addParam("uTintColor", MaterialParamType::Color);
layout->addParam("uOpacity", MaterialParamType::Float);
layout->finalize();
Ptr<Material> material = makePtr<Material>();
material->setShader(shaders_.getPtr(defaultShader_));
material->setLayout(layout);
material->setColor("uColor", Color::White);
material->setColor("uTintColor", Color::White);
material->setFloat("uOpacity", 1.0f);
material->setTexture("uTexture", textures_.getPtr(defaultTexture_), 0);
defaultMaterial_ = materials_.insert(material);
}
{
Ptr<Mesh> mesh = Mesh::createQuad(Vec2(1.0f, 1.0f));
if (!mesh) {
return false;
}
defaultQuad_ = meshes_.insert(mesh);
}
E2D_DEBUG("BuiltinAssetFactory: 默认资源创建完成");
return true;
}
void BuiltinAssetFactory::destroy() {
materials_.remove(defaultMaterial_);
meshes_.remove(defaultQuad_);
textures_.remove(defaultTexture_);
shaders_.remove(defaultShader_);
defaultMaterial_ = Handle<Material>::invalid();
defaultQuad_ = Handle<Mesh>::invalid();
defaultTexture_ = Handle<Texture>::invalid();
defaultShader_ = Handle<Shader>::invalid();
}
Handle<Texture> BuiltinAssetFactory::defaultTexture() const {
return defaultTexture_;
}
Handle<Shader> BuiltinAssetFactory::defaultShader() const { return defaultShader_; }
Handle<Material> BuiltinAssetFactory::defaultMaterial() const {
return defaultMaterial_;
}
Handle<Mesh> BuiltinAssetFactory::defaultQuad() const { return defaultQuad_; }
} // namespace extra2d

View File

@ -0,0 +1,313 @@
#include <assets/core/asset_system.h>
#include <assets/loaders/shader_loader.h>
#include <filesystem>
#include <utils/logger.h>
namespace extra2d {
AssetSystem::AssetSystem(AssetStorage<Texture> &textures,
AssetStorage<Shader> &shaders,
AssetStorage<Material> &materials,
AssetStorage<Mesh> &meshes,
std::unique_ptr<AssetLoader<Texture>> &textureLoader,
std::unique_ptr<AssetLoader<Shader>> &shaderLoader)
: textures_(textures), shaders_(shaders), materials_(materials), meshes_(meshes),
textureLoader_(textureLoader), shaderLoader_(shaderLoader),
hotReloadRuntime_(fileSystem_),
builtinFactory_(textures, shaders, materials, meshes, fileSystem_) {}
Handle<Texture> AssetSystem::loadTexture(const std::string &path) {
Handle<Texture> cached = textureCache_.find(path);
if (cached.isValid() && textures_.isValid(cached)) {
return cached;
}
if (!textureLoader_) {
return Handle<Texture>::invalid();
}
Ptr<Texture> texture = textureLoader_->load(path);
if (!texture) {
return Handle<Texture>::invalid();
}
Handle<Texture> handle = textures_.insert(texture);
textureCache_.set(path, handle);
if (hotReloadRuntime_.enabled()) {
hotReloadRuntime_.addWatch(path, handle);
}
return handle;
}
Handle<Texture> AssetSystem::loadTextureFromMemory(const std::string &key,
const uint8_t *data,
size_t size) {
Handle<Texture> cached = textureCache_.find(key);
if (cached.isValid() && textures_.isValid(cached)) {
return cached;
}
if (!textureLoader_) {
return Handle<Texture>::invalid();
}
Ptr<Texture> texture = textureLoader_->loadFromMemory(data, size);
if (!texture) {
return Handle<Texture>::invalid();
}
Handle<Texture> handle = textures_.insert(texture);
textureCache_.set(key, handle);
return handle;
}
std::vector<Handle<Texture>>
AssetSystem::loadTextureDir(const std::string &directory, bool recursive) {
std::vector<Handle<Texture>> handles;
if (!textureLoader_ || !fileSystem_.exists(directory)) {
return handles;
}
std::filesystem::path dirPath(directory);
auto loadFile = [this, &handles](const std::filesystem::path &filePath) {
std::string ext = filePath.extension().string();
for (const auto &supported : textureLoader_->getExtensions()) {
if (ext == supported) {
Handle<Texture> handle = loadTexture(filePath.string());
if (handle.isValid()) {
handles.push_back(handle);
}
break;
}
}
};
try {
if (recursive) {
for (const auto &entry :
std::filesystem::recursive_directory_iterator(dirPath)) {
if (entry.is_regular_file()) {
loadFile(entry.path());
}
}
} else {
for (const auto &entry : std::filesystem::directory_iterator(dirPath)) {
if (entry.is_regular_file()) {
loadFile(entry.path());
}
}
}
} catch (...) {
return {};
}
return handles;
}
Handle<Shader> AssetSystem::loadShader(const std::string &path) {
Handle<Shader> cached = shaderCache_.find(path);
if (cached.isValid() && shaders_.isValid(cached)) {
return cached;
}
if (!shaderLoader_) {
return Handle<Shader>::invalid();
}
Ptr<Shader> shader = shaderLoader_->load(path);
if (!shader) {
return Handle<Shader>::invalid();
}
Handle<Shader> handle = shaders_.insert(shader);
shaderCache_.set(path, handle);
if (hotReloadRuntime_.enabled()) {
hotReloadRuntime_.addWatch(path, handle);
}
return handle;
}
Handle<Shader> AssetSystem::loadShader(const std::string &vertPath,
const std::string &fragPath) {
std::string key = vertPath + "|" + fragPath;
Handle<Shader> cached = shaderCache_.find(key);
if (cached.isValid() && shaders_.isValid(cached)) {
return cached;
}
if (!shaderLoader_) {
return Handle<Shader>::invalid();
}
ShaderLoader *shaderLoader = static_cast<ShaderLoader *>(shaderLoader_.get());
Ptr<Shader> shader = shaderLoader->load(vertPath, fragPath);
if (!shader) {
return Handle<Shader>::invalid();
}
Handle<Shader> handle = shaders_.insert(shader);
shaderCache_.set(key, handle);
if (hotReloadRuntime_.enabled()) {
hotReloadRuntime_.addWatch(vertPath, handle);
hotReloadRuntime_.addWatch(fragPath, handle);
}
return handle;
}
void AssetSystem::initAsync(uint32_t threadCount) {
if (asyncRuntime_.running()) {
return;
}
asyncRuntime_.init(threadCount);
}
void AssetSystem::shutdownAsync() {
if (!asyncRuntime_.running()) {
return;
}
asyncRuntime_.shutdown();
}
void AssetSystem::loadTextureAsync(
const std::string &path, std::function<void(Handle<Texture>)> callback) {
if (!asyncRuntime_.running()) {
initAsync();
}
auto handle = std::make_shared<Handle<Texture>>(Handle<Texture>::invalid());
AssetAsyncLoader::Task task;
task.priority = AssetAsyncLoader::Priority::Normal;
task.work = [this, path, handle]() { *handle = loadTexture(path); };
task.onComplete = [handle, callback]() {
if (callback) {
callback(*handle);
}
};
asyncRuntime_.submit(task);
}
void AssetSystem::loadShaderAsync(
const std::string &path, std::function<void(Handle<Shader>)> callback) {
if (!asyncRuntime_.running()) {
initAsync();
}
auto handle = std::make_shared<Handle<Shader>>(Handle<Shader>::invalid());
AssetAsyncLoader::Task task;
task.priority = AssetAsyncLoader::Priority::Normal;
task.work = [this, path, handle]() { *handle = loadShader(path); };
task.onComplete = [handle, callback]() {
if (callback) {
callback(*handle);
}
};
asyncRuntime_.submit(task);
}
void AssetSystem::processAsyncCallbacks() { asyncRuntime_.processCallbacks(); }
void AssetSystem::registerTextureLoader(
std::unique_ptr<AssetLoader<Texture>> loader) {
textureLoader_ = std::move(loader);
}
void AssetSystem::registerShaderLoader(std::unique_ptr<AssetLoader<Shader>> loader) {
shaderLoader_ = std::move(loader);
}
bool AssetSystem::createDefaults() { return builtinFactory_.create(); }
void AssetSystem::destroyDefaults() { builtinFactory_.destroy(); }
Handle<Texture> AssetSystem::defaultTexture() const {
return builtinFactory_.defaultTexture();
}
Handle<Shader> AssetSystem::defaultShader() const {
return builtinFactory_.defaultShader();
}
Handle<Material> AssetSystem::defaultMaterial() const {
return builtinFactory_.defaultMaterial();
}
Handle<Mesh> AssetSystem::defaultQuad() const {
return builtinFactory_.defaultQuad();
}
void AssetSystem::enableHotReload(bool enable) { hotReloadRuntime_.enable(enable); }
void AssetSystem::setHotReloadInterval(float interval) {
hotReloadRuntime_.setInterval(interval);
}
void AssetSystem::checkForChanges() {
hotReloadRuntime_.check(
[this](const AssetHotReloader::FileWatchInfo &info) { reloadTexture(info); },
[this](const AssetHotReloader::FileWatchInfo &info) { reloadShader(info); });
}
void AssetSystem::reloadTexture(const AssetHotReloader::FileWatchInfo &info) {
if (!info.textureHandle.isValid()) {
return;
}
Texture *texture = textures_.get(info.textureHandle);
if (!texture) {
return;
}
if (!texture->reloadFromFile(info.path)) {
return;
}
notifyTextureReloaded(info.textureHandle);
}
void AssetSystem::reloadShader(const AssetHotReloader::FileWatchInfo &info) {
if (!info.shaderHandle.isValid()) {
return;
}
Shader *shader = shaders_.get(info.shaderHandle);
if (!shader) {
return;
}
std::string key = shaderCache_.findKeyByHandle(info.shaderHandle);
size_t sep = key.find('|');
if (sep == std::string::npos) {
return;
}
std::string vertPath = key.substr(0, sep);
std::string fragPath = key.substr(sep + 1);
std::string vsSource = fileSystem_.readString(vertPath);
std::string fsSource = fileSystem_.readString(fragPath);
if (vsSource.empty() || fsSource.empty()) {
return;
}
if (!shader->reloadFromSource(vsSource, fsSource)) {
return;
}
notifyShaderReloaded(info.shaderHandle);
}
void AssetSystem::registerDependency(Handle<Material> material,
Handle<Texture> texture) {
dependencyGraph_.registerDependency(material, texture);
}
void AssetSystem::registerDependency(Handle<Material> material,
Handle<Shader> shader) {
dependencyGraph_.registerDependency(material, shader);
}
void AssetSystem::notifyTextureReloaded(Handle<Texture> texture) {
dependencyGraph_.notifyTextureReloaded(texture, [](Handle<Material>) {});
}
void AssetSystem::notifyShaderReloaded(Handle<Shader> shader) {
dependencyGraph_.notifyShaderReloaded(shader, [this, shader](Handle<Material> material) {
Material *ptr = materials_.get(material);
if (ptr) {
ptr->setShader(shaders_.getPtr(shader));
}
});
}
void AssetSystem::clear() {
shutdownAsync();
hotReloadRuntime_.clear();
dependencyGraph_.clear();
textureCache_.clear();
shaderCache_.clear();
}
AssetSystem::Stats AssetSystem::stats() const {
Stats s;
s.textureCount = textures_.count();
s.shaderCount = shaders_.count();
s.materialCount = materials_.count();
s.meshCount = meshes_.count();
return s;
}
} // namespace extra2d

View File

@ -0,0 +1,81 @@
#include <algorithm>
#include <assets/dependency/asset_dependency_graph.h>
namespace extra2d {
void AssetDependencyGraph::clear() {
std::unique_lock<std::shared_mutex> lock(mutex_);
textureDeps_.clear();
shaderDeps_.clear();
}
void AssetDependencyGraph::registerDependency(Handle<Material> material,
Handle<Texture> texture) {
if (!material.isValid() || !texture.isValid()) {
return;
}
std::unique_lock<std::shared_mutex> lock(mutex_);
auto &deps = textureDeps_[texture.index()];
deps.texture = texture;
auto it = std::find(deps.materials.begin(), deps.materials.end(), material);
if (it == deps.materials.end()) {
deps.materials.push_back(material);
}
}
void AssetDependencyGraph::registerDependency(Handle<Material> material,
Handle<Shader> shader) {
if (!material.isValid() || !shader.isValid()) {
return;
}
std::unique_lock<std::shared_mutex> lock(mutex_);
auto &deps = shaderDeps_[shader.index()];
deps.shader = shader;
auto it = std::find(deps.materials.begin(), deps.materials.end(), material);
if (it == deps.materials.end()) {
deps.materials.push_back(material);
}
}
void AssetDependencyGraph::notifyTextureReloaded(
Handle<Texture> texture,
const std::function<void(Handle<Material>)> &onMaterialUpdate) const {
if (!texture.isValid()) {
return;
}
std::vector<Handle<Material>> materials;
{
std::shared_lock<std::shared_mutex> lock(mutex_);
auto it = textureDeps_.find(texture.index());
if (it == textureDeps_.end()) {
return;
}
materials = it->second.materials;
}
for (const auto &material : materials) {
onMaterialUpdate(material);
}
}
void AssetDependencyGraph::notifyShaderReloaded(
Handle<Shader> shader,
const std::function<void(Handle<Material>)> &onMaterialUpdate) const {
if (!shader.isValid()) {
return;
}
std::vector<Handle<Material>> materials;
{
std::shared_lock<std::shared_mutex> lock(mutex_);
auto it = shaderDeps_.find(shader.index());
if (it == shaderDeps_.end()) {
return;
}
materials = it->second.materials;
}
for (const auto &material : materials) {
onMaterialUpdate(material);
}
}
} // namespace extra2d

View File

@ -1,89 +0,0 @@
#include <algorithm>
#include <assets/deps/asset_dependency_tracker.h>
namespace extra2d {
void AssetDependencyTracker::clear() {
std::unique_lock<std::shared_mutex> lock(mutex_);
textureDependencies_.clear();
shaderDependencies_.clear();
}
void AssetDependencyTracker::registerMaterialDependency(Handle<Material> material,
Handle<Texture> texture) {
if (!material.isValid() || !texture.isValid()) {
return;
}
std::unique_lock<std::shared_mutex> lock(mutex_);
auto &info = textureDependencies_[texture.index()];
info.texture = texture;
auto it = std::find(info.dependentMaterials.begin(),
info.dependentMaterials.end(), material);
if (it == info.dependentMaterials.end()) {
info.dependentMaterials.push_back(material);
}
}
void AssetDependencyTracker::registerMaterialDependency(Handle<Material> material,
Handle<Shader> shader) {
if (!material.isValid() || !shader.isValid()) {
return;
}
std::unique_lock<std::shared_mutex> lock(mutex_);
auto &info = shaderDependencies_[shader.index()];
info.shader = shader;
auto it = std::find(info.dependentMaterials.begin(),
info.dependentMaterials.end(), material);
if (it == info.dependentMaterials.end()) {
info.dependentMaterials.push_back(material);
}
}
void AssetDependencyTracker::notifyTextureReloaded(
Handle<Texture> texture,
const std::function<void(Handle<Material>)> &onMaterialUpdate) const {
if (!texture.isValid()) {
return;
}
std::vector<Handle<Material>> materials;
{
std::shared_lock<std::shared_mutex> lock(mutex_);
auto it = textureDependencies_.find(texture.index());
if (it == textureDependencies_.end()) {
return;
}
materials = it->second.dependentMaterials;
}
for (const auto &material : materials) {
onMaterialUpdate(material);
}
}
void AssetDependencyTracker::notifyShaderReloaded(
Handle<Shader> shader,
const std::function<void(Handle<Material>)> &onMaterialUpdate) const {
if (!shader.isValid()) {
return;
}
std::vector<Handle<Material>> materials;
{
std::shared_lock<std::shared_mutex> lock(mutex_);
auto it = shaderDependencies_.find(shader.index());
if (it == shaderDependencies_.end()) {
return;
}
materials = it->second.dependentMaterials;
}
for (const auto &material : materials) {
onMaterialUpdate(material);
}
}
} // namespace extra2d

View File

@ -0,0 +1,18 @@
#include <assets/runtime/asset_async_runtime.h>
namespace extra2d {
void AssetAsyncRuntime::init(uint32_t threadCount) { loader_.init(threadCount); }
void AssetAsyncRuntime::shutdown() { loader_.shutdown(); }
bool AssetAsyncRuntime::running() const { return loader_.isRunning(); }
void AssetAsyncRuntime::submit(const AssetAsyncLoader::Task &task) {
loader_.submit(task);
}
void AssetAsyncRuntime::processCallbacks() { loader_.processCallbacks(); }
} // namespace extra2d

View File

@ -0,0 +1,37 @@
#include <assets/runtime/asset_hot_reload_runtime.h>
namespace extra2d {
AssetHotReloadRuntime::AssetHotReloadRuntime(const AssetFileSystem &fileSystem)
: reloader_(fileSystem) {}
void AssetHotReloadRuntime::enable(bool enable) { reloader_.enable(enable); }
bool AssetHotReloadRuntime::enabled() const { return reloader_.enabled(); }
void AssetHotReloadRuntime::setInterval(float interval) {
reloader_.setInterval(interval);
}
void AssetHotReloadRuntime::addWatch(const std::string &path,
Handle<Texture> handle) {
reloader_.addFileWatch(path, handle);
}
void AssetHotReloadRuntime::addWatch(const std::string &path,
Handle<Shader> handle) {
reloader_.addFileWatch(path, handle);
}
void AssetHotReloadRuntime::clear() { reloader_.clear(); }
void AssetHotReloadRuntime::check(
const std::function<void(const AssetHotReloader::FileWatchInfo &)>
&reloadTexture,
const std::function<void(const AssetHotReloader::FileWatchInfo &)>
&reloadShader) {
reloader_.checkForChanges(reloadTexture, reloadShader);
}
} // namespace extra2d