[펌] 디바이스 소실

아래 글은 http://www.gpgstudy.com/에서 가져온 글입니다.

< 디바이스가 소실되는경우 >

# 풀스크린 모드일때..
- 다른 애플리케이션이 활성화되는 경우 (ALT+TAB같은 키사용)

참고)
WM_ACTIVATEAPP메세지에서 wParam이 FALSE이면 해당 프로그램이 비활성화된다.

- 다른 애플리케이션이 풀스크린 모드로 생성될경우    (위와 같은 내용)

- 전력관리 이벤트(시스템 대기모드라든지 스크린 세이버)가 발생 할 경우

참고)
 이 경우 해당 메세지를 막아주면 됨

    case WM_SYSCOMMAND:
          // prevent screensaver or monitor powersave mode from starting
          if (wParam == SC_SCREENSAVE || wParam == SC_MONITORPOWER) return 1;

          break;

    case WM_POWERBROADCAST:
            switch( wParam )
            {
                #ifndef PBT_APMQUERYSUSPEND
                    #define PBT_APMQUERYSUSPEND 0x0000
                #endif
                case PBT_APMQUERYSUSPEND:
                    // At this point, the app should save any data for open
                    // network connections, files, etc., and prepare to go into
                    // a suspended mode.
                    return true;

                #ifndef PBT_APMRESUMESUSPEND
                    #define PBT_APMRESUMESUSPEND 0x0007
                #endif
                case PBT_APMRESUMESUSPEND:
                    // At this point, the app should recover any data, network
                    // connections, files, etc., and resume running from when
                    // the app was suspended.
                    return true;
            }
            break;


# 윈도우 모드일때..
- 전력관리 이벤트가 발생했을 경우

풀스크린모드와 같음

 - 다른 애플리케이션이 풀스크린 모드로 생성될경우..
    (이경우 테스트 해봐야 함..  Radeon 9000 128M에서는 디바이스가 소실되지 않음)


< 디바이스 소실될때 복구 여부 >
1. 디바이스 소실되었고 복구 불가능의 경우
: IDirect3DDevice를 통해 생성된 모든 리소스들과 IDirect3DDeivce객체 전부를 해제 후 처음부터 다시 생성한다.
    
2. 디바이스 소실되었고 복구 가능의 경우
: 이 경우에는 디바이스에 올라간 리소스(리소스 생성시 D3DPOOL_DEFAULT 플래그를 이용하여 생성된 객체들)들만 새로 로딩하면 된다.


< 디바이스의 소실 여부 >

IDirect3DDevice::Present의 리턴값으로 디바이스가 소실되었는지 알아 낼수 있다. 그러나 Present는 EndScene을 호출하고 나서 호출하는 경우가 대부분이므로 주의해서 코딩해야 한다. 다만 위에서 디바이스가 소실되는 경우를 적어 놓았듯이

예) 풀스크린모드시 WM_ACTIVATEAPP메세지에서 wParam이 FALSE되는 경우
예와 같은 경우는 항상 소실되므로 이때의 디바이스를 복구하는 것도 좋은 방법이 되겠다. CD3DApplication(Direct3D Wizzard에서 제공)의 방식을 보면 대체로 미리 소실이 예상되는 부분에서 처리를 하는 방식을 사용하고 있다.

따라서, 완벽한 프로그램을 만들려면 다음과 같이 해야 한다


1) Present 의 결과값 처리

2) 디바이스 소실이 예상되는 부분 대처


< 디바이스 소실 처리 >

디바이스가 소실 되었다면 복구 할 수 있는지 검사해 본다.

IDirect3DDevice::TestCooperativeLevel()을 호출하여, 반환값(HRESULT)이

1) D3DERR_DEVICELOST라면 : 디바이스 소실 & 복구 불가

       1.1) 모든 리소스들 릴리즈

       1.2) 디바이스들 릴리즈 (당연히 Vertex Shaders와 Pixel Shaders도..)

       1.3) 디바이스들 새로 생성

       1.4) 디바이스를 다시 셋팅한다.(랜더스테이트, 뷰 행렬등..)
       1.5) 리소스들을 다시 로드한다.



2) D3DERR_DEVICENOTRESET이라면 : 디바이스 소실 & 복구 가능 한 상태이다. 다만 이때 주의할 것이 복구 가능한 상태일지라도 IDirect3DDevice::Reset()를 호출했을 때 다시 에러가 난다면 1)와 같은 처리를 해주어야 한다.  

즉,

       2.1) D3DPOOL_DEFAULT로 잡은 리소스들(다른 옵션인 것은 안해도 된단다)을 전부 릴리즈한다
             (안그러면 다음에 호출될 IDirect3DDevice::Reset()이 실패할것이다)

       2.2) IDirect3DDevice::Reset()을 호출한다. 결과값 검사하여 에러나면 1)의 처리로..
             (2.1을 제대로 하면 Reset이 에러가 거의 안 나지만, 혹시나..)

       2.3) 디바이스를 다시 셋팅한다.(랜더스테이트, 뷰 행렬등..)

       2.4) D3DPOOL_DEFAULT로 잡은 리소스들을 다시 로드한다.


( 참고 : DX9.0에서는 Reset 할때 Vertex Shaders와 Pixel Shaders를 새로 만들 필요가 없다.  이전 버전에서는 새로 만들어야 했다고 한다..)


사실 2)번의 경우도 1)번의 경우처럼 처리해 버려도 상관은 없으나 1)번과 같은 복구는 리소스가 많으면 많을 수록 시간이 많이 소요되므로 피할 수 있다면 피하는 것이 최선일 것이다.


< 참조 코드 설계 >


//**** 디바이스 리소스 인터페이스..
// 디바이스 의존적인 리소스들의 추상클래스.
struct IDeviceRes
{
   virtual void Invalidate() = 0;
   virtual bool SetValidate() = 0;
};

//**** 텍스쳐 클래스
class CTextureRes : public IDeviceRes
{
public:
   virtual void Invalidate()      { 텍스쳐 해제; }
   virtual bool SetValidate()    { 텍스쳐 다시 로드; }
  
   bool    Load();
   void    Unload();
  
protected:
   char             m_szFileName[256];
   LPDIRECT3DTEXTURE9 m_pTexture;
};


//**** 텍스쳐를 관리할 클래스(클라이언트에서 접근하여 사용)

class CDataContainer
{
public:
   LoadTexture( const char* szFileName )
   {
      // 1. 텍스쳐 로딩
      // 2. 만약 텍스쳐가 VRam에 잡혔다면..(D3DPOOL_DEFAULT)
      //    2.1 랜더러의 의 VRam리스트에 등록(m_pRenderer->m_VRamRes)
      // 3. 텍스쳐가 다른모드로 생성되었다면..(D3DPOOL_MANAGED)
      //   3.1 랜더러의 의 VRam리스트에 등록(m_pRenderer->m_ManagedRes)
   }
  
   UnloadTexture( const char* szFileName )
   {
      // 랜더러의 디바이스 목록에서 지운다.
      m_pRenderer->Unregist( szFileName );
   }

protected:
   vector<CTextureRes*>    m_TextureContainer;
   CRenderer*          m_pRenderer;
}

//**** 문제의 랜더러 클래스
class CRenderer
{
public:
   // 디바이스가 해제 ,거나 해제 하라.
   // - 이 함수는 외부에서 임의로 호출이 가능하다.
   void InvalidateDevice( HRESULT hrDeviceState )
   {
      if ( FAILED( hrDeviceState )
      {
         if ( D3DERR_DEVICENOTRESET == hrDeviceState )
         {
            // 1. m_VRamRes 리소스만 해제한다.
         }
         else if ( D3DERR_DEVICELOST == hrDeviceState )
         {
            // 1. m_VRamRes과 m_ManagedRes들에 대해서 해제한다.(전 리소스들에 대해 수행)
            // 2. 디바이스를 릴리즈한다.
         }
      }
      m_hrDeviceState = hrDeviceState; // 디바이스 상태값 업데이트
   }
  
   // 디바이스가 활성화하라.
   // - 이 함수도 외부에서 임의로 호출이 가능하다.
   bool SetValidateDevice()
   {
      if ( FAILED(m_hrDeviceState) )
      {
         if ( D3DERR_DEVICENOTRESET == m_hrDeviceState )
         {
            //**** 디바이스를 복구할수 있다.
            // 1. IDrect3DDevice::Reset()한다.
            // 2. 랜더 스테이트 / 뷰행렬 셋팅
            // 3. m_VRamRes 리소스들을 다시 로딩한다.
         }
         else if ( D3DERR_DEVICELOST == hrDeviceState )
         {
            //**** m_VRamRes 복구할수 없다.
            // 1. 디바이스를 재생성한다.
            // 2. 랜더 스테이트 / 뷰행렬 셋팅
            // 3. m_VRamRes / m_ManagedRes 리소스를 로딩한다.
         }
        
         // 검증해본다.
         m_hrDeviceState = m_pDevice->TestCooperativeLevel();
         return SUCCEEDED(m_hrDeviceState);
      }
      return true;
   }

   // 랜더링한다.
   void RenderAll()
   {
      //**** 디바이스가 유효한 상태가 아니라면 그냥 리턴.
      if ( FAILED(m_hrDeviceState) ) return;
      
      // ...
      //**** 모든 그래픽들에 대한 랜더링을 한다.
      // ...
      
      //**** 백버퍼 플리핑.
      m_hrDeviceState = m_pDevice->Present( NULL, NULL, NULL, NULL );
      if ( FAILED(m_hrDeviceState) )
      {
         // 디바이스에 뭔가 이상이 있다면,,
         InvalidateDevice( m_hrDeviceState );
        
         // 외부에 알려준다. 아마 누군가에게 알려줄 필요가 있을껏이다.
         // CApp나 혹은 다른 누군가에게..
      }
   }
  
protected:
   //**** properties
   // 디바이스 의존적인 메모리 관리
   vector<IDeviceResource*>    m_VRamRes;    // POOL_DEFAULT
   vector<IDeviceResource*>    m_ManagedRes;   // MANAGED or etc
   HRESULT                m_hrDeviceState;       // 현재의 디바이스 상태값
  
   LPDIRECT3DDEVICE9         m_pDevice;
}
;

 

[펌]

http://www.gpgstudy.com

http://leechen.wzsoft.com