Direct3D* 12 概要 パート 3: リソースバインド

ゲーム特集

この記事は、インテル® デベロッパー・ゾーンに掲載されている「Direct3D 12 Overview Part 3: Resource Binding」(https://software.intel.com/en-us/blogs/2014/07/30/direct3d-12-overview-part-3-resource-binding) の日本語参考訳です。


本シリーズの パート 2 では、パイプライン状態オブジェクトや PSO そして、ハードウェアとのミスマッチによるオーバーヘッドの排除による利点について説明しました。この記事では、リソースのバインドと D3D 12 の開発チームがこの分野でどのように CPU オーバーヘッドを軽減することに取り組んだかを説明します。先に進む前に、D3D 11 におけるリソースのバインドについて簡単に触れておきます。以下は、描画コンテキストの図で、左が D3D 12 PSO、右側が D3D 11 におけるリソース・バインド・モデルです:

Direct3D_v3_Picture1

各シェーダーの右側に、明示的なバインドポイントがあることがわかります。明示的なバインドモデルとは、パイプラインの各ステージが参照できる特定のリソースを有することを意味します。それらのバインドポイントは、GPU メモリーの参照リソースを指します。それらは、テクスチャー、描画ターゲット、バッファー、UAVなどです。リソースのバインドは、実際には D3D 以前から長い時間行われています。シナリオに隠れた複数のプロパティーを処理するアイディアは、ゲームが効率良く描画コマンドを送出するのを支援します。しかし、システムは 3 つの主要分野で多くのバインドを検証する必要があります。それらの分野を見直し、D3D チームがどのように D3D 12 向けに最適化を行ったか見ていきましょう:

リソースハザード:

ハザードは、描画ターゲットからテクスチャーへの移動のような通常の移行です。ゲームがフレームを描画する時、それが周辺のシーンの環境マップであることを意味します。また、ゲームが環境マップの描画を終了すると、テクスチャーとしてそれを使用することを考慮します。何かが描画ターゲットやテクスチャーとバウンドされている場合、その処理中はランタイムとドライバーの両方が追跡しています。ランタイムとドライバーがバインドを検出すると、古い設定をアンバインドし、新しい設定を適用します。この方法でゲームは必要に応じてスイッチを作り、ソフトウェア・スタックはシーンの背後にあるスイッチを管理します。さらに、ドライバーは描画ターゲットをテクスチャーとして使用するため、GPU パイプラインをフラッシュする必要があります。そうしないと、GPU でリタイアする前にピクセルが読み取られると、コヒーレント状態を保てません。本質的に、ハザードはデータのコヒーレントを確実にするため、GPU 内で余分な処理を要することがあります。これは一例にすぎず、さらに多くの可能性があります。ここでは簡単に説明するため、この例を使用します。

D3D 12 におけるそのほかの機能と拡張と同様に、このソリューションはゲームに多くの制御を可能にします。フレーム内に 1 つのポイントがあるとき、なぜ API とドライバーはすべてのワークを追跡しなければいけないのでしょうか?ここでは、およそ 60 分の 1 秒で、1 つのリソースから他のリソースへ切り替えることを対象にしています。制御をゲームに戻すことですべてのオーバーヘッドが軽減され、ゲームがリソースの変換を必要とするときに、一度だけコストを要するだけで済みます。以下は、D3D 12 で追加された実際のリソースバリア API です:

D3D12_RESOURCE_BARRIER_DESC Desc;
Desc.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
Desc.Transition.pResource   = pRTTexture;
Desc.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
Desc.Transition.StateBefore = D3D12_RESOURCE_USAGE_RENDER_TARGET;
Desc.Transition.StateAfter  = D3D12_RESOURCE_USAGE_PIXEL_SHADER_RESOURCE;
pContext->ResourceBarrier( 1, &Desc );

API はより直感的で、リソースを宣言し、ソースとターゲットの両方の使い方を定義します。ランタイムとドライバーに変換を伝えるため呼び出しが続きます。多くの条件付きロジックによってフレーム表示にまたがって追跡する代わりに、明示的になります。ゲームではそれはすでによく知られています。すべての追加ロジックを引き出すという利点があります。フレームごとに一定時間もしくは、任意の周波数で、ゲームに必要な遷移を可能にします。

リソースの有効期間:

D3D 11 (および以前のバージョン)では、呼び出しはキューに投入されるように振る舞います。呼び出しが行われると、ゲームは API が即座に呼び出しを実行すると想定しています。しかし、実際はそうではありません。GPU によって延期され、その後実行されるコマンドキューがあります。これは、GPU と CPU 間の高い並列性と効率を可能にしますが、多くの参照カウントと追尾を必要とします。すべてのカウントと追尾は、CPU パワーを消費します。

ゲームでこの問題を解決するには、リソースの有効期間を明示的に取得します。D3D 12 では、もはや GPU のキューの特質を不透過にせず、ゲームは送出した、その後実行されるであろうコマンドリストを知ることができます。Fence API は、GPU の進行状況を追跡するため追加されました。ゲームは、与えられたポイント(おそらくフレームあたり一度)でチェックでき、どのリソースがもはや必要ないかを判断し、メモリーであれば解放することができます。リソースを解放するため追加ロジックを使用して存続を追跡する代わりに、必要なくなったときにメモリーを解放します。

リソース残留管理:

GPU は多くのビデオメモリーを使用しますが、実際は頻繁にさらに多くを必要とします。これは、大量のメモリーを搭載する専用ビデオカードでは特に顕著です。そこで、コマンドフローのようにメモリー内と外にあるパージを管理するリソース残留管理があります。メモリー管理によって、ゲームからは無制限のメモリーがあるように見えます。繰り返しますが、参照カウントと追跡にはコストがかかります。

リソースの有効期間と同じように、ゲームはリソースの残留を明示的な制御によって取得します。D3D 11 は、オペレーティング・システムによって残留と制御フローを追尾します。一般的に、ゲームは描画コマンドのシーケンスが参照する一連のリソースを知ることができます。D3D 12 では、ゲームがそれらをメモリーに移動するのを明示的にオペレーティング・システムに伝えることができます。そしてコマンドが実行された後に、ゲームはメモリーからリソースが削除されるのを待機できます。

状態ミラーリング:

前述した 3 つの領域が最適化されると、より効果的な 4 つ目のスポットが明らかになります(パフォーマンスゲインはわずかですが)。バインドポイントがランタイム追尾を設定すると、ゲームはパイプラインに何がバインドされているかを調査するため後に呼び出すことができます。バインドポイントは、ミラーリングもしくはコピーされます。これはミドルウェアが容易に処理を行うため設計された機能です。そのため、コンポーネント化されたソフトウェアは、描画コンテキストの現在の状態を調べることができます。一旦リソースのバインドが最適化されると、ミラーリングされた状態の複製は必要なくなります。フロー制御を前の 3 つの領域から排除することに加え、状態ミラーリングも同様に削除されます。

これは、D3D 12 のリソースバインドで改善及び効率化されています。多くのチャーンは排除されました。これまでリソースバインドは、制御フローと追尾ロジック、ランタイムとドライバーで設定と取得が必要でした。これらのすべては、ゲームに制御を戻すため排除されました。もはや、D3D は、GPU のキューの振る舞いを隠匿しません。リソース管理におけるすべての細々したことは、今やゲーム内で発生し、今度はゲーム開発者が必要であると見なしています。

次のパート 4 では、ヒープとテーブルに関して説明します。

BUILD 2014 からの図は、マイクロソフトの D3D 開発リードである Max McCullen によるプレゼンテーションから抜粋したものです。

コンパイラーの最適化に関する詳細は、最適化に関する注意事項を参照してください。

タイトルとURLをコピーしました