취미 게임 엔진을위한 중앙 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();
};
문제는 총 데이터 사용량이 소프트 한계 주위로 / 이동하도록 유지하려면 관리자가 언로드 할 객체를 현명하게 결정해야한다는 것입니다.
마지막 역 참조 시간 및 리소스 크기와 결합 된 일종의 우선 순위 시스템 (예 : 임시 우선 순위, 자주 사용되는 우선 순위, 영구 우선 순위)을 사용하여 제거시기를 결정하려고합니다. 그러나 나는 적절한 체계를 사용하거나 신속하게 관리하는 데 필요한 올바른 데이터 구조를 생각할 수 없습니다.
이와 같은 시스템을 구현 한 사람이 자신의 작동 방식에 대한 개요를 제공 할 수 있습니까? 내가 빠진 명백한 디자인 패턴이 있습니까? 이것을 너무 복잡하게 했습니까? 이상적으로는 효율적이고 남용하기 어려운 시스템이 필요합니다. 어떤 아이디어?