D3D设备创建后无法进行双重计算

rea*_*l4x 3 c++ direct3d

我正在编写一个 D3D 应用程序,用于DXUT初始化设备并处理所有事件。不过,我发现了一个奇怪的行为:一旦我创建了一个设备,应用程序中的所有双精度计算都会被破坏。经过一些调试后,我将代码简化为:

bool CALLBACK AlwaysTrue(D3DCAPS9*, D3DFORMAT, D3DFORMAT, bool, void*) {
  return true;
}

INT WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, INT) {
  // Commenting line below solves the problem.
  DXUTSetCallbackD3D9DeviceAcceptable(AlwaysTrue);
  DXUTInit(true, true);
  DXUTCreateWindow(L"Issue with doubles");

  __int64 val = 1326778320508821LL;
  double a1 = 0.000001 * val;  // 1326778320.5088210
  DXUTCreateDevice(true, 640, 480);
  double a2 = 0.000001 * val;  // 1326778368.0000000

  DXUTMainLoop();
  return DXUTGetExitCode();
}
Run Code Online (Sandbox Code Playgroud)

好吧,我很确定这里有 float/double 问题,但我无法理解它是如何触发的以及如何解决它。我尝试在 asm 级别进行调试,发现代码 100% 相同a1a2这让我认为这是 FPU 状态问题。

由于某种原因,注释掉主方法中的第一行可以解决问题。

有谁知道这里发生了什么,并且可能有一些文档可以了解有关此问题的更多信息?我的应用程序肯定需要双精度计算。

附言。VS2008 SP1,SSE/SSE2关闭,浮点模型:精确。

zeu*_*xcg 5

当使用默认标志创建 D3D9 设备时,它将 FPU 精度设置为 24 位。为了防止这种情况,您必须在创建设备时设置 D3DCREATE_FPU_PRESERVE 标志。

请注意,DXSDK 文档不鼓励您这样做,以防您更改 FPU 状态以启用异常:

Portions of Direct3D assume floating-point unit exceptions are masked;
unmasking these exceptions may result in undefined behavior.
Run Code Online (Sandbox Code Playgroud)

使用默认的 FPU 状态应该没问题。

为了使用 DXUT 执行此操作,您必须使用 ModifyDeviceSettings 回调,通过 DXUTSetCallbackDeviceChanging 设置,即:

bool CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, void* pUserContext )
{
    assert( DXUT_D3D9_DEVICE == pDeviceSettings->ver );

    pDeviceSettings->d3d9.BehaviorFlags |= D3DCREATE_FPU_PRESERVE;
}

DXUTSetCallbackDeviceChanging(ModifyDeviceSettings);
Run Code Online (Sandbox Code Playgroud)

至于为什么设置设备可接受的回调可以解决这个问题:我认为在这种情况下,通过设置 D3D9 回调可以强制 DXUT 使用 D3D9;如果您不强制它使用 D3D9,它会尝试创建 D3D10 设备并成功;创建 D3D10 设备不会更改 FPU 状态,因此该错误消失。