Extra2D/include/resource/resource_manager.h

363 lines
12 KiB
C
Raw Normal View History

2026-02-11 19:40:26 +08:00
#pragma once
#include <atomic>
#include <audio/sound.h>
#include <core/types.h>
#include <functional>
#include <future>
#include <graphics/alpha_mask.h>
#include <graphics/font.h>
#include <graphics/texture.h>
2026-02-11 19:40:26 +08:00
#include <mutex>
#include <queue>
#include <string>
#include <thread>
#include <unordered_map>
#include <vector>
2026-02-11 19:40:26 +08:00
2026-02-11 19:40:26 +08:00
namespace extra2d {
// ============================================================================
// 资源管理器 - 统一管理纹理、字体、音效等资源
// 支持异步加载和纹理压缩
2026-02-11 19:40:26 +08:00
// ============================================================================
// 纹理格式枚举
enum class TextureFormat {
Auto = 0, // 自动选择最佳格式
RGBA8, // 32位 RGBA
RGB8, // 24位 RGB
DXT1, // BC1/DXT1 压缩1 bit alpha
DXT5, // BC3/DXT5 压缩(完整 alpha
ETC2, // ETC2 压缩(移动平台)
ASTC4x4, // ASTC 4x4 压缩(高质量)
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>)>;
2026-02-11 19:40:26 +08:00
class ResourceManager {
public:
// ------------------------------------------------------------------------
// 单例访问
// ------------------------------------------------------------------------
static ResourceManager &getInstance();
// ------------------------------------------------------------------------
// 纹理资源 - 同步加载
2026-02-11 19:40:26 +08:00
// ------------------------------------------------------------------------
/// 加载纹理(带缓存)
Ptr<Texture> loadTexture(const std::string &filepath);
/// 加载纹理(指定是否异步)
Ptr<Texture> loadTexture(const std::string &filepath, bool async);
/// 加载纹理(完整参数:异步 + 压缩格式)
Ptr<Texture> loadTexture(const std::string &filepath, bool async,
TextureFormat format);
/// 异步加载纹理(带回调)
void loadTextureAsync(const std::string &filepath,
TextureLoadCallback callback);
/// 异步加载纹理(指定格式 + 回调)
void loadTextureAsync(const std::string &filepath, TextureFormat format,
TextureLoadCallback callback);
2026-02-11 19:40:26 +08:00
/// 加载纹理并生成Alpha遮罩用于不规则形状图片
Ptr<Texture> loadTextureWithAlphaMask(const std::string &filepath);
/// 通过key获取已缓存的纹理
Ptr<Texture> getTexture(const std::string &key) const;
/// 检查纹理是否已缓存
bool hasTexture(const std::string &key) const;
/// 卸载指定纹理
void unloadTexture(const std::string &key);
// ------------------------------------------------------------------------
// Alpha遮罩资源
// ------------------------------------------------------------------------
/// 获取纹理的Alpha遮罩如果已生成
const AlphaMask *getAlphaMask(const std::string &textureKey) const;
/// 为已加载的纹理生成Alpha遮罩
bool generateAlphaMask(const std::string &textureKey);
/// 检查纹理是否有Alpha遮罩
bool hasAlphaMask(const std::string &textureKey) const;
// ------------------------------------------------------------------------
// 字体图集资源
// ------------------------------------------------------------------------
/// 加载字体图集(带缓存)
Ptr<FontAtlas> loadFont(const std::string &filepath, int fontSize,
bool useSDF = false);
/// 通过key获取已缓存的字体图集
Ptr<FontAtlas> getFont(const std::string &key) const;
/// 检查字体是否已缓存
bool hasFont(const std::string &key) const;
/// 卸载指定字体
void unloadFont(const std::string &key);
// ------------------------------------------------------------------------
// 音效资源
// ------------------------------------------------------------------------
/// 加载音效(带缓存)
Ptr<Sound> loadSound(const std::string &filepath);
Ptr<Sound> loadSound(const std::string &name, const std::string &filepath);
/// 通过key获取已缓存的音效
Ptr<Sound> getSound(const std::string &key) const;
/// 检查音效是否已缓存
bool hasSound(const std::string &key) const;
/// 卸载指定音效
void unloadSound(const std::string &key);
// ------------------------------------------------------------------------
// 文本文件资源
// ------------------------------------------------------------------------
/// 加载文本文件(带缓存)
/// @param filepath 文件路径,支持 romfs:/ 前缀
/// @return 文件内容字符串,加载失败返回空字符串
std::string loadTextFile(const std::string &filepath);
/// 加载文本文件(指定编码)
/// @param filepath 文件路径
/// @param encoding 文件编码(默认 UTF-8
/// @return 文件内容字符串
std::string loadTextFile(const std::string &filepath,
const std::string &encoding);
/// 通过key获取已缓存的文本内容
std::string getTextFile(const std::string &key) const;
/// 检查文本文件是否已缓存
bool hasTextFile(const std::string &key) const;
/// 卸载指定文本文件
void unloadTextFile(const std::string &key);
/// 清理所有文本文件缓存
void clearTextFileCache();
// ------------------------------------------------------------------------
// JSON 文件资源
// ------------------------------------------------------------------------
/// 加载并解析 JSON 文件
/// @param filepath 文件路径,支持 romfs:/ 前缀
/// @return JSON 字符串内容,加载或解析失败返回空字符串
/// @note 返回的是原始 JSON 字符串,需要自行解析
std::string loadJsonFile(const std::string &filepath);
/// 通过key获取已缓存的 JSON 内容
std::string getJsonFile(const std::string &key) const;
/// 检查 JSON 文件是否已缓存
bool hasJsonFile(const std::string &key) const;
/// 卸载指定 JSON 文件
void unloadJsonFile(const std::string &key);
/// 清理所有 JSON 文件缓存
void clearJsonFileCache();
2026-02-11 19:40:26 +08:00
// ------------------------------------------------------------------------
// 缓存清理
// ------------------------------------------------------------------------
/// 清理所有失效的弱引用(自动清理已释放的资源)
void purgeUnused();
/// 清理指定类型的所有缓存
void clearTextureCache();
void clearFontCache();
void clearSoundCache();
/// 清理所有资源缓存
void clearAllCaches();
/// 获取各类资源的缓存数量
size_t getTextureCacheSize() const;
size_t getFontCacheSize() const;
size_t getSoundCacheSize() const;
size_t getTextFileCacheSize() const;
size_t getJsonFileCacheSize() const;
2026-02-11 19:40:26 +08:00
// ------------------------------------------------------------------------
// 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);
// ------------------------------------------------------------------------
// 异步加载控制
// ------------------------------------------------------------------------
/// 初始化异步加载系统(可选,自动在首次异步加载时初始化)
void initAsyncLoader();
/// 关闭异步加载系统
void shutdownAsyncLoader();
/// 等待所有异步加载完成
void waitForAsyncLoads();
/// 检查是否有正在进行的异步加载
bool hasPendingAsyncLoads() const;
2026-02-11 19:40:26 +08:00
ResourceManager();
~ResourceManager();
ResourceManager(const ResourceManager &) = delete;
ResourceManager &operator=(const ResourceManager &) = delete;
private:
2026-02-11 19:40:26 +08:00
// 生成字体缓存key
std::string makeFontKey(const std::string &filepath, int fontSize,
bool useSDF) const;
// 内部加载实现
Ptr<Texture> loadTextureInternal(const std::string &filepath,
TextureFormat format);
// 选择最佳纹理格式
TextureFormat selectBestFormat(TextureFormat requested) const;
// 压缩纹理数据
std::vector<uint8_t> compressTexture(const uint8_t *data, int width,
int height, int channels,
TextureFormat format);
2026-02-11 19:40:26 +08:00
// 互斥锁保护缓存
mutable std::mutex textureMutex_;
2026-02-11 19:40:26 +08:00
mutable std::mutex fontMutex_;
mutable std::mutex soundMutex_;
mutable std::mutex textFileMutex_;
mutable std::mutex jsonFileMutex_;
2026-02-11 19:40:26 +08:00
// 资源缓存 - 使用弱指针实现自动清理
std::unordered_map<std::string, WeakPtr<FontAtlas>> fontCache_;
std::unordered_map<std::string, WeakPtr<Sound>> soundCache_;
// 文本文件缓存 - 使用强引用(字符串值类型)
std::unordered_map<std::string, std::string> textFileCache_;
std::unordered_map<std::string, std::string> jsonFileCache_;
// ============================================================================
// 纹理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;
2026-02-11 19:40:26 +08:00
};
} // namespace extra2d