日別アーカイブ: 2013/11/11

フルスクリーン描画時のリフレッシュレート

DirectXで、フルスクリーン描画をする際のディスプレイのリフレッシュレートについてです。

最近のPC向けディスプレイ

近年のPC向けディスプレイは、さまざまな垂直同期周波数を持っているケースがあります。具体的には、60Hzのほかに、120Hzや、144Hzなどです。
これ等のリフレッシュレートは、垂直同期を有効にした場合でも、60Hzのディスプレイに比べ、高頻度に画面を書き換える機会を有しています。また、垂直同期を無効にした場合でも、同様に、高頻度に画面を書き換える機会を有しているため、ティアリングによる不快感を軽減(分断された絵の差分を最小限に)できる可能性があります。

ゲーム製作者側の留意点

ゲーム製作者側が留意しなくてはならないのは、当然ながら、垂直同期:有効=60Hzという前提でゲームを製作しないということです。これに関しては、殆どのPC向けのゲームでは、フレーム間の時間間隔は可変となっているので、問題になることは無いと思います。また、垂直同期を無効にしたケースではプログラム側は特に留意することは無いように思えます。
ただし一点だけ気をつけたほうが良いことがあります。それは、フルスクリーン描画を行う際の、RefreshRateの設定値です。デバイス作成時に設定するRefreshRateは、そのままディスプレイの垂直同期周波数として設定され、D3DPRESENT_INTERVALがどのように設定されても、その値が使用されます。
つまり、120Hzの垂直同期周波数をサポートするディスプレイでは、60Hz/120Hzいずれにも設定して、垂直同期を無効にしてフルスクリーン描画を行うことができます。その際に観測されるFPSは、基本的には全く差が無い状態となりますが、ディスプレイの駆動周波数は異なるため、プレイヤーの体験としては違うものになってしまいます。
では、どうすれば良いかですが、現在のディスプレイのRefreshRateを取得し、同一のRefreshRateで使用可能なスクリーン解像度を使用すると良いと思います。また、ゲーム内のメニューから明示的に設定できるようにしても良いと思います。

初期化の例

以下の例では、現在設定されているRefreshRateを尊重しつつ、19×10解像度のフルスクリーン描画の初期化を行うコードです。解像度が存在しない場合の処理などは省略されています。

if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
  return E_FAIL;

D3DDISPLAYMODE curDM;
g_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &curDM);

D3DDISPLAYMODE targetDM;
UINT targetRes[2] = {1920, 1080};

ZeroMemory(&targetDM, sizeof(targetDM));

// check if there is 19x10 resolution with current refresh rate and color depth.
UINT cnt = g_pD3D->GetAdapterModeCount(D3DADAPTER_DEFAULT,
      curDM.Format);
for (UINT i=0; iEnumAdapterModes(D3DADAPTER_DEFAULT,
  curDM.Format, i, &dm);

  if (curDM.Format == dm.Format &&
      curDM.RefreshRate == dm.RefreshRate &&
      targetRes[0] == dm.Width &&
      targetRes[1] == dm.Height) {
    targetDM = dm;
    break;
  }
}

// you'll need proper handler...
if (targetDM.Width != targetRes[0])
  return E_FAIL;

static D3DPRESENT_PARAMETERS d3dpp = {
  targetDM.Width, 
  targetDM.Height,
  targetDM.Format,
  2,
  D3DMULTISAMPLE_NONE,
  0,
  D3DSWAPEFFECT_DISCARD,
  hWnd,
  0, //Full screen
  0,
  D3DFMT_UNKNOWN,
  0,
  targetDM.RefreshRate, // set the refresh rate that have been set.
   //D3DPRESENT_INTERVAL_ONE // enable v-sync
  D3DPRESENT_INTERVAL_IMMEDIATE // disable v-sync
};

// create a device.
if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
 D3DCREATE_SOFTWARE_VERTEXPROCESSING,
 &d3dpp, &g_pd3dDevice ) ) )