264 lines
6.3 KiB
C++
264 lines
6.3 KiB
C++
#pragma once
|
||
|
||
#include <extra2d/asset/asset.h>
|
||
#include <extra2d/asset/asset_handle.h>
|
||
#include <extra2d/asset/asset_types.h>
|
||
#include <extra2d/core/types.h>
|
||
#include <algorithm>
|
||
#include <chrono>
|
||
#include <cstdint>
|
||
#include <deque>
|
||
#include <list>
|
||
#include <memory>
|
||
#include <mutex>
|
||
#include <shared_mutex>
|
||
#include <unordered_map>
|
||
#include <vector>
|
||
|
||
namespace extra2d {
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// CacheEntry - 缓存条目
|
||
// ---------------------------------------------------------------------------
|
||
|
||
/**
|
||
* @brief 缓存条目结构
|
||
*
|
||
* 存储资源引用和访问信息,用于LRU缓存管理。
|
||
*/
|
||
struct CacheEntry {
|
||
Ref<Asset> asset;
|
||
std::chrono::steady_clock::time_point lastAccess;
|
||
size_t accessCount = 0;
|
||
|
||
/**
|
||
* @brief 构造缓存条目
|
||
* @param a 资源引用
|
||
*/
|
||
explicit CacheEntry(Ref<Asset> a)
|
||
: asset(std::move(a)),
|
||
lastAccess(std::chrono::steady_clock::now()),
|
||
accessCount(1) {}
|
||
|
||
/**
|
||
* @brief 更新访问信息
|
||
*/
|
||
void touch() {
|
||
lastAccess = std::chrono::steady_clock::now();
|
||
++accessCount;
|
||
}
|
||
};
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// AssetCache - 资源缓存
|
||
// ---------------------------------------------------------------------------
|
||
|
||
/**
|
||
* @brief 资源缓存类
|
||
*
|
||
* 实现享元模式,提供资源共享和缓存管理功能。
|
||
*
|
||
* 特性:
|
||
* - LRU缓存淘汰策略
|
||
* - 线程安全(读写锁)
|
||
* - 引用计数自动回收
|
||
* - 缓存统计和监控
|
||
* - 内存上限管理
|
||
*/
|
||
class AssetCache {
|
||
public:
|
||
/**
|
||
* @brief 构造函数
|
||
* @param limit 缓存内存上限(字节),0表示无限制
|
||
*/
|
||
explicit AssetCache(size_t limit = 0);
|
||
|
||
~AssetCache() = default;
|
||
|
||
AssetCache(const AssetCache&) = delete;
|
||
AssetCache& operator=(const AssetCache&) = delete;
|
||
|
||
// -------------------------------------------------------------------------
|
||
// 资源管理
|
||
// -------------------------------------------------------------------------
|
||
|
||
/**
|
||
* @brief 添加资源到缓存
|
||
* @tparam T 资源类型
|
||
* @param asset 资源引用
|
||
* @return 资源句柄
|
||
*/
|
||
template<typename T>
|
||
AssetHandle<T> add(Ref<T> asset) {
|
||
static_assert(std::is_base_of_v<Asset, T>,
|
||
"T must derive from Asset");
|
||
|
||
if (!asset) {
|
||
return AssetHandle<T>();
|
||
}
|
||
|
||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||
|
||
AssetID id = asset->id();
|
||
|
||
size_t memSize = asset->memSize();
|
||
bytes_ += memSize;
|
||
|
||
auto it = lruList_.insert(lruList_.end(), id);
|
||
|
||
entries_[id] = CacheEntryData{
|
||
ptr::makeUnique<CacheEntry>(std::static_pointer_cast<Asset>(asset)),
|
||
it
|
||
};
|
||
|
||
++stats_.count;
|
||
|
||
if (limit_ > 0 && bytes_ > limit_) {
|
||
evict();
|
||
}
|
||
|
||
return AssetHandle<T>(id, Weak<T>(asset));
|
||
}
|
||
|
||
/**
|
||
* @brief 从缓存获取资源
|
||
* @tparam T 资源类型
|
||
* @param id 资源ID
|
||
* @return 资源句柄
|
||
*/
|
||
template<typename T>
|
||
AssetHandle<T> get(const AssetID& id) {
|
||
static_assert(std::is_base_of_v<Asset, T>,
|
||
"T must derive from Asset");
|
||
|
||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||
|
||
auto it = entries_.find(id);
|
||
if (it == entries_.end()) {
|
||
++stats_.misses;
|
||
return AssetHandle<T>();
|
||
}
|
||
|
||
it->second.entry->touch();
|
||
|
||
lruList_.erase(it->second.lruIterator);
|
||
it->second.lruIterator = lruList_.insert(lruList_.end(), id);
|
||
|
||
++stats_.hits;
|
||
|
||
auto typedAsset = std::static_pointer_cast<T>(it->second.entry->asset);
|
||
return AssetHandle<T>(id, Weak<T>(typedAsset));
|
||
}
|
||
|
||
/**
|
||
* @brief 检查缓存是否包含资源
|
||
* @param id 资源ID
|
||
* @return 包含返回 true
|
||
*/
|
||
bool has(const AssetID& id) const;
|
||
|
||
/**
|
||
* @brief 从缓存移除资源
|
||
* @param id 资源ID
|
||
* @return 移除成功返回 true
|
||
*/
|
||
bool remove(const AssetID& id);
|
||
|
||
// -------------------------------------------------------------------------
|
||
// 缓存管理
|
||
// -------------------------------------------------------------------------
|
||
|
||
/**
|
||
* @brief 设置缓存内存上限
|
||
* @param limit 上限(字节),0表示无限制
|
||
*/
|
||
void setLimit(size_t limit);
|
||
|
||
/**
|
||
* @brief 获取缓存内存上限
|
||
* @return 上限(字节)
|
||
*/
|
||
size_t limit() const { return limit_; }
|
||
|
||
/**
|
||
* @brief 获取当前缓存内存使用量
|
||
* @return 使用量(字节)
|
||
*/
|
||
size_t bytes() const { return bytes_; }
|
||
|
||
/**
|
||
* @brief 获取缓存条目数量
|
||
* @return 条目数量
|
||
*/
|
||
size_t count() const;
|
||
|
||
/**
|
||
* @brief 获取当前缓存内存使用量(别名)
|
||
* @return 使用量(字节)
|
||
*/
|
||
size_t size() const { return bytes_; }
|
||
|
||
/**
|
||
* @brief 记录缓存命中
|
||
*/
|
||
void hit() { ++stats_.hits; }
|
||
|
||
/**
|
||
* @brief 记录缓存未命中
|
||
*/
|
||
void miss() { ++stats_.misses; }
|
||
|
||
/**
|
||
* @brief 清理无外部引用的资源
|
||
* @return 清理的资源数量
|
||
*/
|
||
size_t purge();
|
||
|
||
/**
|
||
* @brief 清空所有缓存
|
||
*/
|
||
void clear();
|
||
|
||
/**
|
||
* @brief 获取缓存统计信息
|
||
* @return 统计信息
|
||
*/
|
||
CacheStats stats() const;
|
||
|
||
/**
|
||
* @brief 重置统计信息
|
||
*/
|
||
void resetStats();
|
||
|
||
private:
|
||
/**
|
||
* @brief 缓存条目数据(包含LRU迭代器)
|
||
*/
|
||
struct CacheEntryData {
|
||
Ref<CacheEntry> entry;
|
||
std::list<AssetID>::iterator lruIterator;
|
||
};
|
||
|
||
/**
|
||
* @brief 淘汰资源(LRU策略)
|
||
*/
|
||
void evict();
|
||
|
||
/**
|
||
* @brief 检查资源是否可以被淘汰
|
||
* @param entry 缓存条目
|
||
* @return 可淘汰返回 true
|
||
*/
|
||
bool canEvict(const CacheEntry& entry) const;
|
||
|
||
mutable std::shared_mutex mutex_;
|
||
std::unordered_map<AssetID, CacheEntryData> entries_;
|
||
std::list<AssetID> lruList_;
|
||
|
||
size_t limit_ = 0;
|
||
size_t bytes_ = 0;
|
||
mutable CacheStats stats_;
|
||
};
|
||
|
||
}
|