跳转到主要内容

Vulkan填坑学习Day05—逻辑设备与队列

demi 提交于

上一篇:<a href="http://imgtec.eetrend.com/blog/2021/100100025.html"><u>Vulkan填坑学习Day04—物理设备与队列</u></a>

<font size="3" style="line-height: 45px;" color="#c200ff"><strong>简介</strong></font>

Vulkan 逻辑设备与队列,在选择要使用的物理设备之后,我们需要设置一个逻辑设备用于交互。逻辑设备创建过程与instance创建过程类似,也需要描述我们需要使用的功能。因为我们已经查询过哪些队列簇可用,在这里需要进一步为逻辑设备创建具体类型的命令队列。如果有不同的需求,也可以基于同一个物理设备创建多个逻辑设备。

<center><img src="http://imgtec.eetrend.com/files/2021-04/博客/100104811-203191-1.png&quot; alt=""></center><br>

首先添加一个新的类成员来存储逻辑设备句柄。

<pre>VkDevice device;</pre>

接下来创建一个新的函数createLogicalDevice,并在initVulkan函数中调用,以创建逻辑设备。

<pre>void initVulkan() {
createInstance();
setupDebugCallback();
pickPhysicalDevice();
createLogicalDevice();
}

void createLogicalDevice() {
}
</pre>

<font size="3" style="line-height: 45px;" color="#c200ff"><strong>一、指定创建的队列</strong></font>

创建逻辑设备需要在结构体中明确具体的信息,首先第一个结构体VkDeviceQueueCreateInfo。这个结构体描述队列簇中预要申请使用的队列数量。现在我们仅关心具备图形能力的队列。

<pre>QueueFamilyIndices indices = findQueueFamilies(physicalDevice);

VkDeviceQueueCreateInfo queueCreateInfo = {};
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo.queueFamilyIndex = indices.graphicsFamily;
queueCreateInfo.queueCount = 1;
</pre>

当前可用的驱动程序所提供的队列簇只允许创建少量的队列,并且很多时候没有必要创建多个队列。这是因为可以在多个线程上创建所有命令缓冲区,然后在主线程一次性的以较低开销的调用提交队列。

Vulkan允许使用0.0到1.0之间的浮点数分配队列优先级来影响命令缓冲区执行的调用。即使只有一个队列也是必须的:

<pre>float queuePriority = 1.0f;
queueCreateInfo.pQueuePriorities = &queuePriority;</pre>

<font size="3" style="line-height: 45px;" color="#c200ff"><strong>二、指定使用的设备特性</strong></font>

下一个要明确的信息有关设备要使用的功能特性。这些是我们在上一节中用vkGetPhysicalDeviceFeatures查询支持的功能,比如geometry shaders。现在我们不需要任何特殊的功能,所以我们可以简单的定义它并将所有内容保留到VK_FALSE。一旦我们要开始用Vulkan做更多的事情,我们会回到这个结构体,进一步设置。

<pre>VkPhysicalDeviceFeatures deviceFeatures = {};</pre>

<font size="3" style="line-height: 45px;" color="#c200ff"><strong>三、创建逻辑设备</strong></font>

使用前面的两个结构体,我们可以填充VkDeviceCreateInfo结构。

<pre>VkDeviceCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
</pre>

首先添加指向队列创建信息的结构体和设备功能结构体:

<pre>createInfo.pQueueCreateInfos = &queueCreateInfo;
createInfo.queueCreateInfoCount = 1;
createInfo.pEnabledFeatures = &deviceFeatures;</pre>

结构体其余的部分与VkInstanceCreateInfo相似,需要指定扩展和validation layers,总而言之这次不同之处是为具体的设备设置信息。

设置具体扩展的一个案例是VK_KHR_swapchain,它允许将来自设备的渲染图形呈现到Windows。系统中的Vulkan设备可能缺少该功能,例如仅仅支持计算操作。我们将在交换链章节中展开这个扩展。

就像之前validation layers小节中提到的,允许为instance开启validation layers,现在我们将为设备开启validation layers,而不需要为设备指定任何扩展。

<pre>createInfo.enabledExtensionCount = 0;

if (enableValidationLayers) {
createInfo.enabledLayerCount = static_cast&lt;uint32_t&gt;(validationLayers.size());
createInfo.ppEnabledLayerNames = validationLayers.data();
} else {
createInfo.enabledLayerCount = 0;
}</pre>

就这样,我们现在可以通过调用vkCreateDevice函数来创建实例化逻辑设备。

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

这些参数分别是包含具体队列使用信息的物理设备,可选的分配器回调指针以及用于存储逻辑设备的句柄。与instance创建类似,此调用可能由于启用不存在的扩展或者指定不支持的功能,导致返回错误。

在cleanup函数中逻辑设备需要调用vkDestroyDevice销毁:

<pre>void cleanup() {
vkDestroyDevice(device, nullptr);
...
}
</pre>

逻辑设备不与instance交互,所以参数中不包含instance。

<font size="3" style="line-height: 45px;" color="#c200ff"><strong>四、检索队列处理</strong></font>

这些队列与逻辑设备自动的一同创建,但是我们还没有一个与它们进行交互的句柄。在这里添加一个新的类成员来存储图形队列句柄:

<pre>VkQueue graphicsQueue;</pre>

设备队列在设备被销毁的时候隐式清理,所以我们不需要在cleanup函数中做任何操作。

我们可以使用vkGetDeviceQueue函数来检测每个队列簇中队列的句柄。参数是逻辑设备,队列簇,队列索引和存储获取队列变量句柄的指针。因为我们只是从这个队列簇创建一个队列,所以需要使用索引 0。

<pre>vkGetDeviceQueue(device, indices.graphicsFamily, 0, &graphicsQueue);</pre>

在成功获取逻辑设备和队列句柄后,我们可以通过显卡做一些实际的事情了,在接下来的几章节中,我们会设置资源并将相应的结果提交到窗体系统。

附:源代码

<pre>// logical_device.cpp
#define GLFW_INCLUDE_VULKAN
#include &lt;GLFW/glfw3.h&gt;

#include &lt;iostream&gt;
#include &lt;stdexcept&gt;
#include &lt;vector&gt;
#include &lt;cstring&gt;
#include &lt;cstdlib&gt;
#include &lt;optional&gt;

const int WIDTH = 800;
const int HEIGHT = 600;

const std::vector&lt;const char*&gt; validationLayers = {
"VK_LAYER_KHRONOS_validation"
};

#ifdef NDEBUG
const bool enableValidationLayers = false;
#else
const bool enableValidationLayers = true;
#endif

VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger) {
auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
if (func != nullptr) {
return func(instance, pCreateInfo, pAllocator, pDebugMessenger);
}
else {
return VK_ERROR_EXTENSION_NOT_PRESENT;
}
}

void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator) {
auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
if (func != nullptr) {
func(instance, debugMessenger, pAllocator);
}
}

struct QueueFamilyIndices {
uint32_t graphicsFamily;

bool isComplete() {
return graphicsFamily &gt;= 0;
}
};

class HelloTriangleApplication {
public:
void run() {
initWindow();
initVulkan();
mainLoop();
cleanup();
}

private:
GLFWwindow* window;

VkInstance instance;
VkDebugUtilsMessengerEXT debugMessenger;

VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
VkDevice device;

VkQueue graphicsQueue;

void initWindow() {
glfwInit();

glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);

window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);
}

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

void mainLoop() {
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
}
}

void cleanup() {
vkDestroyDevice(device, nullptr);

if (enableValidationLayers) {
DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
}

vkDestroyInstance(instance, nullptr);

glfwDestroyWindow(window);

glfwTerminate();
}

void createInstance() {
if (enableValidationLayers && !checkValidationLayerSupport()) {
throw std::runtime_error("validation layers requested, but not available!");
}

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;

VkInstanceCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
createInfo.pApplicationInfo = &appInfo;

auto extensions = getRequiredExtensions();
createInfo.enabledExtensionCount = static_cast&lt;uint32_t&gt;(extensions.size());
createInfo.ppEnabledExtensionNames = extensions.data();

VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo;
if (enableValidationLayers) {
createInfo.enabledLayerCount = static_cast&lt;uint32_t&gt;(validationLayers.size());
createInfo.ppEnabledLayerNames = validationLayers.data();

populateDebugMessengerCreateInfo(debugCreateInfo);
createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*)&debugCreateInfo;
}
else {
createInfo.enabledLayerCount = 0;

createInfo.pNext = nullptr;
}

if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
throw std::runtime_error("failed to create instance!");
}
}

void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) {
createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
createInfo.pfnUserCallback = debugCallback;
}

void setupDebugMessenger() {
if (!enableValidationLayers) return;

VkDebugUtilsMessengerCreateInfoEXT createInfo;
populateDebugMessengerCreateInfo(createInfo);

if (CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
throw std::runtime_error("failed to set up debug messenger!");
}
}

void pickPhysicalDevice() {
uint32_t deviceCount = 0;
vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);

if (deviceCount == 0) {
throw std::runtime_error("failed to find GPUs with Vulkan support!");
}

std::vector&lt;VkPhysicalDevice&gt; devices(deviceCount);
vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());

for (const auto& device : devices) {
if (isDeviceSuitable(device)) {
physicalDevice = device;
break;
}
}

if (physicalDevice == VK_NULL_HANDLE) {
throw std::runtime_error("failed to find a suitable GPU!");
}
}

void createLogicalDevice() {
QueueFamilyIndices indices = findQueueFamilies(physicalDevice);

VkDeviceQueueCreateInfo queueCreateInfo = {};
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo.queueFamilyIndex = indices.graphicsFamily;
queueCreateInfo.queueCount = 1;

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

VkPhysicalDeviceFeatures deviceFeatures = {};

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

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

createInfo.pEnabledFeatures = &deviceFeatures;

createInfo.enabledExtensionCount = 0;

if (enableValidationLayers) {
createInfo.enabledLayerCount = static_cast&lt;uint32_t&gt;(validationLayers.size());
createInfo.ppEnabledLayerNames = validationLayers.data();
}
else {
createInfo.enabledLayerCount = 0;
}

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

vkGetDeviceQueue(device, indices.graphicsFamily, 0, &graphicsQueue);
}

bool isDeviceSuitable(VkPhysicalDevice device) {
QueueFamilyIndices indices = findQueueFamilies(device);

return indices.isComplete();
}

QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) {
QueueFamilyIndices indices;

uint32_t queueFamilyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);

std::vector&lt;VkQueueFamilyProperties&gt; queueFamilies(queueFamilyCount);
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());

int i = 0;
for (const auto& queueFamily : queueFamilies) {
if (queueFamily.queueCount &gt; 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
indices.graphicsFamily = i;
}

if (indices.isComplete()) {
break;
}

i++;
}

return indices;
}

std::vector&lt;const char*&gt; getRequiredExtensions() {
uint32_t glfwExtensionCount = 0;
const char** glfwExtensions;
glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);

std::vector&lt;const char*&gt; extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);

if (enableValidationLayers) {
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
}

return extensions;
}

bool checkValidationLayerSupport() {
uint32_t layerCount;
vkEnumerateInstanceLayerProperties(&layerCount, nullptr);

std::vector&lt;VkLayerProperties&gt; availableLayers(layerCount);
vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());

for (const char* layerName : validationLayers) {
bool layerFound = false;

for (const auto& layerProperties : availableLayers) {
if (strcmp(layerName, layerProperties.layerName) == 0) {
layerFound = true;
break;
}
}

if (!layerFound) {
return false;
}
}

return true;
}

static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) {
std::cerr &lt;&lt; "validation layer: " &lt;&lt; pCallbackData-&gt;pMessage &lt;&lt; std::endl;

return VK_FALSE;
}
};

int main() {
HelloTriangleApplication app;

try {
app.run();
}
catch (const std::exception& e) {
std::cerr &lt;&lt; e.what() &lt;&lt; std::endl;
return EXIT_FAILURE;
}

return EXIT_SUCCESS;
}</pre>

<font color="#9a9a9a">版权声明:本文为博主 </font><a href="https://blog.csdn.net/qq_35312463"><u>沉默的舞台剧</u></a&gt; 原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。</font>
<font color="#9a9a9a">原文链接:</font><a href="https://blog.csdn.net/qq_35312463/article/details/103862429"><font color="#9a9a9a"><u>https://blog.csdn.net/qq_35312463/article/details/103862429</u></font><…;