Невозможно выполнить двойные вычисления после создания устройства D3D - PullRequest
3 голосов
/ 17 января 2012

Я пишу приложение 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();
}

Ну, я почти уверен, что у нас есть проблема с плавающей запятой / двойной ошибкой, но я не понимаю, как она запускается и как ее обойти. Я попытался отладить на уровне asm и обнаружил, что код на 100% одинаков для a1 и a2, что заставляет меня думать, что это проблема состояния FPU.

По какой-то причине комментирование первой строки в методе main решает проблему.

Кто-нибудь знает, что здесь происходит, и, возможно, некоторые документы помогут вам узнать больше об этой проблеме? Мое приложение определенно нуждается в вычислениях двойной точности.

PS. VS2008 SP1, SSE / SSE2 выключен, модель с плавающей запятой: точная.

1 Ответ

4 голосов
/ 17 января 2012

Когда устройство 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.

С состоянием 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);

Относительно того, почему установка приемлемого обратного вызова устройства устраняет проблему: я думаю, что в этом случае путем настройки обратного вызова D3D9 вы заставляете DXUT использовать D3D9; если вы не заставляете его использовать D3D9, он пытается создать устройство D3D10 и добивается успеха; создание устройства D3D10 не меняет состояние FPU, поэтому ошибка исчезает.

...