ResourceManager 클래스 디자인


17

취미 게임 엔진을위한 중앙 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();

};

문제는 총 데이터 사용량이 소프트 한계 주위로 / 이동하도록 유지하려면 관리자가 언로드 할 객체를 현명하게 결정해야한다는 것입니다.

마지막 역 참조 시간 및 리소스 크기와 결합 된 일종의 우선 순위 시스템 (예 : 임시 우선 순위, 자주 사용되는 우선 순위, 영구 우선 순위)을 사용하여 제거시기를 결정하려고합니다. 그러나 나는 적절한 체계를 사용하거나 신속하게 관리하는 데 필요한 올바른 데이터 구조를 생각할 수 없습니다.

이와 같은 시스템을 구현 한 사람이 자신의 작동 방식에 대한 개요를 제공 할 수 있습니까? 내가 빠진 명백한 디자인 패턴이 있습니까? 이것을 너무 복잡하게 했습니까? 이상적으로는 효율적이고 남용하기 어려운 시스템이 필요합니다. 어떤 아이디어?


4
명백한 질문은 "구현하기 위해 설정 한 기능이 필요합니까?"입니다. 예를 들어 PC에서 작업하는 경우 메모리 소프트 캡 설정이 불필요 할 수 있습니다. 게임이 레벨로 나뉘어져 있고 레벨에서 어떤 자산을 사용할지 결정할 수 있다면 게임 시작시 모든 것을로드하고로드 / 언로드를 피하십시오.
Tetrad

답변:


8

이것이 귀하의 질문 100 %와 관련이 있는지 확실하지 않지만 몇 가지 조언 팁은 다음과 같습니다.

  1. 손잡이로 자원을 감싸십시오. 리소스는 설명 (일반적으로 XML)과 실제 데이터의 두 가지로 나뉩니다. 엔진은 게임 시작시 모든 리소스 설명을로드하고 이에 대한 모든 핸들을 만들어야합니다. 구성 요소가 리소스를 요청하면 핸들이 반환됩니다. 그렇게하면 함수가 정상적으로 진행될 수 있습니다 (여전히 크기 등을 요청할 수 있습니다). 리소스를 아직로드하지 않은 경우 어떻게해야합니까? 가져 오려고 시도했지만 아직로드되지 않은 모든 리소스를 바꾸는 데 사용되는 '널 리소스'를 만듭니다.

더 많은 것이 있습니다. 나는 최근에이 책 " Game Engine Design and Implementation "을 읽었 으며 리소스 관리자 클래스를 디자인하고 다루는 섹션이 아주 훌륭합니다.

ResourceHandle 및 Memory Budget 기능이 없으면이 책에서 권장하는 사항이 있습니다.

typedef enum
{
    RESOURCE_NULL = 0,
    RESOURCE_GRAPHIC = 1,
    RESOURCE_MOVIE = 2,
    RESOURCE_AUDIO = 3,
    RESOURCE_TEXT =4,
}RESOURCE_TYPE;


class Resource : public EngineObject
{
public:
    Resource() : _resourceID(0), _scope(0), _type(RESOURCE_NULL) {}
    virtual ~Resource() {}
    virtual void Load() = 0;
    virtual void Unload()= 0;

    void SetResourceID(UINT ID) { _resourceID = ID; }
    UINT GetResourceID() const { return _resourceID; }

    void SetFilename(std::string filename) { _filename = filename; }
    std::string GetFilename() const { return _filename; }

    void SetResourceType(RESOURCE_TYPE type) { _type = type; }
    RESOURCE_TYPE GetResourceType() const { return _type; }

    void SetResourceScope(UINT scope) { _scope = scope; }
    UINT GetResourceScope() const { return _scope; }

    bool IsLoaded() const { return _loaded; }
    void SetLoaded(bool value) { _loaded = value; }

protected:
    UINT _resourceID;
    UINT _scope;
    std::string _filename;
    RESOURCE_TYPE _type;
    bool _loaded;
private:
};

class ResourceManager : public Singleton<ResourceManager>, public EngineObject
{
public:
    ResourceManager() : _currentScope(0), _resourceCount(0) {};
    virtual ~ResourceManager();
    static ResourceManager& GetInstance() { return *_instance; }

    Resource * FindResourceByID(UINT ID);
    void Clear();
    bool LoadFromXMLFile(std::string filename);
    void SetCurrentScope(UINT scope);
    const UINT GetResourceCount() const { return _resourceCount; }
protected:
    UINT _currentScope;
    UINT _resourceCount; //Total number of resources unloaded and loaded
    std::map<UINT, std::list<Resource*> > _resources; //Map of form <scope, resource list>

private:
};

SetScope 기능은 ScopeLevel이 Scene #을 나타내는 Scene-Layered Engine Design을 나타냅니다. 장면이 입력 / 종료되면 해당 범위에 따른 모든 리소스가로드되고 전역 범위에없는 리소스가 언로드됩니다.


나는 NULL 객체 아이디어와 범위를 추적하는 아이디어를 정말 좋아합니다. 방금 '게임 엔진 디자인 및 구현'을 찾기 위해 학교 도서관 사냥을했지만 운이 없었습니다. 이 책은 메모리 예산을 어떻게 처리 할 것인지에 대해 자세히 설명합니까?
Darcy Rayner

몇 가지 간단한 메모리 관리 체계에 대해 자세히 설명합니다. 궁극적으로 기본적인 것조차도 일반 malloc보다 훨씬 뛰어나야합니다.
Setheron

나는 이것과 비슷한 디자인을 골랐다.
Darcy Rayner
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.