#include #include #include #include #include #include #include #include namespace extra2d { 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(); shaderLoader_ = std::make_unique(); // 监听窗口显示事件,在 OpenGL 上下文创建完成后创建默认资源 onShowListener_ = std::make_unique(); 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 lock(mutex_); textures_.clear(); shaders_.clear(); materials_.clear(); meshes_.clear(); texturePathCache_.clear(); shaderPathCache_.clear(); fileWatchList_.clear(); } // 清空依赖关系 { std::unique_lock 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 AssetsModule::load(const std::string &path) { // 先检查缓存(读锁) { std::shared_lock 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::invalid(); } Ptr texture = textureLoader_->load(path); if (!texture) { E2D_LOG_ERROR("Failed to load texture: {}", path); return Handle::invalid(); } // 插入资源(写锁) Handle handle; { std::unique_lock 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 AssetsModule::loadFromMemory(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::invalid(); } Ptr texture = textureLoader_->loadFromMemory(data, size); if (!texture) { E2D_LOG_ERROR("Failed to load texture from memory: {}", key); return Handle::invalid(); } Handle handle = textures_.insert(texture); texturePathCache_[key] = handle; return handle; } //=========================================================================== // Shader 加载特化 //=========================================================================== template <> Handle AssetsModule::load(const std::string &path) { // 先检查缓存(读锁) { std::shared_lock 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::invalid(); } Ptr shader = shaderLoader_->load(path); if (!shader) { E2D_LOG_ERROR("Failed to load shader: {}", path); return Handle::invalid(); } // 插入资源(写锁) Handle handle; { std::unique_lock 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 AssetsModule::load(const std::string &vertPath, const std::string &fragPath) { std::string cacheKey = vertPath + "|" + fragPath; // 先检查缓存(读锁) { std::shared_lock 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::invalid(); } ShaderLoader *shaderLoader = static_cast(shaderLoader_.get()); Ptr shader = shaderLoader->load(vertPath, fragPath); if (!shader) { E2D_LOG_ERROR("Failed to load shader: {} + {}", vertPath, fragPath); return Handle::invalid(); } // 插入资源(写锁) Handle handle; { std::unique_lock 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> AssetsModule::loadDir(const std::string &directory, const std::string &pattern, bool recursive) { std::vector> handles; try { std::filesystem::path dirPath(directory); if (!std::filesystem::exists(dirPath)) { 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 handle = load(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( const std::string &path, std::function)> callback) { std::thread([this, path, callback]() { Handle handle = load(path); if (callback) { callback(handle); } }).detach(); } //=========================================================================== // 加载器注册 //=========================================================================== template <> void AssetsModule::registerLoader( std::unique_ptr> loader) { textureLoader_ = std::move(loader); } template <> void AssetsModule::registerLoader( std::unique_ptr> loader) { shaderLoader_ = std::move(loader); } //=========================================================================== // 默认资源 //=========================================================================== bool AssetsModule::createDefaultResources() { { Ptr texture = makePtr(); 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"); } { // 从文件加载默认着色器 std::filesystem::path vertPath = "shader/default.vert"; std::filesystem::path fragPath = "shader/default.frag"; Ptr shader = makePtr(); if (!std::filesystem::exists(vertPath) || !std::filesystem::exists(fragPath)) { E2D_LOG_ERROR("Default shader files not found: {}, {}", vertPath.string(), fragPath.string()); return false; } if (!shader->loadFromFile(vertPath.string(), fragPath.string())) { E2D_LOG_ERROR("Failed to load default shader from files: {}, {}", vertPath.string(), fragPath.string()); return false; } defaultShader_ = shaders_.insert(shader); E2D_LOG_DEBUG("Loaded default shader from files: {}, {}", vertPath.string(), fragPath.string()); } { // 创建材质布局(与着色器中的 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 layout = makePtr(); layout->addParam("uColor", MaterialParamType::Color); layout->addParam("uTintColor", MaterialParamType::Color); layout->addParam("uOpacity", MaterialParamType::Float); layout->finalize(); Ptr material = makePtr(); 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::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::invalid(); defaultQuad_ = Handle::invalid(); defaultTexture_ = Handle::invalid(); defaultShader_ = Handle::invalid(); } Handle AssetsModule::getDefaultTexture() { return defaultTexture_; } Handle AssetsModule::getDefaultShader() { return defaultShader_; } Handle AssetsModule::getDefaultMaterial() { return defaultMaterial_; } Handle 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 AssetsModule::getInstancedShader() { return instancedShader_; } Handle AssetsModule::getInstancedMaterial() { return instancedMaterial_; } Shader *AssetsModule::getInstancedShaderPtr() { return shaders_.get(instancedShader_); } Material *AssetsModule::getInstancedMaterialPtr() { return materials_.get(instancedMaterial_); } bool AssetsModule::createInstancedResources() { // 加载实例化着色器 std::filesystem::path vertPath = "shader/sprite_instanced.vert"; std::filesystem::path fragPath = "shader/sprite_instanced.frag"; if (!std::filesystem::exists(vertPath) || !std::filesystem::exists(fragPath)) { E2D_LOG_ERROR("Instanced shader files not found: {}, {}", vertPath.string(), fragPath.string()); return false; } Ptr shader = makePtr(); if (!shader->loadInstancedFromFile(vertPath.string(), fragPath.string())) { E2D_LOG_ERROR("Failed to load instanced shader from files: {}, {}", vertPath.string(), fragPath.string()); return false; } instancedShader_ = shaders_.insert(shader); E2D_LOG_DEBUG("Loaded instanced shader from files: {}, {}", vertPath.string(), fragPath.string()); // 创建实例化材质布局 Ptr layout = makePtr(); layout->addParam("uColor", MaterialParamType::Color); layout->addParam("uTintColor", MaterialParamType::Color); layout->addParam("uOpacity", MaterialParamType::Float); layout->finalize(); Ptr material = makePtr(); 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 handle) { try { if (!std::filesystem::exists(path)) { return; } FileWatchInfo info; info.path = path; info.lastWriteTime = std::filesystem::last_write_time(path); info.textureHandle = handle; info.isTexture = true; std::unique_lock 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 handle) { try { if (!std::filesystem::exists(path)) { return; } FileWatchInfo info; info.path = path; info.lastWriteTime = std::filesystem::last_write_time(path); info.shaderHandle = handle; info.isTexture = false; std::unique_lock lock(mutex_); fileWatchList_.push_back(info); E2D_LOG_DEBUG("Watching shader file: {}", path); } catch (const std::exception &e) { E2D_LOG_ERROR("Failed to add file watch for {}: {}", path, e.what()); } } void AssetsModule::reloadTexture(const FileWatchInfo &info) { if (!textureLoader_ || !info.textureHandle.isValid()) { return; } E2D_LOG_INFO("Reloading texture: {}", info.path); Ptr 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 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 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_.get()); Ptr 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 lock(mutex_); for (auto &info : fileWatchList_) { try { if (!std::filesystem::exists(info.path)) { continue; } auto currentTime = std::filesystem::last_write_time(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 lock(queueMutex_); loadQueue_.clear(); E2D_LOG_INFO("Async loader shutdown"); } void AssetsModule::submitLoadTask(const LoadTask &task) { { std::lock_guard lock(queueMutex_); loadQueue_.push_back(task); // 按优先级排序 std::sort(loadQueue_.begin(), loadQueue_.end(), [](const LoadTask &a, const LoadTask &b) { return static_cast(a.priority) > static_cast(b.priority); }); } queueCV_.notify_one(); } void AssetsModule::workerThreadLoop() { while (asyncLoaderRunning_) { LoadTask task; { std::unique_lock 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 handle = load(task.path); if (task.textureCallback) { std::lock_guard callbackLock(callbackMutex_); completedCallbacks_.push_back( [handle, callback = task.textureCallback]() { callback(handle); }); } } else if (task.type == LoadTask::Type::Shader) { Handle handle; if (task.secondaryPath.empty()) { handle = load(task.path); } else { handle = load(task.path, task.secondaryPath); } if (task.shaderCallback) { std::lock_guard callbackLock(callbackMutex_); completedCallbacks_.push_back( [handle, callback = task.shaderCallback]() { callback(handle); }); } } } } void AssetsModule::processAsyncCallbacks() { std::vector> callbacks; { std::lock_guard lock(callbackMutex_); callbacks = std::move(completedCallbacks_); completedCallbacks_.clear(); } for (auto &callback : callbacks) { callback(); } } //=========================================================================== // 资源依赖跟踪 //=========================================================================== void AssetsModule::registerMaterialDependency(Handle material, Handle texture) { if (!material.isValid() || !texture.isValid()) { return; } std::unique_lock 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, Handle shader) { if (!material.isValid() || !shader.isValid()) { return; } std::unique_lock 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) { if (!texture.isValid()) { return; } std::shared_lock 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) { if (!shader.isValid()) { return; } std::shared_lock 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 lock(mutex_); Stats stats; stats.textureCount = textures_.count(); stats.shaderCount = shaders_.count(); stats.materialCount = materials_.count(); stats.meshCount = meshes_.count(); return stats; } } // namespace extra2d