refactor(assets): 重构资源系统架构,提取核心逻辑到 AssetSystem
- 新增 AssetSystem 类,整合异步加载、热重载、依赖跟踪等核心功能 - 将 AssetAsyncRuntime、AssetHotReloadRuntime 等组件提取为独立运行时类 - 重命名 AssetDependencyTracker 为 AssetDependencyGraph,改进数据结构 - 新增 BuiltinAssetFactory 负责默认资源的创建与管理 - 新增 AssetCache 提供线程安全的资源缓存机制 - 简化 AssetsModule 实现,将其职责委托给 AssetSystem - 保持原有 API 兼容性,仅重构内部实现
This commit is contained in:
parent
41817c9e8a
commit
b672b16b83
|
|
@ -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_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 全局访问
|
// 全局访问
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
Loading…
Reference in New Issue