refactor(resource): 重构资源管理系统并移除TexturePool

- 移除TexturePool类,将纹理缓存功能直接集成到ResourceManager
- 实现基于LRU的纹理缓存管理,提高缓存效率
- 添加GPUContext类用于安全处理GPU资源生命周期
- 重构对象池实现,解决循环引用问题
- 优化应用关闭时的资源释放顺序
- 改进纹理加载错误处理
- 更新SpriteFrameCache直接加载纹理
- 清理无用头文件和代码
This commit is contained in:
ChestnutYueyue 2026-02-12 12:20:14 +08:00
parent c46be5287d
commit 85420634aa
13 changed files with 772 additions and 957 deletions

View File

@ -1,7 +1,9 @@
#pragma once #pragma once
#include <extra2d/animation/sprite_frame.h> #include <extra2d/animation/sprite_frame.h>
#include <extra2d/graphics/texture_pool.h> #include <extra2d/graphics/opengl/gl_texture.h>
#include <extra2d/utils/logger.h>
#include <stb/stb_image.h>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
@ -39,7 +41,7 @@ public:
void addSpriteFramesFromGrid(const std::string &texturePath, int frameWidth, void addSpriteFramesFromGrid(const std::string &texturePath, int frameWidth,
int frameHeight, int frameCount = -1, int frameHeight, int frameCount = -1,
int spacing = 0, int margin = 0) { int spacing = 0, int margin = 0) {
auto texture = TexturePool::getInstance().get(texturePath); auto texture = loadTextureFromFile(texturePath);
if (!texture) if (!texture)
return; return;
addSpriteFramesFromGrid(texture, texturePath, frameWidth, frameHeight, addSpriteFramesFromGrid(texture, texturePath, frameWidth, frameHeight,
@ -104,8 +106,8 @@ public:
return it->second; return it->second;
} }
// 缓存未命中,从 TexturePool 加载纹理并创建 SpriteFrame // 缓存未命中,加载纹理并创建 SpriteFrame
auto texture = TexturePool::getInstance().get(texturePath); auto texture = loadTextureFromFile(texturePath);
if (!texture) if (!texture)
return nullptr; return nullptr;
@ -161,6 +163,34 @@ private:
SpriteFrameCache(const SpriteFrameCache &) = delete; SpriteFrameCache(const SpriteFrameCache &) = delete;
SpriteFrameCache &operator=(const SpriteFrameCache &) = delete; SpriteFrameCache &operator=(const SpriteFrameCache &) = delete;
/**
* @brief
* @param filepath
* @return nullptr
*/
Ptr<Texture> 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<GLTexture>(width, height, pixels, 4);
stbi_image_free(pixels);
if (!texture || !texture->isValid()) {
E2D_ERROR("创建纹理失败: {}", filepath);
return nullptr;
}
return texture;
}
mutable std::mutex mutex_; mutable std::mutex mutex_;
std::unordered_map<std::string, Ptr<SpriteFrame>> frames_; std::unordered_map<std::string, Ptr<SpriteFrame>> frames_;
}; };

View File

@ -19,7 +19,7 @@
#include <extra2d/graphics/font.h> #include <extra2d/graphics/font.h>
#include <extra2d/graphics/camera.h> #include <extra2d/graphics/camera.h>
#include <extra2d/graphics/shader_system.h> #include <extra2d/graphics/shader_system.h>
#include <extra2d/graphics/texture_pool.h>
#include <extra2d/graphics/render_target.h> #include <extra2d/graphics/render_target.h>
#include <extra2d/graphics/vram_manager.h> #include <extra2d/graphics/vram_manager.h>

View File

@ -0,0 +1,36 @@
#pragma once
#include <atomic>
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<bool> valid_{false};
};
} // namespace extra2d

View File

@ -1,208 +0,0 @@
#pragma once
#include <extra2d/core/types.h>
#include <extra2d/graphics/texture.h>
#include <functional>
#include <list>
#include <mutex>
#include <string>
#include <unordered_map>
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<Texture> get(const std::string &filepath);
/**
* @brief
* @param filepath
* @param callback
*/
void getAsync(const std::string &filepath,
std::function<void(Ptr<Texture>)> callback);
/**
* @brief
* @param name
* @param data
* @param width
* @param height
* @param format
* @return
*/
Ptr<Texture> 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> 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> texture;
size_t size; // 纹理大小(字节)
float lastAccessTime; // 最后访问时间
uint32_t accessCount; // 访问次数
uint32_t lruIndex; // LRU节点索引
};
mutable std::mutex mutex_;
TexturePoolConfig config_;
// 缓存存储
std::unordered_map<std::string, CacheEntry> cache_;
// 侵入式LRU链表 - 使用数组索引代替指针,提高缓存局部性
std::vector<LRUNode> 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<void(Ptr<Texture>)> callback;
};
std::vector<AsyncLoadTask> 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<Texture> 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

View File

@ -6,16 +6,21 @@
#include <extra2d/graphics/font.h> #include <extra2d/graphics/font.h>
#include <extra2d/graphics/texture.h> #include <extra2d/graphics/texture.h>
#include <functional> #include <functional>
#include <future>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <queue>
#include <thread>
#include <atomic>
#include <list>
#include <vector> #include <vector>
namespace extra2d { namespace extra2d {
// ============================================================================ // ============================================================================
// 资源管理器 - 统一管理纹理、字体、音效等资源 // 资源管理器 - 统一管理纹理、字体、音效等资源
// 使用 TexturePool 作为纹理管理后端 // 支持异步加载和纹理压缩
// ============================================================================ // ============================================================================
// 纹理格式枚举 // 纹理格式枚举
@ -30,6 +35,16 @@ enum class TextureFormat {
ASTC8x8, // ASTC 8x8 压缩(高压缩率) ASTC8x8, // ASTC 8x8 压缩(高压缩率)
}; };
// ============================================================================
// 纹理LRU缓存项
// ============================================================================
struct TextureCacheEntry {
Ptr<Texture> texture;
size_t size = 0; // 纹理大小(字节)
float lastAccessTime = 0.0f; // 最后访问时间
uint32_t accessCount = 0; // 访问次数
};
// 异步加载回调类型 // 异步加载回调类型
using TextureLoadCallback = std::function<void(Ptr<Texture>)>; using TextureLoadCallback = std::function<void(Ptr<Texture>)>;
@ -40,13 +55,6 @@ public:
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
static ResourceManager &getInstance(); static ResourceManager &getInstance();
// ------------------------------------------------------------------------
// 更新(在主循环中调用)
// ------------------------------------------------------------------------
/// 更新资源管理器,触发纹理池自动清理等
void update(float dt);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// 纹理资源 - 同步加载 // 纹理资源 - 同步加载
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@ -146,24 +154,39 @@ public:
size_t getSoundCacheSize() const; size_t getSoundCacheSize() const;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// 异步加载控制(已弃用,保留接口兼容性) // LRU 缓存管理
// 纹理异步加载由 TexturePool 内部处理 // ------------------------------------------------------------------------
/// 设置纹理缓存参数
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(); void initAsyncLoader();
/// 关闭异步加载系统 /// 关闭异步加载系统
/// @deprecated 纹理池自动管理生命周期,无需调用
void shutdownAsyncLoader(); void shutdownAsyncLoader();
/// 等待所有异步加载完成 /// 等待所有异步加载完成
/// @deprecated 使用回调机制处理异步加载结果
void waitForAsyncLoads(); void waitForAsyncLoads();
/// 检查是否有正在进行的异步加载 /// 检查是否有正在进行的异步加载
/// @deprecated 始终返回 false
bool hasPendingAsyncLoads() const; bool hasPendingAsyncLoads() const;
ResourceManager(); ResourceManager();
@ -186,14 +209,91 @@ private:
std::vector<uint8_t> compressTexture(const uint8_t* data, int width, int height, std::vector<uint8_t> compressTexture(const uint8_t* data, int width, int height,
int channels, TextureFormat format); int channels, TextureFormat format);
// 互斥锁保护缓存(字体和音效缓存仍需锁保护) // 互斥锁保护缓存
mutable std::mutex textureMutex_;
mutable std::mutex fontMutex_; mutable std::mutex fontMutex_;
mutable std::mutex soundMutex_; mutable std::mutex soundMutex_;
// 资源缓存 - 使用弱指针实现自动清理 // 资源缓存 - 使用弱指针实现自动清理
// 纹理缓存已移至 TexturePool此处仅保留字体和音效缓存
std::unordered_map<std::string, WeakPtr<FontAtlas>> fontCache_; std::unordered_map<std::string, WeakPtr<FontAtlas>> fontCache_;
std::unordered_map<std::string, WeakPtr<Sound>> soundCache_; std::unordered_map<std::string, WeakPtr<Sound>> 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<std::string, TextureCacheEntry> textureCache_;
// 侵入式LRU链表 - 使用数组索引代替指针,提高缓存局部性
std::vector<LRUNode> 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<Ptr<Texture>> promise;
};
std::queue<AsyncLoadTask> asyncTaskQueue_;
std::mutex asyncQueueMutex_;
std::condition_variable asyncCondition_;
std::unique_ptr<std::thread> asyncThread_;
std::atomic<bool> asyncRunning_{false};
std::atomic<int> 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 } // namespace extra2d

View File

@ -7,6 +7,7 @@
#include <mutex> #include <mutex>
#include <type_traits> #include <type_traits>
#include <vector> #include <vector>
#include <iostream>
namespace extra2d { namespace extra2d {
@ -22,33 +23,18 @@ public:
static_assert(std::is_destructible_v<T>, "T must be destructible"); static_assert(std::is_destructible_v<T>, "T must be destructible");
ObjectPool() = default; ObjectPool() = default;
~ObjectPool() { clear(); } ~ObjectPool() {
// 确保所有对象都已归还后再销毁
clear();
}
// 禁止拷贝 // 禁止拷贝
ObjectPool(const ObjectPool&) = delete; ObjectPool(const ObjectPool&) = delete;
ObjectPool& operator=(const ObjectPool&) = delete; ObjectPool& operator=(const ObjectPool&) = delete;
// 允许移动 // 禁止移动(避免地址失效问题)
ObjectPool(ObjectPool&& other) noexcept { ObjectPool(ObjectPool&& other) noexcept = delete;
std::lock_guard<std::mutex> lock(other.mutex_); ObjectPool& operator=(ObjectPool&& other) noexcept = delete;
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<std::mutex> lock1(mutex_);
std::lock_guard<std::mutex> lock2(other.mutex_);
clear();
blocks_ = std::move(other.blocks_);
freeList_ = std::move(other.freeList_);
allocatedCount_ = other.allocatedCount_.load();
other.allocatedCount_ = 0;
}
return *this;
}
/** /**
* @brief * @brief
@ -57,6 +43,11 @@ public:
T* allocate() { T* allocate() {
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
// 检查对象池是否已销毁
if (isDestroyed_) {
return nullptr;
}
if (freeList_.empty()) { if (freeList_.empty()) {
grow(); grow();
} }
@ -78,6 +69,11 @@ public:
T* allocate(Args&&... args) { T* allocate(Args&&... args) {
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
// 检查对象池是否已销毁
if (isDestroyed_) {
return nullptr;
}
if (freeList_.empty()) { if (freeList_.empty()) {
grow(); grow();
} }
@ -95,18 +91,24 @@ public:
/** /**
* @brief * @brief
* @param obj * @param obj
* @return true false
*/ */
void deallocate(T* obj) { bool deallocate(T* obj) {
if (obj == nullptr) { if (obj == nullptr) {
return; return false;
} }
// 调用析构函数 // 调用析构函数
obj->~T(); obj->~T();
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
// 检查对象池是否还在运行(避免在对象池销毁后访问)
if (!isDestroyed_) {
freeList_.push_back(obj); freeList_.push_back(obj);
allocatedCount_--; allocatedCount_--;
return true;
}
return false;
} }
/** /**
@ -138,9 +140,12 @@ public:
void clear() { void clear() {
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
isDestroyed_ = true;
// 释放所有内存块 // 释放所有内存块
for (auto& block : blocks_) { for (auto& block : blocks_) {
::operator delete[](block, std::align_val_t(alignof(T))); // 使用与分配时匹配的释放方式
::operator delete[](block);
} }
blocks_.clear(); blocks_.clear();
freeList_.clear(); freeList_.clear();
@ -152,8 +157,8 @@ private:
* @brief * @brief
*/ */
void grow() { void grow() {
// 分配新的内存块 // 分配新的内存块(使用标准对齐)
T* block = static_cast<T*>(::operator new[](sizeof(T) * BlockSize, std::align_val_t(alignof(T)))); T* block = static_cast<T*>(::operator new[](sizeof(T) * BlockSize));
blocks_.push_back(block); blocks_.push_back(block);
@ -167,6 +172,7 @@ private:
std::vector<T*> blocks_; // 内存块列表 std::vector<T*> blocks_; // 内存块列表
std::vector<T*> freeList_; // 空闲对象列表 std::vector<T*> freeList_; // 空闲对象列表
std::atomic<size_t> allocatedCount_{0}; std::atomic<size_t> allocatedCount_{0};
bool isDestroyed_ = false; // 标记对象池是否已销毁
}; };
// ============================================================================ // ============================================================================
@ -186,8 +192,13 @@ public:
*/ */
template <typename... Args> template <typename... Args>
Ptr<T> makeShared(Args&&... args) { Ptr<T> makeShared(Args&&... args) {
// 使用 weak_ptr 避免循环引用
std::weak_ptr<PoolType> weakPool = pool_;
T* obj = pool_->allocate(std::forward<Args>(args)...); T* obj = pool_->allocate(std::forward<Args>(args)...);
return Ptr<T>(obj, Deleter{pool_}); if (!obj) {
return nullptr;
}
return Ptr<T>(obj, Deleter{weakPool});
} }
/** /**
@ -199,13 +210,19 @@ public:
private: private:
struct Deleter { struct Deleter {
std::shared_ptr<PoolType> pool; std::weak_ptr<PoolType> pool;
void operator()(T* obj) const { void operator()(T* obj) const {
if (pool) { // 尝试获取 pool
pool->deallocate(obj); if (auto sharedPool = pool.lock()) {
// 尝试归还给对象池
if (!sharedPool->deallocate(obj)) {
// 对象池已销毁,手动释放内存
::operator delete[](obj);
}
} else { } else {
delete obj; // Pool 已被销毁,释放内存
::operator delete[](obj);
} }
} }
}; };
@ -226,20 +243,41 @@ public:
*/ */
template <typename T, size_t BlockSize = 64> template <typename T, size_t BlockSize = 64>
std::shared_ptr<ObjectPool<T, BlockSize>> getPool() { std::shared_ptr<ObjectPool<T, BlockSize>> getPool() {
static std::shared_ptr<ObjectPool<T, BlockSize>> pool = // 使用静态局部变量确保延迟初始化和正确析构顺序
std::make_shared<ObjectPool<T, BlockSize>>(); static auto pool = std::make_shared<ObjectPool<T, BlockSize>>();
return pool; return pool;
} }
/** /**
* @brief 使 * @brief 使
* 退
*/ */
template <typename T, size_t BlockSize = 64, typename... Args> template <typename T, size_t BlockSize = 64, typename... Args>
Ptr<T> makePooled(Args&&... args) { Ptr<T> makePooled(Args&&... args) {
auto pool = getPool<T, BlockSize>(); auto pool = getPool<T, BlockSize>();
std::weak_ptr<ObjectPool<T, BlockSize>> weakPool = pool;
T* obj = pool->allocate(std::forward<Args>(args)...); T* obj = pool->allocate(std::forward<Args>(args)...);
return Ptr<T>(obj, [pool](T* p) { pool->deallocate(p); }); if (!obj) {
return nullptr;
} }
return Ptr<T>(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: private:
ObjectPoolManager() = default; ObjectPoolManager() = default;

View File

@ -176,24 +176,36 @@ void Application::shutdown() {
sceneManager_->end(); sceneManager_->end();
} }
// 清理子系统 // ========================================
sceneManager_.reset(); // 1. 先清理所有持有 GPU 资源的子系统
resourceManager_.reset(); // 必须在渲染器关闭前释放纹理等资源
// ========================================
sceneManager_.reset(); // 场景持有纹理引用
resourceManager_.reset(); // 纹理缓存持有 GPU 纹理
camera_.reset(); // 相机可能持有渲染目标
// ========================================
// 2. 关闭音频(不依赖 GPU
// ========================================
AudioEngine::getInstance().shutdown();
// ========================================
// 3. 清理其他子系统
// ========================================
timerManager_.reset(); timerManager_.reset();
eventQueue_.reset(); eventQueue_.reset();
eventDispatcher_.reset(); eventDispatcher_.reset();
camera_.reset();
// 关闭音频 // ========================================
AudioEngine::getInstance().shutdown(); // 4. 最后关闭渲染器和窗口
// 必须在所有 GPU 资源释放后才能关闭 OpenGL 上下文
// 关闭渲染器 // ========================================
if (renderer_) { if (renderer_) {
renderer_->shutdown(); renderer_->shutdown();
renderer_.reset(); renderer_.reset();
} }
// 销毁窗口(包含 SDL_Quit // 销毁窗口(包含 SDL_Quit,会销毁 OpenGL 上下文
if (window_) { if (window_) {
window_->destroy(); window_->destroy();
window_.reset(); window_.reset();
@ -314,11 +326,6 @@ void Application::update() {
if (sceneManager_) { if (sceneManager_) {
sceneManager_->update(deltaTime_); sceneManager_->update(deltaTime_);
} }
// 更新资源管理器(触发纹理池自动清理)
if (resourceManager_) {
resourceManager_->update(deltaTime_);
}
} }
void Application::render() { void Application::render() {

View File

@ -0,0 +1,22 @@
#include <extra2d/graphics/gpu_context.h>
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

View File

@ -3,6 +3,7 @@
#include <cmath> #include <cmath>
#include <cstring> #include <cstring>
#include <extra2d/core/string.h> #include <extra2d/core/string.h>
#include <extra2d/graphics/gpu_context.h>
#include <extra2d/graphics/opengl/gl_font_atlas.h> #include <extra2d/graphics/opengl/gl_font_atlas.h>
#include <extra2d/graphics/opengl/gl_renderer.h> #include <extra2d/graphics/opengl/gl_renderer.h>
#include <extra2d/graphics/opengl/gl_texture.h> #include <extra2d/graphics/opengl/gl_texture.h>
@ -62,6 +63,9 @@ bool GLRenderer::init(Window *window) {
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// 标记 GPU 上下文为有效
GPUContext::getInstance().markValid();
E2D_LOG_INFO("OpenGL Renderer initialized"); E2D_LOG_INFO("OpenGL Renderer initialized");
E2D_LOG_INFO("OpenGL Version: {}", E2D_LOG_INFO("OpenGL Version: {}",
reinterpret_cast<const char *>(glGetString(GL_VERSION))); reinterpret_cast<const char *>(glGetString(GL_VERSION)));
@ -70,6 +74,10 @@ bool GLRenderer::init(Window *window) {
} }
void GLRenderer::shutdown() { void GLRenderer::shutdown() {
// 标记 GPU 上下文为无效
// 这会在销毁 OpenGL 上下文之前通知所有 GPU 资源
GPUContext::getInstance().markInvalid();
spriteBatch_.shutdown(); spriteBatch_.shutdown();
if (shapeVbo_ != 0) { if (shapeVbo_ != 0) {

View File

@ -1,4 +1,5 @@
#include <extra2d/graphics/opengl/gl_texture.h> #include <extra2d/graphics/opengl/gl_texture.h>
#include <extra2d/graphics/gpu_context.h>
#include <extra2d/graphics/vram_manager.h> #include <extra2d/graphics/vram_manager.h>
#define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION
#include <cstring> #include <cstring>
@ -130,8 +131,12 @@ GLTexture::GLTexture(const std::string &filepath)
GLTexture::~GLTexture() { GLTexture::~GLTexture() {
if (textureID_ != 0) { if (textureID_ != 0) {
// 检查 GPU 上下文是否仍然有效
// 如果 OpenGL 上下文已销毁,则跳过 glDeleteTextures 调用
if (GPUContext::getInstance().isValid()) {
glDeleteTextures(1, &textureID_); glDeleteTextures(1, &textureID_);
// VRAM 跟踪: 释放纹理显存 }
// VRAM 跟踪: 释放纹理显存(无论上下文是否有效都需要更新统计)
if (dataSize_ > 0) { if (dataSize_ > 0) {
VRAMManager::getInstance().freeTexture(dataSize_); VRAMManager::getInstance().freeTexture(dataSize_);
} }

View File

@ -1,518 +0,0 @@
#include <extra2d/graphics/opengl/gl_texture.h>
#include <extra2d/graphics/texture_pool.h>
#include <extra2d/utils/logger.h>
#include <stb/stb_image.h>
namespace extra2d {
// ============================================================================
// TexturePool实现
// ============================================================================
TexturePool &TexturePool::getInstance() {
static TexturePool instance;
return instance;
}
bool TexturePool::init(const TexturePoolConfig &config) {
std::lock_guard<std::mutex> 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<std::mutex> lock(mutex_);
if (!initialized_) {
return;
}
E2D_INFO("关闭纹理池...");
printStats();
clear();
initialized_ = false;
}
Ptr<Texture> TexturePool::get(const std::string &filepath) {
std::lock_guard<std::mutex> 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<void(Ptr<Texture>)> 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<std::mutex> lock(mutex_);
asyncTasks_.push_back({filepath, callback});
}
Ptr<Texture> TexturePool::createFromData(const std::string &name,
const uint8_t *data, int width,
int height, PixelFormat format) {
std::lock_guard<std::mutex> 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<GLTexture>(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<uint32_t>(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> 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<std::mutex> 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<std::mutex> lock(mutex_);
return cache_.find(key) != cache_.end();
}
void TexturePool::clear() {
std::lock_guard<std::mutex> 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<std::mutex> lock(mutex_);
while (totalSize_ > targetSize && lruTail_ != 0) {
evict();
}
E2D_INFO("纹理缓存已清理到 {} MB", totalSize_ / (1024 * 1024));
}
size_t TexturePool::getTextureCount() const {
std::lock_guard<std::mutex> lock(mutex_);
return cache_.size();
}
size_t TexturePool::getCacheSize() const {
std::lock_guard<std::mutex> lock(mutex_);
return totalSize_;
}
float TexturePool::getHitRate() const {
std::lock_guard<std::mutex> lock(mutex_);
uint64_t total = hitCount_ + missCount_;
if (total == 0) {
return 0.0f;
}
return static_cast<float>(hitCount_) / static_cast<float>(total);
}
void TexturePool::printStats() const {
std::lock_guard<std::mutex> 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<Texture> 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<GLTexture>(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<size_t>(width * height * bytesPerPixel);
}
void TexturePool::processAsyncTasks() {
std::vector<AsyncLoadTask> tasks;
{
std::lock_guard<std::mutex> 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

View File

@ -3,7 +3,6 @@
#include <extra2d/audio/audio_engine.h> #include <extra2d/audio/audio_engine.h>
#include <extra2d/graphics/opengl/gl_font_atlas.h> #include <extra2d/graphics/opengl/gl_font_atlas.h>
#include <extra2d/graphics/opengl/gl_texture.h> #include <extra2d/graphics/opengl/gl_texture.h>
#include <extra2d/graphics/texture_pool.h>
#include <extra2d/resource/resource_manager.h> #include <extra2d/resource/resource_manager.h>
#include <extra2d/utils/logger.h> #include <extra2d/utils/logger.h>
#include <sys/stat.h> #include <sys/stat.h>
@ -80,17 +79,10 @@ static std::string resolveResourcePath(const std::string &filepath) {
return ""; return "";
} }
ResourceManager::ResourceManager() { ResourceManager::ResourceManager() = default;
// 初始化纹理池
TexturePoolConfig config;
config.maxCacheSize = 128 * 1024 * 1024; // 128MB
config.maxTextureCount = 512;
config.enableAsyncLoad = true;
TexturePool::getInstance().init(config);
}
ResourceManager::~ResourceManager() { ResourceManager::~ResourceManager() {
TexturePool::getInstance().shutdown(); shutdownAsyncLoader();
} }
ResourceManager &ResourceManager::getInstance() { ResourceManager &ResourceManager::getInstance() {
@ -99,11 +91,77 @@ ResourceManager &ResourceManager::getInstance() {
} }
// ============================================================================ // ============================================================================
// 更新(在主循环中调用) // 异步加载系统
// ============================================================================ // ============================================================================
void ResourceManager::update(float dt) { void ResourceManager::initAsyncLoader() {
TexturePool::getInstance().update(dt); if (asyncRunning_) {
return;
}
asyncRunning_ = true;
asyncThread_ = std::make_unique<std::thread>(&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<std::mutex> 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) { void ResourceManager::loadTextureAsync(const std::string &filepath, TextureFormat format, TextureLoadCallback callback) {
// 解析资源路径 // 确保异步加载系统已启动
std::string fullPath = resolveResourcePath(filepath); if (!asyncRunning_) {
if (fullPath.empty()) { initAsyncLoader();
E2D_LOG_ERROR("ResourceManager: texture file not found: {}", filepath); }
// 检查缓存
{
std::lock_guard<std::mutex> lock(textureMutex_);
auto it = textureCache_.find(filepath);
if (it != textureCache_.end()) {
// 缓存命中更新LRU并立即回调
touchTexture(filepath);
textureHitCount_++;
it->second.accessCount++;
if (callback) { if (callback) {
callback(nullptr); callback(it->second.texture);
} }
return; return;
} }
}
// 使用纹理池的异步加载 // 添加到异步任务队列
TexturePool::getInstance().getAsync(fullPath, AsyncLoadTask task;
[callback, filepath](Ptr<Texture> texture) { task.filepath = filepath;
if (texture) { task.format = format;
E2D_LOG_DEBUG("ResourceManager: async texture loaded: {}", filepath); task.callback = callback;
} else {
E2D_LOG_ERROR("ResourceManager: failed to async load texture: {}", filepath); {
std::lock_guard<std::mutex> lock(asyncQueueMutex_);
asyncTaskQueue_.push(std::move(task));
} }
if (callback) {
callback(texture); pendingAsyncLoads_++;
} asyncCondition_.notify_one();
});
E2D_LOG_DEBUG("ResourceManager: queued async texture load: {}", filepath);
} }
Ptr<Texture> ResourceManager::loadTextureInternal(const std::string &filepath, TextureFormat format) { Ptr<Texture> ResourceManager::loadTextureInternal(const std::string &filepath, TextureFormat format) {
std::lock_guard<std::mutex> 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:/ 前缀) // 解析资源路径(优先尝试 romfs:/ 前缀)
std::string fullPath = resolveResourcePath(filepath); std::string fullPath = resolveResourcePath(filepath);
if (fullPath.empty()) { if (fullPath.empty()) {
@ -165,22 +253,51 @@ Ptr<Texture> ResourceManager::loadTextureInternal(const std::string &filepath, T
return nullptr; return nullptr;
} }
// 使用纹理池获取纹理 // 创建新纹理
auto texture = TexturePool::getInstance().get(fullPath); try {
auto texture = makePtr<GLTexture>(fullPath);
if (!texture) { if (!texture->isValid()) {
E2D_LOG_ERROR("ResourceManager: failed to load texture: {}", filepath); E2D_LOG_ERROR("ResourceManager: failed to load texture: {}", filepath);
return nullptr; return nullptr;
} }
// 如果需要压缩,处理纹理格式(记录日志,实际压缩需要在纹理创建时处理) // 如果需要压缩,处理纹理格式
if (format != TextureFormat::Auto && format != TextureFormat::RGBA8) { if (format != TextureFormat::Auto && format != TextureFormat::RGBA8) {
// 注意:实际压缩需要在纹理创建时处理
// 这里仅记录日志,实际实现需要在 GLTexture 中支持
E2D_LOG_DEBUG("ResourceManager: texture format {} requested for {}", E2D_LOG_DEBUG("ResourceManager: texture format {} requested for {}",
static_cast<int>(format), filepath); static_cast<int>(format), filepath);
} }
E2D_LOG_DEBUG("ResourceManager: loaded texture: {}", 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; return texture;
} catch (...) {
E2D_LOG_ERROR("ResourceManager: exception loading texture: {}", filepath);
return nullptr;
}
} }
TextureFormat ResourceManager::selectBestFormat(TextureFormat requested) const { TextureFormat ResourceManager::selectBestFormat(TextureFormat requested) const {
@ -226,92 +343,80 @@ ResourceManager::loadTextureWithAlphaMask(const std::string &filepath) {
const AlphaMask * const AlphaMask *
ResourceManager::getAlphaMask(const std::string &textureKey) const { ResourceManager::getAlphaMask(const std::string &textureKey) const {
// 解析路径获取完整路径 std::lock_guard<std::mutex> lock(textureMutex_);
std::string fullPath = resolveResourcePath(textureKey);
if (fullPath.empty()) {
return nullptr;
}
// 从纹理池获取纹理 auto it = textureCache_.find(textureKey);
auto texture = TexturePool::getInstance().get(fullPath); if (it != textureCache_.end()) {
if (!texture) { GLTexture *glTexture = static_cast<GLTexture *>(it->second.texture.get());
return nullptr;
}
GLTexture *glTexture = static_cast<GLTexture *>(texture.get());
return glTexture->getAlphaMask(); return glTexture->getAlphaMask();
} }
return nullptr;
}
bool ResourceManager::generateAlphaMask(const std::string &textureKey) { bool ResourceManager::generateAlphaMask(const std::string &textureKey) {
// 解析路径获取完整路径 std::lock_guard<std::mutex> lock(textureMutex_);
std::string fullPath = resolveResourcePath(textureKey);
if (fullPath.empty()) {
return false;
}
// 从纹理池获取纹理 auto it = textureCache_.find(textureKey);
auto texture = TexturePool::getInstance().get(fullPath); if (it != textureCache_.end()) {
if (!texture) { GLTexture *glTexture = static_cast<GLTexture *>(it->second.texture.get());
return false;
}
GLTexture *glTexture = static_cast<GLTexture *>(texture.get());
if (!glTexture->hasAlphaMask()) { if (!glTexture->hasAlphaMask()) {
glTexture->generateAlphaMask(); glTexture->generateAlphaMask();
} }
return glTexture->hasAlphaMask(); return glTexture->hasAlphaMask();
} }
return false;
}
bool ResourceManager::hasAlphaMask(const std::string &textureKey) const { bool ResourceManager::hasAlphaMask(const std::string &textureKey) const {
// 解析路径获取完整路径 std::lock_guard<std::mutex> lock(textureMutex_);
std::string fullPath = resolveResourcePath(textureKey);
if (fullPath.empty()) {
return false;
}
// 从纹理池获取纹理 auto it = textureCache_.find(textureKey);
auto texture = TexturePool::getInstance().get(fullPath); if (it != textureCache_.end()) {
if (!texture) { GLTexture *glTexture = static_cast<GLTexture *>(it->second.texture.get());
return false;
}
GLTexture *glTexture = static_cast<GLTexture *>(texture.get());
return glTexture->hasAlphaMask(); return glTexture->hasAlphaMask();
} }
return false;
}
Ptr<Texture> ResourceManager::getTexture(const std::string &key) const { Ptr<Texture> ResourceManager::getTexture(const std::string &key) const {
// 解析路径获取完整路径 std::lock_guard<std::mutex> lock(textureMutex_);
std::string fullPath = resolveResourcePath(key);
if (fullPath.empty()) { auto it = textureCache_.find(key);
if (it != textureCache_.end()) {
const_cast<ResourceManager*>(this)->touchTexture(key);
const_cast<ResourceManager*>(this)->textureHitCount_++;
const_cast<uint32_t&>(it->second.accessCount)++;
return it->second.texture;
}
return nullptr; return nullptr;
} }
// 从纹理池获取纹理
return TexturePool::getInstance().get(fullPath);
}
bool ResourceManager::hasTexture(const std::string &key) const { bool ResourceManager::hasTexture(const std::string &key) const {
// 解析路径获取完整路径 std::lock_guard<std::mutex> lock(textureMutex_);
std::string fullPath = resolveResourcePath(key); return textureCache_.find(key) != textureCache_.end();
if (fullPath.empty()) {
return false;
}
// 检查纹理池是否有此纹理
return TexturePool::getInstance().has(fullPath);
} }
void ResourceManager::unloadTexture(const std::string &key) { void ResourceManager::unloadTexture(const std::string &key) {
// 解析路径获取完整路径 std::lock_guard<std::mutex> lock(textureMutex_);
std::string fullPath = resolveResourcePath(key);
if (fullPath.empty()) { auto it = textureCache_.find(key);
return; 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<uint32_t>(nodeIt - lruNodes_.begin()) + 1);
freeLRUNode(static_cast<uint32_t>(nodeIt - lruNodes_.begin()) + 1);
} }
// 从纹理池移除 // 更新总大小
TexturePool::getInstance().remove(fullPath); totalTextureSize_ -= it->second.size;
// 从缓存移除
textureCache_.erase(it);
E2D_LOG_DEBUG("ResourceManager: unloaded texture: {}", key); E2D_LOG_DEBUG("ResourceManager: unloaded texture: {}", key);
} }
}
// ============================================================================ // ============================================================================
// 字体图集资源 // 字体图集资源
@ -468,11 +573,8 @@ void ResourceManager::unloadSound(const std::string &key) {
// ============================================================================ // ============================================================================
void ResourceManager::purgeUnused() { void ResourceManager::purgeUnused() {
// 纹理缓存由 TexturePool 自动管理,无需手动清理 // 纹理缓存使用LRU策略不需要清理失效引用
// 但我们可以触发 TexturePool 的清理 // 字体缓存
TexturePool::getInstance().trim(TexturePool::getInstance().getCacheSize() * 0.8f);
// 清理字体缓存
{ {
std::lock_guard<std::mutex> lock(fontMutex_); std::lock_guard<std::mutex> lock(fontMutex_);
for (auto it = fontCache_.begin(); it != fontCache_.end();) { for (auto it = fontCache_.begin(); it != fontCache_.end();) {
@ -485,7 +587,7 @@ void ResourceManager::purgeUnused() {
} }
} }
// 清理音效缓存 // 音效缓存
{ {
std::lock_guard<std::mutex> lock(soundMutex_); std::lock_guard<std::mutex> lock(soundMutex_);
for (auto it = soundCache_.begin(); it != soundCache_.end();) { for (auto it = soundCache_.begin(); it != soundCache_.end();) {
@ -500,8 +602,15 @@ void ResourceManager::purgeUnused() {
} }
void ResourceManager::clearTextureCache() { void ResourceManager::clearTextureCache() {
TexturePool::getInstance().clear(); std::lock_guard<std::mutex> lock(textureMutex_);
E2D_LOG_INFO("ResourceManager: texture cache cleared via TexturePool"); 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() { void ResourceManager::clearFontCache() {
@ -531,7 +640,210 @@ void ResourceManager::clearAllCaches() {
} }
size_t ResourceManager::getTextureCacheSize() const { size_t ResourceManager::getTextureCacheSize() const {
return TexturePool::getInstance().getTextureCount(); std::lock_guard<std::mutex> lock(textureMutex_);
return textureCache_.size();
}
// ============================================================================
// LRU 缓存管理
// ============================================================================
void ResourceManager::setTextureCache(size_t maxCacheSize, size_t maxTextureCount,
float unloadInterval) {
std::lock_guard<std::mutex> lock(textureMutex_);
maxCacheSize_ = maxCacheSize;
maxTextureCount_ = maxTextureCount;
unloadInterval_ = unloadInterval;
}
size_t ResourceManager::getTextureCacheMemoryUsage() const {
std::lock_guard<std::mutex> lock(textureMutex_);
return totalTextureSize_;
}
float ResourceManager::getTextureCacheHitRate() const {
std::lock_guard<std::mutex> lock(textureMutex_);
uint64_t total = textureHitCount_ + textureMissCount_;
if (total == 0) {
return 0.0f;
}
return static_cast<float>(textureHitCount_) / static_cast<float>(total);
}
void ResourceManager::printTextureCacheStats() const {
std::lock_guard<std::mutex> 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<std::mutex> lock(textureMutex_);
// 自动清理
autoUnloadTimer_ += dt;
if (autoUnloadTimer_ >= unloadInterval_) {
autoUnloadTimer_ = 0.0f;
// 如果缓存超过80%清理到50%
if (totalTextureSize_ > maxCacheSize_ * 0.8f) {
size_t targetSize = static_cast<size_t>(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<uint32_t>(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<uint32_t>(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<size_t>(width * height * bytesPerPixel);
} }
size_t ResourceManager::getFontCacheSize() const { size_t ResourceManager::getFontCacheSize() const {
@ -544,27 +856,4 @@ size_t ResourceManager::getSoundCacheSize() const {
return soundCache_.size(); 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 } // namespace extra2d

View File

@ -8,4 +8,10 @@ ObjectPoolManager& ObjectPoolManager::getInstance() {
return instance; return instance;
} }
void ObjectPoolManager::cleanup() {
// 静态对象池会在程序退出时自动清理
// 这个方法用于显式触发清理(如果需要)
// 由于使用了 weak_ptr循环引用问题已解决
}
} // namespace extra2d } // namespace extra2d