我已经决定要为我的爱好游戏引擎编写一个中央ResourceManager / ResourceCache类,但是在设计缓存方案时遇到了麻烦。
这个想法是ResourceManager对所有游戏资源组合使用的总内存有一个软目标。其他类将创建处于卸载状态的资源对象,并将它们传递给ResourceManager。然后,ResourceManager决定何时加载/卸载给定的资源,同时牢记软限制。
当另一个类需要资源时,会将请求发送到ResourceManager(使用字符串ID或唯一标识符)。如果加载了资源,则对该资源的只读引用将传递到调用函数(包装在引用的计数的weak_ptr中)。如果未加载资源,则管理器将在下一次机会(通常在绘制框架结束时)标记要加载的对象。
请注意,尽管我的系统确实做了一些引用计数,但它仅在读取资源时才进行计数(因此,引用计数可能为0,但实体可能仍在跟踪其uid)。
还可以在首次使用之前就标记好要加载的资源。这是我正在使用的类的略图:
typedef unsigned int ResourceId;
// Resource is an abstract data type.
class Resource
{
Resource();
virtual ~Resource();
virtual bool load() = 0;
virtual bool unload() = 0;
virtual size_t getSize() = 0; // Used in determining how much memory is
// being used.
bool isLoaded();
bool isMarkedForUnloading();
bool isMarkedForReload();
void reference();
void dereference();
};
// This template class works as a weak_ptr, takes as a parameter a sub-class
// of Resource. Note it only hands give a const reference to the Resource, as
// it is read only.
template <class T>
class ResourceGuard
{
public:
ResourceGuard(T *_resource): resource(_resource)
{
resource->reference();
}
virtual ~ResourceGuard() { resource->dereference();}
const T* operator*() const { return (resource); }
};
class ResourceManager
{
// Assume constructor / destructor stuff
public:
// Returns true if resource loaded successfully, or was already loaded.
bool loadResource(ResourceId uid);
// Returns true if the resource could be reloaded,(if it is being read
// it can't be reloaded until later).
bool reloadResource(ResourceId uid)
// Returns true if the resource could be unloaded,(if it is being read
// it can't be unloaded until later)
bool unloadResource(ResourceId uid);
// Add a resource, with it's named identifier.
ResourceId addResource(const char * name,Resource *resource);
// Get the uid of a resource. Returns 0 if it doesn't exist.
ResourceId getResourceId(const char * name);
// This is the call most likely to be used when a level is running,
// load/reload/unload might get called during level transitions.
template <class T>
ResourceGuard<T> &getResource(ResourceId resourceId)
{
// Calls a private method, pretend it exits
T *temp = dynamic_cast<T*> (_getResource(resourceId));
assert(temp != NULL);
return (ResourceGuard<T>(temp));
}
// Generally, this will automatically load/unload data, and is called
// once per frame. It's also where the caching scheme comes into play.
void update();
};
问题是,要使总数据使用量徘徊在软限制之内/之下,管理器将不得不采用一种明智的方式来确定要卸载的对象。
我正在考虑使用某种优先级系统(例如临时优先级,常用优先级,永久优先级),结合上一次取消引用的时间和资源的大小来确定何时删除它。但是我想不出要使用的像样的方案,也没有想到快速管理它们所需的正确数据结构。
实施了这种系统的人能否概述一下他们的工作方式。有没有我遗漏的明显设计模式?我把这个变得太复杂了吗?理想情况下,我需要一个高效且不易滥用的系统。有任何想法吗?