Vulkan Tutorial 5 创建交换链

交换链是图片的列表。其中一个图片在前台显示,其他图片在后台。Vulkan的绘制会在后台图片上进行一些列的绘制操作,直到绘制完成,然后交换前台图片和绘制完成的后台图片。此时,当前后台图片成为前台图片,当前前台图片成为后台图片。交换链如下所示:

                

Vulkan和视窗系统

与其他图形API一样,Vulkan也将视窗系统与核心图形API分离。在Vulkan中,窗口系统细节通过WSI(Window System Integration 窗口系统集成)扩展来公开。

WSI扩展包含对各种平台的支持,可通过以下宏定义开启:

  • VK_USE_PLATFORM_ANDROID_KHR - Android
  • VK_USE_PLATFORM_WAYLAND_KHR - Wayland
  • VK_USE_PLATFORM_WIN32_KHR - Microsoft Windows
  • VK_USE_PLATFORM_XCB_KHR - X Window System, using the XCB library
  • VK_USE_PLATFORM_XLIB_KHR - X Window System, using the XLib library

名称中的KHR表示在Khronos中定义

Surface抽象

Vulkan用VkSurfaceKHR对象抽象本地平台Surface和窗口。这个符号被定义为VK_KHR_surface扩展的一部分。WSI扩展提供各种函数用于创建、操作、释放这个对象。

访问实例和设备扩展

实例扩展

为了使用WSI扩展,必须激活一般surface扩展。以下代码添加VK_KHR_SURFACE_EXTENSION_NAME添加到要加载的实例扩展列表中。

void init_instance_extension_names(struct sample_info &info) {
    info.instance_extension_names.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
#ifdef __ANDROID__
    info.instance_extension_names.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
#elif defined(_WIN32)
    info.instance_extension_names.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
#elif defined(VK_USE_PLATFORM_IOS_MVK)
    info.instance_extension_names.push_back(VK_MVK_IOS_SURFACE_EXTENSION_NAME);
#elif defined(VK_USE_PLATFORM_MACOS_MVK)
    info.instance_extension_names.push_back(VK_MVK_MACOS_SURFACE_EXTENSION_NAME);
#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
    info.instance_extension_names.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);
#else
    info.instance_extension_names.push_back(VK_KHR_XCB_SURFACE_EXTENSION_NAME);
#endif
}

还要注意,添加与平台匹配的扩展,如果是windows,则用VK_KHR_WIN32_SURFACE_EXTENSION_NAME。当Vulkan实例被创建时,这些扩展被加载。

设备扩展

交换链是一个图片列表,用于GPU绘制和显示设备扫描输出。因为是GPU硬件负责写入图片,因此设备级扩展需要与交换链一起工作。所以在代码中需要将VK_KHR_SWAPCHAIN_EXTENSION_NAME加入设备扩展列表。

info.device_extension_names.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);

关于实例和设备扩展的更详细的信息可以在LunarG LunarXchange website中找到。

队列族和Present(呈现?)

Present操作其实就是在交换链里面获取一张图片,并且显示在显示设备。当应用程序要在屏幕上显示一张图片时,应用程序调用vkQueuePresentKHR()函数将请求放入一个GPU队列。所以,此函数引用的队列必须能够支持呈现请求或者同时支持图形和Present请求。验证是否支持,可如下操作:

// Iterate over each queue to learn whether it supports presenting:
VkBool32 *pSupportsPresent = (VkBool32 *)malloc(info.queue_family_count * sizeof(VkBool32));
for (uint32_t i = 0; i < info.queue_family_count; i++) {
    vkGetPhysicalDeviceSurfaceSupportKHR(info.gpus[0], i, info.surface, &pSupportsPresent[i]);
}

// Search for a graphics and a present queue in the array of queue
// families, try to find one that supports both
info.graphics_queue_family_index = UINT32_MAX;
info.present_queue_family_index = UINT32_MAX;
for (uint32_t i = 0; i < info.queue_family_count; ++i) {
    if ((info.queue_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) {
        if (info.graphics_queue_family_index == UINT32_MAX)
            info.graphics_queue_family_index = i;

        if (pSupportsPresent[i] == VK_TRUE) {
            info.graphics_queue_family_index = i;
            info.present_queue_family_index = i;
            break;
        }
    }
}

if (info.present_queue_family_index == UINT32_MAX) {
    // If didn't find a queue that supports both graphics and present, then
    // find a separate present queue.
    for (size_t i = 0; i < info.queue_family_count; ++i)
        if (pSupportsPresent[i] == VK_TRUE) {
            info.present_queue_family_index = i;
            break;
        }
}
free(pSupportsPresent);

交换链创建信息

交换链创建信息VkSwapchainCreateInfoKHR声明如下:

typedef struct VkSwapchainCreateInfoKHR {
    VkStructureType                  sType;
    const void*                      pNext;
    VkSwapchainCreateFlagsKHR        flags;
    VkSurfaceKHR                     surface;
    uint32_t                         minImageCount;
    VkFormat                         imageFormat;
    VkColorSpaceKHR                  imageColorSpace;
    VkExtent2D                       imageExtent;
    uint32_t                         imageArrayLayers;
    VkImageUsageFlags                imageUsage;
    VkSharingMode                    imageSharingMode;
    uint32_t                         queueFamilyIndexCount;
    const uint32_t*                  pQueueFamilyIndices;
    VkSurfaceTransformFlagBitsKHR    preTransform;
    VkCompositeAlphaFlagBitsKHR      compositeAlpha;
    VkPresentModeKHR                 presentMode;
    VkBool32                         clipped;
    VkSwapchainKHR                   oldSwapchain;
} VkSwapchainCreateInfoKHR;

创建Surface

在实例和设备中加载了WSI扩展之后,现在可以创建VkSurface,以便继续创建交换链。创建Surface必须区分平台,例如windows平台:

#ifdef _WIN32
    VkWin32SurfaceCreateInfoKHR createInfo = {};
    createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
    createInfo.pNext = NULL;
    createInfo.hinstance = info.connection;
    createInfo.hwnd = info.window;
    res = vkCreateWin32SurfaceKHR(info.inst, &createInfo, NULL, &info.surface);
#endif

info.connection和info.window可在例子里面找到。查看代码,发现,windows平台下,info.connection其实是应用程序实例句柄,info.window是窗口句柄。

接下来就是将创建好的surface加入交换链的创建信息结构中。

VkSwapchainCreateInfoKHR swapchain_ci = {};
swapchain_ci.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
swapchain_ci.pNext = NULL;
swapchain_ci.surface = info.surface;

设备Surface格式

创建交换链时必须设置Surface的像素格式。像素格式用VkFormat枚举出来。

接下来就是创建VkSurfaceFormatKHR列表,里面包含支持所创建的Surface的一些信息。

    uint32_t formatCount;
    res = vkGetPhysicalDeviceSurfaceFormatsKHR(info.gpus[0], info.surface, &formatCount, NULL);
    assert(res == VK_SUCCESS);
    VkSurfaceFormatKHR *surfFormats = (VkSurfaceFormatKHR *)malloc(formatCount * sizeof(VkSurfaceFormatKHR));
    res = vkGetPhysicalDeviceSurfaceFormatsKHR(info.gpus[0], info.surface, &formatCount, surfFormats);
    assert(res == VK_SUCCESS);
    // If the format list includes just one entry of VK_FORMAT_UNDEFINED,
    // the surface has no preferred format.  Otherwise, at least one
    // supported format will be returned.
    if (formatCount == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED) {
        info.format = VK_FORMAT_B8G8R8A8_UNORM;
    } else {
        assert(formatCount >= 1);
        info.format = surfFormats[0].format;
    }
    free(surfFormats);

Surface能力集

物理设备Surface能力集通过vkGetPhysicalDeviceSurfaceCapabilitiesKHR()函数获取和物理设备Surface呈现模式由vkGetPhysicalDeviceSurfacePresentModesKHR()获取。将获取到的信息设置到交换链创建信息:

uint32_t desiredNumberOfSwapChainImages = surfCapabilities.minImageCount;

swapchain_ci.minImageCount = desiredNumberOfSwapChainImages;
swapchain_ci.imageExtent.width = swapChainExtent.width;
swapchain_ci.imageExtent.height = swapChainExtent.height;
swapchain_ci.preTransform = preTransform;
swapchain_ci.presentMode = swapchainPresentMode;

minImageCount表示应用程序所用的缓冲策略,例如双缓冲和三缓冲。关于minImageCount的大小范围,可通过Surface能力集获取,即调用vkGetPhysicalDeviceSurfaceCapabilitiesKHR。

不同队列族的图形和呈现

如果支持图形的队列和支持呈现的队列不同,则需要做如下操作:

swapchain_ci.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
swapchain_ci.queueFamilyIndexCount = 0;
swapchain_ci.pQueueFamilyIndices = NULL;
uint32_t queueFamilyIndices[2] = {
    (uint32_t)info.graphics_queue_family_index,
    (uint32_t)info.present_queue_family_index};
if (info.graphics_queue_family_index != info.present_queue_family_index) {
    // If the graphics and present queues are from different queue families,
    // we either have to explicitly transfer ownership of images between the
    // queues, or we have to create the swapchain with imageSharingMode
    // as VK_SHARING_MODE_CONCURRENT
    swapchain_ci.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
    swapchain_ci.queueFamilyIndexCount = 2;
    swapchain_ci.pQueueFamilyIndices = queueFamilyIndices;
}

创建交换链

当VkSwapchainCreateInfoKHR设置完毕,可直接创建交换链:

res = vkCreateSwapchainKHR(info.device, &swapchain_ci, NULL, &info.swap_chain);

创建完交换链之后,可通过如下方法获取交换链里面的图像缓存:

    res = vkGetSwapchainImagesKHR(info.device, info.swap_chain, &info.swapchainImageCount, NULL);
    assert(res == VK_SUCCESS);

    VkImage *swapchainImages = (VkImage *)malloc(info.swapchainImageCount * sizeof(VkImage));
    assert(swapchainImages);
    res = vkGetSwapchainImagesKHR(info.device, info.swap_chain, &info.swapchainImageCount, swapchainImages);
    assert(res == VK_SUCCESS);

创建Image视图

创建完交换链后,还需要创建Image视图,视图就是一些附加信息,用于告诉Vulkan如何使用交换链。

视图可以定义图像内存的存储方式,比如RGBA的排列方式,以及维度等。例如,图像可以是1D、2D和3D。创建视图的代码如下:

    for (uint32_t i = 0; i < info.swapchainImageCount; i++) {
        VkImageViewCreateInfo color_image_view = {};
        color_image_view.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
        color_image_view.pNext = NULL;
        color_image_view.flags = 0;
        color_image_view.image = info.buffers[i].image;
        color_image_view.viewType = VK_IMAGE_VIEW_TYPE_2D;
        color_image_view.format = info.format;
        color_image_view.components.r = VK_COMPONENT_SWIZZLE_R;
        color_image_view.components.g = VK_COMPONENT_SWIZZLE_G;
        color_image_view.components.b = VK_COMPONENT_SWIZZLE_B;
        color_image_view.components.a = VK_COMPONENT_SWIZZLE_A;
        color_image_view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
        color_image_view.subresourceRange.baseMipLevel = 0;
        color_image_view.subresourceRange.levelCount = 1;
        color_image_view.subresourceRange.baseArrayLayer = 0;
        color_image_view.subresourceRange.layerCount = 1;

        res = vkCreateImageView(info.device, &color_image_view, NULL, &info.buffers[i].view);
        assert(res == VK_SUCCESS);
    }

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值