Vulkan Tutorial (Drawing a triangle/Setup/Logical device and queues) 日本語訳

この記事は [Logical device and queues - Vulkan Tutorial](https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Logical_device_and_queues) の日本語訳です。

# Logical device and queues

## Introduction

使用する物理デバイスを選択したら、それとのインターフェースとなる論理デバイスをセットアップする必要があります。論理デバイスを作成する手順はインスタンスを作成する手順とよく似ていて、私達が使いたい機能を記述します。使用可能なキューファミリーの中から、どのキューを作成するかを指定する必要もあります。もし異なる要件があるなら、同じ物理デバイスから複数の論理デバイスを作成することもできます。

最初に、論理デバイスのハンドルを格納するメンバーをクラスに追加します。

```cpp
VkDevice device;
```

次に、`createLogicalDevice`関数を追加して、`initVulkan`から呼び出すようにします。

```cpp
void initVulkan() {
    createInstance();
    setupDebugMessenger();
    pickPhysicalDevice();
    createLogicalDevice();
}

void createLogicalDevice() {

}
```

## Specifying the queues to be created

論理デバイスの作成には、多くの構造体への詳細設定が必要ですが、その最初は[`VkDeviceQueueCreateInfo`](https://www.khronos.org/registry/vulkan/specs/1.0/man/html/VkDeviceQueueCreateInfo.html)になります。この構造体では、1つのキューファミリーに対して要求するキューの数を設定します。今は、グラフィックス機能を持ったキューにだけ興味があります。

```cpp
QueueFamilyIndices indices = findQueueFamilies(physicalDevice);

VkDeviceQueueCreateInfo queueCreateInfo{};
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo.queueFamilyIndex = indices.graphicsFamily.value();
queueCreateInfo.queueCount = 1;
```

現在利用可能なドライバは、各キューファミリーに対して少数のキュー作成だけを許可しますが、実際にはあなたが複数のキューを必要とすることはないでしょう。なぜなら、複数のスレッドそれぞれにコマンドバッファを作成し、低オーバーヘッドな呼び出しでメインスレッドからそれら全てを一度にサブミットできるからです。

Vulkanでは、コマンドバッファ実行のスケジューリングに影響を与える、キューの優先度を、`0.0`から`1.0`の間の浮動小数点数で指定できます。これはキューが1つの場合でも必須です。

```cpp
float queuePriority = 1.0f;
queueCreateInfo.pQueuePriorities = &queuePriority;
```

## Specifying used device features

次に設定する情報は、私達が使用するデバイス機能のセットです。これらはジオメトリシェーダのような、前章で[`vkGetPhysicalDeviceFeatures`](https://www.khronos.org/registry/vulkan/specs/1.0/man/html/vkGetPhysicalDeviceFeatures.html)を使ってサポートされているかどうか問い合わせた機能です。今は特別なものは必要ないので、単に定義だけして、全てを`VK_FALSE`にしておきます。私達がVulkanでもっと面白いことをし始めたとき、この構造体に戻ってくるでしょう。

```cpp
VkPhysicalDeviceFeatures deviceFeatures{};
```

## Creating the logical device

前の2つの構造体がそろったので、メインの[`VkDeviceCreateInfo`](https://www.khronos.org/registry/vulkan/specs/1.0/man/html/VkDeviceCreateInfo.html)構造体の設定を始めることができます。

```cpp
VkDeviceCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
```

最初に、キューの作成情報とデバイス機能情報へのポインタを追加します。

```cpp
createInfo.pQueueCreateInfos = &queueCreateInfo;
createInfo.queueCreateInfoCount = 1;

createInfo.pEnabledFeatures = &deviceFeatures;
```

残りの情報は[`VkInstanceCreateInfo`](https://www.khronos.org/registry/vulkan/specs/1.0/man/html/VkInstanceCreateInfo.html)構造体と似ており、拡張機能とバリデーションレイヤの設定をする必要があります。違いは、今回はデバイス固有のものであることです。

デバイス固有の拡張機能の例として`VK_KHR_swapchain`があり、レンダリングした画像をデバイスからウィンドウに送ることができるようになります。システム内には、この機能が無いVulkanデバイスが存在する可能性もあり、例えばコンピュート処理だけをサポートしているなどの場合です。私達は、スワップチェインの章でまたこの拡張機能に戻ってきます。

以前のVulkan実装では、インスタンスとデバイス固有のバリデーションレイヤは区別されていましたが、今は[もはやそうではありません](https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/chap40.html#extendingvulkan-layers-devicelayerdeprecation)。これは、最新の実装では[`VkDeviceCreateInfo`](https://www.khronos.org/registry/vulkan/specs/1.0/man/html/VkDeviceCreateInfo.html)の`enabledLayerCount`と`ppEnabledLayerNames`フィールドは無視されるということを意味しています。しかし、古い実装との互換性のため、どちらにせよこれらを設定しておくほうが良いでしょう。

```cpp
createInfo.enabledExtensionCount = 0;

if (enableValidationLayers) {
    createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
    createInfo.ppEnabledLayerNames = validationLayers.data();
} else {
    createInfo.enabledLayerCount = 0;
}
```

今はデバイス固有の拡張機能は必要ありません。

以上で、[`vkCreateDevice`](https://www.khronos.org/registry/vulkan/specs/1.0/man/html/vkCreateDevice.html)関数を呼び出して、論理デバイスを作成する準備ができました。

```cpp
if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
    throw std::runtime_error("failed to create logical device!");
}
```

引数は、インターフェースで接続したい物理デバイス、さきほど設定したキューと使用方法の情報、オプションのアロケーションコールバックへのポインタ、論理デバイスのハンドルを格納する変数へのポインタです。インスタンス作成関数と同様、この呼出は、存在しない拡張機能を有効にしたり、サポートされていない機能を要求した場合にエラーを返すことがあります。

デバイスは、`cleanup`内で[`vkDestroyDevice`](https://www.khronos.org/registry/vulkan/specs/1.0/man/html/vkDestroyDevice.html)関数を呼び出して破棄する必要があります。

```cpp
void cleanup() {
    vkDestroyDevice(device, nullptr);
    ...
}
```

論理デバイスはインスタンスとは直接関わらないので、パラメータには含まれません。

## Retrieving queue handles

キューは論理デバイスと一緒に自動的に作成されていますが、私達はまだそれに対するハンドルを持っていません。まず、グラフィックキューへのハンドルを格納するメンバ変数をクラスに追加します。

```cpp
VkQueue graphicsQueue;
```

デバイスのキューは、デバイスが破棄されるときに自動的にクリーンアップされるので、`cleanup`では何もする必要がありません。

それぞれのキューファミリーからキューのハンドルを取得するには、[`vkGetDeviceQueue`](https://www.khronos.org/registry/vulkan/specs/1.0/man/html/vkGetDeviceQueue.html)関数を使うことができます。引数は、論理デバイス、キューファミリー、キューインデックス、キューのハンドルを格納する変数へのポインタです。キューファミリーから1つのキューだけを作成するので、単にインデックス`0`を使います。

```cpp
vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
```

論理デバイスとキューハンドルがあれば、グラフィックスカードを使って実際に何かを始めることができます！次のいくつかの章では、ウィンドウシステムに結果を送るためのリソースをセットアップします。

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

[前の記事](https://qiita.com/shibainuudon/items/684c84e30a185739243a)
[次の記事](https://qiita.com/shibainuudon/items/0a1035cd1990b655150c)