作業記録(Vulkan)


 このページには、流行のVulkanを用いた3DCG描画機能を実装した結果から得た情報を記しております。
 人に読ませる事を意図した内容ではなく、かつ素人が陥る極端で偏った着眼点で記しております。
 従いまして、無闇に鵜呑みにせず大学専攻の准教授、教授、専門学校の研究者、および書籍などと必ず比較して正しい認識であるかを確認してください

序文

 Vulkanは、GPU(沢山の計算コアを持った計算機。メモリと4成分レジスタが特徴)を操作するAPIです。
 Direct3D9の時代のAPIとは異なり、プログラマブルシェーダと呼ばれるプログラムを軸に動作します。
 Direct3D11や12と共通する点は多いですが、感覚的にはDirect3D12とほぼ同じじゃないかと感じました。
 特徴的な点は、次の通りでDirect3D11にはなかった仕組みが存在します。

 特徴的な機能集合としては、この程度だと思います。
 このページでは、これらを一つづつ簡単に説明していきます。

機能が司る役割についての説明

インスタンス(vkInstance)

 Vulkan ドライバと接する部分でユーザプロセス側のインスタンスであるからインスタンスと名付けているように見受けました。
 ドライバとの通信を確立する接点であり、ここで機能レイヤと拡張機能を選択します。
 取り立てて、多くの引数がありませんので、とりあえず、この程度の認識で大過ないと考えます。

論理デバイス(vkDevice)

 アプリケーション側の観点で言えば、ここからGPU(Direct3Dで言う所のアダプタ?)を選択するようです。
 つまり、複数のアダプタを持っている端末の場合は、ここからアダプタを選択します。
 SLI 環境で、どのように提示されるのかは、環境がないので未確認ですが、何となくデバイスが提供するドライバに依存するように思います。
 これを書きながら、ふと気になったのは、マルチディスプレイの場合は、どうなるんだろう? そういった情報を照会するAPIを見かけませんでした。
 よほどの事がない限りは、とりあえずはGPU アダプタを選択するものと認識しておけばよいように思います。

コマンドプール(vkCommandPool)

 コマンドバッファを生成するファクトリ。 C++ で言うヒープを司る受付と認識しておけば充分に思います。

コマンドバッファ(vkCommandBuffer)

 GPU に投入する命令をリスト構造で管理するアルゴリズムをラップする部分と認識すれば問題なさそうです。
 なので、私の中では「命令リスト」と呼んでます。 Direct3D11だと、ここがデバイスコンテキストの一部に当たると思います。
 それよりは洗練された仕組みです。

物理メモリ(vkMemory)

 GPU 上の物理メモリを指す。 これ自体が管理するのは、メモリ量とポインタな様子です。

バッファ(vkBuffer)

 物理メモリの役割を規定する仕組みを指す。
「この物理メモリは、バッファとして使う(この作戦は、俺一人で遂行する(WolfTeam))」とドライバに伝えるものなようです。

イメージ(vkImage)

 物理メモリの役割を規定する仕組みを指す。
「この物理メモリは、イメージ(画像)として使う」とドライバに伝えるものな用です。
 何故、伝える必要があるか?と言えば、ドライバに便宜を図って貰うためですね。

サーフェース(vkSurface)

 プラットフォームに依存するウィンドウと接続する仕組みを指します。
 サンプルプログラムが面白く、Windows, Android, X-WindowSystemの例もありました。
 最近だとQtが流行っているので、X-WindowSystemを直接使っている人は、まずいないだろうな、と思いました。
 この部分は、かなり繊細に扱う必要があります。
 繊細といっても難しい事はなく、端的に言えば、「タイミング」と「相互のライフタイム」の調停をシビアに扱う必要がある。
 実際、Windows で試した時に簡単にコアダンプしました。
 例えば、ウィンドウを最小化したタイミングで描画を行うと私の環境だと自然にプログラムが落ちました。

スワップチェイン(vkSwapchain)

 サーフェースに表示するフレームバッファを管理する仕組みを指します。
 そして、これしかやっていないように思います。 機能的には、本当に切り替えることしかやっていない。
 その切替動作一つをとっても、深く覗き込めば、ディスプレイとの通信(DisplayPort/HDMI, CPM)部分があります。
 ですから、言葉で言えば簡単というだけの話になります。 デバイスメーカーの技術者の胃腸のおかげです。

フレームバッファ(vkFrameBuffer)

 描画ターゲットを管理する構造です。 つまり画素バッファや深度バッファを集合にして管理する構造体という考え方です。
 こうする事でデバイスや中間レイヤ、並びに描画パイプラインが描画ターゲットを排他制御することを可能にしています。
 並列化を進めるために必要となった仕組みという印象を受けました。

描画パス(vkRenderPass)

 Direct3D11には見当たらなかった部分です。 ざっくり言えば、普通に解説される描画パスそのものです。
 ここでは、フレームバッファの取り扱いを既定します。 付随的な位置づけでマルチサンプリングの要否を選択します。
 少し私見を混ぜますと、遅延レンダリング(代表例で言えば、深度バッファシャドウ手法)やマルチパスを取り扱うために設計されているように感じました。
 描画パスとフレームバッファの関係は密で、描画パスを主体と捉えれば、フレームバッファは対象という関係づけに見えます。
 ここは、かなり面白く洗練された仕組みです。 この仕組みを観て8段のマルチパスを組み立てたいと思ったぐらいです。
 但し、性能追求のために厳しい制限がつきまといます。
 平たく言えば、全ての寸法は同一。 それ以外は許さないです。
 つまりハードウェア処理に無駄をなくして、単純かつ高性能を実現するための設計になっているようです。
 ざっくり言えば、画素数が違うと面倒くさい。 ハードウェアにおける面倒臭いとは、複雑な物理実装を要求される、という事です。
 それよりはソフトウェアで頑張れよ、逃げ道は用意してあるから、と言われている気がしました。

ディスクリプタプール(vkDescriptorPool)

 シェーダに関係する仕組みです。 ディスクリプタプールと代替似たような位置づけだと捉えました。
 プールと名前がついていますが、むしろクォータに近いように思います。

ディスクリプタレイアウト(vkDescriptorLayout)

 シェーダが求める入力パラメータの関数プロトタイプ宣言。 この一言で全てと言って問題ないと考えます。
 単位は、Set単位。

描画パイプラインレイアウト(vkPipelineLayout)

 ディスクリプタレイアウトの集合を指す。
 シェーダに与える入力パラメータを規定する構造体の宣言だと思います。
 ディスクリプタレイアウトが部分集合で、こちらがサブセットに当たる。
 シェーダを類型に当てて管理する上で、欲しくなる分離だと思います。
 Direct3D11で言えば、テクスチャと定数バッファを明確に分けて取り扱いますが、それを抽象的に扱いたい要請から設計されたのだろうと思います。

ディスクリプタセット(vkDescriptorSet:セットとは「集合」の意味)

 シェーダに引き渡す入力パラメータ(=ポインタ)の実体を指す。
 構造体のインスタンスと言えば、アプリケーション側の認識として間違いないと思います。
 この構造体のメンバは、ポインタしか持たない。

描画パイプライン(vkPipeline)

 固定機能のパラメータとシェーダ、および描画パスを紐付ける関係づけ情報を指す。
 RDBMSの用語で言うとマテリアライズドビューに近いように思います。
 正直、最適化を意識してアプリケーション側からは扱いづらい印象を持ちました。
 しかし、スレッド化を求めた場合には、欠くべからざる位置づけの構造です。
 そしてAndroidで実装した時、この生成には、テクスチャのロードよりも遙かに長い処理時間を消費しました。
 実装に依存しますが、ハードウェア構造を直してんじゃないかって思うぐらいに長いです。
 尚「描画パイプライン」と書きましたが、実際の所は「計算パイプライン」も扱えます。

休憩

 一通りフレームワークを実装仕切った観点から振り返ると、やはり洗練されており、かつ、かなり使いやすいAPIであると感じます。
 Vulkanは、Mantleと呼ばれるAMD社が下地を作った設計を土台にしていると聴きます。
 これ自体が既にフレームワークに近く、かつハードウェアをプリミティヴに扱える設計であると感じました。
 GPUを透明化したサブシステムとして計算資源を扱えるようにする仕組みです。
 GPUを計算機として扱える仕組みなっている以上、この段階から先は、もう大きな変化はないように感じます。
 この辺りの認識を共有するために次節では、入力、処理、出力という観点で整理していきます。

整理

接合部

 インスタンス、論理デバイス、コマンドプール、ディスクリプタプール、

入力

1.構造

2.実体

処理

 描画パス、描画パイプライン、スワップチェイン、

出力

 フレームバッファ、サーフェース、