정점 정보나 텍스쳐 같이 여러 곳에서 사용되는 자원들이 있습니다. 이런 자원들은 생성하고 나서 언제 메모리를 해제 해줘야 할지 가늠하기가 어렵습니다. A라는 텍스쳐를 B와 C가 사용하고 있는데, B가 소멸하면서 A 텍스쳐를 같이 소멸시키면 같은 A를 들고 있던 C는 심각한 위험에 빠져들게 됩니다. 어디선가 A 텍스쳐를 사용하고 있다면 그 텍스쳐는 모든 사용자가 소멸되기전까지는 소멸되지 말아야 합니다. 이 요구 조건을 충족시키기 위해 스마트 포인터를 이용한 자원 관리자를 사용합니다.
// 리소스 인터페이스 클래스 // 스마트 포인터 사용을 위해 참조 카운트 클래스를 상속 받음 class DLL_API ev_IResource : public ev_CRef { public: ev_IResource( ev_IResourceManager* pMyManager, eResourceGroup eGroup ); virtual ~ev_IResource(); public: virtual HRESULT LoadImpl( void ) = 0; public: HRESULT Load( const wstring& strFileName ); public: inline const wstring& GetFileName( void ); inline const wstring& GetResourceName( void ); inline eLoadState GetLoadState( void ); inline eResourceGroup GetGroup( void ); private: wstring m_strFileName; wstring m_strResName; eLoadState m_eLoadState; eResourceGroup m_eResourceGroup; ev_IResourceManager* m_pMyManager; }; // 스마트 포인터 타입 정의 typedef ev_CSmartPointer< ev_IResource > ev_IResourcePtr; // 리소스 매니저 인터페이스 클래스 class DLL_API ev_IResourceManager { public: ev_IResourceManager( const wstring& strName ); virtual ~ev_IResourceManager( void ); public: virtual ev_IResourcePtr CreateResourceImpl( const wstring& strFileName ) = 0; public: ev_IResourcePtr CreateResource( const wstring& strFileName ); void AddResource( ev_IResource* pRes, bool bCheckDupl = true ); ev_IResourcePtr GetResource( const wstring& strResFileName ); void RemoveResouce( ev_IResource* pRes ); private: wstring m_strManagerName; ResourceList_HashMap m_pResourceList_HashMap; }; // 리소스 생성 함수 ev_IResourcePtr ev_IResourceManager::CreateResource( const wstring& strFileName ) { ev_IResourcePtr spRes = GetResource( strFileName ); if( spRes ) return spRes; spRes = CreateResourceImpl( strFileName ); if( NULL == spRes ) return NULL; AddResource( spRes, false ); return spRes; }
모든 리소스는 리소스 매니저에 의해 관리 됩니다. 메시, 텍스쳐등 각각의 매니져가 존재하며 각 리소스와 매니저들은 위의 인터페이스 클래스를 상속 받아 구현됩니다. 리소스 매니져에 의해 생성된 리소스는 리소스 매니저의 리스트에 보관되며, 외부에서 특정 리소스를 요구 할시에 이미 생성된 리소스가 있는지 검사 후, 해당 리소스 포인터를 반환하게 됩니다.
다음은 메시와 메시 매니저의 경우의 예입니다.
// 메시 클래스 class DLL_API ev_CMesh : public ev_IResource { evDeclBoostPool; public: ev_CMesh( ev_IResourceManager* pMyManager, eResourceGroup eGroup ); virtual ~ev_CMesh(); public: virtual HRESULT LoadImpl( void ); private: ev_IVertexBuffer* m_pVertexBuffer; ev_IIndexBuffer* m_pIndexBuffer; }; // 메시 매니저 클래스 ( 싱글톤 ) class DLL_API ev_CMeshManager : public ev_IResourceManager, public ev_TSingleton< ev_CMeshManager > { evDeclSingleton( ev_CMeshManager ); public: ev_CMeshManager( void ); virtual ~ev_CMeshManager( void ); public: virtual ev_IResourcePtr CreateResourceImpl( const wstring& strFileName ); }; // 메시 생성 ev_IResourcePtr ev_CMeshManager::CreateResourceImpl( const wstring& strFileName ) { ev_IResourcePtr spMesh = new ev_CMesh( this, RES_GROUP_MESH ); if( S_FALSE == spMesh->Load( strFileName ) ) { evWriteLogErr( L"ev_CMeshManager::CreateResourceImpl - Failed to Create Mesh [ %s ]", strFileName.c_str() ); return NULL; } return spMesh; }
매니져들은 싱글톤 패턴을 활용해 하나의 객체만 생성됨을 보장합니다. 실제 구현부는 보시는 바와 같이 매우 단순합니다.
다음은 리소스 소멸에 관한 부분입니다. 스마트 포인터를 이용해 리소스를 사용하기 때문에 위에서 예를 들었던 상황을 통해 보자면, A를 사용하는 B와 C의 객체가 소멸하면서 자동으로 A의 참조 카운트가 0이 됩니다. A는 알아서 자신을 소멸시키게 됩니다. 이 때, 자신을 생성한 매니저에게 자신이 소멸됨을 통보하도록 합니다.
// 리소스 소멸시 매니져에 소멸됨을 알림 ev_IResource::~ev_IResource() { assert( m_pMyManager ); m_pMyManager->RemoveResouce( this ); } // 리소스 리스트에서 해당 리소스 삭제 void ev_IResourceManager::RemoveResouce( ev_IResource* pRes ) { wstring strResFileName = pRes->GetFileName(); ResourceList_HashMap_IT it = m_pResourceList_HashMap.find( strResFileName ); if( m_pResourceList_HashMap.end() != it ) { m_pResourceList_HashMap.erase( it ); return; } evWriteLogWar( L"%s::RemoveResouce - [%s] Failed to Find Resource!!", m_strManagerName.c_str(), strResFileName.c_str() ); }
짤방으로 마무리