一个Direct3D设备, 有两种状态: 操作状态或丢失状态.操作状态: 是设备的正常状态, 设备按预期运行, 并且能present所有渲染效果
丢失状态: 所有渲染操作悄然失败, IDirect3DDevice9::present返回错误码D3DERR_DEVICELOST
可能导致设备丢失的情况有:
窗口丢失焦点, 用户按下了ALT+TAB, 弹出了一个系统对话框, 电源管理事件, 另一个应用程序进行全屏操作
IDirect3DDevice9::Reset调用失败时, 设备也会被设置为丢失状态
如何发现设备丢失:
IDirect3DDevice9::present返回错误码D3DERR_DEVICELOST或D3DERR_DRIVERINTERNALERROR(这种情况下基本上要exit了)
IDirect3DDevice9::TestCooperativeLevel返回错误码D3DERR_DEVICELOST或D3DERR_DRIVERINTERNALERROR(DX10整合到present中了)
发现设备丢失后:
1.查询设备状态, 看是否可以将之恢复到操作状态. 如果不行, 则必须要等到设备可以被恢复为止.
2.释放所有在D3DPOOL_DEFAULT中分配的资源, 包括用IDirect3DDevice9::CreateRenderTarget和IDirect3DDevice9::CreateDepthStencilSurface方法创建的资源. (不进行这一步, 下一步的Reset操作会失败)
3.调用IDirect3DDevice::Reset进行恢复. Reset方法是当设备丢失时, 唯一有效的方法, 并且是应用程序可用来把设备从丢失状态恢复到操作状态的唯一方法.
4.重建在步骤2中释放的资源, 同时还需要重新设置state等
管理资源:
1.资源管理是将资源从系统内存提升到设备可访问存储器及从设备可访问存储器中抛弃的过程
2.D3DPOOL_MANAGED标志指定一个由系统管理的资源。由系统管理的资源在设备的丢失状态和操作状态间的转换中持续存在。通过调用IDirect3DDevice9::Reset设备可以被重置,并且这类资源可以继续正常运作而无需重新载入图片。但是,如果设备必须被销毁和重建,那么所有用D3DPOOL_MANAGED创建的资源也必须被重建
3.D3DPOOL_DEFAULT标志指定把资源放在默认的池中。在默认的池中的资源在设备从丢失状态到操作状态的转换过程中不持续存在,这些资源必须在调用Reset之前释放,然后重建
4.不是所有的类型和用途都支持资源管理。例如,用D3DUSAGE_RENDERTARGET标志创建的对象不支持资源管理。另外,不建议对需要频繁改变其内容的对象使用资源管理。例如,在某些硬件上对一个每帧都需改变的顶点缓存进行自动管理会严重降低性能。但是,对纹理资源来说这不是一个问题
DXUT代码:
MainLoop
if( GetDXUTState().GetDeviceLost() && !GetDXUTState().GetRenderingPaused() ) { // Test the cooperative level to see if it's okay to render. if( FAILED( hr = pd3dDevice->TestCooperativeLevel() ) ) { if( D3DERR_DEVICELOST == hr ) { // The device has been lost but cannot be reset at this time. // So wait until it can be reset. return; } // If we are windowed, read the desktop format and // ensure that the Direct3D device is using the same format // since the user could have changed the desktop bitdepth if( DXUTIsWindowed() ) { D3DDISPLAYMODE adapterDesktopDisplayMode; IDirect3D9* pD3D = DXUTGetD3D9Object(); DXUTDeviceSettings* pDeviceSettings = GetDXUTState().GetCurrentDeviceSettings(); pD3D->GetAdapterDisplayMode( pDeviceSettings->d3d9.AdapterOrdinal, &adapterDesktopDisplayMode ); if( pDeviceSettings->d3d9.AdapterFormat != adapterDesktopDisplayMode.Format ) { DXUTMatchOptions matchOptions; matchOptions.eAPIVersion = DXUTMT_PRESERVE_INPUT; matchOptions.eAdapterOrdinal = DXUTMT_PRESERVE_INPUT; matchOptions.eDeviceType = DXUTMT_PRESERVE_INPUT; matchOptions.eWindowed = DXUTMT_PRESERVE_INPUT; matchOptions.eAdapterFormat = DXUTMT_PRESERVE_INPUT; matchOptions.eVertexProcessing = DXUTMT_CLOSEST_TO_INPUT; matchOptions.eResolution = DXUTMT_CLOSEST_TO_INPUT; matchOptions.eBackBufferFormat = DXUTMT_CLOSEST_TO_INPUT; matchOptions.eBackBufferCount = DXUTMT_CLOSEST_TO_INPUT; matchOptions.eMultiSample = DXUTMT_CLOSEST_TO_INPUT; matchOptions.eSwapEffect = DXUTMT_CLOSEST_TO_INPUT; matchOptions.eDepthFormat = DXUTMT_CLOSEST_TO_INPUT; matchOptions.eStencilFormat = DXUTMT_CLOSEST_TO_INPUT; matchOptions.ePresentFlags = DXUTMT_CLOSEST_TO_INPUT; matchOptions.eRefreshRate = DXUTMT_CLOSEST_TO_INPUT; matchOptions.ePresentInterval = DXUTMT_CLOSEST_TO_INPUT; DXUTDeviceSettings deviceSettings = DXUTGetDeviceSettings(); deviceSettings.d3d9.AdapterFormat = adapterDesktopDisplayMode.Format; hr = DXUTFindValidDeviceSettings( &deviceSettings, &deviceSettings, &matchOptions ); if( FAILED( hr ) ) // the call will fail if no valid devices were found { DXUTDisplayErrorMessage( DXUTERR_NOCOMPATIBLEDEVICES ); DXUTShutdown(); } // Change to a Direct3D device created from the new device settings. // If there is an existing device, then either reset or recreate the scene hr = DXUTChangeDevice( &deviceSettings, NULL, NULL, false, false ); if( FAILED( hr ) ) { // If this fails, try to go fullscreen and if this fails also shutdown. if( FAILED( DXUTToggleFullScreen() ) ) DXUTShutdown(); } return; } } // Try to reset the device if( FAILED( hr = DXUTReset3DEnvironment9() ) ) { if( D3DERR_DEVICELOST == hr ) { // The device was lost again, so continue waiting until it can be reset. return; } else if( DXUTERR_RESETTINGDEVICEOBJECTS == hr || DXUTERR_MEDIANOTFOUND == hr ) { DXUTDisplayErrorMessage( hr ); DXUTShutdown(); return; } else { // Reset failed, but the device wasn't lost so something bad happened, // so recreate the device to try to recover DXUTDeviceSettings* pDeviceSettings = GetDXUTState().GetCurrentDeviceSettings(); if( FAILED( DXUTChangeDevice( pDeviceSettings, NULL, NULL, true, false ) ) ) { DXUTShutdown(); return; } } } } GetDXUTState().SetDeviceLost( false ); } // Update the FPS stats DXUTUpdateFrameStats(); // Animate the scene by calling the app's frame move callback LPDXUTCALLBACKFRAMEMOVE pCallbackFrameMove = GetDXUTState().GetFrameMoveFunc(); if( pCallbackFrameMove != NULL ) { pCallbackFrameMove( fTime, fElapsedTime, GetDXUTState().GetFrameMoveFuncUserContext() ); pd3dDevice = DXUTGetD3D9Device(); if( NULL == pd3dDevice ) // Handle DXUTShutdown from inside callback return; } if( !GetDXUTState().GetRenderingPaused() ) { // Render the scene by calling the app's render callback LPDXUTCALLBACKD3D9FRAMERENDER pCallbackFrameRender = GetDXUTState().GetD3D9FrameRenderFunc(); if( pCallbackFrameRender != NULL ) { pCallbackFrameRender( pd3dDevice, fTime, fElapsedTime, GetDXUTState().GetD3D9FrameRenderFuncUserContext() ); pd3dDevice = DXUTGetD3D9Device(); if( NULL == pd3dDevice ) // Handle DXUTShutdown from inside callback return; } // Show the frame on the primary surface. hr = pd3dDevice->Present( NULL, NULL, NULL, NULL ); if( FAILED( hr ) ) { if( D3DERR_DEVICELOST == hr ) { GetDXUTState().SetDeviceLost( true ); } else if( D3DERR_DRIVERINTERNALERROR == hr ) { // When D3DERR_DRIVERINTERNALERROR is returned from Present(), // the application can do one of the following: // // - End, with the pop-up window saying that the application cannot continue // because of problems in the display adapter and that the user should // contact the adapter manufacturer. // // - Attempt to restart by calling IDirect3DDevice9::Reset, which is essentially the same // path as recovering from a lost device. If IDirect3DDevice9::Reset fails with // D3DERR_DRIVERINTERNALERROR, the application should end immediately with the message // that the user should contact the adapter manufacturer. // // The framework attempts the path of resetting the device // GetDXUTState().SetDeviceLost( true ); } } }
DXUTReset3Denvironment9
HRESULT DXUTReset3DEnvironment9() { HRESULT hr; IDirect3DDevice9* pd3dDevice = DXUTGetD3D9Device(); assert( pd3dDevice != NULL ); // Call the app's device lost callback if( GetDXUTState().GetDeviceObjectsReset() == true ) { GetDXUTState().SetInsideDeviceCallback( true ); LPDXUTCALLBACKD3D9DEVICELOST pCallbackDeviceLost = GetDXUTState().GetD3D9DeviceLostFunc(); if( pCallbackDeviceLost != NULL ) pCallbackDeviceLost( GetDXUTState().GetD3D9DeviceLostFuncUserContext() ); GetDXUTState().SetDeviceObjectsReset( false ); GetDXUTState().SetInsideDeviceCallback( false ); } // Reset the device DXUTDeviceSettings* pDeviceSettings = GetDXUTState().GetCurrentDeviceSettings(); assert( pDeviceSettings != NULL ); hr = pd3dDevice->Reset( &pDeviceSettings->d3d9.pp ); if( FAILED( hr ) ) { if( hr == D3DERR_DEVICELOST ) return D3DERR_DEVICELOST; // Reset could legitimately fail if the device is lost else return DXUT_ERR( L"Reset", DXUTERR_RESETTINGDEVICE ); } // Update back buffer desc before calling app's device callbacks DXUTUpdateBackBufferDesc(); // Setup cursor based on current settings (window/fullscreen mode, show cursor state, clip cursor state) DXUTSetupCursor(); // Call the app's OnDeviceReset callback GetDXUTState().SetInsideDeviceCallback( true ); const D3DSURFACE_DESC* pBackBufferSurfaceDesc = DXUTGetD3D9BackBufferSurfaceDesc(); LPDXUTCALLBACKD3D9DEVICERESET pCallbackDeviceReset = GetDXUTState().GetD3D9DeviceResetFunc(); hr = S_OK; if( pCallbackDeviceReset != NULL ) hr = pCallbackDeviceReset( pd3dDevice, pBackBufferSurfaceDesc, GetDXUTState().GetD3D9DeviceResetFuncUserContext() ); GetDXUTState().SetInsideDeviceCallback( false ); if( FAILED( hr ) ) { // If callback failed, cleanup DXUT_ERR( L"DeviceResetCallback", hr ); if( hr != DXUTERR_MEDIANOTFOUND ) hr = DXUTERR_RESETTINGDEVICEOBJECTS; GetDXUTState().SetInsideDeviceCallback( true ); LPDXUTCALLBACKD3D9DEVICELOST pCallbackDeviceLost = GetDXUTState().GetD3D9DeviceLostFunc(); if( pCallbackDeviceLost != NULL ) pCallbackDeviceLost( GetDXUTState().GetD3D9DeviceLostFuncUserContext() ); GetDXUTState().SetInsideDeviceCallback( false ); return hr; } // Success GetDXUTState().SetDeviceObjectsReset( true ); return S_OK; }
伪代码:
HRESULT hr; hr = m_pIDirect3DDevice->Present(NULL, NULL, NULL, NULL); if(hr == D3DERR_DEVICELOST) { if(m_pIDirect3DDevice->TestCooperativeLevel() == D3DERR_DEVICENOTRESET) { OnLostDevice(); OnResetDevice(); } } void OnLostDevice(void) { m_sprite->OnLostDevice(); m_font->OnLostDevice(); } void OnResetDevice(void) { if(FAILED(m_pIDirect3DDevice->Reset(&d3dpp))) { return; } m_sprite->OnResetDevice(); m_font->OnResetDevice(); InitDevice(); } void InitDevice() { m_pIDirect3DDevice->SetRenderState( D3DRS_ZENABLE, TRUE ); m_pIDirect3DDevice->SetRenderState( D3DRS_AMBIENT, 0xffffffff ); m_pIDirect3DDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); m_pIDirect3DDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); m_pIDirect3DDevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_POINT); D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, 1.0f, 1.0f, 1000.0f ); m_pIDirect3DDevice->SetTransform( D3DTS_PROJECTION, &matProj ); vEyeVec=D3DXVECTOR3(0.0f,0.0f,-1.0f); vLookatVec=D3DXVECTOR3(0.0f,0.0f,0.0f); vUpVec=D3DXVECTOR3(0.0f,1.0f,0.0f); D3DXMatrixLookAtLH( &matView, &vEyeVec, &vLookatVec, &vUpVec ); m_pIDirect3DDevice->SetTransform( D3DTS_VIEW, &matView ); }