DirectX3D設備丟失(lost device)的處理(二)

一個Microsoft? Direct3D?能夠處於操做狀態或丟失狀態。操做狀態是設備的正常狀態,設備按預期運行並present全部渲染結果。當事件發生時,如全屏應用程序失去鍵盤輸入焦點,設備就轉變到丟失狀態,這會致使渲染沒法進行。丟失狀態表現爲全部渲染操做的悄然失敗,這意味着即便渲染操做失敗全部的渲染方法仍能夠返回成功碼。在這種狀況下,IDirect3DDevice9:resent返回錯誤碼D3DERR_DEVICELOST。算法

Direct3D有意沒有對可能致使設備丟失的全部狀況進行詳細說明。一些典型的例子包括窗口失去焦點,例如用戶按下了ALT+TAB或彈出了一個系統對話框。設備也會由於電源管理事件而丟失,或者另外一個應用程序進行全屏操做。另外,任何對IDirect3DDevice9::Reset調用的失敗會把設備置爲丟失狀態。緩存

注意    能夠保證全部繼承自IUnknown的方法在設備丟失後仍能正常工做。設備丟失後,每一個函數通常有三種可能:框架

調用失敗,返回值爲D3DERR_DEVICELOST – 這意味着應用程序必須發現設備已經丟失,從而知道一些事情沒有按照預期進行。 
悄然失敗,返回值爲S_OK或其它值 – 若函數調用悄然失敗,則應用程序通常沒法區分出「調用成功」或「悄然失敗」。 
函數返回一個返回值。 
對丟失的設備做出響應函數

設備在被重置後,應該從新建立資源(包括顯存資源)。若是設備丟失了,那麼應用程序應該查詢設備狀態,看是否能夠將之恢復回操做狀態。若是不行,那麼就等到設備能夠被恢復爲止。oop

若是設備能夠被恢復,那麼應用程序應該銷燬全部顯存資源和交換鏈,並準備恢復。而後,應用程序調用IDirect3DDevice9::Reset方法。Reset方法是當設備丟失時惟一有效的方法,而且是應用程序可用來把設備從丟失狀態恢復到操做狀態的惟一方法。除非應用程序釋放全部在D3DPOOL_DEFAULT中分配的資源,包括用IDirect3DDevice9::CreateRenderTarget和IDirect3DDevice9::CreateDepthSstencilSurface方法建立的資源,不然Reset將會失敗。性能

Direct3D中大部分被頻繁調用的方法不返回任何關於設備是否已丟失的信息。應用程序能夠繼續調用渲染方法,如IDirect3DDevice9:rawPrimitive,而不會收到設備丟失的通知。在Direct3D內部,這些操做被拋棄,直到設備被重置爲操做狀態爲止。ui

經過查詢IDirect3DDevice9::TestCooperativeLevel方法的返回值,應用程序能夠決定在遇到設備丟失時如何處理。指針

管理資源code


資源管理是將資源從系統內存提高到設備可訪問存儲器及從設備可訪問存儲器中拋棄的過程。Microsoft? Direct3D?運行庫有本身的基於最近最少使用(least-recently-used)優先級技術的管理算法。當Direct3D檢測到在一幀中——在IDirect3DDevice9::BeginScene和IDirect3DDevice9::EndScene調用之間——設備可訪問內存沒法同時存儲全部資源時,它就切換到最近最多使用(most-recently-used)優先級技術。對象

在建立時使用D3DPOOL_MANAGED標誌指定一個由系統管理的資源。由系統管理的資源在設備的丟失狀態和操做狀態間的轉換中持續存在。經過調用IDirect3DDevice9::Reset設備能夠被重置,而且這類資源能夠繼續正常運做而無需從新載入圖片。可是,若是設備必須被銷燬和重建,那麼全部用D3DPOOL_MANAGED建立的資源也必須被重建。

在建立時使用D3DPOOL_DEFAULT標誌指定把資源放在默認的池中。在默認的池中的資源在設備從丟失狀態到操做狀態的轉換過程當中不持續存在,這些資源必須在調用Reset以前釋放,而後重建。

更多有關設備的丟失狀態的信息,請參閱丟失的設備。

注意不是全部的類型和用途都支持資源管理。例如,用D3DUSAGE_RENDERTARGET標誌建立的對象不支持資源管理。另外,不建議對須要頻繁改變其內容的對象使用資源管理。例如,在某些硬件上對一個每幀都需改變的頂點緩存進行自動管理會嚴重下降性能。可是,對紋理資源來講這不是一個問題。


例子(摘自codesampler):
......

『當設備丟失以後』

  不論經過任何方式發生了設備丟失,全部的操做幾乎都會失效,只有Release()能夠用——其實D3D會保證有部分操做能夠成功,可是也僅僅是「能夠」成功而不是「必定」成功,因此你還不如認定丟失的時候全都會失敗比較好——以及IDirect3DDevice9::TestCooperativeLevel。所以在設備丟失以後,你應該中止整個遊戲循環,而經過反覆調用IDirect3DDevice9::TestCooperativeLevel判斷設備是否可用。

『IDirect3DDevice9::TestCooperativeLevel』

  這個方法檢測當前的設備狀態。返回值有四種:D3D_OK一切正常,D3DERR_DEVICELOST設備丟失,D3DERR_DEVICENOTRESET設備能夠Reset。另外還有D3D9新增的D3DERR_DRIVERINTERNALERROR,遇到這個你就完蛋了,基本不可能恢復了,終止程序吧。
  按照順序來說,若是遊戲在正常運行,D3D_OK會返回;若是發生了設備丟失而且在這個時候不能恢復,好比全屏幕模式的時候用戶切換到了Windows桌面,就會返回D3DERR_DEVICELOST;若是用戶又切換回了遊戲,設備能夠恢復了(還沒恢復呢!只是「能夠」恢復而已),就會返回D3DERR_DEVICENOTRESET。
  另外,IDirect3DDevice9::Present也會返回相似的值,不過你最好別期望這個,老老實實的用TestCooperativeLevel。由於Present在設備能夠恢復的時候仍是返回D3DERR_DEVICELOST(外一句:D3D10的時候TestCooperativeLevel就會徹底整合到Present裏面了,可喜可賀可喜可賀)

『處理設備丟失』

  看下面的僞代碼:

switch (IDirect3DDevice9::TestCooperativeLevel()){
  case D3D_OK:
    GameLoop();
    break;
  case D3DERR_DEVICELOST:
    break;
  case D3DERR_DEVICENOTRESET
    OnLostDevice();
    IDirect3DDevice9::Reset();
    OnResetDevice();
    break;
  default:
    QuitGame();
    break;
}

  GameLoop()就是你的遊戲運行的過程了。把這個switch寫在咱們遊戲框架的GameMain()部分,具體的位置能夠看任何一話附帶的源代碼。
  好像我一直沒有講IDirect3DDevice9::Reset的參數啊?由於只有一個參數,就是指向D3DPRESENT_PARAMS的指針。把你第一次建立設備時使用的D3DPRESENT_PARAMS結構保存起來,供Reset來用。
  OnLostDevice()就是Release掉全部D3DPOOL_DEFAULT的資源,OnResetDevice()就是Create*()恢復啦!你可能注意到ID3DXFont、ID3DXSprite等等都有同名的方法,就是在這個時候調用的。若是你沒有這麼作,也就是說還保留着任何D3DPOOL_DEFAULT的資源的話,IDirect3DDevice9::Reset就必定會失敗。
  另外在OnResetDevice裏面你還要從新進行SetRenderState、SetSamplerState等等,Reset以後這些東西也丟失了。實際上Reset和從新建立一次設備相似,所不一樣的是從新建立設備的話你須要連D3DPOOL_MANAGED的資源也Release掉。這個話題就不討論了。
  從代碼能夠看出來,D3DERR_DEVICELOST時程序什麼都沒作,只是在傻等。我認爲這是一個好習慣,由於實在不能保證在D3DERR_DEVICELOST時除了Release還能幹什麼,與其這樣還不如等設備能用了再說。

  實在懶得管資源的話,所有D3DPOOL_MANAGED好了。至於渲染對象?本身想辦法。

『人工製造「設備丟失」』

  「幹嗎還要製造設備丟失啊?」若是更改遊戲分辨率、色深、切換全屏幕及窗口狀態,進行這樣的操做也要經過Reset,一樣的,Reset以前也要釋放掉全部D3DPOOL_DEFAULT資源(其實嚴格來講,還有更多的資源也要釋放,不過在2D下基本不會建立這類資源,你就不用管了)而且調用ID3DXSprite::OnLostDevice之類的方法。這就是人工製造「設備丟失」了。實際上在這個過程設備並無真正的丟失,只是會有一段時間處於不可用的狀態,此時Reset還沒有返回,整個D3D設備就好像死了同樣。舉個例子,你切換桌面分辨率,會有那麼一段時間顯示器上什麼都不顯示,而後很快就正常了。和這個現象是同一個緣由。Reset成功後記得恢復資源。   你可能注意到這裏的Reset和上面的Reset不是一回事。的確是這樣,這裏是爲了重設狀態而不是恢復設備。所以更改分辨率、色深的Reset須要寫到switch外面,也就是別和它攪和的意思-_-bb。並且你只須要OnLostDevice -> Reset -> OnResetDevice。記住:正確的調用Reset不會形成設備丟失,這個概念別弄混了。

相關文章
相關標籤/搜索