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
#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 <string>
#include <unordered_map>
@ -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<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_;
std::unordered_map<std::string, Ptr<SpriteFrame>> frames_;
};

View File

@ -19,7 +19,7 @@
#include <extra2d/graphics/font.h>
#include <extra2d/graphics/camera.h>
#include <extra2d/graphics/shader_system.h>
#include <extra2d/graphics/texture_pool.h>
#include <extra2d/graphics/render_target.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/texture.h>
#include <functional>
#include <future>
#include <mutex>
#include <string>
#include <unordered_map>
#include <queue>
#include <thread>
#include <atomic>
#include <list>
#include <vector>
namespace extra2d {
// ============================================================================
// 资源管理器 - 统一管理纹理、字体、音效等资源
// 使用 TexturePool 作为纹理管理后端
// 支持异步加载和纹理压缩
// ============================================================================
// 纹理格式枚举
@ -30,6 +35,16 @@ enum class TextureFormat {
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>)>;
@ -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<uint8_t> 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<std::string, WeakPtr<FontAtlas>> fontCache_;
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

View File

@ -7,6 +7,7 @@
#include <mutex>
#include <type_traits>
#include <vector>
#include <iostream>
namespace extra2d {
@ -22,33 +23,18 @@ public:
static_assert(std::is_destructible_v<T>, "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<std::mutex> 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<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;
}
// 禁止移动(避免地址失效问题)
ObjectPool(ObjectPool&& other) noexcept = delete;
ObjectPool& operator=(ObjectPool&& other) noexcept = delete;
/**
* @brief
@ -56,17 +42,22 @@ public:
*/
T* allocate() {
std::lock_guard<std::mutex> 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 <typename... Args>
T* allocate(Args&&... args) {
std::lock_guard<std::mutex> lock(mutex_);
// 检查对象池是否已销毁
if (isDestroyed_) {
return nullptr;
}
if (freeList_.empty()) {
grow();
}
T* obj = freeList_.back();
freeList_.pop_back();
// 在原地构造对象
new (obj) T(std::forward<Args>(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<std::mutex> 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<std::mutex> 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<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);
// 将新块中的对象添加到空闲列表
for (size_t i = 0; i < BlockSize; ++i) {
freeList_.push_back(&block[i]);
@ -167,6 +172,7 @@ private:
std::vector<T*> blocks_; // 内存块列表
std::vector<T*> freeList_; // 空闲对象列表
std::atomic<size_t> allocatedCount_{0};
bool isDestroyed_ = false; // 标记对象池是否已销毁
};
// ============================================================================
@ -186,8 +192,13 @@ public:
*/
template <typename... Args>
Ptr<T> makeShared(Args&&... args) {
// 使用 weak_ptr 避免循环引用
std::weak_ptr<PoolType> weakPool = pool_;
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:
struct Deleter {
std::shared_ptr<PoolType> pool;
std::weak_ptr<PoolType> 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 <typename T, size_t BlockSize = 64>
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;
}
/**
* @brief 使
* 退
*/
template <typename T, size_t BlockSize = 64, typename... Args>
Ptr<T> makePooled(Args&&... args) {
auto pool = getPool<T, BlockSize>();
std::weak_ptr<ObjectPool<T, BlockSize>> weakPool = pool;
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:
ObjectPoolManager() = default;
~ObjectPoolManager() = default;

View File

@ -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() {

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 <cstring>
#include <extra2d/core/string.h>
#include <extra2d/graphics/gpu_context.h>
#include <extra2d/graphics/opengl/gl_font_atlas.h>
#include <extra2d/graphics/opengl/gl_renderer.h>
#include <extra2d/graphics/opengl/gl_texture.h>
@ -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<const char *>(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) {

View File

@ -1,4 +1,5 @@
#include <extra2d/graphics/opengl/gl_texture.h>
#include <extra2d/graphics/gpu_context.h>
#include <extra2d/graphics/vram_manager.h>
#define STB_IMAGE_IMPLEMENTATION
#include <cstring>
@ -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_);
}

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/graphics/opengl/gl_font_atlas.h>
#include <extra2d/graphics/opengl/gl_texture.h>
#include <extra2d/graphics/texture_pool.h>
#include <extra2d/resource/resource_manager.h>
#include <extra2d/utils/logger.h>
#include <sys/stat.h>
@ -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<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) {
// 解析资源路径
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> 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<std::mutex> 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<std::mutex> lock(asyncQueueMutex_);
asyncTaskQueue_.push(std::move(task));
}
pendingAsyncLoads_++;
asyncCondition_.notify_one();
E2D_LOG_DEBUG("ResourceManager: queued async texture load: {}", filepath);
}
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:/ 前缀)
std::string fullPath = resolveResourcePath(filepath);
if (fullPath.empty()) {
@ -165,22 +253,51 @@ Ptr<Texture> 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<GLTexture>(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<int>(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<int>(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<std::mutex> 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<GLTexture *>(it->second.texture.get());
return glTexture->getAlphaMask();
}
GLTexture *glTexture = static_cast<GLTexture *>(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<std::mutex> 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<GLTexture *>(it->second.texture.get());
if (!glTexture->hasAlphaMask()) {
glTexture->generateAlphaMask();
}
return glTexture->hasAlphaMask();
}
GLTexture *glTexture = static_cast<GLTexture *>(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<std::mutex> 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<GLTexture *>(it->second.texture.get());
return glTexture->hasAlphaMask();
}
GLTexture *glTexture = static_cast<GLTexture *>(texture.get());
return glTexture->hasAlphaMask();
return false;
}
Ptr<Texture> ResourceManager::getTexture(const std::string &key) const {
// 解析路径获取完整路径
std::string fullPath = resolveResourcePath(key);
if (fullPath.empty()) {
return nullptr;
}
std::lock_guard<std::mutex> lock(textureMutex_);
// 从纹理池获取纹理
return TexturePool::getInstance().get(fullPath);
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;
}
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<std::mutex> 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<std::mutex> 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<uint32_t>(nodeIt - lruNodes_.begin()) + 1);
freeLRUNode(static_cast<uint32_t>(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<std::mutex> lock(fontMutex_);
for (auto it = fontCache_.begin(); it != fontCache_.end();) {
@ -485,7 +587,7 @@ void ResourceManager::purgeUnused() {
}
}
// 清理音效缓存
// 音效缓存
{
std::lock_guard<std::mutex> 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<std::mutex> 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<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 {
@ -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

View File

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