Using the Main Loop with the Sample Framework
Microsoft DirectX 9.0 SDK Update (October 2004)

Using the Main Loop with the Sample Framework


After the window and device are created, the application needs to use a main loop (also called a render loop or message loop) that responds to window messages, updates and renders the scene, and handles device events. The application can either implement the main loop itself, or it can use the sample framework's implementation. Registration of callback functions allows the framework to handle device, frame, and message events.

Entering the Main Loop

To use the framework's main loop, simply call DXUTMainLoop with the single parameter set to NULL.

Although it is easiest for the framework to handle the main loop, for some advanced applications a custom main loop may be a better design. It is possible to use the DXUTMainLoop function with a custom main loop, but more code is required in the application, as shown in the following code example:

INT WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, INT )
{
    DXUTSetCallbackDeviceCreated( OnCreateDevice );
    DXUTSetCallbackDeviceReset( OnResetDevice );
    DXUTSetCallbackDeviceLost( OnLostDevice );
    DXUTSetCallbackDeviceDestroyed( OnDestroyDevice );
    DXUTSetCallbackFrameRender( OnFrameRender );
    DXUTSetCallbackFrameMove( OnFrameMove );

    DXUTInit( TRUE, TRUE, TRUE );
    DXUTCreateWindow( L"BasicHLSL" );
    DXUTCreateDevice( D3DADAPTER_DEFAULT, 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();
}

In this code example, the application calls the function DXUTRender3DEnvironment to have the framework 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.

Handling Events

The framework uses a callback function mechanism to allow the application to respond to events. The application simply needs to register, or set, a function pointer with the framework, and the framework will call this function when the event occurs. The framework does not require that any callback function is registered, so the application is free to register only as many callback functions as needed.

Three types of events occur in the framework:

Device Events

While an application is rendering to a Microsoft Direct3D device, it is possible for the device to become lost. This can happen for a number of reasons, such as when a user presses ALT+TAB to leave a full-screen application, when a user presses CTRL+ALT+DEL, or when the user runs another full-screen 3-D application. The Direct3D application programming interface (API) notifies the application when this happens by returning D3DERR_DEVICELOST when calling certain functions such as IDirect3DDevice9::Present. (See Lost Devices.)

When a device becomes lost, it is the application's responsibility to release all Direct3D objects that do not survive when the device was lost, such as objects created in D3DPOOL_DEFAULT memory. If such objects are not released, the device cannot be reset when it returns from its lost state.

The application must wait while the device is lost. When the device returns, the application must call IDirect3DDevice9::Reset and recreate all device objects that did not survive the reset procedure.

With the framework, this process is simplified by using callback functions in the application to handle the various device events: device created, reset, lost, or destroyed. The framework will note when the device becomes lost and will properly reset the device when it returns from the lost state. The framework uses the application's callback functions to release and recreate the device objects at the appropriate times. All the application needs to do is to register and implement callback functions as follows.

Callback Registration FunctionApplication Callback FunctionWhen Called By the FrameworkResource CreationResource Release
DXUTSetCallbackDeviceCreatedLPDXUTCALLBACKDEVICECREATEDCalled immediately after the Direct3D device has been created, which will happen during application initialization and device recreation.Create D3DPOOL_MANAGED resources because these resources need to be reloaded whenever the device is destroyed.Resources created in this callback function should be released by calling the device destroyed callback function (LPDXUTCALLBACKDEVICEDESTROYED).
DXUTSetCallbackDeviceResetLPDXUTCALLBACKDEVICERESETCalled immediately after the Direct3D device has been reset, which will happen after the device is lost.Create D3DPOOL_DEFAULT resources because these resources need to be reloaded whenever the device is lost.Resources created in this callback function should be released by calling the device lost callback function (LPDXUTCALLBACKDEVICELOST).
DXUTSetCallbackDeviceLostLPDXUTCALLBACKDEVICELOSTCalled immediately after the Direct3D device has entered a lost state and before IDirect3DDevice9::Reset is called. -Resources created in the device reset callback function (LPDXUTCALLBACKDEVICERESET), which generally includes all D3DPOOL_DEFAULT resources, should be released by this application callback function.
DXUTSetCallbackDeviceDestroyedLPDXUTCALLBACKDEVICEDESTROYEDCalled immediately after the Direct3D device has been destroyed, which generally happens as a result of application termination or device recreation.-Resources created in the device created callback function (LPDXUTCALLBACKDEVICECREATED), which generally includes all D3DPOOL_MANAGED resources, should be released by this application callback function.

When the device is toggled between windowed and full-screen modes, it is usually reset, but sometimes it will have to be recreated by Direct3D.

It is optional to call any of these callback registration functions. However, if the application does not register the device destroyed and device created callback functions by calling DXUTSetCallbackDeviceDestroyed and DXUTSetCallbackDeviceCreated, then the framework cannot recreate the device because it will have no way of notifying the application that the device is being destroyed or created. In this case, changing devices or toggling between hardware abstraction layer (HAL) or reference devices will not work.

Similarly, if the application does not register the device lost and device reset callback functions by calling DXUTSetCallbackDeviceLost and DXUTSetCallbackDeviceReset, then the framework has no way of notifying the application when the device is lost or reset. In this case, typically if all the device objects are not in D3DPOOL_MANAGED memory, then Direct3D will fail to reset the device.

Frame Events

The framework also provides frame events that are called every frame during the rendering process. The application should register and implement callback functions as follows.

Application Callback FunctionCallback Registration FunctionWhen Called By the FrameworkScene Rendering
LPDXUTCALLBACKFRAMEMOVEDXUTSetCallbackFrameMoveCalled once at the beginning of every frame.This callback function is the best location for your application to handle updates to the scene, but it should not contain actual rendering calls, which should instead be placed in the frame render callback function (LPDXUTCALLBACKFRAMERENDER).
LPDXUTCALLBACKFRAMERENDERDXUTSetCallbackFrameRenderCalled at the end of every frame, or if the window needs to be repainted.All the rendering calls for the scene should be performed in this callback function. After this callback function has returned, the framework will call IDirect3DDevice9::Present to display the contents of the next buffer in the swap chain.

Message Events

The framework passes window messages, keyboard events, and mouse events through the following callback functions and their corresponding registration functions. Program the application to provide appropriate responses to such events.

Application Callback FunctionCallback Registration FunctionDescription
LPDXUTCALLBACKMSGPROCDXUTSetCallbackMsgProcProcesses window messages from the framework message pump.
LPDXUTCALLBACKKEYBOARDDXUTSetCallbackKeyboardProcesses keyboard events from the framework message pump.
LPDXUTCALLBACKMOUSEDXUTSetCallbackMouseProcesses mouse events from the framework message pump.


© 2004 Microsoft Corporation. All rights reserved.
Feedback? Please provide us with your comments on this topic.
For more help, visit the DirectX Developer Center.