Error Handling with DXUT

This topic covers recommended procedures for handling errors returned from applications that use DXUT.

Efficient Error Handling

The Direct3D API is designed to make it easy to handle failures. Even though most elements of the Direct3D API return HRESULTs, the API is designed such that only a few methods and functions return device errors such as D3DERR_DEVICELOST or D3DERR_DRIVERINTERNALERROR. However, a typical Direct3D application will use many various API methods and functions which, when passed bad parameters, will return D3DERR_INVALIDCALL.

When a Direct3D application is being developed, it should check all API calls for failures, and if a failure occurs that was not expected, the application should be designed to immediately notify the developer or log the error. In this way the developer can quickly catch situations where API functions are called incorrectly. However, a mature application that is correctly calling the API functions can safely ignore error codes from failed calls to most Direct3D APIs, with the exception of errors returned by a few key API functions such as IDirect3DDevice9::Present or IDirect3DDevice9::TestCooperativeLevel.

By handling only the most important Direct3D errors, you can improve execution speed and make the application code more predictable and robust because there are only a few places in code that need to handle errors. Failures in the other few API functions that must be handled properly, such as IDirect3DDevice9::Present, are handled inside the framework.

Error Messages

Error handling in the framework matches how error handling in the Direct3D API was designed. For serious errors such as missing media, the application can notify the user and terminate. For most API functions that are called every frame, the errors are only handled during debug builds by displaying an error message box to the developer. For retail builds these errors are ignored. The framework accomplishes this with several macros defined in Dxstdafx.h:

#if defined(DEBUG) | defined(_DEBUG)
    #define V(x)
    {
        hr = x;
        if( FAILED(hr) )
        {
            DXUTTrace( __FILE__, (DWORD)__LINE__, hr, L#x, TRUE );
        }
    }
    #define V_RETURN(x)
    {
        hr = x;
        if( FAILED(hr) )
        {
            return DXUTTrace( __FILE__, (DWORD)__LINE__, hr, L#x, TRUE );
        }
    }
#else
    #define V(x)
    {
        hr = x;
    {
    #define V_RETURN(x)
    {
        hr = x;
        if( FAILED(hr) )
        {
            return hr;
        }
    }
#endif

A typical application can use the V() macro as follows:

V( g_pEffect->BeginPass( iPass ) );
V( g_pEffect->CommitChanges() );
V( g_pMesh->DrawSubset( 0 ) );

Each of these calls uses the V() macro that ignores the API return code for release builds. For debug builds, the macro will display an error message that looks something like the following:

Figure 1.  Error message returned by V() macro for debug build

The debug output window of the compiler will display a message such as the following:

c:\basichlsl\basichlsl.cpp(242):
    D3DXCreateFont( pd3dDevice, 15, 0, FW_BOLD, 1, FALSE, DEFAULT_CHARSET,
    OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Arial",
    NULL ) hr=D3DERR_INVALIDCALL (0x8876086c)

To jump to the line of source code where the error occurred when using Visual Studio .NET, simply double-click on this line in the debug output window.

An application can use the V_RETURN() macro as follows:

V_RETURN( D3DXCreateEffectFromFile( pd3dDevice, str,
                                    NULL, NULL, dwShaderFlags,
                                    NULL, &g_pEffect, NULL ) );

This macro will display a similar error message in debug builds, but for both debug and release builds it will also return any failed HRESULT to the calling function. The display of these error messages can be turned off with a call to DXUTInit or with the following command-line argument:

-noerrormsgboxes

See Tutorials and Samples for more examples of how these macros are used.