Platform SDK: DirectX

DirectDraw Cooperative Levels and FPU Precision

[Visual Basic]

Note  The information in this topic applies only to applications written in C++. DirectX for Visual Basic does not support changing floating-point unit (FPU) precision.

[C++]

Direct3D always uses single-precision floating-point calculations to increase the performance of rendering a 3-D scene. By default, when you create a rendering device that supports the IDirect3DDevice7 interface, Direct3D sets the FPU to single-precision, round-to-nearest mode with exceptions disabled. From that point on, the system assumes that the state of the FPU does not change until the device is released; it then resets the FPU to double-precision mode.

Note  This behavior is different from that of the legacy IDirect3DDevice3 interface. Devices that expose IDirect3DDevice3 check FPU mode on each rendering pass unless the DDSCL_FPUSETUP flag was used when the DirectDraw cooperative level was set in a prior call to IDirectDraw4::SetCooperative method.

Applications that require double-precision accuracy can include the DDSCL_FPUPRESERVE flag when calling the IDirectDraw7::SetCooperativeLevel method to set the DirectDraw cooperative level. When the DDSCL_FPUPRESERVE flag is used, the system still uses single-precision mode, but it saves the state of the FPU prior to changing the precision mode, then restores the mode when it returns control to the application. (Calling the method again using the DDSCL_FPUSETUP flag returns Direct3D to its default behavior.) Obviously, this check, save, then restore process takes time and can noticeably affect performance. This flag should only be used when the application must have double-precision accuracy and you do not want to include code to manually set and restore the FPU mode as the application requires.

Floating-point precision is thread-specific, so when developing multithreaded applications, be careful to check the FPU precision state to ensure that it is set and reset as appropriate for each thread.

Important  Loading some dynamic-link libraries (DLLs) at run time can cause the FPU to be reset to double-precision mode. Some compilers, such as Visual C++, set the default DLL entry point to _DllMainCrtStartup, a function that the compiler supplies to initialize the C/C++ run-time components. This function also sets the FPU precision mode to double precision. If an application sets the DDSCL_FPUSETUP cooperative level, then loads a DLL, Direct3D does not detect that the FPU has been reset, and performance suffers.

If your application loads DLLs at run time, it should check and reset the FPU precision mode immediately after the LoadLibrary Win32 function returns, and before calling any Direct3D functions. You can reset the precision mode explicitly or by calling the IDirectDraw7::SetCooperativeLevel method again with the DDSCL_FPUSETUP flag set. Using SetCooperativeLevel to set the FPU precision mode can also cause DirectDraw surfaces to be lost.

You can explicitly set the entry point for DLLs that you compile by using the /ENTRY: linker switch. However, if you do, the C/C++ run time is not initialized automatically.

The following is a sample source file for a console application that checks and sets the FPU precision setting by using inline assembly language.

#include <windows.h>
#include <math.h>
 
// This function evaluates whether the floating-point
// control Word is set to single precision/round to nearest/
// exceptions disabled. If not, the
// function changes the control Word to set them and returns
// TRUE, putting the old control Word value in the passback
// location pointed to by pwOldCW.
BOOL MungeFPCW( WORD *pwOldCW )
{
    BOOL ret = FALSE;
    WORD wTemp, wSave;
 
    __asm fstcw wSave
    if (wSave & 0x300 ||            // Not single mode
        0x3f != (wSave & 0x3f) ||   // Exceptions enabled
        wSave & 0xC00)              // Not round to nearest mode
    {
        __asm
        {
            mov ax, wSave
            and ax, not 300h    ;; single mode
            or  ax, 3fh         ;; disable all exceptions
            and ax, not 0xC00   ;; round to nearest mode
            mov wTemp, ax
            fldcw   wTemp
        }
        ret = TRUE;
    }
    *pwOldCW = wSave;
    return ret;
}
 
void RestoreFPCW(WORD wSave)
{
    __asm fldcw wSave
}
 
void __cdecl main()
{
    WORD wOldCW;
    BOOL bChangedFPCW = MungeFPCW( &wOldCW );
    // Do something with control Word, as set by MungeFPCW.
    if ( bChangedFPCW )
        RestoreFPCW( wOldCW );
}