はかせのラボ

私の頭の中を書いていく雑記ブログです

DirectX12 デスクリプタヒープと友達になりたい

あいさつ

どうも、はかせです。
前回はMMDもといPMDを出すために
PMDファイルの読み込みをやりました。

読み込んだら後は表示するだけ~なんて思ってたんですが
一向にできません。

前回の参考元のサイトを参考に
プログラムを組んでいるんですが、
常にD3D12ERRORの何かが出ますw

なのでPMDファイルを表示するプログラムとかについては
また後日に上げます。
(どんな形でも実装してあげます)

てなわけで進捗報告は無理なので
今回はやってくなかでデスクリプタヒープの
扱いがちょこっとだけわかったので
その部分を備忘録がてら共有したいと思います。

デスクリプタヒープとは

以前記事にも軽く上げましたが
もう一度改めて書きます。

DirectX12には11までにはなかった
デスクリプタというものが出てきます。

これは11までのViewに該当するもので
今までシェーダーリソースビューとか
レンダーターゲットビューとかに分かれてたものが
このデスクリプタに集約されているようです。

そしてこのデスクリプタを管理するのが
デスクリプタヒープの役割となります。
よくデスクリプタの管理配列なんて言われますね。

というかプログラマが直接触るのは基本
デスクリプタヒープです。
デスクリプタ本体だけで使ったりすることはまずないです。

基本的にデスクリプタヒープにあれこれ設定して使います。

使い方

まず作り方です。
・D3D12_DESCRIPTOR_HEAP_DESC構造体に値を入れる
・CreateDescriptorHeapを呼ぶ

D3D12_DESCRIPTOR_HEAP_DESC構造体は下記の値を持っており
必要な値をセットしデスクリプタヒープの設定を行います。

・Type このデスクリプタは何のViewなのか設定
・NumDescriptors デスクリプタヒープ内に入れるデスクリプタの数
・Flags シェーダーから参照するか
・NodeMask どのGPU使うか

細かい説明はしません。
というかできません。

CreateDescriptorHeapはただ引数当てはめて
呼び出すだけなので特に難しいことは無いです。

デスクリプタヒープ内のデスクリプタを参照する

デスクリプタヒープが出来たところで
次は管理しているデスクリプタ本体の参照方法です。
ぶっちゃけここの理解だけで今日が溶けましたw

まずはイメージ掴んでもらうために先にコードを上げます。

//レンダーターゲットを作成する処理
//積まれているGPUによって各データのサイズが変わるためどれだけずらしていけばいいかを取得する
UINT size = mDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
for (UINT i = 0; i < RTV_NUM; ++i)
{
	//スワップチェインからバッファを受け取る
	ThrowFailed(mSwapChain->GetBuffer(i, IID_PPV_ARGS(&mRenderTarget[i])));
	//デスクリプタヒープの先頭アドレスを取得
	mRenderTargetViewHandleArray[i] = mRenderTargetViewDescriptorHeap->GetCPUDescriptorHandleForHeapStart();
	//目的の要素までポインタをインクリメント
	mRenderTargetViewHandleArray[i].ptr += size * i;
	mDevice->CreateRenderTargetView(mRenderTarget[i].Get(), nullptr, mRenderTargetViewHandleArray[i]);
}

やってることはほとんどコメントで書いた通りです。
(勉強メインのコードだとこういうコメントいっぱいつくよね・・・?)

デスクリプタヒープはデスクリプタの管理配列と言われることがある通り
デスクリプタが内部にいくつかあります。

この中から目的のデスクリプタにアクセスしたいわけですが、
インデックスを使ってアクセスはできません。
なので実際に先頭から何番目かという形で要素を指定します。
イテレーター使って配列操作する感じですね。
(この発想が出なきゃ今でもはてなだらけだったと思う)

そしてポインタのオフセットを決め打ちでなくて
動的に取得している理由なんですが、
調べたところDirectX12がGPU内に確保するデスクリプタのサイズは
積まれているGPUによって変わる
らしいです。
(GPUごとにメモリ管理やらなんやらが違うんですかね)

なのでGetDescriptorHandleIncrementSizeを呼び出して
ポインタをずらすオフセットを取得し、
オフセット*何番目かでポインタをずらして目的のデスクリプタにアクセスします。

あとがき

今回はデスクリプタヒープの話でした。
ポインタをインクリメントするっていう
火遊び大好きな私からするとワクワクしかしないアクションを
強要されるというDirectX12の設計思想には感服いたします。

ただ調子乗ってうぇーいってインクリメントしてると
管理してるメモリの範囲外に出てバグるっていうね。
(アプリの強制シャットダウンや最悪ブルスクとか)

11の時は内部的なセーフ機構が一定値有ったので
火遊びしても大して怖くなかったんですが、
12はその辺ないのでやりすぎると
本気でパソコン逝くんじゃないかと少しビビり始めました。
(既にこのパソコン一回逝ってるし)

パソコンを壊さない範囲で楽しみたいと思いまーす。
それでは今回はこの辺でノシ