丢失的设备
一个Microsoft? Direct3D?可以处于操作状态或丢失状态。操作状态是设备的正常状态,设备按预期运行并present所有渲染结果。当事件发生时,如全屏应用程序失去键盘输入焦点,设备就转变到丢失状态,这会导致渲染无法进行。丢失状态表现为所有渲染操作的悄然失败,这意味着即使渲染操作失败所有的渲染方法仍可以返回成功码。在这种情况下,IDirect3DDevice9:resent返回错误码D3DERR_DEVICELOST。
Direct3D有意没有对可能导致设备丢失的所有情况进行详细说明。一些典型的例子包括窗口失去焦点,例如用户按下了ALT+TAB或弹出了一个系统对话框。设备也会因为电源管理事件而丢失,或者另一个应用程序进行全屏操作。另外,任何对IDirect3DDevice9::Reset调用的失败会把设备置为丢失状态。
注意 可以保证所有继承自IUnknown的方法在设备丢失后仍能正常工作。设备丢失后,每个函数一般有三种可能:
调用失败,返回值为D3DERR_DEVICELOST – 这意味着应用程序必须发现设备已经丢失,从而知道一些事情没有按照预期进行。 悄然失败,返回值为S_OK或其它值 – 若函数调用悄然失败,则应用程序一般无法区分出“调用成功”或“悄然失败”。 函数返回一个返回值。 对丢失的设备作出响应
设备在被重置后,应该重新创建资源(包括显存资源)。如果设备丢失了,那么应用程序应该查询设备状态,看是否可以将之恢复回操作状态。如果不行,那么就等到设备可以被恢复为止。
如果设备可以被恢复,那么应用程序应该销毁所有显存资源和交换链,并准备恢复。然后,应用程序调用IDirect3DDevice9::Reset方法。Reset方法是当设备丢失时唯一有效的方法,并且是应用程序可用来把设备从丢失状态恢复到操作状态的唯一方法。除非应用程序释放所有在D3DPOOL_DEFAULT中分配的资源,包括用IDirect3DDevice9::CreateRenderTarget和IDirect3DDevice9::CreateDepthSstencilSurface方法创建的资源,否则Reset将会失败。
Direct3D中大部分被频繁调用的方法不返回任何关于设备是否已丢失的信息。应用程序可以继续调用渲染方法,如IDirect3DDevice9:rawPrimitive,而不会收到设备丢失的通知。在Direct3D内部,这些操作被抛弃,直到设备被重置为操作状态为止。
通过查询IDirect3DDevice9::TestCooperativeLevel方法的返回值,应用程序可以决定在遇到设备丢失时如何处理。
管理资源
资源管理是将资源从系统内存提升到设备可访问存储器及从设备可访问存储器中抛弃的过程。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):
以下内容为程序代码:
//----------------------------------------------------------------------------- // Name: invalidateDeviceObjects() // Desc: If the lost device can be restored, the application prepares the // device by destroying all video-memory resources and any // swap chains. This is typically accomplished by using the SAFE_RELEASE // macro. //----------------------------------------------------------------------------- HRESULT invalidateDeviceObjects( void ) { // // To see how mismanagement of an object''s reference counter can cause us // problems when calling Reset on the device, uncomment the line below. // The line below will call AddRef() on the vertex buffer object, which w // ill add one to the vertex buffer''s reference count. This will cause it // to hang around after we call Release() on it, which is not what we // wanted to happen here. // //g_pVertexBuffer->AddRef();
// // NOTE: You could use the SAFE_RELEASE macro to invalidate your device // objects like so: // SAFE_RELEASE( g_pTexture ) SAFE_RELEASE( g_pVertexBuffer ) SAFE_RELEASE( g_pTeapotMesh ) return S_OK; }
//----------------------------------------------------------------------------- // Name: restoreDeviceObjects() // Desc: You are encouraged to develop applications with a single code path to // respond to device loss. This code path is likely to be similar, if not // identical, to the code path taken to initialize the device at startup. //----------------------------------------------------------------------------- HRESULT restoreDeviceObjects( void ) { // // Set some important state settings... //
D3DXMATRIX matProj; D3DXMatrixPerspectiveFovLH( &matProj, D3DXToRadian( 45.0f ), 640.0f / 480.0f, //(float)(g_d3dpp.BackBufferWidth / g_d3dpp.BackBufferHeight), 0.1f, 100.0f ); g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );
g_pd3dDevice->SetRenderState( D3DRS_ZENABLE, TRUE ); g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, TRUE ); g_pd3dDevice->SetRenderState( D3DRS_SPECULARENABLE, TRUE );
g_pd3dDevice->SetLight( 0, &g_pLight0 ); g_pd3dDevice->LightEnable( 0, TRUE );
g_pd3dDevice->SetRenderState( D3DRS_AMBIENT, D3DCOLOR_COLORVALUE( 0.2f, 0.2f, 0.2f, 1.0f ) );
// // Create a texture object... //
D3DXCreateTextureFromFile( g_pd3dDevice, "test.bmp", &g_pTexture );
g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR ); g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
// // Create a vertex buffer... // // NOTE: When a device is lost, vertex buffers created using // D3DPOOL_DEFAULT must be released properly before calling // IDirect3DDevice9::Reset. //
g_pd3dDevice->CreateVertexBuffer( 4*sizeof(QuadVertex), D3DUSAGE_WRITEONLY, QuadVertex::FVF_Flags, //D3DPOOL_MANAGED, // Does not have to be properly Released before calling IDirect3DDevice9::Reset D3DPOOL_DEFAULT, // Must be Released properly before calling IDirect3DDevice9::Reset &g_pVertexBuffer, NULL ); void *pVertices = NULL;
g_pVertexBuffer->Lock( 0, sizeof(g_quadVertices), (void**)&pVertices, 0 ); memcpy( pVertices, g_quadVertices, sizeof(g_quadVertices) ); g_pVertexBuffer->Unlock();
// // Create a mesh object... // // NOTE: When a device is lost, meshes created using D3DXMESH_DYNAMIC // must be released properly before calling IDirect3DDevice9::Reset. //
D3DXLoadMeshFromX( "teapot.x", //D3DXMESH_SYSTEMMEM, // Does not have to be properly Released before calling IDirect3DDevice9::Reset //D3DXMESH_MANAGED, // Does not have to be properly Released before calling IDirect3DDevice9::Reset //D3DXMESH_WRITEONLY, // Does not have to be properly Released before calling IDirect3DDevice9::Reset D3DXMESH_DYNAMIC, // Must be Released properly before calling IDirect3DDevice9::Reset g_pd3dDevice, NULL, NULL, NULL, NULL, &g_pTeapotMesh ); return S_OK; }
//----------------------------------------------------------------------------- // Name: render() // Desc: //----------------------------------------------------------------------------- void render( void ) { // // Before we render, we need to make sure we haven''t lost our device. // If we have, we''ll need to restore it before we can continue. //
HRESULT hr;
if( g_bDeviceLost == true ) { // Yield some CPU time to other processes Sleep( 100 ); // 100 milliseconds
// // Test the cooperative level to see if it''s okay to render. // The application can determine what to do on encountering a lost // device by querying the return value of the TestCooperativeLevel // method. //
if( FAILED( hr = g_pd3dDevice->TestCooperativeLevel() ) ) { // The device has been lost but cannot be reset at this time. // Therefore, rendering is not possible and we''ll have to return // and try again at a later time. if( hr == D3DERR_DEVICELOST ) return;
// The device has been lost but it can be reset at this time. if( hr == D3DERR_DEVICENOTRESET ) { // // If the device can be restored, the application prepares the // device by destroying all video-memory resources and any // swap chains. //
invalidateDeviceObjects();
// // Then, the application calls the Reset method. // // Reset is the only method that has an effect when a device // is lost, and is the only method by which an application can // change the device from a lost to an operational state. // Reset will fail unless the application releases all // resources that are allocated in D3DPOOL_DEFAULT, including // those created by the IDirect3DDevice9::CreateRenderTarget // and IDirect3DDevice9::CreateDepthStencilSurface methods. //
hr = g_pd3dDevice->Reset( &g_d3dpp );
if( FAILED(hr ) ) return;
// // Finally, a lost device must re-create resources (including // video memory resources) after it has been reset. //
restoreDeviceObjects(); }
return; }
g_bDeviceLost = false; }
// // Render a teapot and textured quad... //
g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_COLORVALUE(0.35f,0.53f,0.7f,1.0f), 1.0f, 0 );
g_pd3dDevice->BeginScene(); { D3DXMATRIX matView; D3DXMATRIX matWorld; D3DXMATRIX matRotation; D3DXMATRIX matTranslation;
D3DXMatrixIdentity( &matView ); g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView );
// Place and render first teapot... D3DXMatrixRotationYawPitchRoll( &matRotation, D3DXToRadian(g_fSpinX), D3DXToRadian(g_fSpinY), 0.0f ); D3DXMatrixTranslation( &matTranslation, 1.5f, 0.0f, 6.0f ); matWorld = matRotation * matTranslation; g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );
g_pd3dDevice->SetMaterial( &g_teapotMtrl ); g_pTeapotMesh->DrawSubset(0);
// Place and render textured quad... D3DXMatrixTranslation( &matTranslation, -1.5f, 0.0f, 6.0f ); matWorld = matRotation * matTranslation; g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );
g_pd3dDevice->SetMaterial( &g_quadMtrl ); g_pd3dDevice->SetTexture( 0, g_pTexture ); g_pd3dDevice->SetStreamSource( 0, g_pVertexBuffer, 0, sizeof(QuadVertex) ); g_pd3dDevice->SetFVF( QuadVertex::FVF_Flags ); g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2 ); } g_pd3dDevice->EndScene();
// // If Present fails with D3DERR_DEVICELOST the application needs to be // notified so it cleanup resources and reset the device. //
hr = g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
if( g_bHandleLostDevice == true ) { if( hr == D3DERR_DEVICELOST ) g_bDeviceLost = true; } } | | |