D3D中设备丢失的处理
什么是设备丢失
D3D中设备(Device)有两种状态,⼀种是可操作状态,也就是正常状态,另⼀种是丢失状态(Lost),处于丢失状态的设备是不能进⾏渲染操作的。简单的说,设备丢失是只应⽤程序(Device)与显卡失去了联系,因此⽆法使⽤显存。这⾥的Device不是只硬件,⽽是我们在程序中创建的Device对象,可以理解为上下⽂环境。
什么情况会导致设备丢失
当某些事件发⽣时,设备会由正常状态转换到丢失状态。这些事件包括
程序在全屏状态下失去键盘焦点(全屏时按下Alt+Tab或Win+D键或Win+L键)
其他程序进⼊全屏状态
电源管理事件,⽐如屏保等
设备丢失的现象
渲染窗⼝内模型丢失
渲染窗⼝背景⾊变⿊或者⼲脆没有背景⾊
程序失去响应或者Crash
如何检测设备丢失
设备丢失以后,⼤多数渲染操作都只是silent failure,所以仍然会返回正确代码,但是Present函数会返回D3DERR_DEVICELOST,所以可以检测该函数的返回值,如果该函数失败,那么就重置设备。当然失败可能还有其他原因,不仅仅是设备丢失⽽已,这些情况我们都在ResetDevie函数⾥⾯⼀并处理。
// Present the back buffer contents to the display
HRESULT hr = g_pd3dDevice->Present(NULL, NULL, NULL, NULL);
// Render failed, try to reset device
if(FAILED(hr))
{
ResetDevice(d3dpp) ;
}
如何处理设备丢失
设备丢失以后,应该进⾏重置(Reset),可以使⽤Reset函数来重置设备,Reset函数是设备丢失以后唯⼀起作⽤的函数,也是唯⼀能将设备从丢失状态恢复到正常状态的函数。由于设备丢失后,设备与显卡就失去了联系,所以⼀切与显卡有关的资源也都⽆法再通过设备访问了,那么这些资源必须释放并重新创建,⼀般来说,以D3DPOOL_DEFAULT为参数创建的资源,都是在显卡中分配的内存,所以,在重置设备之前要保证所有D3DPOOL_DEFAULT类型的资源都必须释放掉,⽽且如果程序中使⽤了额外的SwapChain,那么也要重新创建之,否则Reset函数会失败。设备丢失的处理可以⼤致分成如下三个步骤
1. 使⽤TestCooperativeLevel函数检测当前设备状态,如果可以重置,则重置,并重建使⽤D3DPOOL_DEFAULT创建的资源及
SwapChain。
2. 如果设备仍然处于丢失状态,则等待⼀段时间
resized
3. 如果是其他状态,⽐如驱动内部错误,则告知⽤户(⽐如显⽰错误框)
处理设备丢失的代码如下
// Reset device
HRESULT ResetDevice(D3DPRESENT_PARAMETERS d3dpp)
{
// Check device state
HRESULT hr = g_pd3dDevice->TestCooperativeLevel() ;
// Device can be reset now
if (SUCCEEDED(hr) || hr == D3DERR_DEVICENOTRESET)
{
// Release resource allocated as D3DPOOL_DEFAULT
/
/ Reset device
HRESULT hr = g_pd3dDevice->Reset(&d3dpp) ;
if (SUCCEEDED(hr))
{
ResizeD3DScene(d3dpp.BackBufferWidth, d3dpp.BackBufferHeight) ;
}
else// Reset device failed, show error box
{
const WCHAR* errorString = DXGetErrorString(hr) ;
DXTRACE_ERR_MSGBOX(errorString, hr) ;
}
}
// Device is still in lost state, wait
elseif (hr == D3DERR_DEVICELOST)
{
Sleep(25) ;
}
else// Other error, Show error box
{
const WCHAR* errorString = DXGetErrorString(hr) ;
DXTRACE_ERR_MSGBOX(errorString, hr) ;
}
return hr ;
}
需要注意的是,在重置Device的过程中(即调⽤Reset函数时),仍然会发⽣错误,这时我们简单的输出⼀个错误框告知⽤户。由于重置设备后,窗⼝的⼤⼩可能改变,所以我们要调⽤ResizeD3DScene来调整投影窗⼝的纵横⽐,该函数如下
// Reset the scene by rebuild the viewing frustum
void ResizeD3DScene(int width, int height)
{
if (height ==0 ) // Prevent A Divide By Zero By
height =1; // Making Height Equal One
// Compute aspect ratio
float fAspectRatio = width / (FLOAT)height;
// Setup Projection matrix
g_Camera.SetProjParams( D3DX_PI/4, fAspectRatio, 1.0f, 1000.0f );
g_Camera.SetWindow( width, height );
}
验证⼀个3D程序是否正确处理了设备丢失
如果不能正确的处理设备丢失,则D3D程序经常会产⽣莫名其妙的现象,我们可以使⽤如下⽅法验证
1 在程序处于渲染状态时(窗⼝或者全屏态皆可)按下锁屏键(Win+L),然后返回程序,看看渲染内容是否丢失。
2 在程序处于全屏状态时,按下Win+D回到桌⾯,或者按下Atl+Tab切换到其他程序,然后在再到渲染程序,看看渲染内容是否丢失。
3 还有其他的,⼤家发挥想象⼒。。。
Vista及后续系统的情况
前⾯讨论的情况都是针对XP系统⽽⾔,如果你使⽤的是Window Vista及更⾼版本,那么情况则有所不同了,Vista使⽤的是WDDM
driver(Windows Device Driver Model),⽽XP使⽤的是XPDM (XP Driver Model),前⾯说了,设备丢失的情况很多,并没有⼀个完整的列表来描述这些情况,但是在Vista系统上则不然,设备丢失只有两种情况,⼀是硬件hanging,另⼀种是driver stopped。
如果是硬件挂起,那么可以通过ResetEx来重置设备,但是纹理内存会丢失。
如果是驱动停⽌了,那么所有IDirect9Ex 对象必须重新创建以便继续渲染。
在窗⼝模式下,如果渲染区域被其他窗⼝遮挡,或者全屏模式下,程序最⼩化,PresentEx函数都会返回
S_D3DPRESENTATIONOCCLUDED,全屏程序在收到WM_ACTIVATEAPP消息时会继续渲染。
在以前版本的DX中,当应⽤程序经历模式改变时,唯⼀的恢复办法就是reset device并重新创建所有显存资源及swap chain,但是在Vista上的DirectX,在模式改变后调⽤reset函数,纹理内存不会丢失,纹理渲染状态信息也不会丢失,这些资源都不必重新创建了。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论