He decidido que quiero escribir una clase central de ResourceManager / ResourceCache para mi motor de juego de hobby, pero tengo problemas para diseñar un esquema de almacenamiento en caché.
La idea es que ResourceManager tenga un objetivo flexible para la memoria total utilizada por todos los recursos combinados del juego. Otras clases crearán objetos de recursos, que estarán en un estado descargado, y los pasarán al ResourceManager. El ResourceManager luego decide cuándo cargar / descargar los recursos dados, teniendo en cuenta el límite flexible.
Cuando otra clase necesita un recurso, se envía una solicitud al ResourceManager para ello (ya sea utilizando una identificación de cadena o un identificador único). Si se carga el recurso, se pasa una referencia de solo lectura al recurso a la función que realiza la llamada (envuelta en un punto débil contado al que se hace referencia). Si el recurso no está cargado, el administrador marcará el objeto que se cargará en la próxima oportunidad (generalmente al final del dibujo del marco).
Tenga en cuenta que, aunque mi sistema hace un recuento de referencias, solo cuenta cuando se lee el recurso (por lo tanto, el recuento de referencia puede ser 0, pero una entidad aún puede estar haciendo un seguimiento de su uid).
También es posible marcar recursos para cargar con mucha anticipación al primer uso. Aquí hay un boceto de las clases que estoy usando:
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();
};
El problema es que, para mantener el uso total de datos rondando / por debajo del límite flexible, el administrador tendrá que tener una forma inteligente de determinar qué objetos descargar.
Estoy pensando en usar algún tipo de sistema de prioridad (por ejemplo, Prioridad temporal, Prioridad de uso frecuente, Prioridad permanente), combinado con el tiempo de la última desreferencia y el tamaño del recurso, para determinar cuándo eliminarlo. Pero no puedo pensar en un esquema decente para usar, o las estructuras de datos correctas requeridas para administrarlos rápidamente.
¿Podría alguien que ha implementado un sistema como este dar una visión general de cómo ha funcionado? ¿Hay un patrón de diseño obvio que me estoy perdiendo? ¿He hecho esto demasiado complicado? Idealmente, necesito un sistema eficiente y difícil de abusar. ¿Algunas ideas?