DXGI_MODE_DESC1初期化のコンパイルエラー原因と対策|DXGI_RATIONALとメンバー順(C++/DirectX)

DirectX/DXGIのDXGI_MODE_DESC1をドキュメントの順番どおりに波括弧で初期化したのに、なぜかコンパイルエラー——。原因は「順番」ではなく、途中にあるDXGI_RATIONALなど“構造体メンバーの型”です。混同しやすいDXGI_MODE_DESCとの違いも含め、確実に通る初期化パターンを整理します。

目次

DXGI_MODE_DESC1とは:まず「定義(メンバーの型)」を確認する

DXGI_MODE_DESC1は、表示モード(解像度・リフレッシュレート・ピクセルフォーマット等)を表すDXGIの構造体です。C++で問題が起きやすいのは、メンバーの一部が「数値」ではなく「別の構造体」や「列挙型」になっている点です。

代表的な定義は次のとおりです(Windows SDKのヘッダーに定義されています)。

typedef struct DXGI_MODE_DESC1
{
    UINT                     Width;
    UINT                     Height;
    DXGI_RATIONAL            RefreshRate;
    DXGI_FORMAT              Format;
    DXGI_MODE_SCANLINE_ORDER ScanlineOrdering;
    DXGI_MODE_SCALING        Scaling;
    BOOL                     Stereo;
} DXGI_MODE_DESC1;

そして、問題の中心になるのがRefreshRateの型であるDXGI_RATIONALです。これは「分子/分母」でレートを表す、もう一つの構造体です。

typedef struct DXGI_RATIONAL
{
    UINT Numerator;   // 分子
    UINT Denominator; // 分母
} DXGI_RATIONAL;
メンバー型意味初期化で注意する点
WidthUINT横解像度単なる数値でOK
HeightUINT縦解像度単なる数値でOK
RefreshRateDXGI_RATIONALリフレッシュレート(Hz相当)構造体なので{分子, 分母}が必要
FormatDXGI_FORMATピクセルフォーマット列挙値を指定
ScanlineOrderingDXGI_MODE_SCANLINE_ORDER走査線順通常はUNSPECIFIEDで十分な場面が多い
ScalingDXGI_MODE_SCALINGスケーリング方式通常はUNSPECIFIEDで十分な場面が多い
StereoBOOLステレオ3Dの有無FALSE/TRUE(またはfalse/true)を指定

「ドキュメント順に書いたのに」コンパイルエラーになる理由

結論から言うと、エラーの原因は順番ではなく型です。DXGI_MODE_DESC1の3番目のメンバーはDXGI_RATIONALなので、そこに60のようなUINTを1つだけ渡すと型が一致しません。

ありがちなNG例:RefreshRateに60だけ渡してしまう

DXGI_MODE_DESC1 desc = {
    1920, 1080,
    60, // ← ここがDXGI_RATIONALではないので不一致
    DXGI_FORMAT_R8G8B8A8_UNORM,
    DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED,
    DXGI_MODE_SCALING_UNSPECIFIED,
    FALSE
};

このコードは「3番目にDXGI_RATIONALが必要なのにUINTしか渡していない」ため、多くの環境でコンパイルエラーになります。エラーメッセージはコンパイラや設定で変わりますが、概ね次のようなニュアンスです。

  • 初期化リストからDXGI_RATIONALへ変換できない
  • DXGI_RATIONALの初期化が不正
  • 初期化子の型が一致しない

OK例:RefreshRateは{分子,分母}で渡す

60Hzを指定したいなら、60/1として表現します。

DXGI_MODE_DESC1 desc = {
    1920,                        // Width
    1080,                        // Height
    { 60, 1 },                   // RefreshRate (DXGI_RATIONAL)
    DXGI_FORMAT_R8G8B8A8_UNORM,  // Format
    DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED, // ScanlineOrdering
    DXGI_MODE_SCALING_UNSPECIFIED,        // Scaling
    FALSE                        // Stereo
};

ポイントは3番目だけ二重の波括弧になることです。C++のリスト初期化は型チェックが厳密なので、「それっぽい数値」を並べても通りません。

波括弧でまとめて初期化する場合:メンバー順は固定で動かせない

DXGI_MODE_DESC1は(少なくとも一般的なWindows SDKでは)コンストラクタを持たない単純な構造体です。そのため、

  • DXGI_MODE_DESC1 desc = { ... };
  • DXGI_MODE_DESC1 desc{ ... };

のような書き方は集成(aggregate)初期化になり、値は構造体で宣言された順番に割り当てられます。

つまり、波括弧で書く限り「ドキュメントの並び」ではなくヘッダーにある実際のメンバー順がルールです。今回提示されている定義では、Width→Height→RefreshRate→…の順で固定になります。

順番を崩すとどうなる?

順番を入れ替えると、型が違えばコンパイルエラーで気づけます。しかし、型が同じ(または暗黙変換できる)フィールド同士を入れ替えると「コンパイルは通るのに意味が壊れる」という厄介な事故が起きます。

入れ替え例コンパイル何が困るか対策
Format と ScanlineOrdering をうっかり入れ替え多くの場合はエラー列挙型同士でも型が違うため、気づけることが多い順番を固定で守る/代入式にする
ScanlineOrdering と Scaling を入れ替え多くの場合はエラーこちらも別の列挙型なのでエラーになりやすい代入式にする
Width と Height を入れ替え通る解像度が縦横逆になり、実行時に画面サイズが想定とズレる代入式にして意図を明確化

「順番は関係ないのでは?」という疑問はもっともですが、波括弧で一括初期化する限り順番は関係します。逆に言えば、順番を気にしたくないなら、次のセクションの方法が実務では安全です。

実務でおすすめ:ゼロ初期化してからメンバー代入する

DirectX/DXGIの構造体はメンバーが増えたり、似た構造体が複数存在したりします。波括弧で一発初期化は短く書ける反面、1つでも型や順番を間違えるとビルドが止まる/あるいは気づきにくいバグになることがあります。

そこでおすすめなのが「まずゼロで初期化 → 必要なメンバーだけ代入」です。

DXGI_MODE_DESC1 desc = {}; // 全メンバーを0で初期化(Width/Heightも0になる)
desc.Width  = 1920;
desc.Height = 1080;
desc.RefreshRate = { 60, 1 }; // 60Hz
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
desc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
desc.Stereo = FALSE;

この書き方のメリットは、

  • 順番を意識しなくていい(代入なのでどこから書いても同じ)
  • メンバー名が見えるので、レビュー時にミスを見つけやすい
  • 途中に構造体(DXGI_RATIONAL)があっても迷いにくい

「= {}」と「未初期化」の違いはかなり大きい

DXGIの構造体は、未初期化のままAPIに渡すと予測不能な動作になりやすい領域です。特にBOOL Stereoのようなフラグ系は、偶然1になってしまうと意図しない分岐を引き起こします。

書き方初期状態おすすめ度コメント
DXGI_MODE_DESC1 desc = {};全メンバーが0高簡潔で安全。まずこれでOK
DXGI_MODE_DESC1 desc{};全メンバーが0高C++らしい書き方。意味はほぼ同じ
DXGI_MODE_DESC1 desc;未定義(ゴミ値)低代入し忘れがあると危険
ZeroMemory(&desc, sizeof(desc));全メンバーが0中古典的。C++では{}の方が読みやすいことが多い

C++20以降なら指定初期化も選べる(ただし制約あり)

C++20ではC言語のような指定初期化(designated initializers)が使えます。DXGIの構造体はまさにこの恩恵を受けやすい代表例です。

DXGI_MODE_DESC1 desc{
    .Width  = 1920,
    .Height = 1080,
    .RefreshRate = { 60, 1 },
    .Format = DXGI_FORMAT_R8G8B8A8_UNORM,
    .ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED,
    .Scaling = DXGI_MODE_SCALING_UNSPECIFIED,
    .Stereo = FALSE
};

ただしC++の指定初期化は、C言語ほど自由ではありません。一般に次の点を覚えておくと安全です。

  • メンバーの宣言順を飛び越えて並べ替えることはできない(宣言順に沿って書く必要がある)
  • 指定初期化と非指定初期化を混在させない方が無難
  • プロジェクトの言語規格設定(/std:c++20 など)が必要

「順番を完全に無視したい」という用途には向きませんが、メンバー名を明示しながら一括で初期化できるので、可読性は高いです。

DXGI_MODE_DESC と DXGI_MODE_DESC1 を混同しない

質問の文脈で非常に多いのが、DXGI_MODE_DESC1を触っているつもりで、実際には似た名前のDXGI_MODE_DESCを初期化しているケースです。両者は「ほぼ同じ」に見えますが、別の型です。

型主なメンバー特徴よくある事故
DXGI_MODE_DESCWidth, Height, RefreshRate, Format, ScanlineOrdering, ScalingDXGIの基本モード記述最後にStereoを書いてしまい「初期化子が多すぎる」
DXGI_MODE_DESC1上記に加えて StereoDXGI 1.2以降で拡張(Stereo追加)DESCのつもりで6要素だけ書くとStereoは0になる(意図せず固定されることがある)

混同すると起きる代表的なコンパイルエラー

例えば、DXGI_MODE_DESCを初期化しているのに7要素渡すと、典型的には次のような状況になります。

DXGI_MODE_DESC desc = {
    1920, 1080,
    { 60, 1 },
    DXGI_FORMAT_R8G8B8A8_UNORM,
    DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED,
    DXGI_MODE_SCALING_UNSPECIFIED,
    FALSE // ← DXGI_MODE_DESCにはStereoがない
};

この場合は「初期化子が多すぎる」「余計な要素がある」といったエラーになりやすく、原因は「順番」ではなくそもそも型が違うことです。

IDEの「定義へ移動(F12)」で型を開き、実際に使っている構造体がどちらなのかをまず確認すると、解決までが早くなります。

RefreshRate(DXGI_RATIONAL)の実用的な指定例

DXGI_RATIONALは「分子/分母」で値を表します。実際のHz相当の値はNumerator / Denominatorです。浮動小数点を直接渡すのではなく、よく使う値を決め打ちで用意しておくと読みやすくなります。

一般的な表記DXGI_RATIONAL(Numerator/Denominator)メモ
60Hz{ 60, 1 }最も一般的
120Hz{ 120, 1 }ハイリフレッシュでよく使う
144Hz{ 144, 1 }ゲーミングモニターの定番
59.94Hz{ 60000, 1001 }映像/放送系で頻出(約59.94)
23.976Hz{ 24000, 1001 }約23.976。映画系コンテンツで見かける

このあたりは「よくある値」を覚えるより、列挙した表示モードからそのままコピーするのが堅実です。たとえばIDXGIOutput/IDXGIOutput1で取得したモード一覧(DXGI_MODE_DESCやDXGI_MODE_DESC1の配列)には、実機がサポートする正確なDXGI_RATIONALが入っています。

ドキュメントは古い?間違っている?判断のコツ

WindowsのAPIドキュメントは基本的に信頼できますが、実務では「ドキュメントの表示」と「手元のWindows SDKヘッダー」の間にズレが生じることもあります。ズレを疑うときは、次の優先順位で確認すると迷いません。

  • 最優先:手元のWindows SDKのヘッダー定義(コンパイルが参照しているものが正)
  • 次点:IDEでの型定義(F12で開く、IntelliSenseの表示を確認)
  • 参考:Web上のドキュメント(更新タイミングの差があり得る)

今回のように「ドキュメント順で初期化したらエラー」になるケースは、実際には型の渡し方が間違っている、または似た型を取り違えていることがほとんどです。提示の定義どおり、RefreshRateがDXGI_RATIONALである限り、60単体で初期化できないのは自然な挙動です。

トラブルシュート手順:原因を最短で切り分ける

DXGIの構造体初期化で詰まったときは、次の順で確認すると無駄がありません。

  1. 変数の型を確認:本当にDXGI_MODE_DESC1か?DXGI_MODE_DESCになっていないか?
  2. 3番目の要素を確認:RefreshRateに{ Numerator, Denominator }を渡しているか?
  3. 一括初期化をやめて代入に切り替える:順番・省略・型の問題を一気に排除できる
  4. ヘッダー定義を開く:インストールされているWindows SDKでのメンバー順・型を確認する
  5. 列挙結果を利用する:表示モードを自分で組み立てず、取得したDXGI_MODE_DESC1を基にする

すぐ使える「安全な初期化」テンプレート

最後に、ミスが起きにくいテンプレートを載せます。まずはこれをベースにして、必要に応じて値だけ差し替えるのが確実です。

// 例:1920x1080 / 60Hz / RGBA8 / Stereoなし
DXGI_MODE_DESC1 MakeModeDesc1_1080p60()
{
    DXGI_MODE_DESC1 d = {};
    d.Width  = 1920;
    d.Height = 1080;
    d.RefreshRate = DXGI_RATIONAL{ 60, 1 };
    d.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    d.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
    d.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
    d.Stereo = FALSE;
    return d;
}

「一括初期化の短さ」を優先したい場合でも、RefreshRateだけは必ず構造体として渡す、という点は変わりません。どちらのスタイルを選ぶかはチームの方針次第ですが、レビューや保守の観点ではゼロ初期化+代入が強い場面が多いです。

まとめ:順番は固定、エラーの本質は「型」と「取り違え」

  • 波括弧で初期化する場合、値の並びは構造体のメンバー順で固定
  • RefreshRateはDXGI_RATIONALなので、{ 60, 1 }のように2要素で渡す
  • DXGI_MODE_DESCとDXGI_MODE_DESC1を取り違えると、初期化子の数や型が合わずに詰まりやすい
  • 迷ったらゼロ初期化+メンバー代入に切り替えると、順番問題を根こそぎ回避できる
  • ドキュメントの表示に不安があるときは、最終的に手元のWindows SDKヘッダーを正とする

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次