VulkanのDescriptorの構造をようやく理解したメモ

Vulkanはむずい。とにかくむずい。でも情報は探せばきちんとある部類のネタで、公式ドキュメントを読み込めば必ず理解できる。そのへんが楽しい。

最近Descriptorの仕組みが分かったのでメモる。DescriptorSet、DescriptorPool、DescriptorSetLayoutという主に3つのオブジェクトがあるのだが、PoolにだけSetと付かないのはなんでだろうと思っていたら割と簡単な理由だった。

そもDescriptorとは

UBOやテクスチャなど、シェーダに渡すUniform変数の類を設定するためのオブジェクト。リソースディスクリプター。これが無いと例えば座標変換行列とか、複数の頂点で共有するような情報をシェーダに渡すことができず、頂点バッファの全ての頂点データに重複する同じデータを書き込む羽目になる。(実はPushConstantという別の手段はある…)

DescriptorSetとは

複数のDescriptorをまとめたもの。基本的にシェーダにはDescriptorではなくDescriptorSetの形で渡すことになる。具体的にはシェーダ描画のためのコマンド群をコマンドバッファに詰め込む際、VkCmdBindDescriptorSetsという関数で紐づける。

実は、VulkanのAPIで触れるオブジェクトに「VkDescriptorSet」はあるが「VkDescriptor」なるものは存在しない。いじるときはVkDescriptorSetの何番目のDescriptor、という形でいじる。この辺が自分の引っかかっていた原因だった。

DescriptorPoolとは

Descriptorの実体を持っているオブジェクト。DescriptorSetを作る際には作る前に予めDescriptorPoolを作っておく必要があり、そこからDescriptorを確保してDescriptorSetを構築する必要がある。メモリ空間からオブジェクトをnewで確保してポインターに入れるのと同じ話で、この例えで言えばメモリがDescriptorPool、ポインタがDescriptorSetにあたる。

ただし普通のメモリとは違う点として、Descriptorには種類がある。

例えば「UBOデスクリプタ3個、画像リソースデスクリプタ2個」という大きさのDescriptorPoolを作成した場合、そこからUBOデスクリプタ4個を持つDescriptorSetを作ることは出来ない。必要な種類のDescriptorを必要な数だけ持ったDescriptorPoolを作る必要がある。

DescriptorSetLayoutとは

そのまんまDescriptorSetの構造を表すオブジェクト。DescriptorSetを作成するときにはこれが必要。

DescriptorSetの中でDescriptorは0から始まる番号で並んでいる。何番目のDescriptorはどんなDescriptorなのか、というのを示すのがDescriptorSetLayout。

シェーダは基本的にパイプラインと紐づけられるため、パイプラインを作る際にもDescriptorSetLayoutを渡す必要がある。コマンドバッファに描画コマンドを記録するときにパイプラインとDescriptorSetをバインドするが、その際にそのパイプラインに渡したDescriptorSetLayoutとコマンドバッファにバインドしたDescriptorSetが合わないと多分怒られる。

余談だが、DescriptorのDescriptorSet内での「何番目」という数字をbinding numberと言うらしい。GLSLのシェーダにおいてもUniform変数は以下のように指定する。

なお、layout構文はbinding以外にもsetという引数を指定できる。これは、あるコマンドに複数のDescriptorSetをバインドしたときに何番目のDescriptorSetという意味で使えるものと思われる。バインドはvkCmdBindDescriptorSetsというAPIで行うがSetsという複数形からも分かる通り複数のDescriptorSetをバインドできる。なおその場合、それに対応してパイプラインにも複数のDescriptorSetLayoutを渡す必要があると思われる。(その辺未確認)

 

この辺の話は全部公式ドキュメントを見れば分かるには分かるのだが、日本語でしっかり0から学べる資料というのがなかなかないので、知見が多少固まったら備忘録も兼ねて書いて固めてみたいと思う。いつものやるやる詐欺になりそうだけど…

コメントを残す

メールアドレスが公開されることはありません。