ฉันตัดสินใจว่าฉันต้องการเขียนคลาส ResourceManager / ResourceCache สำหรับเอนจิ้นเกมอดิเรกของฉัน แต่ฉันมีปัญหาในการออกแบบรูปแบบแคช
แนวคิดก็คือ ResourceManager มีเป้าหมายนุ่มนวลสำหรับหน่วยความจำทั้งหมดที่ใช้โดยทรัพยากรทั้งหมดของเกมรวมกัน คลาสอื่น ๆ จะสร้างออบเจ็กต์ทรัพยากรซึ่งจะอยู่ในสถานะไม่โหลดและส่งไปยัง ResourceManager จากนั้น ResourceManager จะตัดสินใจเมื่อใดที่จะโหลด / ยกเลิกการโหลดทรัพยากรที่กำหนดโดยคำนึงถึงขีด จำกัด ซอฟต์
เมื่อต้องการทรัพยากรโดยชั้นเรียนอื่นคำขอจะถูกส่งไปยัง ResourceManager สำหรับมัน (โดยใช้ id ของสตริงหรือตัวระบุที่ไม่ซ้ำกัน) หากโหลดทรัพยากรแล้วการอ้างอิงแบบอ่านอย่างเดียวไปยังทรัพยากรจะถูกส่งผ่านไปยังฟังก์ชั่นการโทร หากไม่ได้โหลดทรัพยากรผู้จัดการจะทำเครื่องหมายวัตถุที่จะโหลดในโอกาสถัดไป (โดยปกติจะอยู่ที่ส่วนท้ายของการวาดเฟรม)
โปรดทราบว่าแม้ว่าระบบของฉันจะทำการนับการอ้างอิงบางอย่างจะนับเฉพาะเมื่อมีการอ่านทรัพยากร (ดังนั้นจำนวนการอ้างอิงอาจเป็น 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();
};
ปัญหาคือเพื่อให้การใช้งานข้อมูลโดยรวมมีการวนรอบ / ต่ำกว่าขีด จำกัด ซอฟต์แวร์ผู้จัดการจะต้องมีวิธีที่ชาญฉลาดในการกำหนดว่าวัตถุใดที่จะทำการขนถ่าย
ฉันกำลังคิดที่จะใช้ระบบการจัดลำดับความสำคัญบางอย่าง (เช่นลำดับความสำคัญชั่วคราวลำดับความสำคัญที่ใช้บ่อยลำดับความสำคัญถาวร) รวมกับเวลาของการเพิกถอนครั้งสุดท้ายและขนาดของทรัพยากรเพื่อกำหนดว่าจะลบเมื่อใด แต่ฉันไม่สามารถนึกถึงรูปแบบที่เหมาะสมที่จะใช้หรือโครงสร้างข้อมูลที่ถูกต้องซึ่งจำเป็นในการจัดการอย่างรวดเร็ว
คนที่ใช้ระบบเช่นนี้สามารถให้ภาพรวมของการทำงานของพวกเขาได้ไหม มีรูปแบบการออกแบบที่ชัดเจนที่ฉันพลาดไปหรือไม่? ฉันทำสิ่งนี้ซับซ้อนเกินไปหรือไม่ เป็นการดีที่ฉันต้องการระบบที่มีประสิทธิภาพและยากที่จะละเมิด ความคิดใด ๆ