Using Devices with DXUT

DirectX device creation is streamlined with DXUT. You can instead have your application create a device directly and other features of the framework will still be available.

Creating a Device

You would typically create a device with the standard Direct3D method, IDirect3D9::CreateDevice:

HRESULT CreateDevice(
    UINT                  Adapter,
    D3DDEVTYPE            DeviceType,
    HWND                  hFocusWindow,
    DWORD                 BehaviorFlags,
    D3DPRESENT_PARAMETERS *pPresentationParameters,
    IDirect3DDevice9      **ppReturnedDeviceInterface
);

This method requires a valid adapter, device type (hal or reference), window handle, behavior flags (software/hardware vertex processing and other driver flags), and presentation parameters. Furthermore, the D3DPRESENT_PARAMETERS structure has numerous members that specify back buffer settings, multisampling settings, swap effects, windowed mode, depth-stencil buffer settings, refresh rate, presentation interval, and presentation flags.

Selecting valid settings for all of these parameters can be challenging. The framework simplifies this selection process with the DXUTCreateDevice function:

HRESULT DXUTCreateDevice(
    UINT AdapterOrdinal  = D3DADAPTER_DEFAULT,
    BOOL bWindowed       = TRUE,
    INT nSuggestedWidth  = 640,
    INT nSuggestedHeight = 480,
    LPDXUTCALLBACKISDEVICEACCEPTABLE pCallbackIsDeviceAcceptable     = NULL,
    LPDXUTCALLBACKMODIFYDEVICESETTINGS pCallbackModifyDeviceSettings = NULL
);

The most basic usage is simply to call the function using the default parameters:

DXUTCreateDevice();

With this simple call, the framework creates a device with default settings that should work for most cases. The default device creation settings are as follows:

Direct3D Creation Flag Description Default Value from DXUTCreateDevice
AdapterFormat parameter of IDirect3D9::CheckDeviceFormat Adapter surface format. Desktop display mode, or D3DFMT_X8R8G8B8 if the desktop display mode is less than 32 bits.
Adapter parameter of IDirect3D9::CreateDevice Display adapter ordinal. D3DADAPTER_DEFAULT
D3DPRESENT_PARAMETERS. BackBufferCount Number of back buffers. 2, indicating triple buffering.
D3DPRESENT_PARAMETERS. BackBufferFormat Back buffer format. Desktop display mode, or D3DFMT_X8R8G8B8 if the desktop display mode is less than 32 bits.
D3DPRESENT_PARAMETERS. AutoDepthStencilFormat Depth format of the automatic depth-stencil surface that the device will create. D3DFMT_D16 if the backbuffer format is 16 bits or less, or D3DFMT_D32 otherwise.
The DeviceType parameter of IDirect3D9::CreateDevice Enumerated type of the device. D3DDEVTYPE_HAL if available, otherwise D3DDEVTYPE_REF or failure code if neither is available.
D3DPRESENT_PARAMETERS. MultiSampleQuality Quality level. MultiSampleQuality = 0, indicating multisampling is disabled.
D3DPRESENT_PARAMETERS. Flags Presentation parameters flags. D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL
D3DPRESENT_PARAMETERS. PresentationInterval Presentation interval. D3DPRESENT_INTERVAL_IMMEDIATE for windowed mode, or D3DPRESENT_INTERVAL_DEFAULT for full-screen mode.
D3DPRESENT_PARAMETERS. FullScreen_RefreshRateInHz Rate at which the display adapter refreshes the screen. 0, indicating windowed mode.
D3DPRESENT_PARAMETERS. BackBufferWidth and .BackBufferHeight Display mode resolution. 640 x 480 pixels for windowed mode, or the desktop resolution for full-screen mode.
D3DPRESENT_PARAMETERS. AutoDepthStencilFormat Stencil format of the automatic depth-stencil surface that the device will create. D3DFMT_D16 if the backbuffer format is 16 bits or less, or D3DFMT_D32 otherwise.
D3DPRESENT_PARAMETERS. SwapEffect Swap effect. D3DSWAPEFFECT_DISCARD
BehaviorFlags parameter of IDirect3D9::CreateDevice Vertex processing flags. D3DCREATE_HARDWARE_VERTEXPROCESSING if supported, otherwise D3DCREATE_SOFTWARE_VERTEXPROCESSING.
D3DPRESENT_PARAMETERS. Windowed Windowed or full-screen mode. true, indicating windowed mode.
hFocusWindow parameter of IDirect3D9::CreateDevice Handle to the created window (see Using Application Windows with DXUT). hWndFocus parameter of DXUTSetWindow
D3DPRESENT_PARAMETERS. hDeviceWindow Handle to the device window. hWndDeviceFullScreen or hWndDeviceWindowed parameters of DXUTSetWindow
D3DPRESENT_PARAMETERS. EnableAutoDepthStencil Depth-stencil buffer creation flag. true.

Rather than just using a device created with these defaults, the application can apply more control of device creation through the parameters passed to IDirect3D9::CreateDevice. For example, you can change the window size through the nSuggestedWidth and nSuggestedHeight parameters:

DXUTCreateDevice(
    D3DADAPTER_DEFAULT,
    false,
    1024,
    786,
    NULL,
    NULL,
    NULL
);

For even more control, the application can use the two optional callback functions, LPDXUTCALLBACKISDEVICEACCEPTABLE and LPDXUTCALLBACKMODIFYDEVICESETTINGS.

Choosing the Best Device Settings

You can use the IsDeviceAcceptable callback function in your application to help the framework choose the best device settings for the application, as in the following code:

bool CALLBACK IsDeviceAcceptable(
D3DCAPS9*     pCaps,
D3DFORMAT     AdapterFormat,
D3DFORMAT     BackBufferFormat,
bool          bWindowed,
void*         pUserContext )
{
    // TODO: return true for acceptable settings and false otherwise.
    return true;
}

This callback function is modeled on the prototype LPDXUTCALLBACKISDEVICEACCEPTABLE. The framework calls this function once for each unique valid combination of the following five settings:

D3DDEVTYPE DeviceType;
UINT       AdapterOrdinal;
D3DFORMAT  AdapterFormat;
D3DFORMAT  BackBufferFormat;
bool       Windowed;

Note that the adapter ordinal and device type are not passed directly into the callback function, but are, respectively, the DeviceType and AdapterOrdinal members of the D3DCAPS9 structure.

Within this callback function, the application can reject any combination that it does not support or want. As an example, the application can use the following code to reject 16-bit back buffer formats and all devices that do not support at least pixel shader ps_2_0:

bool CALLBACK IsDeviceAcceptable(
    D3DCAPS9*     pCaps,
    D3DFORMAT     AdapterFormat,
    D3DFORMAT     BackBufferFormat,
    bool          bWindowed )
{
    if( pCaps->PixelShaderVersion < D3DPS_VERSION(2,0) )
	    return false;
    if( BackBufferFormat == D3DFMT_X1R5G5B5 || BackBufferFormat == D3DFMT_R5G6B5 )
        return false;
    return true;
}

After the callback function is called for every unique combination of settings, the framework ranks the remaining acceptable combinations and chooses the best among them. Higher rankings are given to combinations that include the following:

With the highest-ranked combination of these settings chosen, the behavior flags and presentation parameters are still needed to create the device. For these settings, Direct3D uses the defaults as shown in the table above.

Modifying Available Device Settings

The application can modify the choices available to the framework by using an optional second callback function ModifyDeviceSettings similar to the following:

bool CALLBACK ModifyDeviceSettings(
    DXUTDeviceSettings* pDeviceSettings,
    const D3DCAPS9*     pCaps )
{
    // TODO: Include device creation requirements here.  
    // Return true to create device or false to keep current device.
    return true;
}

This callback function is modeled on the prototype LPDXUTCALLBACKMODIFYDEVICESETTINGS. The DXUTDeviceSettings structure is defined by the framework as follows:

struct DXUTDeviceSettings
{
    UINT       AdapterOrdinal;
    D3DDEVTYPE DeviceType;
    D3DFORMAT  AdapterFormat;
    DWORD      BehaviorFlags;
    D3DPRESENT_PARAMETERS pp;
};

This structure contains everything needed to create a device except the window handle, which is assumed to be the handle to the window created earlier. The framework fills this structure with valid values, and then allows the application to change device creation choices through the ModifyDeviceSettings callback function.

In this callback function the application can change the behavior flags as well as the presentation parameters in the DXUTDeviceSettings structure, as well as anything else in the structure. If the application does not change anything in the callback function, the device will be created successfully. However, any device creation setting changed by the application needs to be supported by the device, otherwise device creation will likely fail.

For example, if the application needs a depth-stencil format of D3DFMT_D24S8, it should verify that the device supports it, as in the following code:

bool CALLBACK ModifyDeviceSettings(
    DXUTDeviceSettings* pDeviceSettings,
    const D3DCAPS9*     pCaps )
{
    IDirect3D9* pD3D = DXUTGetD3DObject();
    if( SUCCEEDED( pD3D->CheckDeviceFormat(
	        pDeviceSettings->AdapterOrdinal,
	        pDeviceSettings->DeviceType,
	        pDeviceSettings->AdapterFormat,
	        D3DUSAGE_DEPTHSTENCIL,
	        D3DRTYPE_SURFACE,
	        D3DFMT_D24S8 ) ) )
    {
	    if( SUCCEEDED( pD3D->CheckDepthStencilMatch(
	            pDeviceSettings->AdapterOrdinal,
	            pDeviceSettings->DeviceType,
	            pDeviceSettings->AdapterFormat,
	            pDeviceSettings->pp.BackBufferFormat,
	            D3DFMT_D24S8 ) ) )
    	{
	        pDeviceSettings->pp.AutoDepthStencilFormat = D3DFMT_D24S8;
    	}
    }
    
    return true;
}

Alternately, the callback function could use the framework's CD3DEnumeration object to verify whether D3DFMT_D24S8 is supported:

	bool CALLBACK ModifyDeviceSettings(
    DXUTDeviceSettings* pDeviceSettings,
    const D3DCAPS9*     pCaps )
{
    CD3DEnumeration *pEnum = DXUTGetEnumeration();
    CD3DEnumDeviceSettingsCombo *pCombo;
	
    pCombo = pEnum->GetDeviceSettingsCombo( pDeviceSettings );
	
    if( pCombo->depthStencilFormatList.Contains( D3DFMT_D24S8 ) )
        pDeviceSettings->pp.AutoDepthStencilFormat = D3DFMT_D24S8;
        
    return true;
}

After the application modifies the device settings, the framework creates the device with those new settings.

New for the DirectX April 2005 SDK Update, the ModifyDeviceSettings callback function returns a bool. If the application returns true, the framework proceeds with the device creation as normal. If it returns false, the framework does not change the device and keeps the current device if one exists. This was done to allow an application to be able to reject the framework's requests to change to a device the application can not use. For example, on a multiple monitor configuration by default dragging the window between monitors will cause the framework to change devices. However if the application can not use the other device, it must be able reject this change and continue to use the current device.

Fallback to Software Vertex Processing

If you are setting up a Direct3D device on hardware that supports hardware pixel processing but does not support hardware vertex processing then you need to set the behavior flags accordingly. In order to ensure the proper fallback to software vertex processing, take precautions that you do not reject devices based on the vertex shader version in the IsDeviceAcceptable callback, and ensure that the behavior flags are adjusted correctly in the ModifyDeviceSettings callback. Here's an example of how to do this:

bool CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, 
                                    const D3DCAPS9* pCaps )
{
    // If device doesn't support HW T&L or doesn't support 1.1 vertex 
    // shaders in HW, then switch to SWVP.
    if( (pCaps->DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) == 0 ||
         pCaps->VertexShaderVersion < D3DVS_VERSION(1,1) )
    {
        pDeviceSettings->BehaviorFlags = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
    }

    else
    {
        pDeviceSettings->BehaviorFlags = D3DCREATE_HARDWARE_VERTEXPROCESSING;
    }
    
    return true; 
}

Using Your Own Device

You do not have to rely on the framework to create the Direct3D device. Instead, the application itself can create the device and pass it to the framework to use, similar to the way the application can override the framework's window creation settings. Simply create a device with all desired settings, and then call the DXUTSetDevice function to enable the framework to render on the device.

Note    If the application creates the device independent of the framework, the application must also release the device interface upon cleanup after main loop execution is complete.

See Also

Advanced Device Selection with DXUT