- GPU操作用の低レベルAPI
- OpenGLでおなじみKhronosが主導
- 公式のチュートリアルを読みながら学ぶと良い
- もとは非公式?のチュートリアルだったが、公式に取り入れられて、SlangだのC++20だのVulkan-Hpp (RAII) だの C++ modules だのが取り入れられて神すぎになっている
- しかし手元の環境だとC++ modulesを使ったVulkan-Hppのセットアップが全く上手くいかない(悲しい)
- 非公式チュートリアル時代の作業ログがある→VulkanTutorial作業ログ
- もとは非公式?のチュートリアルだったが、公式に取り入れられて、SlangだのC++20だのVulkan-Hpp (RAII) だの C++ modules だのが取り入れられて神すぎになっている
- Windowsだと Vulkan SDK をインストールして Vulkan Configurator というところから起動すると validation layer (
VK_LAYER_KHRONOS_validation)の出力が見れてうれしい
拙著「地球をつくって3DCGライブラリを比較しよう」でざっくりと解説しているが、以下のものを作成(初期化)して描画を行う
- instance
- PhysicalDevice : おそらくグラフィックカードと対応している
- QueueFamily
- コマンドを格納するqueueのまとまり
- 描画に関するコマンドしか受け付けないqueueや、計算に関するコマンドしか受け付けないqueueといったものが存在する可能性があり、「どのようなコマンドを受け取れるのか」という点でqueueをまとめたものがこれ
- Device (LogicalDeviceとも)
- CommandPool
- CommandBufferのメモリ元みたいな感じ
- xxxPoolと言うとxxxのメモリみたいな雰囲気っぽい
- CommandBufferのメモリ元みたいな感じ
- CommandBuffer
- Surface
- 描画結果を出力する領域
- GLFWだと
glfwCreateWindowSurface()で作成可
- Swapchain(スワップチェーン)
- ShaderModule
- Pipeline
- デカい(作るのにたくさんの構造体を定義しなければならない)
- カリングやアルファブレンド等の設定をする
- Buffer
- 頂点バッファ、インデックスバッファ、Uniform Buffer、etc
- DeviceMemory と紐づいているので、併せて持っておく必要がある
以下は必要に応じて
- RenderPass, FrameBuffer
- 描画対象となるアタッチメント(深度バッファとか)のフォーマットやサイズを定義する
- Vulkan 1.3 から使えるようになった Dynamic Rendering を使うとこれを作らなくても良くなる
- DescriptorSet
- シェーダーがアクセスする、テクスチャやサンプラー、そして Uniform Buffer といったリソースを置く場所
- DescriptorSetのどこにどのようなデータが置かれているのかを示す DescriptorSetLayout をパイプライン作成時に渡したり、メモリ元である DescriptorSetPool を作ったりしないといけない
- Semaphore
- GPU側でCPUを待機する
- ex) Swapchain がGPUに描画される準備を完了するまで描画を待機する
- Fence
- CPU側でGPUを待機する
- ex) 描画が完了するまで待機して、表画面と裏画面を入れ替える
シェーダーに値を渡す方法が2つ?ある
- Push Constant
- シンプルな代わりにサイズの制約が厳しい
- 少なくとも128 byte、期待するにしてもせいぜい 256 byte 程度?(RTX 3090 が 256 byte らしい)
- float4x4 は 4 (byte) * 16 = 64 (byte) なので、行列2つくらいが限界である
- M, V, Pを別々で渡すのは怪しいが、MVPにして渡すと安心
- PipelineLayout でアクセスしたいシェーダーのステージ(Vertex, Fragment, etc)やデータサイズを渡して、コマンドバッファに直接データをブチ込む
- シンプルな代わりにサイズの制約が厳しい
- Uniform Buffer
- DescriptorSetLayout で個数とかを定義するだけではなく、DescriptorPool や DescriptorSet や Buffer を作成しなければならない
- これさえ作ってしまえば Buffer を更新するだけで良い(描画のたびにbindとかしなくて良い)ので、ある意味では楽?