• GPU操作用の低レベルAPI
  • OpenGLでおなじみKhronosが主導
  • 公式のチュートリアルを読みながら学ぶと良い
    • もとは非公式?のチュートリアルだったが、公式に取り入れられて、SlangだのC++20だのVulkan-Hpp (RAII) だの C++ modules だのが取り入れられて神すぎになっている
      • しかし手元の環境だとC++ modulesを使ったVulkan-Hppのセットアップが全く上手くいかない(悲しい)
    • 非公式チュートリアル時代の作業ログがある→VulkanTutorial作業ログ
  • Windowsだと Vulkan SDK をインストールして Vulkan Configurator というところから起動すると validation layer (VK_LAYER_KHRONOS_validation)の出力が見れてうれしい

拙著「地球をつくって3DCGライブラリを比較しよう」でざっくりと解説しているが、以下のものを作成(初期化)して描画を行う

  1. instance
  2. PhysicalDevice : おそらくグラフィックカードと対応している
  3. QueueFamily
    1. コマンドを格納するqueueのまとまり
    2. 描画に関するコマンドしか受け付けないqueueや、計算に関するコマンドしか受け付けないqueueといったものが存在する可能性があり、「どのようなコマンドを受け取れるのか」という点でqueueをまとめたものがこれ
  4. Device (LogicalDeviceとも)
  5. CommandPool
    1. CommandBufferのメモリ元みたいな感じ
      1. xxxPoolと言うとxxxのメモリみたいな雰囲気っぽい
  6. CommandBuffer
  7. Surface
    1. 描画結果を出力する領域
    2. GLFWだと glfwCreateWindowSurface() で作成可
  8. Swapchainスワップチェーン
  9. ShaderModule
  10. Pipeline
    1. デカい(作るのにたくさんの構造体を定義しなければならない)
    2. カリングやアルファブレンド等の設定をする
  11. Buffer
    1. 頂点バッファ、インデックスバッファ、Uniform Buffer、etc
    2. 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つ?ある

  1. Push Constant
    1. シンプルな代わりにサイズの制約が厳しい
      1. 少なくとも128 byte、期待するにしてもせいぜい 256 byte 程度?(RTX 3090 が 256 byte らしい
      2. float4x4 は 4 (byte) * 16 = 64 (byte) なので、行列2つくらいが限界である
        1. M, V, Pを別々で渡すのは怪しいが、MVPにして渡すと安心
    2. PipelineLayout でアクセスしたいシェーダーのステージ(Vertex, Fragment, etc)やデータサイズを渡して、コマンドバッファに直接データをブチ込む
  2. Uniform Buffer
    1. DescriptorSetLayout で個数とかを定義するだけではなく、DescriptorPool や DescriptorSet や Buffer を作成しなければならない
    2. これさえ作ってしまえば Buffer を更新するだけで良い(描画のたびにbindとかしなくて良い)ので、ある意味では楽?