Hobi oyun motorum için merkezi bir ResourceManager / ResourceCache sınıfı yazmak istediğime karar verdim, ancak önbellek şemasını tasarlarken sorun yaşıyorum.
Fikir, ResourceManager'ın tüm oyun kaynakları tarafından kullanılan toplam bellek için yumuşak bir hedefi olması. Diğer sınıflar, yüksüz durumda olacak kaynak nesneleri oluşturur ve bunları ResourceManager'a iletir. Ardından, ResourceManager verilen sınırı ne zaman yükleyeceğini / boşaltacağına karar vererek yazılım sınırını göz önünde bulundurur.
Başka bir sınıf tarafından bir kaynağa ihtiyaç duyulduğunda, bunun için ResourceManager'a bir istek gönderilir (bir dize kimliği veya benzersiz bir tanımlayıcı kullanarak). Kaynak yüklüyse, kaynağa salt okunur bir başvuru çağıran işleve iletilir (başvurulan sayılan bir zayıf_ptr'e sarılır). Kaynak yüklenmemişse, yönetici yüklenecek nesneyi bir sonraki fırsatta işaretler (genellikle çerçevenin çiziminin sonunda).
Sistemim bazı referans sayımları yapsa da, yalnızca kaynak okunurken sayıldığını unutmayın (bu nedenle referans sayısı 0 olabilir, ancak bir varlık yine de kullanıcı kimliğini izliyor olabilir).
İlk kullanımdan önce yükleme için kaynakları işaretlemek de mümkündür. İşte kullandığım sınıfların bir taslağı:
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();
};
Sorun, toplam veri kullanımının geçici sınırın etrafında / altında kalmasını sağlamak için yöneticinin hangi nesneleri kaldıracağını belirlemenin akıllı bir yoluna sahip olması gerekecek.
Ne zaman kaldırılacağını belirlemek için bir tür öncelik sistemi (örn. Geçici Öncelik, Sık Kullanılan Öncelik, Kalıcı Öncelik), son dereference süresi ve kaynağın boyutu ile birlikte kullanmayı düşünüyorum. Ancak, kullanmak için iyi bir plan veya hızlı bir şekilde yönetmek için gerekli doğru veri yapılarını düşünemiyorum.
Böyle bir sistemi uygulayan biri, nasıl çalıştığına dair genel bir bakış verebilir mi? Kaçırdığım açık bir tasarım deseni var mı? Bunu çok karmaşık hale getirdim mi? İdeal olarak, etkili ve kötüye kullanımı zor bir sisteme ihtiyacım var. Herhangi bir fikir?