Vulkan Tutorial (Drawing a triangle/Setup/Instance) 日本語訳

https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Instance

# インスタンス (Instance)

## インスタンスの作成 (Creating an instance)

あなたがまずはじめにやらなければいけないことは、*インスタンス* を作ることでVulkanライブラリを初期化することです。インスタンスとはアプリケーションとVulkanライブラリのコネクションで、作成するにはアプリケーションの詳細をドライバに指定する必要があります。

はじめに`createInstance`関数を追加し、`initVulkan`関数から呼び出します。
```cpp
void initVulkan() {
    createInstance();
}
```
加えて、インスタンスへのハンドルをメンバ変数に追加します。
```cpp
private:
VkInstance instance;
```

次に、インスタンスを作成するために、アプリケーションについての情報を構造体に設定する必要があります。このデータは技術的にはオプションですが、特定のアプリケーションを最適化するためにドライバに有益な情報を提供するかもしれません(例えば、特定の特殊な振る舞いをする、有名なグラフィックスエンジンを使っているためなど)。この構造体は[`VkApplicationInfo`](https://www.khronos.org/registry/vulkan/specs/1.0/man/html/VkApplicationInfo.html)と呼ばれます。
```cpp
void createInstance() {
    VkApplicationInfo appInfo{};
    appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
    appInfo.pApplicationName = "Hello Triangle";
    appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
    appInfo.pEngineName = "No Engine";
    appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
    appInfo.apiVersion = VK_API_VERSION_1_0;
}
```
前述したように、Vulkanの多くの構造体は`sType`メンバで明示的に型を指定するように要求します。これもまた多くの構造体で、`pNext`メンバで将来の拡張情報を設定できます。私たちは、ここでは値初期化を使って、これを`nullptr`のままにしておきます。

Vulkanでは多くの情報は関数の引数の代わりに構造体で渡されます。そしてインスタンスを作るのに十分な情報を提供するために、もう一つの構造体を設定する必要があります。この構造体はオプションではなく、どのグローバル拡張とバリデーションレイヤを使いたいかをVulkanドライバに伝えます。ここでグローバルとは、特定のデバイスではなくプログラム全体に適用されるということを意味しています。これは次のいくつかの章ではっきりとします。
```cpp
VkInstanceCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
createInfo.pApplicationInfo = &appInfo;
```
最初の2つのパラメータは単純です。次の2つのレイヤは要求するグローバル拡張を指定します。概要の章で述べた通り、Vulkanはプラットフォーム不可知論者のAPIなので、ウィンドウシステムのインターフェースに拡張が必要になるということを意味しています。GLFWはそれに必要な拡張を返す便利な組み込み関数を持っていて、私たちはそれをそのまま構造体にわたすことができます。
```cpp
uint32_t glfwExtensionCount = 0;
const char** glfwExtensions;

glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);

createInfo.enabledExtensionCount = glfwExtensionCount;
createInfo.ppEnabledExtensionNames = glfwExtensions;
```
構造体の最後の2つのメンバで、有効にするグローバルバリデーションレイヤを決定します。私たちはこれについて次の章でより深く解説するので、今はこれを空のままにしておきます。
```cpp
createInfo.enabledLayerCount = 0;
```
これで、Vulkanがインスタンスを作るために必要なものは全て設定したので、ようやく[`vkCreateInstance`](https://www.khronos.org/registry/vulkan/specs/1.0/man/html/vkCreateInstance.html)を呼び出すことができます。
```cpp
VkResult result = vkCreateInstance(&createInfo, nullptr, &instance);
```
ご覧のとおり、Vulkanでオブジェクトを作成する関数の引数の一般的なパターンは：
* 作成情報の構造体へのポインタ
* カスタムアロケータのコールバックへのポインタ。このチュートリアルでは常に`nullptr`。
* 新しいオブジェクトへのハンドルを格納する変数へのポインタ

全てが上手く行けば[`VkInstance`](https://www.khronos.org/registry/vulkan/specs/1.0/man/html/VkInstance.html)型のメンバ変数にインスタンスへのハンドルが格納されます。ほとんど全てのVulkan関数は[`VkResult`](https://www.khronos.org/registry/vulkan/specs/1.0/man/html/VkResult.html)型の値を返し、それは`VK_SUCCESS`またはエラーコードのいずれかです。インスタンスの作成に成功したかどうかチェックするのに、結果を変数に格納する必要はなく、たんに成功値かどうかチェックするだけです。
```cpp
if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
    throw std::runtime_error("failed to create instance!");
}
```
ここで、インスタンスの作成に成功しているかどうか確認するためにプログラムを実行してください。

## 拡張サポートのチェック (Checking for extension support)

[`vkCreateInstance`](https://www.khronos.org/registry/vulkan/specs/1.0/man/html/vkCreateInstance.html)のドキュメントを見ると、可能性のあるエラーコードの一つに`VK_ERROR_EXTENSION_NOT_PRESENT`があります。私たちは単純に要求する拡張を指定して、このエラーコードが帰ってきたら停止することもできます。これはウィンドウシステムのインターフェースのような基本的な拡張機能なら理にかなっていますが、オプションの機能についてチェックしたいとしたら、どうしたらいいのでしょうか？

インスタンスを作成する前にサポートされている拡張機能のリストを取得するために、[`vkEnumerateInstanceExtensionProperties`](https://www.khronos.org/registry/vulkan/specs/1.0/man/html/vkEnumerateInstanceExtensionProperties.html)関数があります。この関数は、拡張機能の数を格納する変数へのポインタと、拡張機能の詳細を格納する[`VkExtensionProperties`](https://www.khronos.org/registry/vulkan/specs/1.0/man/html/VkExtensionProperties.html)の配列を受け取ります。また、最初にオプションの引数を取り、特定のバリデーションレイヤによって拡張機能をフィルタリングできますが、今は無視します。

拡張機能の詳細を格納するための配列を確保するため、最初にいくつそれがあるかを知る必要があります。最後の引数を空にしておくことで、拡張機能の数を取得することができます。

```cpp
uint32_t extensionCount = 0;
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
```

次に、拡張機能の詳細を格納する配列を確保します。(`include <vector>`)

```cpp
std::vector<VkExtensionProperties> extensions(extensionCount);
```

最後に、拡張機能の詳細を問い合わせます。

```cpp
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data());
```

それぞれの[`VkExtensionProperties`](https://www.khronos.org/registry/vulkan/specs/1.0/man/html/VkExtensionProperties.html)構造体は、拡張機能の名前とバージョン番号を含みます。シンプルなforループでこれらをリストすることができます。(`\t`はインデントのためのタブです)

```cpp
std::cout << "available extensions:\n";

for (const auto& extension : extensions) {
    std::cout << '\t' << extension.extensionName << '\n';
}
```

あなたがもしVulkanサポートについていくつかの詳細を提供したいなら、このコードを`createInstance`関数に追加することができます。課題として、`glfwGetRequiredInstanceExtensions`関数から返された全ての拡張機能が、サポートされている拡張機能のリストに含まれているかどうか、チェックする関数を作成してみてください。

## クリーンアップ (Cleaning up)

[`VkInstance`](https://www.khronos.org/registry/vulkan/specs/1.0/man/html/VkInstance.html)はプログラムが終了する直前に破棄されなければいけません。`cleanup`の中で[`vkDestroyInstance`](https://www.khronos.org/registry/vulkan/specs/1.0/man/html/vkDestroyInstance.html)関数を呼び出すことで破棄することができます。

```cpp
void cleanup() {
    vkDestroyInstance(instance, nullptr);

    glfwDestroyWindow(window);

    glfwTerminate();
}
```

[`vkDestroyInstance`](https://www.khronos.org/registry/vulkan/specs/1.0/man/html/vkDestroyInstance.html)関数の引数は簡単です。前の章で述べた通り、Vulkanの確保と開放関数はオプションのアロケータのコールバックを持ち、私たちは`nullptr`を渡してこれを無視します。この後に続く章で作成する他の全てのVulkanリソースは、インスタンスが破棄される前にクリーンアップされる必要があります。

インスタンスの作成の後もっと複雑なステップに進む前に、[バリデーションレイヤ](https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Validation_layers)をチェックすることでデバッグオプションを評価するときが来ました。

[C++ code](https://vulkan-tutorial.com/code/01_instance_creation.cpp)

[前の記事](https://qiita.com/shibainuudon/items/855c3058372cb7699b2f)
[次の記事](https://qiita.com/shibainuudon/items/121df78c2231792a3238)