Vulkan API — урок 16. Проверка swap chain

В следующих (начиная с этого) трех уроках будет рассказано, как создать цепочку своппинга (swap chain, цепочка переключений), которая является инфраструктурой, необходимой для рендеринга.

По сути, цепочка своппинга – это очередь из изображений, ожидающих вывода на экран. Наше приложение будет получать изображение для его отрисовки и затем возвращать его в очередь. Как именно работает очередь и условия представления изображения из очереди зависят от настроек цепочки своппинга, но основной целью цепочки является синхронизация представления изображений с частотой обновления экрана.

Проверка поддержки цепи своппинга

Как вы уже наверно догадались, не все видеокарты имеют возможность вывода изображения напрямую на экран по разным причинам, например из-за того, что это серверная карточка, и не имеет вывода на экран в принципе. Так же следует знать, что представление изображения связано с оконной системой, и поверхностью, связанной с теми же окнами, и это уже не относится к ядру Vulkan’a. Нужно включить расширение VK_KHR_swapchain, после проверки на его поддержку.

Для этого мы добавим в функцию isDeviceSuitable проверку этого расширения. Но перед этим рассмотрим как получить список расширений, поддерживаемых VkPhysicalDevice, что достаточно просто. Обратите внимание, что заголовочный файл Vulkan’a имеет макрос VK_KHR_SWAPCHAIN_EXTENSION_NAME,  который определяется как VK_KHR_swapchain. Напомню, что лучше использовать макросы, тогда компилятор будет ловить опечатки.

Для начала объявим список необходимых расширений, аналогично списку слоев валидации.

const std::vector<const char*> deviceExtensions = {
    VK_KHR_SWAPCHAIN_EXTENSION_NAME
};

Далее создадим новую функцию checkDeviceExtensionSupport, которая будет вызываться из isDeviceSuitable в качестве дополнительной проверки:

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

    bool extensionsSupported = checkDeviceExtensionSupport(device);

    return indices.isComplete() && extensionsSupported;
}

bool checkDeviceExtensionSupport(VkPhysicalDevice device) {
    return true;
}

Модифицируем тело функции для перечисления расширений и проверки наличия среди них всех требуемых расширений.

bool checkDeviceExtensionSupport(VkPhysicalDevice device) {
    uint32_t extensionCount;
    vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);

    std::vector<VkExtensionProperties> availableExtensions(extensionCount);
    vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data());

    std::set<std::string> requiredExtensions(deviceExtensions.begin(), deviceExtensions.end());

    for (const auto& extension : availableExtensions) {
        requiredExtensions.erase(extension.extensionName);
    }

    return requiredExtensions.empty();
}

 Был выбран вариант использовать строки для представления неподтвержденных  требуемых расширений. Таким образом мы можем легко отметить во время перечисления доступных расширений. Конечно же вы так же можете использовать вложенный цикл, аналогично таковому в checkValidationLayerSupport. На производительности это существенно не скажется. теперь следует запустить код и убедиться, что ваша видеокарта действительно поддерживает swap chain.

Подключение расширения потребует лишь небольшого редактирования структуры создания логического устройства:

createInfo.enabledExtensionCount = deviceExtensions.size();
createInfo.ppEnabledExtensionNames = deviceExtensions.data();

Детали поддержки swap chain

Просто проверить наличие swap chain не достаточно, т.к. может оказаться, что она не совместима с нашей поверхностью. Создание swap chain так же включает гораздо больше настроек, нежели instance или устройство, так что нам понадобится запросить дополнительные данные перед тем, как двигаться дальше.

Имеется три базовых параметра, что и необходимо проверить:

  • Базовые возможности поверхности (min/max количество изображений в цепи, min/max ширина и высота изображений)
  • Формат поверхности (формат пикселя, цветовая схема)
  • Доступные режимы (модели) представления

Аналогично findQueueFamilies, мы будем использовать структуру для обработки этих параметров, после их получения. Три вышеупомянутых свойства будут получены в виде следующих строктур и списков структур:

struct SwapChainSupportDetails {
    VkSurfaceCapabilitiesKHR capabilities;
    std::vector<VkSurfaceFormatKHR> formats;
    std::vector<VkPresentModeKHR> presentModes;
};

Теперь создаем функцию querySwapChainSupport, которая будет заполнять эту структуру:

SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device) {
    SwapChainSupportDetails details;

    return details;
}

Эта секция будет запрашивать структуры, содержащие нужную информацию. Смысл этих структур и показ какие именно данные они содержат будем обсуждать чуть позже.

Сейчас же разберемся с базовыми возможностями поверхности. Эти свойства просто запросить и возвращаются они в виде VkSurfaceCapabilitiesKHR структуры.

vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities);

Эта функция принимает VkPhysicalDevice и VkSurfaceKHR поверхность окна, и уже на их базе определяет поддерживаемый функционал. Все функции запрашивающие возможности принимают эти два параметра в первую очередь, т.к. они являются основой для swap chain.

Два следующих шага запрашивают поддерживаемые форматы поверхности. Т.к. этот список структур, то возпользуемся уже знакомым способом двоекратного вызова функции:

uint32_t formatCount;
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr);

if (formatCount != 0) {
    details.formats.resize(formatCount);
    vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data());
}

Следует убедиться, что вектор изменил размер и сможет хранить все поддерживаемые форматы. И наконец, запросим поддерживаемые режимы представления при помощи vkGetPhysicalDeviceSurfacePresentModesKHR:

uint32_t presentModeCount;
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr);

if (presentModeCount != 0) {
    details.presentModes.resize(presentModeCount);
    vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data());
}

Все необходимые параметры теперь находятся в структуре, теперь можно доработать метод isDeviceSuitable в очередной раз, что бы убедиться в адекватной поддержки swap chain. В данном конкретном случае нас устроит если есть хотя бы один поддерживаемый формат изображения и один поддерживаемый режим представления для имеющейся у нас поверхности окна.

bool swapChainAdequate = false;
if (extensionsSupported) {
    SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device);
    swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
}

Важно понимать, что мы только попытались запросить поддержку swap chain, после проверки доступности расширений. Последнюю строку функции меняем на:

return indices.isComplete() && extensionsSupported && swapChainAdequate;

Main Admin

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *