DXUT框架剖析(6)
在窗口和设备创建好之后,应用程序需要使用消息循环处理窗口消息、更新和渲染场景、处理设备事件。应用程序可以实现自己的消息循环,也可以使用DXUT消息循环,注册相应的回调函数,可以让DXUT处理设备、帧消息事件。
进入消息循环
为使用DXUT框架的消息循环,可以调用DXUTMainLoop()函数:
Starts the main execution loop of DXUT.
HRESULT DXUTMainLoop(
HACCEL hAccel
);
Parameters
- hAccel
- [in] Handle to an accelerator table to use in translating keyboard messages from the Windows message queue, or NULL if not using an accelerator table. The default value is NULL.
Return Values
If the function succeeds, the return value is S_OK. If the function fails, the return value can be one of the error codes in DXUTERR.
Remarks
This function starts the message loop that will run for the lifetime of the application. During execution, DXUTMainLoop calls the registered callback functions to ask the application to update and render the frame, as well as handle any device or input events.
Custom Main Loop
For some advanced applications a custom main loop may be a better design. It is possible to use DXUT with a custom main loop. An example of how to do this is shown below.
INT WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, INT )
{
DXUTSetCallbackD3D9DeviceAcceptable( IsDeviceAcceptable );
DXUTSetCallbackD3D9DeviceCreated( OnCreateDevice );
DXUTSetCallbackD3D9DeviceReset( OnResetDevice );
DXUTSetCallbackD3D9FrameRender( OnFrameRender );
DXUTSetCallbackD3D9DeviceLost( OnLostDevice );
DXUTSetCallbackD3D9DeviceDestroyed( OnDestroyDevice );
DXUTSetCallbackMsgProc( MsgProc );
DXUTSetCallbackKeyboard( KeyboardProc );
DXUTSetCallbackFrameMove( OnFrameMove );
DXUTSetCallbackDeviceChanging( ModifyDeviceSettings );
DXUTInit( true, true );
DXUTCreateWindow( L"Example" );
DXUTCreateDevice( true, 640, 480 );
// Custom main loop
HWND hWnd = DXUTGetHWND();
BOOL bGotMsg;
MSG msg;
msg.message = WM_NULL;
PeekMessage( &msg, NULL, 0U, 0U, PM_NOREMOVE );
while( WM_QUIT != msg.message )
{
// Use PeekMessage() so we can use idle time to render the scene
bGotMsg = ( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) != 0 );
if( bGotMsg )
{
// Translate and dispatch the message
if( 0 == TranslateAccelerator( hWnd, NULL, &msg ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
}
else
{
// Render a frame during idle time (no messages are waiting)
DXUTRender3DEnvironment();
}
}
return DXUTGetExitCode();
}
This example calls DXUTRender3DEnvironment to have DXUT update and render the scene and handle device events. While it is possible for the application to completely replicate this functionality, it is not recommended.
DXUTRender3DEnvironment
Renders the 3D environment.
VOID DXUTRender3DEnvironment();
Parameters
None.
Return Values
No return value.
Remarks
This method does not normally need to be called. It is useful only when the application does not use DXUTMainLoop but still wants DXUT to assist with rendering.
This method checks whether the device is lost. If so, the method attempts to reset the device and then calls the LPDXUTCALLBACKFRAMEMOVE and LPDXUTCALLBACKD3D10FRAMERENDER callback functions.
If the application window is minimized or the application is paused, CPU time is yielded to other processes.
处理事件
框架使用回调函数机制来使应用程序对事件做出反应。应用程序只需对框架注册和设置相应的函数指针,则当事件发生时,框架就会调用相应的函数。框架不需要注册所有的回调函数,所以应用程序只须对所需要的回调函数进行注册即可。通过为回调函数设置参数pUserContext,回调函数可以从应用程序接受内容,比如将该参数设置为一个指向类对象的指针。
DXUT框架可以处理以下事件类型:
(1)设备事件
当应用程序使用Direct3D设备渲染图形时,该设备有可能处于丢失状态。这种情况的发生有多种原因,例如按下Alt + Tab键离开一个全屏模式的应用程序,或者按下Ctrl + Alt + Del键,或者启动了另一个全屏3D应用程序。发生这种情况时,当调用一些函数(如Present)时,Direct3D API通过返回D3DERR_DEVICELOST通知应用程序设备丢失。
当设备丢失时,应用程序负责释放所有不能在设备丢失时存在的Direct3D资源对象,如在D3DPOOL_DEFAULT内存池中创建的对象。如果没有释放这些对象,那么该设备从丢失状态返回时就不能被重新设置。当设备丢失时,应用程序必须等待。当设备返回时,应用程序必须调用函数IDirect3DDevice9::Reset(),并重新创建所有不能在Reset()函数中存在的对象。
通过DXUT框架,这个过程可以通过在应用程序中使用回调函数来简化,这些回调函数处理各种设备事件:设备改变、创建、重新设置、丢失或销毁。当设备丢失时,框架会有提示;当它从丢失状态返回时,框架会适当调用相应的回调函数,重新设置该设备,即框架使用应用程序的回调函数在适当的时间释放和重新创建设备对象。应用程序需要做的是注册并实现相关回调函数,各回调函数的类型、注册、调用时机等细节见下表:
注册函数
应用程序回调函数
框架调用时机
创建资源
释放资源
DXUTSetCallback-
DeviceChanging
LPDXUTCALLBACK-
MODIFYDEVICESETTINGS
在创建Direct3D设备之前调用,应用程序可以返回FALSE,拒绝改变该设备。
x
x
DXUTSetCallback-
D3D9DeviceCreated
LPDXUTCALLBACK-
D3D9DEVICECREATED
当应用程序初始化和重新创建设备时,在Direct3D设备创建之后立即调用。
创建D3DPOOL_MANAGED资源,因为这些资源无论什么时候被销毁都需要重新加载,但这些资源被重新设置时不需要重新加载。在这里创建的资源需要在LPDXUTCALLBACK-DEVICEDESTROYED中释放。
x
DXUTSetCallback-
D3D9DeviceReset
LPDXUTCALLBACK-
D3D9DEVICERESET
当Direct3D设备丢失又被重新设置后立即调用。
创建D3DPOOL_DEFAULT资源,因为这些资源无论什么时候丢失或重新设置时都需要重新加载,在这里创建的资源需要在LPDXUTCALLBACK-DEVICELOST中释放。
x
DXUTSetCallback-
D3D9DeviceLost
LPDXUTCALLBACK-
D3D9DEVICELOST
当Direct3D设备变为丢失状态且在Reset调用之前,立即调用。
x
释放在回调函数LPDXUTCALLBACK-D3D9DEVICERESET中创建的资源,这些资源通常包括所有的D3DPOOL_DEFAULT资源。
DXUTSetCallback-
D3D9DeviceDestroyed
LPDXUTCALLBACK-
D3D9DEVICEDESTROYED
当应用程序终止或重新创建设备时,Direct3D设备被销毁后,立即调用。
x
释放在回调函数LPDXUTCALLBACK-
D3D9DEVICECREATED中创建的资源,这些资源通常包括所有的D3DPOOL_MANAGED资源。
当设备在窗口和全屏模式间切换时常常需要重新设置,但有时它必须通过Direct3D重新创建。
调用这些回调函数是可选的,但如果应用程序没有使用函数DXUTSetCallbackD3D9DeviceDestroyed()和DXUTSetCallbackD3D9DeviceCreated()注册销毁回调函数和创建回调函数,则改变设备或在硬件抽象层设备和参考设备间切换都不能进行。
类似地,如果没有用函数DXUTSetCallbackD3D9DeviceLost()和DXUTSetCallbackD3D9DeviceReset()注册丢失回调函数和重置回调函数,则当设备丢失或重置设备时,框架无法通知应用程序。这样一来,所有不在D3DPOOL_MANAGED内存中的设备对象都不能重新设置。