Помимо информации о слояхустройства (enabledLayerCount/ppEnabledLayerNames), которые, кстати говоря, почти никак не связаны со слоямиобразца интерфейса, и информации о расширениях (enabledExtensionCount/ppEnabledExtensionNames), она содержит два более интересных поля: информацию о особенностяхустройства (pEnabledFeatures) и очередях (queueCreateInfoCount/pQueueCreateInfos).
Полное описание структуры, используемых особенностейустройства – VkPhysicalDeviceFeatures несколько громоздкое. Каждое поле отвечает за (не)использование конкретной особенности. По возможности, следует сократить до минимума требовательность к особенностям, ведь не все видеокарты их поддерживают. В этой статье мы не будем использовать их вообще, а потому обнулим все поля этой структуры (демонстрация кода, наверняка, излишня).
Далее следует подготовить информацию о очередях. В контексте Vulkan API подразумевается очередь команд, которые поступают на выполнение GPU. Логическое устройство может иметь несколько семейств очередей, каждое из которых может иметь определенное количество… очередей. О самих очередях мы поговорим позже.
Каждое семейство имеет свой уникальный индекс, количество очередей и их приоритеты. Большинство этих значений нам принципиально не важны, а потому мы создадим всего лишь одно семейство с одной единственной очередью нулевого приоритета.
Для вывода изображения на экран нам понадобятся два объекта: поверхность вывода (surface) и цепочка обмена (swapchain).
План:
• Задействовать поддержку платформы Win32
• Создать поверхность вывода
• Перечислить поддерживаемые поверхностью выводаформаты и особенности
• Создать цепочку обмена
Поддержка Win32
Следует заметить, что эта часть кода является зависимой от платформы. Само собой, у нас не получится создать поверхность вывода Win32 на Linux.
В первую очередь следует задействовать поддержку расширений для платформы Win32. Для этого необходимо объявить перед включением библиотеки «vulkan.h» следующий макрос:
#define VK_USE_PLATFORM_WIN32_KHR 1
включить в список расширенийобразца интерфейсарасширения для поверхности вывода и поверхности вывода Win32:
Некоторые слои могут ругаться, если не проверить поддержку поверхности выводафизическим устройством, а потому в код была включена функция vkGetPhysicalDeviceSurfaceSupportKHR.
Для дальнейшей работы нам также понадобится список доступных для поверхности форматов:
std::vector<VkSurfaceFormatKHR> vk_surfaceFormats;
{
uint32_t vk_surfaceFormatsCount;
if(vkGetPhysicalDeviceSurfaceFormatsKHR(vk_physicalDevice, vk_surfaceKHR, &vk_surfaceFormatsCount, nullptr) != VkResult::VK_SUCCESS)
{
throw std::exception("failed to get surface formats count");
}
vk_surfaceFormats.resize(vk_surfaceFormatsCount);
if(vkGetPhysicalDeviceSurfaceFormatsKHR(vk_physicalDevice, vk_surfaceKHR, &vk_surfaceFormatsCount, vk_surfaceFormats.data()) != VkResult::VK_SUCCESS)
{
throw std::exception("failed to get surface formats");
}
}
Описание функции vkGetPhysicalDeviceSurfaceFormatsKHR упускается намерено, из-за сходства с остальными функциями для перечислений. Единственное, что стоит подчеркнуть, так это наличие параметра vk_physicalDevice, т.е. функция на самом деле перечисляет форматы, с которыми может работать реальное железо.
Еще один интересный момент: мы можем узнать, на что способна выбранная нами поверхность вывода. В дальнейшем это понадобится, дабы работать не с «значениями с потолка», а оперировать реальными данными:
VkSurfaceCapabilitiesKHR vk_surfaceCapabilitiesKHR;
{
if(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vk_physicalDevice, vk_surfaceKHR, &vk_surfaceCapabilitiesKHR) != VkResult::VK_SUCCESS)
{
throw std::exception("failed to get surface capabilities");
}
}
Например, будем использовать значение поля currentExtent, как размеры экрана.
Цепочка обмена
Данный объект будет позволять нам двойную (вообще говоря, сколь-угодно кратную) буферизацию, т.е. рисовать в одно изображение, в то время как другое выводится на поверхность.
За создание отвечает:
Мы удержимся от рассмотрения всех параметров и обойдемся лишь наиболее важными. surface связывает цепочку с определенной поверхностью, minImageCount говорит о количестве буферов (нам необходимы хотя бы 2), imageFormat и imageColorSpace это используемые форматы и пространства цветов, а imageExtent – размер буферов (условно говоря, размер экрана).
для нее автоматически сгенерируются изображения (буферы, о которых мы говорили). Поскольку нам придется как выводить в эти изображения, так и выводить их самих (на поверхность), стоило бы перечислить их:
std::vector<VkImage> vk_swapchainImages;
{
uint32_t vk_swapchainImagesCount;
if(vkGetSwapchainImagesKHR(vk_device, vk_swapchainKHR, &vk_swapchainImagesCount, nullptr) != VkResult::VK_SUCCESS)
{
throw std::exception("failed to get swapchain images count");
}
vk_swapchainImages.resize(vk_swapchainImagesCount);
if(vkGetSwapchainImagesKHR(vk_device, vk_swapchainKHR, &vk_swapchainImagesCount, vk_swapchainImages.data()) != VkResult::VK_SUCCESS)
{
throw std::exception("failed to get swapchain images");
}
}
Нам предстоит проделать еще немного работы с цепочкой обмена, но для этого нам уже нужна очередь.