趣味のゲームエンジン用に中央の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();
};
問題は、総データ使用量をソフト制限の下/下に維持するために、マネージャーがアンロードするオブジェクトを決定するスマートな方法を持たなければならないことです。
私は、ある種の優先度システム(たとえば、一時的な優先度、頻繁に使用される優先度、永続的な優先度)を使用して、最後の参照解除の時刻とリソースのサイズを組み合わせて、いつ削除するかを決定しようと考えています。しかし、適切なスキームを使用したり、それらを迅速に管理するために必要な適切なデータ構造を考えることはできません。
このようなシステムを実装した人が、その仕組みの概要を教えてくれませんか。見逃している明らかなデザインパターンはありますか?これをあまりにも複雑にしましたか?理想的には、効率的で使いにくいシステムが必要です。何か案は?