Vulkan 1.1 プログラミングのメモ(OpenGL 2.x, OpenCL 2.x 経験者向け)

## 背景

* OpenGL 2.x おじさんだが, 世間に取り残されてしまった. そこで Vulkan を手っ取り早く極めたい 

## チュートリアルとか, 環境構築とか

Qiita に他にいい記事があります. あとはネットで検索しましょう. 
日本語だと　すらりんラボの(電子)書籍がいいでしょうか.

https://slash-labo.booth.pm/

### とりあえず動かす

Sascha 先生の Vulkan-glTF-PBR が一式まとまっていて, Linux, Windows, Android どれでも動いておすすめ.

https://github.com/SaschaWillems/Vulkan-glTF-PBR

コードはエラーチェックとか適当ですが...
(一応 ASAN + Vulkan validation layers でチェックいれたりして修正を PR したので, 最新 `master` では少なくともへんなところで落ちることは無いです) 

### RenderDoc をつかう

https://renderdoc.org/

Graphics デバッガーです. Vulkan 対応.
Vulkan validation layers で補足できないようなエラー(binding の番号違いとか, シェーダにきちんとデータがわたっているか, etc)がだいたいわかって便利です. できるかぎり使いましょう.

## Quick reference

https://www.khronos.org/registry/vulkan/specs/1.1/refguide/Vulkan-1.1-web.pdf

## Vulkan API reference

https://www.khronos.org/registry/vulkan/

HTML での spec は, どうしてこうなったという感じでサイトが劇重(1950X 16 コア + 2080 Ti で開いても劇重) + 4K display で見るとフォント小さすぎなのでおすすめしません.

https://www.khronos.org/registry/vulkan/specs/1.2/html

(フォントを大きくするだけで数十秒かかる)

API については, PDF をローカルにダウンロードしておいて, PDF をローカルで見るのがよいでしょう.

API は, 数は多いですが, OpenGL ほどではありません. その代わり Vulkan は構造体定義が多いですね.

## API チュートリアル

https://software.intel.com/en-us/articles/api-without-secrets-introduction-to-vulkan-part-1

一から直接 API とか叩くかんじで, single ページでまとまっていてよいです.

## API 構成

なんとなく OpenGL + OpenCL という感じです.

## エラーチェック

`VK_CHECK_RESULT` など使います. API によっては返り値がないのもあるので, 必ずしも全ての API を `VK_CHECK_RESULT` という形でチェックできるわけではりません.

### ハンドル(オブジェクト)

64bit uint で表現されます.

#### 無効なハンドル

`VK_NULL_HANDLE`(実態は 0)を使う.

https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_NULL_HANDLE.html

#### dispatchable, non-dispatchable

ハンドルは dispatchable か non-dispatchable に分けられる.

https://subscription.packtpub.com/book/application_development/9781786469809/1/ch01lvl1sec10/learning-the-fundamentals-of-vulkan

dispatchable: VkInstance, VkCommandBuffer など.
non-dispachable: VkSemaphore, VkFence など.

dispatchable, non-dispatchable なハンドル両方とも, 可能なかぎり `VK_NULL_HANDLE` で初期化するのがよい. また, handle を destry するときも `VK_NULL_HANDLE` かどうかをチェックする.

## バッファとか

### Push constant

readonly の定数データバッファ. `vkCmdPushConstants` で直接アップデートすることができる
(他の uniform buffer などは, メモリ or コピーコマンドでアップデートする)

readonly で容量が少ないものはこの Push constant を使う(e.g. glTF morph weights など). シェーダごとに指定できる.

容量は maxPushConstantSize で確認できます.
2080 Ti で 256 bytes でした. Adreno 506(ASUS ZenFone Max M2), Pixel4(Snapdragon 855)では 128 bytes でした. Radeon Fury X も 128 bytes でした. 

https://vulkan.gpuinfo.org/listlimits.php

vulkan gpuinfo では [128, 256] となっています.

### UBO(Uniform Buffer Object), SSBO(Shader Storage Buffer Object)

OpenGL の UBO, SSBO と振る舞いはだいたい同じ様です.

UBO: skining の行列やリードオンリーのデータを置く. SwapChain image ごとに生成(アニメーションデータなど, UBO の内容がフレームごとに変わる場合) 
SSBO: より一般的なメモリに近いバッファ. 読み書きが可能. UBO にくらべて容量大きく取れる, UBO より一般的にアクセスが遅い. 主に compute shader 用のデータを置く

### Storage Image

OpenGL image 相当
https://www.khronos.org/opengl/wiki/Image_Load_Store

画像データとして書き込んで, 後続のレンダリングパスでテクスチャとして使う(RenderToTexture)みたいなのがやりやすくなる.

### Descriptor pool

Descriptor pool で, 事前に UBO, SSBO, StorageImage などそれぞれ使う分を確保する必要があることに注意! 
 
### Pipeline

シェーダなどをひとまとめにしたもの(task の概念に近い?). compute, graphics(+ ray tracing) で処理を切り分けるときにもこの pipeline を使う.



## シェーダ言語

Vulkan 自体は SPIR-V 中間言語のみの対応になります. 基本 GLSL を使って, glslang などで SPIR-V に変換して使います.

Vulkan 1.2 から HLSL を正式に使えますが, Android とか SwiftShader が 2020/02/07 時点 1.1 のままなので, HLSL を広く使えるようになるのは来年くらいでしょうか. 


### 構造体のアラインメントや stride

ユニフォームブロックのメモリレイアウト @ゲームプログラマの小話[開発:グラフィックス]
https://qiita.com/hoboaki/items/b188c4495f4708c19002

Vulkan(正確には glslang) では, デフォルト std140 のようでした.

したがって, `float[N]` などは 16byte ごとに stride(alignment は 4)されます.
C/C++ の構造体をそのままの定義で Uniform block などに memcpy などで転送し, GLSL で使うとハマりますので注意ください.

すこし富豪的になりますが, float 配列などを扱いたい場合, 基本 vec4 を使うのがよさそうです.

きちんとやるなら変数ごとにアライメントやオフセットをクエリするのがよいですが, 面倒ですね.
(シェーダ側の構造体定義を変えたら, なんかいい感じで対応する C/C++ コードを生成する generator tool などが, web を探せばあるかもしれません)