ゲーム開発の備忘録

趣味のゲーム開発でのノウハウや、技術的に嵌ったポイントを忘れないように書き記しておくブログです。

D3D11On12利用環境でフルスクリーン/ウィンドウ切替を実行できるようにする

はじめに

 DirectX12でフルスクリーンとウィンドウを切り替える方法については、書籍やWeb上でも情報があります。基本的にはDirectX11の時にも使用していたDXGI側の関数を呼んで切り替えればよいのですが、DirectX12の場合、解像度に依存するリソースの事前解放および再生成が必要になります。具体的には、バックバッファと深度ステンシルバッファの再生成が必要になります。これを実施しないと、IDXGISwapChain4#ResizeBuffers()関数実行時にエラーになります。

 しかし、D3D11On12を利用している場合、上記の対処ではエラーを解消できません。以下のstackoverflowでも議論されていますが、最終的にMicrosoftに不具合報告をするという結論で終わってしまっています。
stackoverflow.com

 本記事では、D3D11On12利用時のフルスクリーン/ウィンドウ切替について、最終的に私がどう対処したかをまとめておきます。

バイスロスト対応

 結論から言うと、D3D11On12を利用している場合は、IDXGISwapChain4#ResizeBuffers()関数実行前にデバイスロスト発生時と同じ対処を行わなければなりません。即ち、ID3D12Deviceまで削除し、また再生成する必要があります。事前生成したデバイスやリソースをどこまで削除したらエラーが発生しなくなるのかステップバイステップで検証しましたが、一度全てをまっさらにしないとエラーが発生してしまうという結果になりました。

 余談ですが、デバイスロストはDirectX9時代では対処必須なほどよく発生していましたが、DirectX11、DirectX12では発生率が低くなっています。しかし、それでも発生する可能性はゼロではないため、フルスクリーン/ウィンドウ切替への対応と同時に対処してしまいましょう。
learn.microsoft.com

リソース再生成の手段について

 デバイスロスト対応を行うにあたり、問題になるのがグラフィックス関連リソースをどのように再生成するかです。ゲーム起動直後に一括して生成しているリソースは対処が簡単で、同様に一括生成すればよいだけです。PCゲームはそこまでメモリに厳しくないこともあり、ほとんどのリソースを一括生成しているのではないでしょうか。私の場合、以下のリソースは一括生成しています。

  • シェーダ
  • パイプラインステート
  • テクスチャ
  • フォントデータ
  • ブラシ
  • テキストフォーマット

上記に該当するリソースは、デバイスロストからの復帰時に生成用の関数を呼ぶだけで済ませています。

 さて、問題はゲーム起動時に生成するわけにはいかないリソースです。具体的には以下が挙げられます。

  • コンスタントバッファ
  • テキストレイアウト
  • テキストパスジオメトリ

後者2つは場合によっては一括生成できるかもしれませんが、ゲーム内で文字列を動的に生成する場合は事前に生成することはできませんよね。これら3つはオンデマンドで生成できるような仕組みを作っておく必要があります。私の場合は全てmapに格納していますので、描画命令実行時にmapから要素が無くなっていることを検知したら、各エフェクト・描画物を表すクラスのメンバ関数として用意していた生成用の関数を、すぐに呼び出すようにしました。このため、各エフェクト・描画物のインスタンス生成時に確保し、以降は値を更新しないようなコンスタントバッファ(例えば、値が固定された色情報など)についても、コンスタントバッファが消失したことをチェックできるようにするために、毎描画時に初期値で値の更新をかけるようにしています。