この記事は、インテル® デベロッパー・ゾーンに公開されている「Direct3D 12 Overview Part 4: Heaps and Tables」(https://software.intel.com/en-us/blogs/2014/08/07/direct3d-12-overview-part-4-heaps-and-tables) の日本語参考訳です。
パート 3 では、D3D 12 におけるリソースバインドの新しい効率性について説明しました。リソースハザード、有効期間、そしてメモリー管理追尾がどのように簡略化されたでしょう?この記事では、まだリソースバインドへの変更について説明を終えていません。これに関して触れなければならない重要な事がもう一つあります。新しい D3D 12 の描画コンテキストのように見えるか最終的には分かります。この記事のパート 1 で述べた "コンソール API の効率とパフォーマンス"の目標に近づくことができます。CPU の複数のコアとスレッドをより効率良く利用することに注目します。
冗長なリソースバインド:
いくつかのゲームを分析した結果、D3D 開発チームは、一般的なゲームはあるフレームから次のフレームへ同じコマンドシーケンスを使用することが分かりました。コマンドだけでなく、フレーム上の同じフレームをバインドする傾向にあります。CPU は、フレーム中のオブジェクトを描画するため、一連のバインドを生成します。多くの場合、次のフレームで CPU は、同じバインドを再び生成する必要があります。なぜそれらのバインドをキャッシュしないのでしょうか?開発者は、同じバインドが再利用できるようキャッシュを指すコマンドを使用するでしょうか?
パート 3 で、キューについて説明しました。呼び出しが行われると、ゲームは API が即座に呼び出しを実行すると想定しています。しかし、実際はそうではありません。GPU によって延期され、その後実行されるコマンド・キューがあります。もしそれらのバインドのうち 1 つを変更する必要がある場合、ドライバーは、すべてのバインドを新たな場所にコピーして、それらを変更しコピーされたバインドを使用して開始するよう GPU に指示します。これらのバインドの多くはスタティック値を持ち、更新の必要があるダイナミック値はわずかです。ゲームがそれらのバインドの一部分を変更しようとする場合、すべてのバインドをコピーを必要とします。たとえ 1 つだけ変更するのであっても、すべてを必要とします。そのため、たとえ小さな変更であっても、CPU には重いコストがかかります。解決策はないでしょうか ?次の文件を読んでみてください。
記述子:
記述子とは?簡単に言うと、リソースパラメータを定義するデータの一部です。基本的に記述子は、D3D 11 ビューオブジェクトの背後にあるものです。オペレーティング・システムがライフタイムを管理されることなく、GPU メモリーにある不透過なデータです。これには、テクスチャーとピクセルデータへのポインターのタイプ、フォーマット情報、MIP カウントが含まれます。記述子は、新しいリソースバインド・モデルの中心であり、D3D 開発チームのリードである Max McCullen は、"atom" を設けました。
ヒープ:
D3D 11 では、ビューが設定されると、記述子が読み込まれる GPU メモリー内の現在の場所へ記述子をコピーします。同じスポットで新しいビューを設定すると、D3D 11 は記述子を新しいメモリー位置へコピーし、GPU へ次の描画コマンドを新しい場所から読み込むことを指示します。この記事のシリーズで繰り返してきたように、D3D 12 は、記述子が作成およびコピーなどされた時にゲームやアプリケーション開発者に明示的な制御を行うことを可能にします。
ヒープは、記述子の大きな配列です。以前の描画やフレームから記述子を再利用でき、必要に応じて新たなストリームも可能です。レイアウトはゲームによって所有され、ヒープを操作するにはわずかなオーバーヘッドが生じます(データコピーによる)。ヒープサイズは、GPU アーキテクチャーに依存しますが、旧式のもしくは低消費電力の GPU は 65K に制限され、ハイエンドの GPU では、メモリー容量に制限されます。低消費電力の GPU に関しては、ヒープを超える可能性があります。そのため、D3D 12 は複数のヒープを可能にし、1 つの記述子から次の奇術師へ切り替えることができます。しかし、ヒープの切り替えは GPU フラッシュの原因となるため、切り替えは最小限にすべきです。
ここでは、記述子とヒープについて説明しました。次の疑問は、特定の記述子や記述子のセットをシェーダーコードにどのように関連付けるかということです。答えは?テーブル です。
テーブル:
テーブルは、ヒープ内の開始インデックスとサイズです。それは、基本的にコンテキスト・ポイントですが、API オブジェクトではありません。開発者は、必要に応じてシェーダーごとに 1 つ以上のテーブルを持つことができます。例えば、バーテックス・シェーダー向けの描画呼び出しは、ヒープのオフセット 20 から 32 の記述子を指すテーブルを持つことができます。次の描画を始める時、オフセットは 32 から 40 へ変更されるかもしれません。
最新の D3D 12 対応ハードウェアを使用すると、PSO におけるシェーダーステージごとに複数のテーブルを持つことができます。呼び出しごとに頻繁に異なるテーブルに切り替えられる可能性があります。呼び出しごと、フレームからフレームなどにスタティックなデータを含む第二テーブルを使用します。そうすると、ある呼び出しから次へ、頻繁に変更される記述子をすべてコピーされるのを回避できます。しかし、旧式の GPU では、シェーダー・ステージごとに 1 つのテーブルに制限されています。複数のテーブルは、最新のおよび今後登場するハードウェアでのみ持つことができます。
バインドなしと効率:
記述子ヒープとテーブルは、D3D チームがバインドなしのレンダリングを行い、PC ハードウェア全体にスケーリングするため保持されています。ローエンドの SoC からハイエンドの専用カードまで、D3D 12 は全てをサポートします。この統一されたアプローチは、ゲーム開発者に様々なバインドを行う可能性を開きます。D3D 11 ストリーミングから、すべて複数のフレームで再利用するため、あらゆるフレームとスタティックバインドのキャッシュを変更します。さらに、新しいモデルは複数の頻度での更新を含みます。再利用と各描画ごとに変更されるデータを持つダイナミック・テーブル向けのスタティック・バインドのテーブルのキャッシュを可能にします。新規の描画ごとに、すべてのバインドのコピーを行う必要が無くなります。
描画コンテキストの再表示:
以下は、これまで説明した D3D 12 による描画コンテキストの変更点です。新しい PSO と取得の除去を示します。しかし、まだ D3D 11 の明示的なバインドポイントがあります。
D3D 11 の描画コンテキストの最後を削除し、記述子テーブルとヒープを追加してみましょう。シェーダー・ステージごとにテーブルを持つか、ピクセルシェーダーで示すように複数のテーブルを持つことができます。
上記の図は、D3D 12 の描画コンテキストです。細粒度のステージオブジェクトは、パイプライン・ステージ・オブジェクトで置き換えられます。ハザード追跡と状態ミラーリングは排除されました。明示的なバインドポイントは、アプリケーションやゲームで管理されるメモリー・オブジェクトでは利用されなくなっています。D3D 12 については、まだ議論することがあります。
次のパート 5 では、バンドルについて説明します。
BUILD 2014 からの図は、マイクロソフトの D3D 開発リードである Max McCullen によるプレゼンテーションから抜粋したものです。
コンパイラーの最適化に関する詳細は、最適化に関する注意事項を参照してください。