この記事は Logical device and queues - Vulkan Tutorial の日本語訳です。
Logical device and queues
Introduction
使用する物理デバイスを選択したら、それとのインターフェースとなる論理デバイスをセットアップする必要があります。論理デバイスを作成する手順はインスタンスを作成する手順とよく似ていて、私達が使いたい機能を記述します。使用可能なキューファミリーの中から、どのキューを作成するかを指定する必要もあります。もし異なる要件があるなら、同じ物理デバイスから複数の論理デバイスを作成することもできます。
最初に、論理デバイスのハンドルを格納するメンバーをクラスに追加します。
VkDevice device;
次に、createLogicalDevice
関数を追加して、initVulkan
から呼び出すようにします。
void initVulkan() {
createInstance();
setupDebugMessenger();
pickPhysicalDevice();
createLogicalDevice();
}
void createLogicalDevice() {
}
Specifying the queues to be created
論理デバイスの作成には、多くの構造体への詳細設定が必要ですが、その最初はVkDeviceQueueCreateInfo
になります。この構造体では、1つのキューファミリーに対して要求するキューの数を設定します。今は、グラフィックス機能を持ったキューにだけ興味があります。
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つの場合でも必須です。
float queuePriority = 1.0f;
queueCreateInfo.pQueuePriorities = &queuePriority;
Specifying used device features
次に設定する情報は、私達が使用するデバイス機能のセットです。これらはジオメトリシェーダのような、前章でvkGetPhysicalDeviceFeatures
を使ってサポートされているかどうか問い合わせた機能です。今は特別なものは必要ないので、単に定義だけして、全てをVK_FALSE
にしておきます。私達がVulkanでもっと面白いことをし始めたとき、この構造体に戻ってくるでしょう。
VkPhysicalDeviceFeatures deviceFeatures{};
Creating the logical device
前の2つの構造体がそろったので、メインのVkDeviceCreateInfo
構造体の設定を始めることができます。
VkDeviceCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
最初に、キューの作成情報とデバイス機能情報へのポインタを追加します。
createInfo.pQueueCreateInfos = &queueCreateInfo;
createInfo.queueCreateInfoCount = 1;
createInfo.pEnabledFeatures = &deviceFeatures;
残りの情報はVkInstanceCreateInfo
構造体と似ており、拡張機能とバリデーションレイヤの設定をする必要があります。違いは、今回はデバイス固有のものであることです。
デバイス固有の拡張機能の例としてVK_KHR_swapchain
があり、レンダリングした画像をデバイスからウィンドウに送ることができるようになります。システム内には、この機能が無いVulkanデバイスが存在する可能性もあり、例えばコンピュート処理だけをサポートしているなどの場合です。私達は、スワップチェインの章でまたこの拡張機能に戻ってきます。
以前のVulkan実装では、インスタンスとデバイス固有のバリデーションレイヤは区別されていましたが、今はもはやそうではありません。これは、最新の実装ではVkDeviceCreateInfo
のenabledLayerCount
とppEnabledLayerNames
フィールドは無視されるということを意味しています。しかし、古い実装との互換性のため、どちらにせよこれらを設定しておくほうが良いでしょう。
createInfo.enabledExtensionCount = 0;
if (enableValidationLayers) {
createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
createInfo.ppEnabledLayerNames = validationLayers.data();
} else {
createInfo.enabledLayerCount = 0;
}
今はデバイス固有の拡張機能は必要ありません。
以上で、vkCreateDevice
関数を呼び出して、論理デバイスを作成する準備ができました。
if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
throw std::runtime_error("failed to create logical device!");
}
引数は、インターフェースで接続したい物理デバイス、さきほど設定したキューと使用方法の情報、オプションのアロケーションコールバックへのポインタ、論理デバイスのハンドルを格納する変数へのポインタです。インスタンス作成関数と同様、この呼出は、存在しない拡張機能を有効にしたり、サポートされていない機能を要求した場合にエラーを返すことがあります。
デバイスは、cleanup
内でvkDestroyDevice
関数を呼び出して破棄する必要があります。
void cleanup() {
vkDestroyDevice(device, nullptr);
...
}
論理デバイスはインスタンスとは直接関わらないので、パラメータには含まれません。
Retrieving queue handles
キューは論理デバイスと一緒に自動的に作成されていますが、私達はまだそれに対するハンドルを持っていません。まず、グラフィックキューへのハンドルを格納するメンバ変数をクラスに追加します。
VkQueue graphicsQueue;
デバイスのキューは、デバイスが破棄されるときに自動的にクリーンアップされるので、cleanup
では何もする必要がありません。
それぞれのキューファミリーからキューのハンドルを取得するには、vkGetDeviceQueue
関数を使うことができます。引数は、論理デバイス、キューファミリー、キューインデックス、キューのハンドルを格納する変数へのポインタです。キューファミリーから1つのキューだけを作成するので、単にインデックス0
を使います。
vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
論理デバイスとキューハンドルがあれば、グラフィックスカードを使って実際に何かを始めることができます!次のいくつかの章では、ウィンドウシステムに結果を送るためのリソースをセットアップします。