From 85420634aa8611a3eead92ed2605a545c1bae28c Mon Sep 17 00:00:00 2001 From: ChestnutYueyue <952134128@qq.com> Date: Thu, 12 Feb 2026 12:20:14 +0800 Subject: [PATCH] =?UTF-8?q?refactor(resource):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E8=B5=84=E6=BA=90=E7=AE=A1=E7=90=86=E7=B3=BB=E7=BB=9F=E5=B9=B6?= =?UTF-8?q?=E7=A7=BB=E9=99=A4TexturePool?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除TexturePool类,将纹理缓存功能直接集成到ResourceManager - 实现基于LRU的纹理缓存管理,提高缓存效率 - 添加GPUContext类用于安全处理GPU资源生命周期 - 重构对象池实现,解决循环引用问题 - 优化应用关闭时的资源释放顺序 - 改进纹理加载错误处理 - 更新SpriteFrameCache直接加载纹理 - 清理无用头文件和代码 --- .../extra2d/animation/sprite_frame_cache.h | 38 +- Extra2D/include/extra2d/extra2d.h | 2 +- .../include/extra2d/graphics/gpu_context.h | 36 ++ .../include/extra2d/graphics/texture_pool.h | 208 ------- .../extra2d/resource/resource_manager.h | 140 ++++- Extra2D/include/extra2d/utils/object_pool.h | 138 +++-- Extra2D/src/app/application.cpp | 35 +- Extra2D/src/graphics/gpu_context.cpp | 22 + Extra2D/src/graphics/opengl/gl_renderer.cpp | 8 + Extra2D/src/graphics/opengl/gl_texture.cpp | 9 +- Extra2D/src/graphics/texture_pool.cpp | 518 ---------------- Extra2D/src/resource/resource_manager.cpp | 569 +++++++++++++----- Extra2D/src/utils/object_pool.cpp | 6 + 13 files changed, 772 insertions(+), 957 deletions(-) create mode 100644 Extra2D/include/extra2d/graphics/gpu_context.h delete mode 100644 Extra2D/include/extra2d/graphics/texture_pool.h create mode 100644 Extra2D/src/graphics/gpu_context.cpp delete mode 100644 Extra2D/src/graphics/texture_pool.cpp diff --git a/Extra2D/include/extra2d/animation/sprite_frame_cache.h b/Extra2D/include/extra2d/animation/sprite_frame_cache.h index aa6a03c..c4a1950 100644 --- a/Extra2D/include/extra2d/animation/sprite_frame_cache.h +++ b/Extra2D/include/extra2d/animation/sprite_frame_cache.h @@ -1,7 +1,9 @@ #pragma once #include -#include +#include +#include +#include #include #include #include @@ -39,7 +41,7 @@ public: void addSpriteFramesFromGrid(const std::string &texturePath, int frameWidth, int frameHeight, int frameCount = -1, int spacing = 0, int margin = 0) { - auto texture = TexturePool::getInstance().get(texturePath); + auto texture = loadTextureFromFile(texturePath); if (!texture) return; addSpriteFramesFromGrid(texture, texturePath, frameWidth, frameHeight, @@ -104,8 +106,8 @@ public: return it->second; } - // 缓存未命中,从 TexturePool 加载纹理并创建 SpriteFrame - auto texture = TexturePool::getInstance().get(texturePath); + // 缓存未命中,加载纹理并创建 SpriteFrame + auto texture = loadTextureFromFile(texturePath); if (!texture) return nullptr; @@ -161,6 +163,34 @@ private: SpriteFrameCache(const SpriteFrameCache &) = delete; SpriteFrameCache &operator=(const SpriteFrameCache &) = delete; + /** + * @brief 从文件加载纹理 + * @param filepath 纹理文件路径 + * @return 纹理对象,失败返回nullptr + */ + Ptr loadTextureFromFile(const std::string &filepath) { + // 使用stb_image加载图像 + stbi_set_flip_vertically_on_load(false); + int width, height, channels; + unsigned char *pixels = + stbi_load(filepath.c_str(), &width, &height, &channels, 4); + if (!pixels) { + E2D_ERROR("加载纹理失败: {} - {}", filepath, stbi_failure_reason()); + return nullptr; + } + + // 创建GLTexture + auto texture = makePtr(width, height, pixels, 4); + stbi_image_free(pixels); + + if (!texture || !texture->isValid()) { + E2D_ERROR("创建纹理失败: {}", filepath); + return nullptr; + } + + return texture; + } + mutable std::mutex mutex_; std::unordered_map> frames_; }; diff --git a/Extra2D/include/extra2d/extra2d.h b/Extra2D/include/extra2d/extra2d.h index 6ec0de3..f8fe2b8 100644 --- a/Extra2D/include/extra2d/extra2d.h +++ b/Extra2D/include/extra2d/extra2d.h @@ -19,7 +19,7 @@ #include #include #include -#include + #include #include diff --git a/Extra2D/include/extra2d/graphics/gpu_context.h b/Extra2D/include/extra2d/graphics/gpu_context.h new file mode 100644 index 0000000..ecd9809 --- /dev/null +++ b/Extra2D/include/extra2d/graphics/gpu_context.h @@ -0,0 +1,36 @@ +#pragma once + +#include + +namespace extra2d { + +// ============================================================================ +// GPU 上下文状态管理器 +// 用于跟踪 OpenGL/Vulkan 等 GPU 上下文的生命周期状态 +// 确保在 GPU 资源析构时能安全地检查上下文是否有效 +// ============================================================================ + +class GPUContext { +public: + /// 获取单例实例 + static GPUContext& getInstance(); + + /// 标记 GPU 上下文为有效(在初始化完成后调用) + void markValid(); + + /// 标记 GPU 上下文为无效(在销毁前调用) + void markInvalid(); + + /// 检查 GPU 上下文是否有效 + bool isValid() const; + +private: + GPUContext() = default; + ~GPUContext() = default; + GPUContext(const GPUContext&) = delete; + GPUContext& operator=(const GPUContext&) = delete; + + std::atomic valid_{false}; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/texture_pool.h b/Extra2D/include/extra2d/graphics/texture_pool.h deleted file mode 100644 index 0971714..0000000 --- a/Extra2D/include/extra2d/graphics/texture_pool.h +++ /dev/null @@ -1,208 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// 纹理池配置 -// ============================================================================ -struct TexturePoolConfig { - size_t maxCacheSize = 64 * 1024 * 1024; // 最大缓存大小 (64MB) - size_t maxTextureCount = 256; // 最大纹理数量 - float unloadInterval = 30.0f; // 自动清理间隔 (秒) - bool enableAsyncLoad = true; // 启用异步加载 -}; - -// ============================================================================ -// 纹理池 - 使用LRU缓存策略管理纹理复用 -// ============================================================================ -class TexturePool { -public: - // ------------------------------------------------------------------------ - // 单例访问 - // ------------------------------------------------------------------------ - static TexturePool &getInstance(); - - // ------------------------------------------------------------------------ - // 初始化和关闭 - // ------------------------------------------------------------------------ - bool init(const TexturePoolConfig &config = TexturePoolConfig{}); - void shutdown(); - - // ------------------------------------------------------------------------ - // 纹理获取 - // ------------------------------------------------------------------------ - - /** - * @brief 从文件获取纹理(带缓存) - * @param filepath 纹理文件路径 - * @return 纹理对象,失败返回nullptr - */ - Ptr get(const std::string &filepath); - - /** - * @brief 异步加载纹理 - * @param filepath 纹理文件路径 - * @param callback 加载完成回调 - */ - void getAsync(const std::string &filepath, - std::function)> callback); - - /** - * @brief 从内存数据创建纹理(自动缓存) - * @param name 纹理名称(用于缓存键) - * @param data 图像数据 - * @param width 宽度 - * @param height 高度 - * @param format 像素格式 - * @return 纹理对象 - */ - Ptr createFromData(const std::string &name, const uint8_t *data, - int width, int height, - PixelFormat format = PixelFormat::RGBA8); - - // ------------------------------------------------------------------------ - // 缓存管理 - // ------------------------------------------------------------------------ - - /** - * @brief 手动添加纹理到缓存 - */ - void add(const std::string &key, Ptr texture); - - /** - * @brief 从缓存移除纹理 - */ - void remove(const std::string &key); - - /** - * @brief 检查纹理是否在缓存中 - */ - bool has(const std::string &key) const; - - /** - * @brief 清空所有缓存 - */ - void clear(); - - /** - * @brief 清理未使用的纹理(LRU策略) - * @param targetSize 目标缓存大小 - */ - void trim(size_t targetSize); - - // ------------------------------------------------------------------------ - // 统计信息 - // ------------------------------------------------------------------------ - - /** - * @brief 获取当前缓存的纹理数量 - */ - size_t getTextureCount() const; - - /** - * @brief 获取当前缓存的总大小(字节) - */ - size_t getCacheSize() const; - - /** - * @brief 获取缓存命中率 - */ - float getHitRate() const; - - /** - * @brief 打印统计信息 - */ - void printStats() const; - - // ------------------------------------------------------------------------ - // 自动清理 - // ------------------------------------------------------------------------ - - /** - * @brief 更新(在主循环中调用,用于自动清理) - */ - void update(float dt); - - /** - * @brief 设置自动清理间隔 - */ - void setAutoUnloadInterval(float interval); - -private: - TexturePool() = default; - ~TexturePool() = default; - TexturePool(const TexturePool &) = delete; - TexturePool &operator=(const TexturePool &) = delete; - - // 侵入式LRU节点 - 减少内存分配和指针跳转 - struct LRUNode { - std::string key; - uint32_t prev; // 数组索引,0表示无效 - uint32_t next; // 数组索引,0表示无效 - bool valid = false; - }; - - // 缓存项 - 紧凑存储 - struct CacheEntry { - Ptr texture; - size_t size; // 纹理大小(字节) - float lastAccessTime; // 最后访问时间 - uint32_t accessCount; // 访问次数 - uint32_t lruIndex; // LRU节点索引 - }; - - mutable std::mutex mutex_; - TexturePoolConfig config_; - - // 缓存存储 - std::unordered_map cache_; - - // 侵入式LRU链表 - 使用数组索引代替指针,提高缓存局部性 - std::vector lruNodes_; - uint32_t lruHead_ = 0; // 最近使用 - uint32_t lruTail_ = 0; // 最久未使用 - uint32_t freeList_ = 0; // 空闲节点链表 - - // 统计 - size_t totalSize_ = 0; - uint64_t hitCount_ = 0; - uint64_t missCount_ = 0; - float autoUnloadTimer_ = 0.0f; - bool initialized_ = false; - - // 异步加载队列 - struct AsyncLoadTask { - std::string filepath; - std::function)> callback; - }; - std::vector asyncTasks_; - - // LRU操作 - uint32_t allocateLRUNode(const std::string& key); - void freeLRUNode(uint32_t index); - void moveToFront(uint32_t index); - void removeFromList(uint32_t index); - std::string evictLRU(); - - // 内部方法 - void touch(const std::string &key); - void evict(); - Ptr loadTexture(const std::string &filepath); - size_t calculateTextureSize(int width, int height, PixelFormat format); - void processAsyncTasks(); -}; - -// ============================================================================ -// 便捷宏 -// ============================================================================ -#define E2D_TEXTURE_POOL() ::extra2d::TexturePool::getInstance() - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/resource/resource_manager.h b/Extra2D/include/extra2d/resource/resource_manager.h index 4aaf7b4..8c6af02 100644 --- a/Extra2D/include/extra2d/resource/resource_manager.h +++ b/Extra2D/include/extra2d/resource/resource_manager.h @@ -6,16 +6,21 @@ #include #include #include +#include #include #include #include +#include +#include +#include +#include #include namespace extra2d { // ============================================================================ // 资源管理器 - 统一管理纹理、字体、音效等资源 -// 使用 TexturePool 作为纹理管理后端 +// 支持异步加载和纹理压缩 // ============================================================================ // 纹理格式枚举 @@ -30,6 +35,16 @@ enum class TextureFormat { ASTC8x8, // ASTC 8x8 压缩(高压缩率) }; +// ============================================================================ +// 纹理LRU缓存项 +// ============================================================================ +struct TextureCacheEntry { + Ptr texture; + size_t size = 0; // 纹理大小(字节) + float lastAccessTime = 0.0f; // 最后访问时间 + uint32_t accessCount = 0; // 访问次数 +}; + // 异步加载回调类型 using TextureLoadCallback = std::function)>; @@ -40,13 +55,6 @@ public: // ------------------------------------------------------------------------ static ResourceManager &getInstance(); - // ------------------------------------------------------------------------ - // 更新(在主循环中调用) - // ------------------------------------------------------------------------ - - /// 更新资源管理器,触发纹理池自动清理等 - void update(float dt); - // ------------------------------------------------------------------------ // 纹理资源 - 同步加载 // ------------------------------------------------------------------------ @@ -146,24 +154,39 @@ public: size_t getSoundCacheSize() const; // ------------------------------------------------------------------------ - // 异步加载控制(已弃用,保留接口兼容性) - // 纹理异步加载由 TexturePool 内部处理 + // LRU 缓存管理 // ------------------------------------------------------------------------ - + + /// 设置纹理缓存参数 + void setTextureCache(size_t maxCacheSize, size_t maxTextureCount, + float unloadInterval); + + /// 获取当前缓存的总大小(字节) + size_t getTextureCacheMemoryUsage() const; + + /// 获取缓存命中率 + float getTextureCacheHitRate() const; + + /// 打印缓存统计信息 + void printTextureCacheStats() const; + + /// 更新缓存(在主循环中调用,用于自动清理) + void update(float dt); + + // ------------------------------------------------------------------------ + // 异步加载控制 + // ------------------------------------------------------------------------ + /// 初始化异步加载系统(可选,自动在首次异步加载时初始化) - /// @deprecated 纹理池已内置异步加载,无需调用 void initAsyncLoader(); - + /// 关闭异步加载系统 - /// @deprecated 纹理池自动管理生命周期,无需调用 void shutdownAsyncLoader(); - + /// 等待所有异步加载完成 - /// @deprecated 使用回调机制处理异步加载结果 void waitForAsyncLoads(); - + /// 检查是否有正在进行的异步加载 - /// @deprecated 始终返回 false bool hasPendingAsyncLoads() const; ResourceManager(); @@ -186,14 +209,91 @@ private: std::vector compressTexture(const uint8_t* data, int width, int height, int channels, TextureFormat format); - // 互斥锁保护缓存(字体和音效缓存仍需锁保护) + // 互斥锁保护缓存 + mutable std::mutex textureMutex_; mutable std::mutex fontMutex_; mutable std::mutex soundMutex_; // 资源缓存 - 使用弱指针实现自动清理 - // 纹理缓存已移至 TexturePool,此处仅保留字体和音效缓存 std::unordered_map> fontCache_; std::unordered_map> soundCache_; + + // ============================================================================ + // 纹理LRU缓存 + // ============================================================================ + + // LRU链表节点 + struct LRUNode { + std::string key; + uint32_t prev = 0; // 数组索引,0表示无效 + uint32_t next = 0; // 数组索引,0表示无效 + bool valid = false; + }; + + // 纹理缓存配置 + size_t maxCacheSize_ = 64 * 1024 * 1024; // 最大缓存大小 (64MB) + size_t maxTextureCount_ = 256; // 最大纹理数量 + float unloadInterval_ = 30.0f; // 自动清理间隔 (秒) + + // 纹理缓存 - 使用强指针保持引用 + std::unordered_map textureCache_; + + // 侵入式LRU链表 - 使用数组索引代替指针,提高缓存局部性 + std::vector lruNodes_; + uint32_t lruHead_ = 0; // 最近使用 + uint32_t lruTail_ = 0; // 最久未使用 + uint32_t freeList_ = 0; // 空闲节点链表 + + // 统计 + size_t totalTextureSize_ = 0; + uint64_t textureHitCount_ = 0; + uint64_t textureMissCount_ = 0; + float autoUnloadTimer_ = 0.0f; + + // 异步加载相关 + struct AsyncLoadTask { + std::string filepath; + TextureFormat format; + TextureLoadCallback callback; + std::promise> promise; + }; + + std::queue asyncTaskQueue_; + std::mutex asyncQueueMutex_; + std::condition_variable asyncCondition_; + std::unique_ptr asyncThread_; + std::atomic asyncRunning_{false}; + std::atomic pendingAsyncLoads_{0}; + + void asyncLoadLoop(); + + // ============================================================================ + // LRU 缓存内部方法 + // ============================================================================ + + /// 分配LRU节点 + uint32_t allocateLRUNode(const std::string &key); + + /// 释放LRU节点 + void freeLRUNode(uint32_t index); + + /// 将节点移到链表头部(最近使用) + void moveToFront(uint32_t index); + + /// 从链表中移除节点 + void removeFromList(uint32_t index); + + /// 驱逐最久未使用的纹理 + std::string evictLRU(); + + /// 访问纹理(更新LRU位置) + void touchTexture(const std::string &key); + + /// 驱逐纹理直到满足大小限制 + void evictTexturesIfNeeded(); + + /// 计算纹理大小 + size_t calculateTextureSize(int width, int height, PixelFormat format) const; }; } // namespace extra2d diff --git a/Extra2D/include/extra2d/utils/object_pool.h b/Extra2D/include/extra2d/utils/object_pool.h index e3bd3db..30e4825 100644 --- a/Extra2D/include/extra2d/utils/object_pool.h +++ b/Extra2D/include/extra2d/utils/object_pool.h @@ -7,6 +7,7 @@ #include #include #include +#include namespace extra2d { @@ -22,33 +23,18 @@ public: static_assert(std::is_destructible_v, "T must be destructible"); ObjectPool() = default; - ~ObjectPool() { clear(); } + ~ObjectPool() { + // 确保所有对象都已归还后再销毁 + clear(); + } // 禁止拷贝 ObjectPool(const ObjectPool&) = delete; ObjectPool& operator=(const ObjectPool&) = delete; - // 允许移动 - ObjectPool(ObjectPool&& other) noexcept { - std::lock_guard lock(other.mutex_); - blocks_ = std::move(other.blocks_); - freeList_ = std::move(other.freeList_); - allocatedCount_ = other.allocatedCount_.load(); - other.allocatedCount_ = 0; - } - - ObjectPool& operator=(ObjectPool&& other) noexcept { - if (this != &other) { - std::lock_guard lock1(mutex_); - std::lock_guard lock2(other.mutex_); - clear(); - blocks_ = std::move(other.blocks_); - freeList_ = std::move(other.freeList_); - allocatedCount_ = other.allocatedCount_.load(); - other.allocatedCount_ = 0; - } - return *this; - } + // 禁止移动(避免地址失效问题) + ObjectPool(ObjectPool&& other) noexcept = delete; + ObjectPool& operator=(ObjectPool&& other) noexcept = delete; /** * @brief 分配一个对象 @@ -56,17 +42,22 @@ public: */ T* allocate() { std::lock_guard lock(mutex_); - + + // 检查对象池是否已销毁 + if (isDestroyed_) { + return nullptr; + } + if (freeList_.empty()) { grow(); } - + T* obj = freeList_.back(); freeList_.pop_back(); - + // 在原地构造对象 new (obj) T(); - + allocatedCount_++; return obj; } @@ -77,17 +68,22 @@ public: template T* allocate(Args&&... args) { std::lock_guard lock(mutex_); - + + // 检查对象池是否已销毁 + if (isDestroyed_) { + return nullptr; + } + if (freeList_.empty()) { grow(); } - + T* obj = freeList_.back(); freeList_.pop_back(); - + // 在原地构造对象 new (obj) T(std::forward(args)...); - + allocatedCount_++; return obj; } @@ -95,18 +91,24 @@ public: /** * @brief 回收一个对象 * @param obj 要回收的对象指针 + * @return true 如果对象成功回收到池中,false 如果池已销毁或对象无效 */ - void deallocate(T* obj) { + bool deallocate(T* obj) { if (obj == nullptr) { - return; + return false; } - + // 调用析构函数 obj->~T(); - + std::lock_guard lock(mutex_); - freeList_.push_back(obj); - allocatedCount_--; + // 检查对象池是否还在运行(避免在对象池销毁后访问) + if (!isDestroyed_) { + freeList_.push_back(obj); + allocatedCount_--; + return true; + } + return false; } /** @@ -137,10 +139,13 @@ public: */ void clear() { std::lock_guard lock(mutex_); - + + isDestroyed_ = true; + // 释放所有内存块 for (auto& block : blocks_) { - ::operator delete[](block, std::align_val_t(alignof(T))); + // 使用与分配时匹配的释放方式 + ::operator delete[](block); } blocks_.clear(); freeList_.clear(); @@ -152,11 +157,11 @@ private: * @brief 扩展池容量 */ void grow() { - // 分配新的内存块 - T* block = static_cast(::operator new[](sizeof(T) * BlockSize, std::align_val_t(alignof(T)))); - + // 分配新的内存块(使用标准对齐) + T* block = static_cast(::operator new[](sizeof(T) * BlockSize)); + blocks_.push_back(block); - + // 将新块中的对象添加到空闲列表 for (size_t i = 0; i < BlockSize; ++i) { freeList_.push_back(&block[i]); @@ -167,6 +172,7 @@ private: std::vector blocks_; // 内存块列表 std::vector freeList_; // 空闲对象列表 std::atomic allocatedCount_{0}; + bool isDestroyed_ = false; // 标记对象池是否已销毁 }; // ============================================================================ @@ -186,8 +192,13 @@ public: */ template Ptr makeShared(Args&&... args) { + // 使用 weak_ptr 避免循环引用 + std::weak_ptr weakPool = pool_; T* obj = pool_->allocate(std::forward(args)...); - return Ptr(obj, Deleter{pool_}); + if (!obj) { + return nullptr; + } + return Ptr(obj, Deleter{weakPool}); } /** @@ -199,13 +210,19 @@ public: private: struct Deleter { - std::shared_ptr pool; + std::weak_ptr pool; void operator()(T* obj) const { - if (pool) { - pool->deallocate(obj); + // 尝试获取 pool + if (auto sharedPool = pool.lock()) { + // 尝试归还给对象池 + if (!sharedPool->deallocate(obj)) { + // 对象池已销毁,手动释放内存 + ::operator delete[](obj); + } } else { - delete obj; + // Pool 已被销毁,释放内存 + ::operator delete[](obj); } } }; @@ -226,21 +243,42 @@ public: */ template std::shared_ptr> getPool() { - static std::shared_ptr> pool = - std::make_shared>(); + // 使用静态局部变量确保延迟初始化和正确析构顺序 + static auto pool = std::make_shared>(); return pool; } /** * @brief 创建使用内存池的对象 + * 注意:返回的对象必须在程序退出前手动释放,否则可能导致悬挂引用 */ template Ptr makePooled(Args&&... args) { auto pool = getPool(); + std::weak_ptr> weakPool = pool; T* obj = pool->allocate(std::forward(args)...); - return Ptr(obj, [pool](T* p) { pool->deallocate(p); }); + if (!obj) { + return nullptr; + } + return Ptr(obj, [weakPool](T* p) { + if (auto sharedPool = weakPool.lock()) { + if (!sharedPool->deallocate(p)) { + // 对象池已销毁 + ::operator delete[](p); + } + } else { + // Pool 已被销毁 + ::operator delete[](p); + } + }); } + /** + * @brief 清理所有未使用的内存池资源 + * 在程序退出前调用,确保资源正确释放 + */ + void cleanup(); + private: ObjectPoolManager() = default; ~ObjectPoolManager() = default; diff --git a/Extra2D/src/app/application.cpp b/Extra2D/src/app/application.cpp index 2cbef9b..ac77df3 100644 --- a/Extra2D/src/app/application.cpp +++ b/Extra2D/src/app/application.cpp @@ -176,24 +176,36 @@ void Application::shutdown() { sceneManager_->end(); } - // 清理子系统 - sceneManager_.reset(); - resourceManager_.reset(); + // ======================================== + // 1. 先清理所有持有 GPU 资源的子系统 + // 必须在渲染器关闭前释放纹理等资源 + // ======================================== + sceneManager_.reset(); // 场景持有纹理引用 + resourceManager_.reset(); // 纹理缓存持有 GPU 纹理 + camera_.reset(); // 相机可能持有渲染目标 + + // ======================================== + // 2. 关闭音频(不依赖 GPU) + // ======================================== + AudioEngine::getInstance().shutdown(); + + // ======================================== + // 3. 清理其他子系统 + // ======================================== timerManager_.reset(); eventQueue_.reset(); eventDispatcher_.reset(); - camera_.reset(); - // 关闭音频 - AudioEngine::getInstance().shutdown(); - - // 关闭渲染器 + // ======================================== + // 4. 最后关闭渲染器和窗口 + // 必须在所有 GPU 资源释放后才能关闭 OpenGL 上下文 + // ======================================== if (renderer_) { renderer_->shutdown(); renderer_.reset(); } - // 销毁窗口(包含 SDL_Quit) + // 销毁窗口(包含 SDL_Quit,会销毁 OpenGL 上下文) if (window_) { window_->destroy(); window_.reset(); @@ -314,11 +326,6 @@ void Application::update() { if (sceneManager_) { sceneManager_->update(deltaTime_); } - - // 更新资源管理器(触发纹理池自动清理) - if (resourceManager_) { - resourceManager_->update(deltaTime_); - } } void Application::render() { diff --git a/Extra2D/src/graphics/gpu_context.cpp b/Extra2D/src/graphics/gpu_context.cpp new file mode 100644 index 0000000..959dbfd --- /dev/null +++ b/Extra2D/src/graphics/gpu_context.cpp @@ -0,0 +1,22 @@ +#include + +namespace extra2d { + +GPUContext& GPUContext::getInstance() { + static GPUContext instance; + return instance; +} + +void GPUContext::markValid() { + valid_.store(true, std::memory_order_release); +} + +void GPUContext::markInvalid() { + valid_.store(false, std::memory_order_release); +} + +bool GPUContext::isValid() const { + return valid_.load(std::memory_order_acquire); +} + +} // namespace extra2d diff --git a/Extra2D/src/graphics/opengl/gl_renderer.cpp b/Extra2D/src/graphics/opengl/gl_renderer.cpp index 44e8a59..d9555e8 100644 --- a/Extra2D/src/graphics/opengl/gl_renderer.cpp +++ b/Extra2D/src/graphics/opengl/gl_renderer.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -62,6 +63,9 @@ bool GLRenderer::init(Window *window) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + // 标记 GPU 上下文为有效 + GPUContext::getInstance().markValid(); + E2D_LOG_INFO("OpenGL Renderer initialized"); E2D_LOG_INFO("OpenGL Version: {}", reinterpret_cast(glGetString(GL_VERSION))); @@ -70,6 +74,10 @@ bool GLRenderer::init(Window *window) { } void GLRenderer::shutdown() { + // 标记 GPU 上下文为无效 + // 这会在销毁 OpenGL 上下文之前通知所有 GPU 资源 + GPUContext::getInstance().markInvalid(); + spriteBatch_.shutdown(); if (shapeVbo_ != 0) { diff --git a/Extra2D/src/graphics/opengl/gl_texture.cpp b/Extra2D/src/graphics/opengl/gl_texture.cpp index 43441e6..d914d07 100644 --- a/Extra2D/src/graphics/opengl/gl_texture.cpp +++ b/Extra2D/src/graphics/opengl/gl_texture.cpp @@ -1,4 +1,5 @@ #include +#include #include #define STB_IMAGE_IMPLEMENTATION #include @@ -130,8 +131,12 @@ GLTexture::GLTexture(const std::string &filepath) GLTexture::~GLTexture() { if (textureID_ != 0) { - glDeleteTextures(1, &textureID_); - // VRAM 跟踪: 释放纹理显存 + // 检查 GPU 上下文是否仍然有效 + // 如果 OpenGL 上下文已销毁,则跳过 glDeleteTextures 调用 + if (GPUContext::getInstance().isValid()) { + glDeleteTextures(1, &textureID_); + } + // VRAM 跟踪: 释放纹理显存(无论上下文是否有效都需要更新统计) if (dataSize_ > 0) { VRAMManager::getInstance().freeTexture(dataSize_); } diff --git a/Extra2D/src/graphics/texture_pool.cpp b/Extra2D/src/graphics/texture_pool.cpp deleted file mode 100644 index feea376..0000000 --- a/Extra2D/src/graphics/texture_pool.cpp +++ /dev/null @@ -1,518 +0,0 @@ -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// TexturePool实现 -// ============================================================================ - -TexturePool &TexturePool::getInstance() { - static TexturePool instance; - return instance; -} - -bool TexturePool::init(const TexturePoolConfig &config) { - std::lock_guard lock(mutex_); - - if (initialized_) { - E2D_WARN("纹理池已经初始化"); - return true; - } - - config_ = config; - - E2D_INFO("初始化纹理池..."); - E2D_INFO(" 最大缓存大小: {} MB", config_.maxCacheSize / (1024 * 1024)); - E2D_INFO(" 最大纹理数量: {}", config_.maxTextureCount); - E2D_INFO(" 自动清理间隔: {} 秒", config_.unloadInterval); - E2D_INFO(" 异步加载: {}", config_.enableAsyncLoad ? "启用" : "禁用"); - - initialized_ = true; - return true; -} - -void TexturePool::shutdown() { - std::lock_guard lock(mutex_); - - if (!initialized_) { - return; - } - - E2D_INFO("关闭纹理池..."); - - printStats(); - - clear(); - - initialized_ = false; -} - -Ptr TexturePool::get(const std::string &filepath) { - std::lock_guard lock(mutex_); - - if (!initialized_) { - E2D_ERROR("纹理池未初始化"); - return nullptr; - } - - // 检查缓存 - auto it = cache_.find(filepath); - if (it != cache_.end()) { - // 缓存命中 - touch(filepath); - hitCount_++; - it->second.accessCount++; - return it->second.texture; - } - - // 缓存未命中,加载纹理 - missCount_++; - - auto texture = loadTexture(filepath); - if (!texture) { - return nullptr; - } - - // 添加到缓存 - add(filepath, texture); - - return texture; -} - -void TexturePool::getAsync(const std::string &filepath, - std::function)> callback) { - if (!initialized_) { - E2D_ERROR("纹理池未初始化"); - if (callback) { - callback(nullptr); - } - return; - } - - if (!config_.enableAsyncLoad) { - // 异步加载禁用,直接同步加载 - auto texture = get(filepath); - if (callback) { - callback(texture); - } - return; - } - - // 添加到异步任务队列 - std::lock_guard lock(mutex_); - asyncTasks_.push_back({filepath, callback}); -} - -Ptr TexturePool::createFromData(const std::string &name, - const uint8_t *data, int width, - int height, PixelFormat format) { - std::lock_guard lock(mutex_); - - if (!initialized_) { - E2D_ERROR("纹理池未初始化"); - return nullptr; - } - - // 检查缓存 - auto it = cache_.find(name); - if (it != cache_.end()) { - touch(name); - return it->second.texture; - } - - // 创建纹理 - int channels = 4; - switch (format) { - case PixelFormat::R8: - channels = 1; - break; - case PixelFormat::RG8: - channels = 2; - break; - case PixelFormat::RGB8: - channels = 3; - break; - case PixelFormat::RGBA8: - channels = 4; - break; - default: - channels = 4; - break; - } - auto texture = makePtr(width, height, data, channels); - if (!texture || !texture->isValid()) { - E2D_ERROR("创建纹理失败: {}", name); - return nullptr; - } - - // 添加到缓存 - add(name, texture); - - E2D_INFO("从数据创建纹理: {} ({}x{})", name, width, height); - - return texture; -} - -uint32_t TexturePool::allocateLRUNode(const std::string& key) { - uint32_t index; - if (freeList_ != 0) { - // 复用空闲节点 - index = freeList_; - freeList_ = lruNodes_[index - 1].next; - } else { - // 分配新节点 - lruNodes_.emplace_back(); - index = static_cast(lruNodes_.size()); - } - - LRUNode& node = lruNodes_[index - 1]; - node.key = key; - node.prev = 0; - node.next = 0; - node.valid = true; - return index; -} - -void TexturePool::freeLRUNode(uint32_t index) { - if (index == 0 || index > lruNodes_.size()) return; - - LRUNode& node = lruNodes_[index - 1]; - node.valid = false; - node.key.clear(); - node.prev = 0; - node.next = freeList_; - freeList_ = index; -} - -void TexturePool::moveToFront(uint32_t index) { - if (index == 0 || index > lruNodes_.size() || index == lruHead_) return; - - removeFromList(index); - - LRUNode& node = lruNodes_[index - 1]; - node.prev = 0; - node.next = lruHead_; - - if (lruHead_ != 0) { - lruNodes_[lruHead_ - 1].prev = index; - } - lruHead_ = index; - - if (lruTail_ == 0) { - lruTail_ = index; - } -} - -void TexturePool::removeFromList(uint32_t index) { - if (index == 0 || index > lruNodes_.size()) return; - - LRUNode& node = lruNodes_[index - 1]; - - if (node.prev != 0) { - lruNodes_[node.prev - 1].next = node.next; - } else { - lruHead_ = node.next; - } - - if (node.next != 0) { - lruNodes_[node.next - 1].prev = node.prev; - } else { - lruTail_ = node.prev; - } -} - -std::string TexturePool::evictLRU() { - if (lruTail_ == 0) return ""; - - uint32_t index = lruTail_; - std::string key = lruNodes_[index - 1].key; - - removeFromList(index); - freeLRUNode(index); - - return key; -} - -void TexturePool::add(const std::string &key, Ptr texture) { - if (!texture || !texture->isValid()) { - return; - } - - // 检查是否需要清理 - while (totalSize_ >= config_.maxCacheSize || - cache_.size() >= config_.maxTextureCount) { - evict(); - } - - // 计算纹理大小 - size_t size = calculateTextureSize(texture->getWidth(), texture->getHeight(), - texture->getFormat()); - - // 分配LRU节点 - uint32_t lruIndex = allocateLRUNode(key); - - // 创建缓存项 - CacheEntry entry; - entry.texture = texture; - entry.size = size; - entry.lastAccessTime = 0.0f; // 将在touch中更新 - entry.accessCount = 0; - entry.lruIndex = lruIndex; - - // 添加到缓存 - cache_[key] = entry; - - // 添加到LRU链表头部 - moveToFront(lruIndex); - - totalSize_ += size; - - E2D_DEBUG_LOG("纹理添加到缓存: {} ({} KB)", key, size / 1024); -} - -void TexturePool::remove(const std::string &key) { - std::lock_guard lock(mutex_); - - auto it = cache_.find(key); - if (it == cache_.end()) { - return; - } - - // 从LRU链表移除 - removeFromList(it->second.lruIndex); - freeLRUNode(it->second.lruIndex); - - // 更新总大小 - totalSize_ -= it->second.size; - - // 从缓存移除 - cache_.erase(it); - - E2D_DEBUG_LOG("纹理从缓存移除: {}", key); -} - -bool TexturePool::has(const std::string &key) const { - std::lock_guard lock(mutex_); - return cache_.find(key) != cache_.end(); -} - -void TexturePool::clear() { - std::lock_guard lock(mutex_); - - cache_.clear(); - lruNodes_.clear(); - lruHead_ = 0; - lruTail_ = 0; - freeList_ = 0; - totalSize_ = 0; - - E2D_INFO("纹理缓存已清空"); -} - -void TexturePool::trim(size_t targetSize) { - std::lock_guard lock(mutex_); - - while (totalSize_ > targetSize && lruTail_ != 0) { - evict(); - } - - E2D_INFO("纹理缓存已清理到 {} MB", totalSize_ / (1024 * 1024)); -} - -size_t TexturePool::getTextureCount() const { - std::lock_guard lock(mutex_); - return cache_.size(); -} - -size_t TexturePool::getCacheSize() const { - std::lock_guard lock(mutex_); - return totalSize_; -} - -float TexturePool::getHitRate() const { - std::lock_guard lock(mutex_); - - uint64_t total = hitCount_ + missCount_; - if (total == 0) { - return 0.0f; - } - - return static_cast(hitCount_) / static_cast(total); -} - -void TexturePool::printStats() const { - std::lock_guard lock(mutex_); - - E2D_INFO("纹理池统计:"); - E2D_INFO(" 缓存纹理数: {}/{}", cache_.size(), config_.maxTextureCount); - E2D_INFO(" 缓存大小: {} / {} MB", totalSize_ / (1024 * 1024), - config_.maxCacheSize / (1024 * 1024)); - E2D_INFO(" 缓存命中: {}", hitCount_); - E2D_INFO(" 缓存未命中: {}", missCount_); - E2D_INFO(" 命中率: {:.1f}%", getHitRate() * 100.0f); -} - -void TexturePool::update(float dt) { - if (!initialized_) { - return; - } - - // 处理异步任务 - if (config_.enableAsyncLoad) { - processAsyncTasks(); - } - - // 自动清理 - autoUnloadTimer_ += dt; - if (autoUnloadTimer_ >= config_.unloadInterval) { - autoUnloadTimer_ = 0.0f; - - // 如果缓存超过80%,清理到50% - if (totalSize_ > config_.maxCacheSize * 0.8f) { - trim(config_.maxCacheSize * 0.5f); - } - } -} - -void TexturePool::setAutoUnloadInterval(float interval) { - config_.unloadInterval = interval; -} - -// ============================================================================ -// 内部方法 -// ============================================================================ - -void TexturePool::touch(const std::string &key) { - auto it = cache_.find(key); - if (it == cache_.end()) { - return; - } - - // 移动到LRU链表头部 - moveToFront(it->second.lruIndex); - - // 更新时间戳 - it->second.lastAccessTime = 0.0f; -} - -void TexturePool::evict() { - // 使用侵入式LRU链表移除最久未使用的项 - std::string key = evictLRU(); - if (key.empty()) return; - - auto it = cache_.find(key); - if (it != cache_.end()) { - totalSize_ -= it->second.size; - cache_.erase(it); - } - - E2D_DEBUG_LOG("纹理被清理出缓存: {}", key); -} - -Ptr TexturePool::loadTexture(const std::string &filepath) { - // 使用stb_image加载图像 - // 不翻转图片,保持原始方向 - // OpenGL纹理坐标原点在左下角,图片数据原点在左上角 - // 在渲染时通过纹理坐标翻转来处理 - stbi_set_flip_vertically_on_load(false); - int width, height, channels; - unsigned char *pixels = - stbi_load(filepath.c_str(), &width, &height, &channels, 4); - if (!pixels) { - E2D_ERROR("stbi_load 失败: {} - {}", filepath, stbi_failure_reason()); - return nullptr; - } - - // 创建GLTexture(直接使用像素数据) - auto texture = std::make_shared(width, height, pixels, 4); - if (!texture || !texture->isValid()) { - stbi_image_free(pixels); - E2D_ERROR("创建纹理失败: {}", filepath); - return nullptr; - } - - stbi_image_free(pixels); - - E2D_INFO("加载纹理: {} ({}x{}, {} channels)", filepath, width, height, - channels); - - return texture; -} - -size_t TexturePool::calculateTextureSize(int width, int height, - PixelFormat format) { - // 压缩纹理格式使用块大小计算 - switch (format) { - case PixelFormat::ETC2_RGB8: { - size_t blocksWide = (width + 3) / 4; - size_t blocksHigh = (height + 3) / 4; - return blocksWide * blocksHigh * 8; - } - case PixelFormat::ETC2_RGBA8: { - size_t blocksWide = (width + 3) / 4; - size_t blocksHigh = (height + 3) / 4; - return blocksWide * blocksHigh * 16; - } - case PixelFormat::ASTC_4x4: { - size_t blocksWide = (width + 3) / 4; - size_t blocksHigh = (height + 3) / 4; - return blocksWide * blocksHigh * 16; - } - case PixelFormat::ASTC_6x6: { - size_t blocksWide = (width + 5) / 6; - size_t blocksHigh = (height + 5) / 6; - return blocksWide * blocksHigh * 16; - } - case PixelFormat::ASTC_8x8: { - size_t blocksWide = (width + 7) / 8; - size_t blocksHigh = (height + 7) / 8; - return blocksWide * blocksHigh * 16; - } - default: - break; - } - - // 非压缩格式 - int bytesPerPixel = 4; - switch (format) { - case PixelFormat::R8: - bytesPerPixel = 1; - break; - case PixelFormat::RG8: - bytesPerPixel = 2; - break; - case PixelFormat::RGB8: - bytesPerPixel = 3; - break; - case PixelFormat::RGBA8: - bytesPerPixel = 4; - break; - default: - bytesPerPixel = 4; - break; - } - - return static_cast(width * height * bytesPerPixel); -} - -void TexturePool::processAsyncTasks() { - std::vector tasks; - - { - std::lock_guard lock(mutex_); - tasks = std::move(asyncTasks_); - asyncTasks_.clear(); - } - - for (auto &task : tasks) { - auto texture = get(task.filepath); - if (task.callback) { - task.callback(texture); - } - } -} - -} // namespace extra2d diff --git a/Extra2D/src/resource/resource_manager.cpp b/Extra2D/src/resource/resource_manager.cpp index 59dc6a3..0ba545d 100644 --- a/Extra2D/src/resource/resource_manager.cpp +++ b/Extra2D/src/resource/resource_manager.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -80,17 +79,10 @@ static std::string resolveResourcePath(const std::string &filepath) { return ""; } -ResourceManager::ResourceManager() { - // 初始化纹理池 - TexturePoolConfig config; - config.maxCacheSize = 128 * 1024 * 1024; // 128MB - config.maxTextureCount = 512; - config.enableAsyncLoad = true; - TexturePool::getInstance().init(config); -} +ResourceManager::ResourceManager() = default; ResourceManager::~ResourceManager() { - TexturePool::getInstance().shutdown(); + shutdownAsyncLoader(); } ResourceManager &ResourceManager::getInstance() { @@ -99,11 +91,77 @@ ResourceManager &ResourceManager::getInstance() { } // ============================================================================ -// 更新(在主循环中调用) +// 异步加载系统 // ============================================================================ -void ResourceManager::update(float dt) { - TexturePool::getInstance().update(dt); +void ResourceManager::initAsyncLoader() { + if (asyncRunning_) { + return; + } + + asyncRunning_ = true; + asyncThread_ = std::make_unique(&ResourceManager::asyncLoadLoop, this); + E2D_LOG_INFO("ResourceManager: async loader initialized"); +} + +void ResourceManager::shutdownAsyncLoader() { + if (!asyncRunning_) { + return; + } + + asyncRunning_ = false; + asyncCondition_.notify_all(); + + if (asyncThread_ && asyncThread_->joinable()) { + asyncThread_->join(); + } + + E2D_LOG_INFO("ResourceManager: async loader shutdown"); +} + +void ResourceManager::waitForAsyncLoads() { + while (pendingAsyncLoads_ > 0) { + std::this_thread::yield(); + } +} + +bool ResourceManager::hasPendingAsyncLoads() const { + return pendingAsyncLoads_ > 0; +} + +void ResourceManager::asyncLoadLoop() { + while (asyncRunning_) { + AsyncLoadTask task; + + { + std::unique_lock lock(asyncQueueMutex_); + asyncCondition_.wait(lock, [this] { return !asyncTaskQueue_.empty() || !asyncRunning_; }); + + if (!asyncRunning_) { + break; + } + + if (asyncTaskQueue_.empty()) { + continue; + } + + task = std::move(asyncTaskQueue_.front()); + asyncTaskQueue_.pop(); + } + + // 执行加载 + auto texture = loadTextureInternal(task.filepath, task.format); + + // 回调 + if (task.callback) { + task.callback(texture); + } + + // 设置 promise + task.promise.set_value(texture); + + pendingAsyncLoads_--; + } } // ============================================================================ @@ -133,31 +191,61 @@ void ResourceManager::loadTextureAsync(const std::string &filepath, TextureLoadC } void ResourceManager::loadTextureAsync(const std::string &filepath, TextureFormat format, TextureLoadCallback callback) { - // 解析资源路径 - std::string fullPath = resolveResourcePath(filepath); - if (fullPath.empty()) { - E2D_LOG_ERROR("ResourceManager: texture file not found: {}", filepath); - if (callback) { - callback(nullptr); - } - return; + // 确保异步加载系统已启动 + if (!asyncRunning_) { + initAsyncLoader(); } - - // 使用纹理池的异步加载 - TexturePool::getInstance().getAsync(fullPath, - [callback, filepath](Ptr texture) { - if (texture) { - E2D_LOG_DEBUG("ResourceManager: async texture loaded: {}", filepath); - } else { - E2D_LOG_ERROR("ResourceManager: failed to async load texture: {}", filepath); - } + + // 检查缓存 + { + std::lock_guard lock(textureMutex_); + auto it = textureCache_.find(filepath); + if (it != textureCache_.end()) { + // 缓存命中,更新LRU并立即回调 + touchTexture(filepath); + textureHitCount_++; + it->second.accessCount++; if (callback) { - callback(texture); + callback(it->second.texture); } - }); + return; + } + } + + // 添加到异步任务队列 + AsyncLoadTask task; + task.filepath = filepath; + task.format = format; + task.callback = callback; + + { + std::lock_guard lock(asyncQueueMutex_); + asyncTaskQueue_.push(std::move(task)); + } + + pendingAsyncLoads_++; + asyncCondition_.notify_one(); + + E2D_LOG_DEBUG("ResourceManager: queued async texture load: {}", filepath); } Ptr ResourceManager::loadTextureInternal(const std::string &filepath, TextureFormat format) { + std::lock_guard lock(textureMutex_); + + // 检查缓存 + auto it = textureCache_.find(filepath); + if (it != textureCache_.end()) { + // 缓存命中 + touchTexture(filepath); + textureHitCount_++; + it->second.accessCount++; + E2D_LOG_TRACE("ResourceManager: texture cache hit: {}", filepath); + return it->second.texture; + } + + // 缓存未命中 + textureMissCount_++; + // 解析资源路径(优先尝试 romfs:/ 前缀) std::string fullPath = resolveResourcePath(filepath); if (fullPath.empty()) { @@ -165,22 +253,51 @@ Ptr ResourceManager::loadTextureInternal(const std::string &filepath, T return nullptr; } - // 使用纹理池获取纹理 - auto texture = TexturePool::getInstance().get(fullPath); - - if (!texture) { - E2D_LOG_ERROR("ResourceManager: failed to load texture: {}", filepath); + // 创建新纹理 + try { + auto texture = makePtr(fullPath); + if (!texture->isValid()) { + E2D_LOG_ERROR("ResourceManager: failed to load texture: {}", filepath); + return nullptr; + } + + // 如果需要压缩,处理纹理格式 + if (format != TextureFormat::Auto && format != TextureFormat::RGBA8) { + // 注意:实际压缩需要在纹理创建时处理 + // 这里仅记录日志,实际实现需要在 GLTexture 中支持 + E2D_LOG_DEBUG("ResourceManager: texture format {} requested for {}", + static_cast(format), filepath); + } + + // 检查是否需要清理缓存 + evictTexturesIfNeeded(); + + // 计算纹理大小 + size_t textureSize = calculateTextureSize(texture->getWidth(), texture->getHeight(), texture->getFormat()); + + // 分配LRU节点 + uint32_t lruIndex = allocateLRUNode(filepath); + + // 创建缓存项 + TextureCacheEntry entry; + entry.texture = texture; + entry.size = textureSize; + entry.lastAccessTime = 0.0f; + entry.accessCount = 1; + + // 添加到缓存 + textureCache_[filepath] = entry; + totalTextureSize_ += textureSize; + + // 添加到LRU链表头部 + moveToFront(lruIndex); + + E2D_LOG_DEBUG("ResourceManager: loaded texture: {} ({} KB)", filepath, textureSize / 1024); + return texture; + } catch (...) { + E2D_LOG_ERROR("ResourceManager: exception loading texture: {}", filepath); return nullptr; } - - // 如果需要压缩,处理纹理格式(记录日志,实际压缩需要在纹理创建时处理) - if (format != TextureFormat::Auto && format != TextureFormat::RGBA8) { - E2D_LOG_DEBUG("ResourceManager: texture format {} requested for {}", - static_cast(format), filepath); - } - - E2D_LOG_DEBUG("ResourceManager: loaded texture: {}", filepath); - return texture; } TextureFormat ResourceManager::selectBestFormat(TextureFormat requested) const { @@ -226,91 +343,79 @@ ResourceManager::loadTextureWithAlphaMask(const std::string &filepath) { const AlphaMask * ResourceManager::getAlphaMask(const std::string &textureKey) const { - // 解析路径获取完整路径 - std::string fullPath = resolveResourcePath(textureKey); - if (fullPath.empty()) { - return nullptr; - } + std::lock_guard lock(textureMutex_); - // 从纹理池获取纹理 - auto texture = TexturePool::getInstance().get(fullPath); - if (!texture) { - return nullptr; + auto it = textureCache_.find(textureKey); + if (it != textureCache_.end()) { + GLTexture *glTexture = static_cast(it->second.texture.get()); + return glTexture->getAlphaMask(); } - - GLTexture *glTexture = static_cast(texture.get()); - return glTexture->getAlphaMask(); + return nullptr; } bool ResourceManager::generateAlphaMask(const std::string &textureKey) { - // 解析路径获取完整路径 - std::string fullPath = resolveResourcePath(textureKey); - if (fullPath.empty()) { - return false; - } + std::lock_guard lock(textureMutex_); - // 从纹理池获取纹理 - auto texture = TexturePool::getInstance().get(fullPath); - if (!texture) { - return false; + auto it = textureCache_.find(textureKey); + if (it != textureCache_.end()) { + GLTexture *glTexture = static_cast(it->second.texture.get()); + if (!glTexture->hasAlphaMask()) { + glTexture->generateAlphaMask(); + } + return glTexture->hasAlphaMask(); } - - GLTexture *glTexture = static_cast(texture.get()); - if (!glTexture->hasAlphaMask()) { - glTexture->generateAlphaMask(); - } - return glTexture->hasAlphaMask(); + return false; } bool ResourceManager::hasAlphaMask(const std::string &textureKey) const { - // 解析路径获取完整路径 - std::string fullPath = resolveResourcePath(textureKey); - if (fullPath.empty()) { - return false; - } + std::lock_guard lock(textureMutex_); - // 从纹理池获取纹理 - auto texture = TexturePool::getInstance().get(fullPath); - if (!texture) { - return false; + auto it = textureCache_.find(textureKey); + if (it != textureCache_.end()) { + GLTexture *glTexture = static_cast(it->second.texture.get()); + return glTexture->hasAlphaMask(); } - - GLTexture *glTexture = static_cast(texture.get()); - return glTexture->hasAlphaMask(); + return false; } Ptr ResourceManager::getTexture(const std::string &key) const { - // 解析路径获取完整路径 - std::string fullPath = resolveResourcePath(key); - if (fullPath.empty()) { - return nullptr; - } + std::lock_guard lock(textureMutex_); - // 从纹理池获取纹理 - return TexturePool::getInstance().get(fullPath); + auto it = textureCache_.find(key); + if (it != textureCache_.end()) { + const_cast(this)->touchTexture(key); + const_cast(this)->textureHitCount_++; + const_cast(it->second.accessCount)++; + return it->second.texture; + } + return nullptr; } bool ResourceManager::hasTexture(const std::string &key) const { - // 解析路径获取完整路径 - std::string fullPath = resolveResourcePath(key); - if (fullPath.empty()) { - return false; - } - - // 检查纹理池是否有此纹理 - return TexturePool::getInstance().has(fullPath); + std::lock_guard lock(textureMutex_); + return textureCache_.find(key) != textureCache_.end(); } void ResourceManager::unloadTexture(const std::string &key) { - // 解析路径获取完整路径 - std::string fullPath = resolveResourcePath(key); - if (fullPath.empty()) { - return; - } + std::lock_guard lock(textureMutex_); - // 从纹理池移除 - TexturePool::getInstance().remove(fullPath); - E2D_LOG_DEBUG("ResourceManager: unloaded texture: {}", key); + auto it = textureCache_.find(key); + if (it != textureCache_.end()) { + // 从LRU链表中移除 + auto nodeIt = std::find_if(lruNodes_.begin(), lruNodes_.end(), + [&key](const LRUNode &node) { return node.valid && node.key == key; }); + if (nodeIt != lruNodes_.end()) { + removeFromList(static_cast(nodeIt - lruNodes_.begin()) + 1); + freeLRUNode(static_cast(nodeIt - lruNodes_.begin()) + 1); + } + + // 更新总大小 + totalTextureSize_ -= it->second.size; + + // 从缓存移除 + textureCache_.erase(it); + E2D_LOG_DEBUG("ResourceManager: unloaded texture: {}", key); + } } // ============================================================================ @@ -468,11 +573,8 @@ void ResourceManager::unloadSound(const std::string &key) { // ============================================================================ void ResourceManager::purgeUnused() { - // 纹理缓存由 TexturePool 自动管理,无需手动清理 - // 但我们可以触发 TexturePool 的清理 - TexturePool::getInstance().trim(TexturePool::getInstance().getCacheSize() * 0.8f); - - // 清理字体缓存 + // 纹理缓存使用LRU策略,不需要清理失效引用 + // 字体缓存 { std::lock_guard lock(fontMutex_); for (auto it = fontCache_.begin(); it != fontCache_.end();) { @@ -485,7 +587,7 @@ void ResourceManager::purgeUnused() { } } - // 清理音效缓存 + // 音效缓存 { std::lock_guard lock(soundMutex_); for (auto it = soundCache_.begin(); it != soundCache_.end();) { @@ -500,8 +602,15 @@ void ResourceManager::purgeUnused() { } void ResourceManager::clearTextureCache() { - TexturePool::getInstance().clear(); - E2D_LOG_INFO("ResourceManager: texture cache cleared via TexturePool"); + std::lock_guard lock(textureMutex_); + size_t count = textureCache_.size(); + textureCache_.clear(); + lruNodes_.clear(); + lruHead_ = 0; + lruTail_ = 0; + freeList_ = 0; + totalTextureSize_ = 0; + E2D_LOG_INFO("ResourceManager: cleared {} textures from cache", count); } void ResourceManager::clearFontCache() { @@ -531,7 +640,210 @@ void ResourceManager::clearAllCaches() { } size_t ResourceManager::getTextureCacheSize() const { - return TexturePool::getInstance().getTextureCount(); + std::lock_guard lock(textureMutex_); + return textureCache_.size(); +} + +// ============================================================================ +// LRU 缓存管理 +// ============================================================================ + +void ResourceManager::setTextureCache(size_t maxCacheSize, size_t maxTextureCount, + float unloadInterval) { + std::lock_guard lock(textureMutex_); + maxCacheSize_ = maxCacheSize; + maxTextureCount_ = maxTextureCount; + unloadInterval_ = unloadInterval; +} + +size_t ResourceManager::getTextureCacheMemoryUsage() const { + std::lock_guard lock(textureMutex_); + return totalTextureSize_; +} + +float ResourceManager::getTextureCacheHitRate() const { + std::lock_guard lock(textureMutex_); + uint64_t total = textureHitCount_ + textureMissCount_; + if (total == 0) { + return 0.0f; + } + return static_cast(textureHitCount_) / static_cast(total); +} + +void ResourceManager::printTextureCacheStats() const { + std::lock_guard lock(textureMutex_); + E2D_LOG_INFO("纹理缓存统计:"); + E2D_LOG_INFO(" 缓存纹理数: {}/{}", textureCache_.size(), maxTextureCount_); + E2D_LOG_INFO(" 缓存大小: {} / {} MB", totalTextureSize_ / (1024 * 1024), + maxCacheSize_ / (1024 * 1024)); + E2D_LOG_INFO(" 缓存命中: {}", textureHitCount_); + E2D_LOG_INFO(" 缓存未命中: {}", textureMissCount_); + E2D_LOG_INFO(" 命中率: {:.1f}%", getTextureCacheHitRate() * 100.0f); +} + +void ResourceManager::update(float dt) { + std::lock_guard lock(textureMutex_); + + // 自动清理 + autoUnloadTimer_ += dt; + if (autoUnloadTimer_ >= unloadInterval_) { + autoUnloadTimer_ = 0.0f; + + // 如果缓存超过80%,清理到50% + if (totalTextureSize_ > maxCacheSize_ * 0.8f) { + size_t targetSize = static_cast(maxCacheSize_ * 0.5f); + while (totalTextureSize_ > targetSize && lruTail_ != 0) { + std::string key = evictLRU(); + if (key.empty()) break; + + auto it = textureCache_.find(key); + if (it != textureCache_.end()) { + totalTextureSize_ -= it->second.size; + textureCache_.erase(it); + E2D_LOG_DEBUG("ResourceManager: auto-evicted texture: {}", key); + } + } + E2D_LOG_INFO("ResourceManager: texture cache trimmed to {} MB", totalTextureSize_ / (1024 * 1024)); + } + } +} + +// ============================================================================ +// LRU 缓存内部方法 +// ============================================================================ + +uint32_t ResourceManager::allocateLRUNode(const std::string &key) { + uint32_t index; + if (freeList_ != 0) { + // 复用空闲节点 + index = freeList_; + freeList_ = lruNodes_[index - 1].next; + } else { + // 分配新节点 + lruNodes_.emplace_back(); + index = static_cast(lruNodes_.size()); + } + + LRUNode &node = lruNodes_[index - 1]; + node.key = key; + node.prev = 0; + node.next = 0; + node.valid = true; + return index; +} + +void ResourceManager::freeLRUNode(uint32_t index) { + if (index == 0 || index > lruNodes_.size()) return; + + LRUNode &node = lruNodes_[index - 1]; + node.valid = false; + node.key.clear(); + node.prev = 0; + node.next = freeList_; + freeList_ = index; +} + +void ResourceManager::moveToFront(uint32_t index) { + if (index == 0 || index > lruNodes_.size() || index == lruHead_) return; + + removeFromList(index); + + LRUNode &node = lruNodes_[index - 1]; + node.prev = 0; + node.next = lruHead_; + + if (lruHead_ != 0) { + lruNodes_[lruHead_ - 1].prev = index; + } + lruHead_ = index; + + if (lruTail_ == 0) { + lruTail_ = index; + } +} + +void ResourceManager::removeFromList(uint32_t index) { + if (index == 0 || index > lruNodes_.size()) return; + + LRUNode &node = lruNodes_[index - 1]; + + if (node.prev != 0) { + lruNodes_[node.prev - 1].next = node.next; + } else { + lruHead_ = node.next; + } + + if (node.next != 0) { + lruNodes_[node.next - 1].prev = node.prev; + } else { + lruTail_ = node.prev; + } +} + +std::string ResourceManager::evictLRU() { + if (lruTail_ == 0) return ""; + + uint32_t index = lruTail_; + std::string key = lruNodes_[index - 1].key; + + removeFromList(index); + freeLRUNode(index); + + return key; +} + +void ResourceManager::touchTexture(const std::string &key) { + // 查找对应的LRU节点 + auto nodeIt = std::find_if(lruNodes_.begin(), lruNodes_.end(), + [&key](const LRUNode &node) { return node.valid && node.key == key; }); + if (nodeIt != lruNodes_.end()) { + uint32_t index = static_cast(nodeIt - lruNodes_.begin()) + 1; + moveToFront(index); + } + + // 更新时间戳 + auto it = textureCache_.find(key); + if (it != textureCache_.end()) { + it->second.lastAccessTime = 0.0f; + } +} + +void ResourceManager::evictTexturesIfNeeded() { + while ((totalTextureSize_ >= maxCacheSize_ || + textureCache_.size() >= maxTextureCount_) && + lruTail_ != 0) { + std::string key = evictLRU(); + if (key.empty()) break; + + auto it = textureCache_.find(key); + if (it != textureCache_.end()) { + totalTextureSize_ -= it->second.size; + textureCache_.erase(it); + E2D_LOG_DEBUG("ResourceManager: evicted texture: {}", key); + } + } +} + +size_t ResourceManager::calculateTextureSize(int width, int height, PixelFormat format) const { + int bytesPerPixel = 4; + switch (format) { + case PixelFormat::R8: + bytesPerPixel = 1; + break; + case PixelFormat::RG8: + bytesPerPixel = 2; + break; + case PixelFormat::RGB8: + bytesPerPixel = 3; + break; + case PixelFormat::RGBA8: + bytesPerPixel = 4; + break; + default: + bytesPerPixel = 4; + break; + } + return static_cast(width * height * bytesPerPixel); } size_t ResourceManager::getFontCacheSize() const { @@ -544,27 +856,4 @@ size_t ResourceManager::getSoundCacheSize() const { return soundCache_.size(); } -// ============================================================================ -// 异步加载控制(已弃用,保留接口兼容性) -// ============================================================================ - -void ResourceManager::initAsyncLoader() { - // 纹理池已内置异步加载,无需额外初始化 - E2D_LOG_DEBUG("ResourceManager: async loader is handled by TexturePool"); -} - -void ResourceManager::shutdownAsyncLoader() { - // 纹理池在 shutdown 时处理 -} - -void ResourceManager::waitForAsyncLoads() { - // 纹理池异步加载通过回调处理,无需等待 - // 如需等待,可通过回调机制实现 -} - -bool ResourceManager::hasPendingAsyncLoads() const { - // 纹理池异步加载通过回调处理,无法直接查询 - return false; -} - } // namespace extra2d diff --git a/Extra2D/src/utils/object_pool.cpp b/Extra2D/src/utils/object_pool.cpp index 9ecca86..31d5b23 100644 --- a/Extra2D/src/utils/object_pool.cpp +++ b/Extra2D/src/utils/object_pool.cpp @@ -8,4 +8,10 @@ ObjectPoolManager& ObjectPoolManager::getInstance() { return instance; } +void ObjectPoolManager::cleanup() { + // 静态对象池会在程序退出时自动清理 + // 这个方法用于显式触发清理(如果需要) + // 由于使用了 weak_ptr,循环引用问题已解决 +} + } // namespace extra2d