Extra2D/src/assets/assets_module.cpp

1043 lines
29 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <algorithm>
#include <assets/assets_module.h>
#include <assets/loaders/shader_loader.h>
#include <assets/loaders/texture_loader.h>
#include <event/events.h>
#include <filesystem>
#include <module/module_registry.h>
#include <platform/file_module.h>
#include <thread>
#include <utils/logger.h>
namespace extra2d {
namespace {
/**
* @brief 获取 FileModule 实例
*/
FileModule *getFileModule() { return getModule<FileModule>(); }
/**
* @brief 检查路径是否为 RomFS 路径
*/
bool isRomfsPath(const std::string &path) {
FileModule *fileModule = getFileModule();
if (fileModule) {
return fileModule->isRomfsPath(path);
}
return false;
}
/**
* @brief 检查文件是否存在(使用 FileModule
*/
bool fileExists(const std::string &path) {
FileModule *fileModule = getFileModule();
if (fileModule) {
return fileModule->exists(path);
}
return false;
}
/**
* @brief 读取文件内容为字符串(使用 FileModule支持 RomFS
*/
std::string readFileToString(const std::string &path) {
FileModule *fileModule = getFileModule();
if (fileModule) {
return fileModule->readString(path);
}
return "";
}
/**
* @brief 获取文件最后写入时间(支持 RomFS 路径)
*/
std::filesystem::file_time_type getLastWriteTime(const std::string &path) {
if (isRomfsPath(path)) {
// RomFS 文件是只读的,返回一个固定时间
return std::filesystem::file_time_type::min();
}
return std::filesystem::last_write_time(path);
}
} // anonymous namespace
AssetsModule *g_assetsModule = nullptr;
AssetsModule::AssetsModule() { g_assetsModule = this; }
AssetsModule::~AssetsModule() {
if (g_assetsModule == this) {
g_assetsModule = nullptr;
}
}
bool AssetsModule::init() {
E2D_LOG_INFO("AssetsModule initializing...");
textureLoader_ = std::make_unique<TextureLoader>();
shaderLoader_ = std::make_unique<ShaderLoader>();
// 监听窗口显示事件,在 OpenGL 上下文创建完成后创建默认资源
onShowListener_ = std::make_unique<events::OnShow::Listener>();
onShowListener_->bind([this]() { this->onGLContextReady(); });
E2D_LOG_INFO(
"AssetsModule initialized successfully (waiting for GL context)");
return true;
}
void AssetsModule::onGLContextReady() {
if (defaultResourcesCreated_) {
return;
}
E2D_LOG_INFO("OpenGL context ready, creating default resources...");
if (!createDefaultResources()) {
E2D_LOG_ERROR("Failed to create default resources");
return;
}
// 创建实例化渲染资源
if (!createInstancedResources()) {
E2D_LOG_WARN("Failed to create instanced resources, instanced rendering "
"will not be available");
// 不返回错误,实例化渲染是可选功能
}
defaultResourcesCreated_ = true;
E2D_LOG_INFO("Default resources created successfully");
}
void AssetsModule::shutdown() {
E2D_LOG_INFO("AssetsModule shutting down...");
// 关闭异步加载系统
shutdownAsyncLoader();
// 释放事件监听器
onShowListener_.reset();
destroyDefaultResources();
// 清空资源(使用写锁保护)
{
std::unique_lock<std::shared_mutex> lock(mutex_);
textures_.clear();
shaders_.clear();
materials_.clear();
meshes_.clear();
texturePathCache_.clear();
shaderPathCache_.clear();
fileWatchList_.clear();
}
// 清空依赖关系
{
std::unique_lock<std::shared_mutex> lock(dependencyMutex_);
textureDependencies_.clear();
shaderDependencies_.clear();
}
textureLoader_.reset();
shaderLoader_.reset();
defaultResourcesCreated_ = false;
E2D_LOG_INFO("AssetsModule shutdown complete");
}
AssetsModule *getAssets() { return g_assetsModule; }
//===========================================================================
// Texture 加载特化
//===========================================================================
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_LOG_ERROR("TextureLoader not registered");
return Handle<Texture>::invalid();
}
Ptr<Texture> texture = textureLoader_->load(path);
if (!texture) {
E2D_LOG_ERROR("Failed to load texture: {}", 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_LOG_DEBUG("Loaded texture: {} -> handle index {}", path, handle.index());
// 如果启用了热重载,添加文件监控
if (hotReloadEnabled_) {
addFileWatch(path, handle);
}
return handle;
}
template <>
Handle<Texture> AssetsModule::loadFromMemory<Texture>(const std::string &key,
const uint8_t *data,
size_t size) {
auto it = texturePathCache_.find(key);
if (it != texturePathCache_.end()) {
if (textures_.isValid(it->second)) {
return it->second;
}
texturePathCache_.erase(it);
}
if (!textureLoader_) {
E2D_LOG_ERROR("TextureLoader not registered");
return Handle<Texture>::invalid();
}
Ptr<Texture> texture = textureLoader_->loadFromMemory(data, size);
if (!texture) {
E2D_LOG_ERROR("Failed to load texture from memory: {}", 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) {
// 先检查缓存(读锁)
{
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_LOG_ERROR("ShaderLoader not registered");
return Handle<Shader>::invalid();
}
Ptr<Shader> shader = shaderLoader_->load(path);
if (!shader) {
E2D_LOG_ERROR("Failed to load shader: {}", 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_LOG_DEBUG("Loaded shader: {} -> handle index {}", path, handle.index());
// 如果启用了热重载,添加文件监控
if (hotReloadEnabled_) {
addFileWatch(path, handle);
}
return handle;
}
template <>
Handle<Shader> AssetsModule::load<Shader>(const std::string &vertPath,
const std::string &fragPath) {
std::string cacheKey = vertPath + "|" + fragPath;
// 先检查缓存(读锁)
{
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_LOG_ERROR("ShaderLoader not registered");
return Handle<Shader>::invalid();
}
ShaderLoader *shaderLoader = static_cast<ShaderLoader *>(shaderLoader_.get());
Ptr<Shader> shader = shaderLoader->load(vertPath, fragPath);
if (!shader) {
E2D_LOG_ERROR("Failed to load shader: {} + {}", 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_LOG_DEBUG("Loaded shader: {} + {} -> handle index {}", vertPath, fragPath,
handle.index());
// 如果启用了热重载,添加文件监控
if (hotReloadEnabled_) {
addFileWatch(vertPath, handle);
addFileWatch(fragPath, handle);
}
return handle;
}
//===========================================================================
// 批量加载
//===========================================================================
template <>
std::vector<Handle<Texture>>
AssetsModule::loadDir<Texture>(const std::string &directory,
const std::string &pattern, bool recursive) {
std::vector<Handle<Texture>> handles;
try {
std::filesystem::path dirPath(directory);
if (!fileExists(directory)) {
E2D_LOG_WARN("Directory not found: {}", directory);
return handles;
}
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_LOG_ERROR("Error loading directory {}: {}", directory, e.what());
}
E2D_LOG_DEBUG("Loaded {} textures from {}", handles.size(), directory);
return handles;
}
//===========================================================================
// 异步加载
//===========================================================================
template <>
void AssetsModule::loadAsync<Texture>(
const std::string &path, std::function<void(Handle<Texture>)> callback) {
std::thread([this, path, callback]() {
Handle<Texture> handle = load<Texture>(path);
if (callback) {
callback(handle);
}
}).detach();
}
//===========================================================================
// 加载器注册
//===========================================================================
template <>
void AssetsModule::registerLoader<Texture>(
std::unique_ptr<AssetLoader<Texture>> loader) {
textureLoader_ = std::move(loader);
}
template <>
void AssetsModule::registerLoader<Shader>(
std::unique_ptr<AssetLoader<Shader>> loader) {
shaderLoader_ = std::move(loader);
}
//===========================================================================
// 默认资源
//===========================================================================
bool AssetsModule::createDefaultResources() {
{
Ptr<Texture> texture = makePtr<Texture>();
uint8_t whitePixel[] = {255, 255, 255, 255};
if (!texture->loadFromMemory(whitePixel, 1, 1, TextureFormat::RGBA8)) {
E2D_LOG_ERROR("Failed to create default texture");
return false;
}
defaultTexture_ = textures_.insert(texture);
E2D_LOG_DEBUG("Created default texture");
}
{
// 从文件加载默认着色器
// 使用 FileModule 的 assetPath 来获取正确的资源路径(支持 RomFS
FileModule *fileModule = getFileModule();
std::string vertPath = fileModule
? fileModule->assetPath("shader/default.vert")
: "shader/default.vert";
std::string fragPath = fileModule
? fileModule->assetPath("shader/default.frag")
: "shader/default.frag";
if (!fileExists(vertPath) || !fileExists(fragPath)) {
E2D_LOG_ERROR("Default shader files not found: {}, {}", vertPath,
fragPath);
return false;
}
// 读取着色器文件内容
std::string vsSource = readFileToString(vertPath);
std::string fsSource = readFileToString(fragPath);
if (vsSource.empty() || fsSource.empty()) {
E2D_LOG_ERROR("Failed to read default shader files: {}, {}", vertPath,
fragPath);
return false;
}
// 从源码加载着色器
Ptr<Shader> shader = makePtr<Shader>();
if (!shader->loadFromSource(vsSource, fsSource)) {
E2D_LOG_ERROR("Failed to compile default shader: {}, {}", vertPath,
fragPath);
return false;
}
defaultShader_ = shaders_.insert(shader);
E2D_LOG_DEBUG("Loaded default shader from files: {}, {}", 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_LOG_DEBUG("Created default material with default texture and layout");
}
{
Ptr<Mesh> mesh = Mesh::createQuad(Vec2(1.0f, 1.0f));
if (!mesh) {
E2D_LOG_ERROR("Failed to create default quad mesh");
return false;
}
defaultQuad_ = meshes_.insert(mesh);
E2D_LOG_DEBUG("Created default quad mesh");
}
return true;
}
void AssetsModule::destroyDefaultResources() {
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> AssetsModule::getDefaultTexture() { return defaultTexture_; }
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() {
return shaders_.get(defaultShader_);
}
Material *AssetsModule::getDefaultMaterialPtr() {
return materials_.get(defaultMaterial_);
}
Mesh *AssetsModule::getDefaultQuadPtr() { return meshes_.get(defaultQuad_); }
Handle<Shader> AssetsModule::getInstancedShader() { return instancedShader_; }
Handle<Material> AssetsModule::getInstancedMaterial() {
return instancedMaterial_;
}
Shader *AssetsModule::getInstancedShaderPtr() {
return shaders_.get(instancedShader_);
}
Material *AssetsModule::getInstancedMaterialPtr() {
return materials_.get(instancedMaterial_);
}
bool AssetsModule::createInstancedResources() {
// 加载实例化着色器
// 使用 FileModule 的 assetPath 来获取正确的资源路径(支持 RomFS
FileModule *fileModule = getFileModule();
std::string vertPath =
fileModule ? fileModule->assetPath("shader/sprite_instanced.vert")
: "shader/sprite_instanced.vert";
std::string fragPath =
fileModule ? fileModule->assetPath("shader/sprite_instanced.frag")
: "shader/sprite_instanced.frag";
if (!fileExists(vertPath) || !fileExists(fragPath)) {
E2D_LOG_ERROR("Instanced shader files not found: {}, {}", vertPath,
fragPath);
return false;
}
// 读取着色器文件内容
std::string vsSource = readFileToString(vertPath);
std::string fsSource = readFileToString(fragPath);
if (vsSource.empty() || fsSource.empty()) {
E2D_LOG_ERROR("Failed to read instanced shader files: {}, {}", vertPath,
fragPath);
return false;
}
// 从源码加载实例化着色器
Ptr<Shader> shader = makePtr<Shader>();
if (!shader->loadInstancedFromSource(vsSource, fsSource)) {
E2D_LOG_ERROR("Failed to compile instanced shader: {}, {}", vertPath,
fragPath);
return false;
}
instancedShader_ = shaders_.insert(shader);
E2D_LOG_DEBUG("Loaded instanced shader from files: {}, {}", vertPath,
fragPath);
// 创建实例化材质布局
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(instancedShader_));
material->setLayout(layout);
// 设置默认材质参数
material->setColor("uColor", Color::White);
material->setColor("uTintColor", Color::White);
material->setFloat("uOpacity", 1.0f);
// 添加默认纹理到材质
material->setTexture("uTexture", getPtr(defaultTexture_), 0);
instancedMaterial_ = materials_.insert(material);
E2D_LOG_DEBUG("Created instanced material with default texture and layout");
return true;
}
//===========================================================================
// 热重载
//===========================================================================
void AssetsModule::enableHotReload(bool enable) {
hotReloadEnabled_ = enable;
if (enable) {
E2D_LOG_INFO("Hot reload enabled");
} else {
E2D_LOG_INFO("Hot reload disabled");
}
}
void AssetsModule::setHotReloadInterval(float interval) {
hotReloadInterval_ = interval;
}
void AssetsModule::addFileWatch(const std::string &path,
Handle<Texture> handle) {
try {
if (!fileExists(path)) {
return;
}
FileWatchInfo info;
info.path = path;
info.lastWriteTime = getLastWriteTime(path);
info.textureHandle = handle;
info.isTexture = true;
std::unique_lock<std::shared_mutex> lock(mutex_);
fileWatchList_.push_back(info);
E2D_LOG_DEBUG("Watching texture file: {}", path);
} catch (const std::exception &e) {
E2D_LOG_ERROR("Failed to add file watch for {}: {}", path, e.what());
}
}
void AssetsModule::addFileWatch(const std::string &path,
Handle<Shader> handle) {
try {
if (!fileExists(path)) {
return;
}
FileWatchInfo info;
info.path = path;
info.lastWriteTime = getLastWriteTime(path);
info.shaderHandle = handle;
info.isTexture = false;
std::unique_lock<std::shared_mutex> lock(mutex_);
fileWatchList_.push_back(info);
E2D_LOG_DEBUG("Watching shader file: {}", path);
} catch (const std::exception &e) {
E2D_LOG_ERROR("Failed to add file watch for {}: {}", path, e.what());
}
}
void AssetsModule::reloadTexture(const FileWatchInfo &info) {
if (!textureLoader_ || !info.textureHandle.isValid()) {
return;
}
E2D_LOG_INFO("Reloading texture: {}", info.path);
Ptr<Texture> newTexture = textureLoader_->load(info.path);
if (!newTexture) {
E2D_LOG_ERROR("Failed to reload texture: {}", info.path);
return;
}
// 替换旧纹理的数据
Texture *oldTexture = textures_.get(info.textureHandle);
if (oldTexture) {
// 这里假设 Texture 类有更新数据的方法
// 如果没有,可能需要重新设计
E2D_LOG_INFO("Texture reloaded: {}", info.path);
notifyTextureReloaded(info.textureHandle);
}
}
void AssetsModule::reloadShader(const FileWatchInfo &info) {
if (!shaderLoader_ || !info.shaderHandle.isValid()) {
return;
}
E2D_LOG_INFO("Reloading shader: {}", info.path);
// 查找缓存键
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_LOG_WARN("Shader cache key not found for: {}", info.path);
return;
}
// 解析顶点/片段着色器路径
size_t sepPos = cacheKey.find('|');
if (sepPos == std::string::npos) {
// 单文件模式
Ptr<Shader> newShader = shaderLoader_->load(info.path);
if (!newShader) {
E2D_LOG_ERROR("Failed to reload shader: {}", info.path);
return;
}
} else {
// 双文件模式
std::string vertPath = cacheKey.substr(0, sepPos);
std::string fragPath = cacheKey.substr(sepPos + 1);
ShaderLoader *loader = static_cast<ShaderLoader *>(shaderLoader_.get());
Ptr<Shader> newShader = loader->load(vertPath, fragPath);
if (!newShader) {
E2D_LOG_ERROR("Failed to reload shader: {} + {}", vertPath, fragPath);
return;
}
}
E2D_LOG_INFO("Shader reloaded: {}", info.path);
notifyShaderReloaded(info.shaderHandle);
}
void AssetsModule::checkForChanges() {
if (!hotReloadEnabled_ || fileWatchList_.empty()) {
return;
}
std::unique_lock<std::shared_mutex> lock(mutex_);
for (auto &info : fileWatchList_) {
try {
// 跳过 RomFS 路径(只读文件系统)
if (isRomfsPath(info.path)) {
continue;
}
if (!fileExists(info.path)) {
continue;
}
auto currentTime = getLastWriteTime(info.path);
if (currentTime != info.lastWriteTime) {
info.lastWriteTime = currentTime;
// 解锁进行重载操作
lock.unlock();
if (info.isTexture) {
reloadTexture(info);
} else {
reloadShader(info);
}
lock.lock();
}
} catch (const std::exception &e) {
E2D_LOG_ERROR("Error checking file {}: {}", info.path, e.what());
}
}
}
//===========================================================================
// 异步加载系统
//===========================================================================
void AssetsModule::initAsyncLoader(uint32_t threadCount) {
if (asyncLoaderRunning_) {
return;
}
if (threadCount == 0) {
threadCount = std::max(1u, std::thread::hardware_concurrency() / 2);
}
asyncLoaderRunning_ = true;
for (uint32_t i = 0; i < threadCount; ++i) {
workerThreads_.emplace_back(&AssetsModule::workerThreadLoop, this);
}
E2D_LOG_INFO("Async loader initialized with {} threads", threadCount);
}
void AssetsModule::shutdownAsyncLoader() {
if (!asyncLoaderRunning_) {
return;
}
asyncLoaderRunning_ = false;
queueCV_.notify_all();
for (auto &thread : workerThreads_) {
if (thread.joinable()) {
thread.join();
}
}
workerThreads_.clear();
std::lock_guard<std::mutex> lock(queueMutex_);
loadQueue_.clear();
E2D_LOG_INFO("Async loader shutdown");
}
void AssetsModule::submitLoadTask(const LoadTask &task) {
{
std::lock_guard<std::mutex> lock(queueMutex_);
loadQueue_.push_back(task);
// 按优先级排序
std::sort(loadQueue_.begin(), loadQueue_.end(),
[](const LoadTask &a, const LoadTask &b) {
return static_cast<int>(a.priority) >
static_cast<int>(b.priority);
});
}
queueCV_.notify_one();
}
void AssetsModule::workerThreadLoop() {
while (asyncLoaderRunning_) {
LoadTask task;
{
std::unique_lock<std::mutex> lock(queueMutex_);
queueCV_.wait(
lock, [this] { return !loadQueue_.empty() || !asyncLoaderRunning_; });
if (!asyncLoaderRunning_) {
break;
}
if (loadQueue_.empty()) {
continue;
}
task = std::move(loadQueue_.back());
loadQueue_.pop_back();
}
// 执行加载任务
if (task.type == LoadTask::Type::Texture) {
Handle<Texture> handle = load<Texture>(task.path);
if (task.textureCallback) {
std::lock_guard<std::mutex> callbackLock(callbackMutex_);
completedCallbacks_.push_back(
[handle, callback = task.textureCallback]() { callback(handle); });
}
} else if (task.type == LoadTask::Type::Shader) {
Handle<Shader> handle;
if (task.secondaryPath.empty()) {
handle = load<Shader>(task.path);
} else {
handle = load<Shader>(task.path, task.secondaryPath);
}
if (task.shaderCallback) {
std::lock_guard<std::mutex> callbackLock(callbackMutex_);
completedCallbacks_.push_back(
[handle, callback = task.shaderCallback]() { callback(handle); });
}
}
}
}
void AssetsModule::processAsyncCallbacks() {
std::vector<std::function<void()>> callbacks;
{
std::lock_guard<std::mutex> lock(callbackMutex_);
callbacks = std::move(completedCallbacks_);
completedCallbacks_.clear();
}
for (auto &callback : callbacks) {
callback();
}
}
//===========================================================================
// 资源依赖跟踪
//===========================================================================
void AssetsModule::registerMaterialDependency(Handle<Material> material,
Handle<Texture> texture) {
if (!material.isValid() || !texture.isValid()) {
return;
}
std::unique_lock<std::shared_mutex> lock(dependencyMutex_);
uint32_t textureIndex = texture.index();
auto &info = textureDependencies_[textureIndex];
info.texture = texture;
// 检查是否已存在
auto it = std::find(info.dependentMaterials.begin(),
info.dependentMaterials.end(), material);
if (it == info.dependentMaterials.end()) {
info.dependentMaterials.push_back(material);
E2D_LOG_DEBUG("Registered material {} dependency on texture {}",
material.index(), textureIndex);
}
}
void AssetsModule::registerMaterialDependency(Handle<Material> material,
Handle<Shader> shader) {
if (!material.isValid() || !shader.isValid()) {
return;
}
std::unique_lock<std::shared_mutex> lock(dependencyMutex_);
uint32_t shaderIndex = shader.index();
auto &info = shaderDependencies_[shaderIndex];
info.shader = shader;
// 检查是否已存在
auto it = std::find(info.dependentMaterials.begin(),
info.dependentMaterials.end(), material);
if (it == info.dependentMaterials.end()) {
info.dependentMaterials.push_back(material);
E2D_LOG_DEBUG("Registered material {} dependency on shader {}",
material.index(), shaderIndex);
}
}
void AssetsModule::notifyTextureReloaded(Handle<Texture> texture) {
if (!texture.isValid()) {
return;
}
std::shared_lock<std::shared_mutex> lock(dependencyMutex_);
uint32_t textureIndex = texture.index();
auto it = textureDependencies_.find(textureIndex);
if (it != textureDependencies_.end()) {
E2D_LOG_INFO("Notifying {} materials of texture reload",
it->second.dependentMaterials.size());
// 材质需要更新纹理引用
// 这里可以触发材质更新事件
for (const auto &materialHandle : it->second.dependentMaterials) {
Material *material = materials_.get(materialHandle);
if (material) {
// 材质自动使用新的纹理数据
E2D_LOG_DEBUG("Material {} updated with reloaded texture",
materialHandle.index());
}
}
}
}
void AssetsModule::notifyShaderReloaded(Handle<Shader> shader) {
if (!shader.isValid()) {
return;
}
std::shared_lock<std::shared_mutex> lock(dependencyMutex_);
uint32_t shaderIndex = shader.index();
auto it = shaderDependencies_.find(shaderIndex);
if (it != shaderDependencies_.end()) {
E2D_LOG_INFO("Notifying {} materials of shader reload",
it->second.dependentMaterials.size());
for (const auto &materialHandle : it->second.dependentMaterials) {
Material *material = materials_.get(materialHandle);
if (material) {
// 更新材质的着色器引用
material->setShader(getPtr(shader));
E2D_LOG_DEBUG("Material {} updated with reloaded shader",
materialHandle.index());
}
}
}
}
//===========================================================================
// 统计
//===========================================================================
AssetsModule::Stats AssetsModule::getStats() const {
std::shared_lock<std::shared_mutex> lock(mutex_);
Stats stats;
stats.textureCount = textures_.count();
stats.shaderCount = shaders_.count();
stats.materialCount = materials_.count();
stats.meshCount = meshes_.count();
return stats;
}
} // namespace extra2d