VulkanとDirect3D12に関するメモ


概要

 3Dモデルにボーンを入れて動かす事を最優先の目的としている。
 こちらに興味がある方は、迷うことはありません。 Unity を活用しましょう。 間違いなく最適解です。
 このページに書き留めた事々は、Unity やUnrealEngineを利用することで、貴方にとってデバッグ時点ですら不要となる筈です。
 このページでは、VulkanとDirect3D12を用いて、簡単なレンダリング関数群を実装した時に理解した事々を連ねております。

予備知識

 読み進める上で必要な事を連ねております。

描画パイプラインのイメージ

 図を作成するのは面倒臭いので省略します。
 簡単に言えば、描画パイプラインとは、並列演算とハードウェア実装による支援を実現することに最適化したプロシージャと考えて、大きな誤りはないと考えております。

 要点のみを簡潔に整理して、外観を俯瞰、理解した気分になるならば、この程度の認識で過不足ないような気がしています。

主文(メモ)

ディスクリプタ(記述子)ってなんぞ?

 シェーダがGPU リソースにアクセスする際に間接参照するポインタテーブル。

 シェーダで迷わない為に必要な前提理解として「明示しない限り、若い番号から昇順にレジスタを割り当てる」点があります。
 Vulkan(GLSL)だとLayoutの宣言でSet, Bindingがレジスタ番号に当たり、HlSLだと「t0, b0, s0」がレジスタ番号に相当するようです。
 ですが、API 仕様とシェーダ言語を見る限り、レジスタというよりも「ディスクリプタ ヒープ テーブル」の要素番号に近い感触があります。
 実際のハードウェア実装では、間違いなくレジスタにロードされるのは疑いありませんが、プログラムレベルでイメージを掴むなら、この感覚の方が理解しやすいと考えます。

 何故、レジスタ番号の話をするのか?と言えば、Vulkanの場合とDirect3Dでは、全く異なる指定方法を取り、この設計に留意しておかないと、まともにシェーダが掛けません。
 そして、このページで記述したい点も、この部分です。

 さて、シェーダと描画パイプラインは、何を参照するのでしょうか?
 予備知識で簡単に書きましたが、描画ターゲット(RTV)、深度ステンシル(DSV)、索引配列、頂点配列、シェーダリソース(SRV:テクスチャ)、定数バッファ(CBV)です。
 ディスクリプタというのは、これらを参照させるためのポインタの役割を持つと理解して、間違いないと考えます。
 これらの理解をオーソライズする大学教授のダメだし(添削)が欲しい所ですが、無料の情報ですから、その辺りの担保は諦めてください。
 自分の実験結果からは、この理解で大きな誤りはない(※漏れがないとは言ってない)と考えております。

 いずれにしろ、大事な事は、ただのポインタテーブルと捉えて、怖がらない、忌避しない、穢れみたいに排斥せず、受け入れる事が肝要です。

ディスクリプタって美味しいの?

 美味しいです。 かなり美味しいと思います。 このディスクリプタ(たぶん日本語だと記述子)の概念が導入された理由は、一言で言えば、これだと考えております。
 固定機能ステートと描画パイプラインを使い回すな。 描画パイプラインが並列化しずらいんじゃ! 並列化時代にメモリ節約思想は、速度性能を落とす
 じゃないかと。 VulkanとDirect3D12(下敷きのMantle)の設計思想は、排他制御(競合)の排除を目指したものと考えております。

ディスクリプタの目的

 シェーダと描画パイプラインが並列に実行する為に必要な情報とは、何か?という観点に直結していると考えます。
 もっとざっくり言えば「シェーダと描画パイプラインは、何を入力して、何を参照して、何を出力するの?」という意味です。

 Direct3D11 時代に確立された概念が次節の理解の助けになると思います。

VulkanとDirect3D12におけるディスクリプタに関する違い

 まず関係する要素を列挙します。

  1. Vulkan
    • ディスクリプタセットレイアウト(VkDescriptorSetLayout)
    • ディスクリプタプール(VkDescriptorPool)
    • ディスクリプタセット(VkDescriptorSet)
  2. Direct3D12
    • ルートシグネチャ(RootSignature)
    • ルートシグネチャ内のパラメータテーブル(ParameterTable)
    • ディスクリプタテーブル(DescriptorTable)
    • ディスクリプタヒープ(DescriptorHeap)
 うん。 吐きそうですよね。 なんじゃこりゃ、と。

役割 Vulkan Direct3D12
シェーダリソースとレジスタの紐付け ディスクリプタセットレイアウト(VkDescriptorSetLayoutBinding) ディスクリプタテーブル(RootSignature.ParameterTable=DESCRIPTOR_TABLE)
シェーダが利用するレジスタ数 ディスクリプタプールサイズ(VkDescriptorPoolSize) (たぶん、ない。 強いて言えば「D3D12_DESCRIPTOR_RANGE」だけど、これはレジスタ番号を決定することが趣旨)
シェーダが参照するポインタを収める実体 ディスクリプタセット(VkDescriptorSet) ディスクリプタヒープ(DescriptorHeap)
シェーダのヘッダ構造体 N/A(D3D12に存在するのは、使いやすくするための工夫) ルートシグネチャ(RooSignature)

 整理すると綺麗ですよね。 シンプルで理解が進む。 数学や物理学は「数式」で整理する。 数式は、本当に美しい。 綺麗で洗練されていて、かつ必要充分に網羅されやすい
 情報工学は「表」で整理、理解すると分かり易い。 
 でも、奇特な人が「2018/08/23」に読んでいたら、この節は、少し懐疑的に読んで欲しいです。 現在、飲み過ぎで、まともに読み直していないので。

まとめ

 その意欲が湧けば、このページは、改訂する予感があります。
 新しい構造と用語で吐き気がするかもしれませんが、自分なりに理解すれば、それなりに分かり易く感じられ、忌避感を軽減できると考えております。
 OpenGL, DirectX9c 頃に黎明期を迎えたコンピュータグラフィクス技術のコンシューマ化は、一つの区切りを迎えたような感覚を覚えています。
 プリミティヴなプログラムとは、別に趣味の領域ではなく、デバッグ、テスト、改修効率の改善、理解の深層化に必要な"情報"であり、不可欠なものです。
 ブラックボックスに理解して、魔術的、手順的な理解で合理化、最適化と思い込むのは、合理ですが、そこで立ち止まり、思考停止するのは、合理的ではありません。

 経済競争の観点で否定するならば「負けを認めて軍門に降る行為」に等しいと感じます。

 アメリカは、LionでありHunter、Frontierを開拓するPionierであっても、まったくScavengerを志していない。
 そこにアメリカを賞賛する種(seeed)がある。 俺もPionierでありたい。

附則

 大した文章ではありませんが、学生、生徒、児童、学術機関、教育機関は、好きに参考、改変、引用、コピペして構いません。 連絡も要りません。
 商業活動で活用するならば、プロなら、せめてコピペせず手打ちでコピーしてください。
 Direct3D13が出るまでが寿命の文章、そして新しい世代が無駄に迷うことを回避(=ショートカット)させる事を趣旨とした文章ですから。
 どうせ「プロw」が守る事はないだろうけどな。

 毒を盛りましたが、やっぱりプリミティヴなAPIを使ったプログラムは面白く、楽しい。
 どこかの大学教授(岡山大学?だったかな?)が数年前にプリミティヴなAPIによる実装に関する迷ったような言葉を発していましたが、そんな事はないと私は思っています。
 自虐的な言葉になりますが、これは「擬似的、妄想的」だけど数学のような楽しさ、好奇心を満たす楽しさがある。
 けれど、商業目的でプログラムをするならば、迷うことなくUnityやUnrealEngineを使え。 特にUnityは、UEとは別格の価値がある。(トーンシェーディング)
 どうせ、不十分な理解でデバッグに挑むと深い理解を必要とするから。