Создание и удаление цепочки буферов в GLFW

Последнее обновление: 07.04.2025

Продолжим работу с приложением на GLFW из прошлой темы и рассмотрим создание и удаление цепочки буферов:

#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>

#include <iostream>
#include <stdexcept>
#include <vector>
#include <cstring>
#include <cstdlib>
#include <set>
#include <cstdint> // для uint32_t
#include <limits> // для std::numeric_limits
#include <algorithm> // для std::clamp


const uint32_t WIDTH = 300;
const uint32_t HEIGHT = 250;

// структура для проверки возможностей поверхности
struct SwapChainSupportDetails {
    VkSurfaceCapabilitiesKHR capabilities;              // возможности поверхности
    std::vector<VkSurfaceFormatKHR> formats;      // доступные форматы
    std::vector<VkPresentModeKHR> presentModes;   // доступные режимы представления
};

// структура для хранения индексов семейств очередей
struct QueueFamilyIndices {
    uint32_t graphicsFamilyIndex;
    uint32_t presentFamilyIndex;

    VkBool32 graphicsSupport;
    VkBool32 presentSupport;

    bool isComplete() {
        return graphicsSupport && presentSupport;
    }
};


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

private:
    GLFWwindow* window;

    VkInstance instance;
    VkSurfaceKHR surface;       // поверхность рисования
    VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;  // физическое устройство
    VkDevice device;    // логическре устройство
    VkQueue graphicsQueue;  // графическая очередь
    VkQueue presentQueue;   // очередь представления
    VkSwapchainKHR swapChain;  // цепочка буферов

    VkFormat swapChainImageFormat; // формат изображений
    VkExtent2D swapChainExtent;     // размер изображений

    // создаем окно
    void initWindow() {
        glfwInit();

        glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
        glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
        window = glfwCreateWindow(WIDTH, HEIGHT, "METANIT.COM", nullptr, nullptr);
    }

    // инициализация всех аспектов Vulkan
    void initVulkan() {
        createInstance();           // создание VkInstance
        createSurface();            // создание поверхности рисования
        selectPhysicalDevice();     // выбор физического устройства
        createLogicalDevice();      // создание логического устройства
        createSwapChain();         // создаение цепочки буферов
    }
    // установка формата изображений
    VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats) {
        for (const auto& availableFormat : availableFormats) {
            if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
                return availableFormat;
            }
        }

        return availableFormats[0];
    }
    // определение режима представления
    VkPresentModeKHR chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes) {
        for (const auto& availablePresentMode : availablePresentModes) {
            if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
                return availablePresentMode;
            }
        }

        return VK_PRESENT_MODE_FIFO_KHR;
    }
    // определение размеров для цепочки буферов
    VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) {
        // если текущая ширина не указана как максимальное значение для uint32_t
        if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) {
            // то возвращаем текущие размеры
            return capabilities.currentExtent;
        } else {
            int width, height;
            glfwGetFramebufferSize(window, &width, &height);

            VkExtent2D actualExtent = {
                static_cast<uint32_t>(width),
                static_cast<uint32_t>(height)
            };

            actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
            actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height);

            return actualExtent;
        }
    }
    // создание цепочки буферов
    void createSwapChain() {
        // получаем поддерживаемые устройством форматы, режимы представления и размеры 
        SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice);

        // выбираем формат поверхности
        VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats);
        // выбираем режим представления
        VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes);
        // устанавливаем размеры
        VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities);

        // определяем минимальное количество изображения, которое требуется для работы 
        // и увеличиваем его на 1 для оптимизации работы драйвера
        uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
        // проверяем, что не превышаем максимальное количество изображений, 
        // делая это, где 0 — это особое значение, которое означает, что максимума нет
        if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) {
            imageCount = swapChainSupport.capabilities.maxImageCount;
        }

        // определяем конфигурацию для создания цепочки буферов
        VkSwapchainCreateInfoKHR createInfo{};
        createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
        createInfo.surface = surface;
        createInfo.minImageCount = imageCount;  // минимальное количество изображений
        createInfo.imageFormat = surfaceFormat.format;      // формат изображений
        createInfo.imageColorSpace = surfaceFormat.colorSpace;  // цветовое пространство 
        createInfo.imageExtent = extent;            // размеры изображений
        createInfo.imageArrayLayers = 1;            // поверхность с одним представлением
        createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;  // изображение используется в качестве цветового прикрепления

        // Указываем, как обрабатывать изображения цепочки буферов, которые будут использоваться в нескольких семействах очередей.
        QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
        uint32_t queueFamilyIndices[] = {indices.graphicsFamilyIndex, indices.presentFamilyIndex};

        // если семейство графической очереди отличается от очереди представления
        if (indices.graphicsFamilyIndex != indices.presentFamilyIndex) {
            createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; // обе очереди совместно владеют изображением
            createInfo.queueFamilyIndexCount = 2;           // две очереди
            createInfo.pQueueFamilyIndices = queueFamilyIndices; // массив индексов семейств очередей
        } else {
            createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; // изображением владеет одна очередь
            createInfo.queueFamilyIndexCount = 0; // Необязательно
            createInfo.pQueueFamilyIndices = nullptr; // Необязательно
        }
        // не применяем никаких преобразований
        createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
        // игнорирум альфа-канал
        createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;

        createInfo.presentMode = presentMode;  // режим представления
        createInfo.clipped = VK_TRUE;        // включаем обрезку скрытых пикселей

        createInfo.oldSwapchain = VK_NULL_HANDLE; // нет старых цепочек буферов

        // создаем цепочку буферов
        if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) {
            throw std::runtime_error("Не удалось создать цепочку буферов!");
        }
    }
    void mainLoop() {
        while (!glfwWindowShouldClose(window)) {
            glfwWaitEvents();
        }
    }

    void cleanup() {
        // удаляем цепочку буферов
        vkDestroySwapchainKHR(device, swapChain, nullptr);
        // удаляем логическое устройство
        vkDestroyDevice(device, nullptr);
        // удаляем поверхность
        vkDestroySurfaceKHR(instance, surface, nullptr);
        // удаляем объект VkInstance
        vkDestroyInstance(instance, nullptr);

        glfwDestroyWindow(window);
        glfwTerminate();
    }

    // создание VkInstance
    void createInstance() {

        VkInstanceCreateInfo createInfo{};
        createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;

        uint32_t glfwExtensionCount = 0;
        const char** glfwExtensions;
        // получаем расширения для GLFW
        glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
        // устанавливаем расширения для VkInstance
        createInfo.enabledExtensionCount = glfwExtensionCount;
        createInfo.ppEnabledExtensionNames = glfwExtensions;

        if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
            throw std::runtime_error("Не удалось создать VkInstance!");
        }
    }

    // создаем поверхность
    void createSurface() {
        if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) {
            throw std::runtime_error("Не удалось создать поверхность для рисования!");
        }
    }
    // проверка поддержки цепочки буферов
    SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device) {
        SwapChainSupportDetails details;

        // проверяем поддерживаемые возможности
        vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities);

        uint32_t formatCount;
        // сначала получаем количество поддерживаемых форматов
        vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr);

        if (formatCount != 0) {
            details.formats.resize(formatCount); // расширяем вектор, чтобы вместить все данные
            // получаем все поддерживаемые форматы
            vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data());
        }

        uint32_t presentModeCount;
        // получаем количество поддерживаемых режимов представления
        vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr);

        if (presentModeCount != 0) {
            details.presentModes.resize(presentModeCount); 
            // получаем все поддерживаемые режимы представления
            vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data());
        }

        return details;
    }
    // выбираем физическое устройство
    void selectPhysicalDevice() {
        uint32_t deviceCount = 0;
        vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);

        if (deviceCount == 0) {
            throw std::runtime_error("Подходящее устройство GPU отсутствует!");
        }

        std::vector<VkPhysicalDevice> 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("Подходящее устройство GPU отсутствует!");
        }
    }

    // создание логического устройства
    void createLogicalDevice() {
        // получаем обе очереди
        QueueFamilyIndices indices = findQueueFamilies(physicalDevice);

        std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
        // набор для индексов семейств очередей, если они совпадают, то остается только один индекс
        std::set<uint32_t> uniqueQueueFamilies = {
            indices.graphicsFamilyIndex,
            indices.presentFamilyIndex
        };

        float queuePriority = 1.0f;
        // проходим по всем индексам семейств очередей и создаем для каждого индекса структуру VkDeviceQueueCreateInfo
        for (uint32_t queueFamily : uniqueQueueFamilies) {
            VkDeviceQueueCreateInfo queueCreateInfo{};
            queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
            queueCreateInfo.queueFamilyIndex = queueFamily;
            queueCreateInfo.queueCount = 1;
            queueCreateInfo.pQueuePriorities = &queuePriority;
            queueCreateInfos.push_back(queueCreateInfo);
        }

        VkDeviceCreateInfo createInfo{};
        createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;

        // устанавливаем обе очереди для создания устройства
        createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
        createInfo.pQueueCreateInfos = queueCreateInfos.data();
        
        // подключаемые расширения
        const std::vector<const char*> deviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME};
        // подключаем расширения 
        // устанавливаем количество расширений
        createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size()); 
        // устанавливаем сами расширения
        createInfo.ppEnabledExtensionNames = deviceExtensions.data();

        // устанавливаем поддерживаемые возможности
        VkPhysicalDeviceFeatures deviceFeatures{};
        createInfo.pEnabledFeatures = &deviceFeatures;

        // создаем логическое устройство
        if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
            throw std::runtime_error("Не удалось создать логическое устройство!");
        }

        // создаем графическую очередь
        vkGetDeviceQueue(device, indices.graphicsFamilyIndex, 0, &graphicsQueue);
        // создаем очередь представления
        vkGetDeviceQueue(device, indices.presentFamilyIndex, 0, &presentQueue);
    }

    // проверяем поддержку цепочки буферов
    bool checkSwapchainExtensionSupport(VkPhysicalDevice device) {
        uint32_t extensionCount;
        // сначала получаем количество расширений
        vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);

        // получаем все доступные расширения
        std::vector<VkExtensionProperties> availableExtensions(extensionCount);
        vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data());

        std::string extensionName {VK_KHR_SWAPCHAIN_EXTENSION_NAME}; // строка, с которой сравниванием

        for (const auto& extension : availableExtensions) {
            // сравнение строк
            if(extensionName == extension.extensionName) return true;
        }

        return false;
    }
   // проверка, является ли устройство подходящим
    bool isDeviceSuitable(VkPhysicalDevice device) {
        // получаем индексы графической очереди и очереди представления
        QueueFamilyIndices indices = findQueueFamilies(device);
        // если не поддерживаются графическая очередь и/или очередь представления
        if(!indices.isComplete()) return false; 
        // если не поддерживаются расширения
        if(!checkSwapchainExtensionSupport(device)) return false;
        // получаем поддержку цепочки буферов
        SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device);
        // считаем устройство подходящим, если поддерживается хотя бы один формат и хотя бы один режим представления
        return !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
    }
    // получаем индексы семейства очередей
    QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) {
        QueueFamilyIndices indices;

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

        std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
        vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());

        int i = 0;
        // проходим по всем очередям
        for (const auto& queueFamily : queueFamilies) {
            // получаем индекс семейства с графической очередью
            if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
                indices.graphicsFamilyIndex = i;
                indices.graphicsSupport = true;
            }
            // смотрим, поддерживается ли очередь представления
            vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &indices.presentSupport);
            if (indices.presentSupport)  indices.presentFamilyIndex = i;
            if (indices.isComplete())  break; // если обе очереди установлены
            i++;
        }
        return indices;
    }
};

int main() {
    HelloApplication app;

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

    return EXIT_SUCCESS;
}

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

VkSwapchainKHR swapChain;
VkFormat swapChainImageFormat; // формат изображений
VkExtent2D swapChainExtent;     // размер изображений

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

И для создания цепочки буферов добавлена функция createSwapChain():

void createSwapChain() {
    // получаем поддерживаемые устройством форматы, режимы представления и размеры 
    SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice);

    // выбираем формат поверхности
    VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats);
    // выбираем режим представления
    VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes);
    // устанавливаем размеры
    VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities);

    // определяем минимальное количество изображения, которое требуется для работы 
    // и увеличиваем его на 1 для оптимизации работы драйвера
    uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
    // проверяем, что не превышаем максимальное количество изображений, 
    // делая это, где 0 — это особое значение, которое означает, что максимума нет
    if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) {
        imageCount = swapChainSupport.capabilities.maxImageCount;
    }

    // определяем конфигурацию для создания цепочки буферов
    VkSwapchainCreateInfoKHR createInfo{};
    createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
    createInfo.surface = surface;
    createInfo.minImageCount = imageCount;  // минимальное количество изображений
    createInfo.imageFormat = surfaceFormat.format;      // формат изображений
    createInfo.imageColorSpace = surfaceFormat.colorSpace;  // цветовое пространство 
    createInfo.imageExtent = extent;        // размеры изображений
    createInfo.imageArrayLayers = 1;        // поверхность с одним представлением
    createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;  // изображение используется в качестве цветового прикрепления

    // Указываем, как обрабатывать изображения цепочки буферов, которые будут использоваться в нескольких семействах очередей.
    QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
    uint32_t queueFamilyIndices[] = {indices.graphicsFamilyIndex, indices.presentFamilyIndex};

    // если семейство графической очереди отличается от очереди представления
    if (indices.graphicsFamilyIndex != indices.presentFamilyIndex) {
        createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; // обе очереди совместно владеют изображением
        createInfo.queueFamilyIndexCount = 2;       // две очереди
        createInfo.pQueueFamilyIndices = queueFamilyIndices; // массив индексов семейств очередей
    } else {
        createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; // изображением владеет одна очередь
        createInfo.queueFamilyIndexCount = 0; // Необязательно
        createInfo.pQueueFamilyIndices = nullptr; // Необязательно
    }
    // не применяем никаких преобразований
    createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
    // игнорирум альфа-канал
    createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;

    createInfo.presentMode = presentMode;  // режим представления
    createInfo.clipped = VK_TRUE;    // включаем обрезку скрытых пикселей

    createInfo.oldSwapchain = VK_NULL_HANDLE; // нет старых цепочек буферов

    // создаем цепочку буферов
    if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) {
        throw std::runtime_error("Не удалось создать цепочку буферов!");
    }
    // сохраняем выбранные формат и размеры изображений для последующего использования
    swapChainImageFormat = surfaceFormat.format;
    swapChainExtent = extent;
}

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

SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice);

VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats);
VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes);
VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities);

Далее определяем минимальное количество изображения, которое требуется для работы:

uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;

if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) {
    imageCount = swapChainSupport.capabilities.maxImageCount;
}

Затем устанавливаем объект конфигурации для создания цепочки буферов:

VkSwapchainCreateInfoKHR createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
createInfo.surface = surface;
createInfo.minImageCount = imageCount;  // минимальное количество изображений
createInfo.imageFormat = surfaceFormat.format;      // формат изображений
createInfo.imageColorSpace = surfaceFormat.colorSpace;  // цветовое пространство 
createInfo.imageExtent = extent;        // размеры изображений
createInfo.imageArrayLayers = 1;        // поверхность с одним представлением
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;  // изображение используется в качестве цветового прикрепления

Далее нужно указать, как обрабатывать изображения, которые будут использоваться в нескольких семействах очередей. Это происходит, если семейство графической очереди отличается от очереди представления. Мы будем рисовать изображения в цепочке буферов из графической очереди, а затем отправлять их в очередь представления на вывод.

QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
uint32_t queueFamilyIndices[] = {indices.graphicsFamilyIndex, indices.presentFamilyIndex};

// если семейство графической очереди отличается от очереди представления
if (indices.graphicsFamilyIndex != indices.presentFamilyIndex) {
    createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; // обе очереди совместно владеют изображением
    createInfo.queueFamilyIndexCount = 2;           // две очереди
    createInfo.pQueueFamilyIndices = queueFamilyIndices; // массив индексов семейств очередей 
} else {
    createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; // изображением владеет одна очередь
    createInfo.queueFamilyIndexCount = 0; // Необязательно
    createInfo.pQueueFamilyIndices = nullptr; // Необязательно
}

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

  • VK_SHARING_MODE_EXCLUSIVE: изображение принадлежит исключительно одному семейству очередей за раз, и право собственности должно быть явно передано перед использованием его в другом семействе очередей. Этот вариант обеспечивает наилучшую производительность.

  • VK_SHARING_MODE_CONCURRENT: изображения могут использоваться в нескольких семействах очередей без явной передачи права собственности.

Если семейства очередей различаются, то мы будем использовать совместный режим владения (VK_SHARING_MODE_EXCLUSIVE). При этом с помощью полей queueFamilyIndexCount и pQueueFamilyIndices требуется указать, какие семейства очередей будут совместно владеть изображениями.

Если же семейство графической очереди и семейство очереди представления совпадают (что характерно для большинства устройств), то лучше применять исключительный режим владения (VK_SHARING_MODE_EXCLUSIVE), что может улучшить производительность.

Также мы можем указать, что надо применять определенное преобразование. Но в данном случае преобразования пока для нас не важны, поэтому чтобы указать, что никакие преобразования не применяются, устанавливается текущее преобразование.

createInfo.preTransform = swapChainSupport.capabilities.currentTransform;

Далее указываем, что хотим игнорировать альфа-канал, для этого для поля compositeAlpha устанавливаем значение VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR:

createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;

Далее устанавливаем режим представления и поле clipped - параметры работы с невидимыми частями поверхности

createInfo.presentMode = presentMode;  // режим представления
createInfo.clipped = VK_TRUE;        // включаем обрезку скрытых пикселей

Значение VK_TRUE для поля clipped означает, что нас не волнует цвет пикселей, которые скрыты, например, потому что перед ними находится другое окно. Это позволяет улучшить производительность, так как нам не нужно иметь возможность считывать скрытые пиксели.

И в конце устанавливаем для поля oldSwapchain значение , так как у нас нет других ранее использованных цепочек буферов, связанных с текущей поверхностью:

createInfo.oldSwapchain = VK_NULL_HANDLE;

Далее собственно создаем цепочку буферов с помощью функции vkCreateSwapchainKHR:

if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) {
    throw std::runtime_error("Не удалось создать цепочку буферов!");
}

И эту функцию вызываем в функции initVulkan():

void initVulkan() {
    ........................
    createSwapChain();         // создаение цепочки буферов
}

Таким образом, после запуска приложения будет создаваться цепочка буферов.

А в функции cleanup(), где происходит очистка ресурсов, удаляем цепочку буферов:

void cleanup() {
    // удаляем цепочку буферов
    vkDestroySwapchainKHR(device, swapChain, nullptr);
    ...........................
}
Помощь сайту
Юмани:
410011174743222
Номер карты:
4048415020898850