Vulkan 交换链

什么是交换链?

Vulkan 交换链
FIFO 呈现模式的交换链

Vulkan 交换链(Swapchain)是 Vulkan 应用程序与窗口系统之间的一座桥梁,负责将渲染结果呈现给用户。

换个说法,交换链是一种图元绘制结果呈现的机制,它可以将绘制结果渲染到平台相关的展示窗口/展示层当中。

交换链包含一组图像(一般作为渲染目标),这些图像被用于在屏幕上显示渲染的帧。

交换链中的图像个数与驱动层的实现是密切相关的。如果交换链中有两幅图像,那么称为双缓存,如果有三幅图像,那么称作三缓存。

在这些图像中,如果有一幅图像在后台已经完成了绘制过程,那么它会被交换到前台的展示窗口。

为了充分利用GPU的性能,此时另一幅图像会交换作为后台的缓存来执行下一帧的绘制过程。

这个过程往复执行,图像的交换过程也就持续进行。使用多幅图像可以保持GPU始终处于渲染某一帧图像的忙碌状态,从而降低总体的等待时间,改善输出帧速率。

交换链的作用

  1. 图像管理交换链维护了一组用于显示的图像,称为交换链图像(Swapchain Images)。交换链图像通常至少有两个,这样就可以实现双缓冲或多缓冲,从而避免屏幕闪烁。
  2. 同步: 管理 CPU 和 GPU 之间的同步,确保渲染操作不会在显示更新之前完成,这有助于防止撕裂(tearing)现象,即在同一帧内看到两个不同图像的一部分。
  3. 图像呈现:
  • 应用程序首先在一个缓冲区上进行渲染,完成渲染后,该缓冲区会被提交给交换链,然后交换链会将这个缓冲区的内容显示到屏幕上。
  • 当一个缓冲区正在显示时,另一个缓冲区可以用于下一轮的渲染,这样就形成了一个循环。

创建交换链

创建交换链之前,我们需要利用上一节创建的 VkSurfaceKHR  对象来获取创建交换链所需的尺寸、格式、颜色空间等信息。

 1 VkSurfaceKHR surface;
 2
 3 //获取表面的特性
 4 VkSurfaceCapabilitiesKHR surfaceCapabilities;
 5 vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface,
 6                                        &surfaceCapabilities);
 7 uint32_t formatCount = 0;
 8 vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface,
 9                                   &formatCount, nullptr);
10
11 //获取表面所支持的格式
12 VkSurfaceFormatKHR* formats = new VkSurfaceFormatKHR[formatCount];
13 vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface,
14                                   &formatCount, formats);

获取上述信息之后,我们来填充 VkSwapchainCreateInfoKHR 结构体。

创建交换链的函数 vkCreateSwapchainKHR :

1 VkResult vkCreateSwapchainKHR(
2    VkDevice                        device,                  // Vulkan 设备句柄
3    const VkSwapchainCreateInfoKHR* pCreateInfo,             // 指向 VkSwapchainCreateInfoKHR 结构体的指针,描述交换链的创建参数
4    const VkAllocationCallbacks*    pAllocator,              // 自定义的内存分配器回调函数指针,可以为 NULL 表示使用默认分配器
5    VkSwapchainKHR*                 pSwapchain               // 指向 VkSwapchainKHR 句柄的指针,函数成功返回时,包含创建的交换链
6)

VkSwapchainCreateInfoKHR 是一个用于描述交换链创建信息的结构体。它包含了创建交换链所需的所有参数,如表面、图像数量、格式、分辨率等。

以下是 VkSwapchainCreateInfoKHR 结构体的定义:

 1 typedef struct VkSwapchainCreateInfoKHR {
 2 VkStructureType                  sType;              // 必须是 VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR
 3 const void*                      pNext;              // 指向扩展结构的指针,通常为 nullptr
 4 VkSwapchainCreateFlagsKHR        flags;              // 交换链创建标志,当前版本 Vulkan 中未使用,设置为 0
 5 VkSurfaceKHR                     surface;            // 渲染表面(窗口或显示设备)
 6 uint32_t                         minImageCount;      // 交换链中图像的最小数量
 7 VkFormat                         imageFormat;        // 交换链图像的格式(颜色格式)
 8 VkColorSpaceKHR                  imageColorSpace;    // 交换链图像的颜色空间
 9 VkExtent2D                       imageExtent;        // 交换链图像的宽度和高度
10 uint32_t                         imageArrayLayers;   // 交换链图像的数组层数(通常为 1)
11 VkImageUsageFlags                imageUsage;         // 交换链图像的用途标志(如颜色附件)
12 VkSharingMode                    imageSharingMode;   // 图像共享模式(独占模式或并发模式)
13 uint32_t                         queueFamilyIndexCount; // 队列家族索引数量(imageSharingMode 为 VK_SHARING_MODE_CONCURRENT 时使用)
14 const uint32_t*                  pQueueFamilyIndices;  // 指向队列家族索引数组的指针(imageSharingMode 为 VK_SHARING_MODE_CONCURRENT 时使用)
15 VkSurfaceTransformFlagBitsKHR    preTransform;       // 表面变换(如旋转或翻转)
16 VkCompositeAlphaFlagBitsKHR      compositeAlpha;     // 复合 Alpha 混合模式
17 VkPresentModeKHR                 presentMode;        // 交换链的呈现模式(如 FIFO、Mailbox 等)
18 VkBool32                         clipped;            // 指定剪裁窗口外的像素(VK_TRUE 为剪裁)
19 VkSwapchainKHR                   oldSwapchain;       // 旧的交换链(用于重新创建交换链时),否则为 VK_NULL_HANDLE
20} VkSwapchainCreateInfoKHR;

VkSwapchainCreateInfoKHR结构体的属性看起来很多,实际上绝大部分情况下都是固定写死的,所以只需要简单了解下即可。

其中 VkColorSpaceKHR 、VkImageUsageFlags、VkSurfaceTransformFlagBitsKHR、VkPresentModeKHR 这个几个稍微关注下。

VkColorSpaceKHR

VkColorSpaceKHR 定义了交换链图像的颜色空间。

颜色空间表示图像数据如何转换为颜色信息,以在显示设备上正确显示,处理 HDR 图像的时候需要重点关注。

 1 typedef enum VkColorSpaceKHR {
 2    VK_COLOR_SPACE_SRGB_NONLINEAR_KHR = 0,                // sRGB 颜色空间,带非线性伽马校正。常用于标准显示器。
 3    VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT = 1000104001, // Display-P3 色域,带非线性伽马校正。用于支持 P3 色域显示器。
 4    VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT = 1000104002, // 扩展 sRGB 颜色空间,线性校正。
 5    VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT = 1000104003,    // Display-P3 色域,线性校正。
 6    VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT = 1000104004,     // DCI-P3 色域,带非线性伽马校正。
 7    VK_COLOR_SPACE_BT709_LINEAR_EXT = 1000104005,         // BT.709 颜色空间,线性校正。
 8    VK_COLOR_SPACE_BT709_NONLINEAR_EXT = 1000104006,      // BT.709 颜色空间,非线性校正。
 9    VK_COLOR_SPACE_BT2020_LINEAR_EXT = 1000104007,        // BT.2020 颜色空间,线性校正。
10    VK_COLOR_SPACE_HDR10_ST2084_EXT = 1000104008,         // HDR10,使用 SMPTE ST 2084 EOTF。
11    VK_COLOR_SPACE_DOLBYVISION_EXT = 1000104009,          // Dolby Vision。
12    VK_COLOR_SPACE_HDR10_HLG_EXT = 1000104010,            // HDR10,使用 Hybrid Log-Gamma。
13    VK_COLOR_SPACE_ADOBERGB_LINEAR_EXT = 1000104011,      // Adobe RGB 颜色空间,线性校正。
14    VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT = 1000104012,   // Adobe RGB 颜色空间,非线性校正。
15    VK_COLOR_SPACE_PASS_THROUGH_EXT = 1000104013,         // 颜色空间通过,不做任何校正。
16    VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT = 1000104014, // 扩展 sRGB 颜色空间,非线性校正。
17    VK_COLOR_SPACE_DISPLAY_NATIVE_AMD = 1000213000,       // 原生显示颜色空间,AMD 扩展。
18    // 更多颜色空间可以通过扩展添加
19 } VkColorSpaceKHR;

VkImageUsageFlags

VkImageUsageFlags 表示图像的用途标志,例如 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT 表示图像将用作颜色附件。

可以组合多个用途标志,以表明图像的多种使用方式。

 1 typedef enum VkImageUsageFlagBits {
 2    VK_IMAGE_USAGE_TRANSFER_SRC_BIT = 0x00000001,                     // 图像可以作为传输操作的源。
 3    VK_IMAGE_USAGE_TRANSFER_DST_BIT = 0x00000002,                     // 图像可以作为传输操作的目标。
 4    VK_IMAGE_USAGE_SAMPLED_BIT = 0x00000004,                          // 图像可以被采样器访问。
 5    VK_IMAGE_USAGE_STORAGE_BIT = 0x00000008,                          // 图像可以作为存储图像使用。
 6    VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT = 0x00000010,                 // 图像可以作为颜色附件使用。
 7    VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT = 0x00000020,         // 图像可以作为深度/模板附件使用。
 8    VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT = 0x00000040,             // 图像内容是暂时的,并不会在帧之间保留。
 9    VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT = 0x00000080,                 // 图像可以作为输入附件使用。
10    VK_IMAGE_USAGE_SHADING_RATE_IMAGE_BIT_NV = 0x00000100,            // 图像可以作为着色率图像使用(NVIDIA 扩展)。
11    VK_IMAGE_USAGE_FRAGMENT_DENSITY_MAP_BIT_EXT = 0x00000200,         // 图像可以作为片段密度图使用(EXT 扩展)。
12    VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR =         // 图像可以作为片段着色率附件使用(KHR 扩展)。
13        VK_IMAGE_USAGE_SHADING_RATE_IMAGE_BIT_NV,
14    VK_IMAGE_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF                    // 枚举类型的最大值。
15 } VkImageUsageFlagBits;

VkSurfaceTransformFlagBitsKHR

VkSurfaceTransformFlagBitsKHR 枚举类型定义了交换链图像在显示之前可以应用的变换操作。

 1 typedef enum VkSurfaceTransformFlagBitsKHR {
 2    VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR = 0x00000001,                    // 不进行任何变换。
 3    VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR = 0x00000002,                   // 顺时针旋转 90 度。
 4    VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR = 0x00000004,                  // 顺时针旋转 180 度。
 5    VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR = 0x00000008,                  // 顺时针旋转 270 度。
 6    VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR = 0x00000010,           // 水平翻转。
 7    VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR = 0x00000020, // 水平翻转后顺时针旋转 90 度。
 8    VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR = 0x00000040,// 水平翻转后顺时针旋转 180 度。
 9    VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR = 0x00000080,// 水平翻转后顺时针旋转 270 度。
10    VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR = 0x00000100                      // 继承当前变换。
11 } VkSurfaceTransformFlagBitsKHR;

VkPresentModeKHR

VkPresentModeKHR 枚举类型定义了交换链的呈现模式。呈现模式决定了图像交换和显示的策略。

1 typedef enum VkPresentModeKHR {
2    VK_PRESENT_MODE_IMMEDIATE_KHR = 0,           // 图像立即提交到显示设备,可能导致撕裂。
3    VK_PRESENT_MODE_MAILBOX_KHR = 1,             // 使用邮箱模式,提交图像时如果显示队列满了,会覆盖旧的图像。
4    VK_PRESENT_MODE_FIFO_KHR = 2,                // 先进先出模式,提交的图像进入队列,显示设备按顺序显示。
5    VK_PRESENT_MODE_FIFO_RELAXED_KHR = 3,        // 类似于 FIFO 模式,但如果显示设备空闲,立即显示图像,减少延迟。
6    VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR = 1000111000, // 共享刷新模式,在显示设备需要时刷新。
7    VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR = 1000111001 // 持续刷新模式,持续刷新图像。
8 } VkPresentModeKHR;

创建交换链、获取交换链图像示例

 1 VkSurfaceKHR surface;//上一节获取的表面
 2
 3 // 获取表面的特性
 4 VkSurfaceCapabilitiesKHR surfaceCapabilities;
 5 vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &surfaceCapabilities);
 6
 7 uint32_t formatCount = 0;
 8 vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, nullptr);
 9
10 // 获取表面所支持的格式
11 VkSurfaceFormatKHR* formats = new VkSurfaceFormatKHR[formatCount];
12 vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, formats);
13
14 // 查找 VK_FORMAT_R8G8B8A8_UNORM 格式
15 uint32_t targetFormatIdx;
16 for (targetFormatIdx = 0; targetFormatIdx < formatCount; targetFormatIdx++) {
17    if (formats[targetFormatIdx].format == VK_FORMAT_R8G8B8A8_UNORM) break;
18 }
19 assert(targetFormatIdx < formatCount); // 确保找到目标格式
20
21 VkSwapchainCreateInfoKHR swapchainCreateInfo = {};
22 swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; // 设置结构体类型
23 swapchainCreateInfo.surface = surface; // 设置表面句柄
24 swapchainCreateInfo.minImageCount = surfaceCapabilities.minImageCount; // 最小图像数量(修正:minImageCoun 改为 minImageCount)
25 swapchainCreateInfo.imageFormat = formats[targetFormatIdx].format; // 图像格式
26 swapchainCreateInfo.imageColorSpace = formats[targetFormatIdx].colorSpace; // 颜色空间
27 swapchainCreateInfo.imageExtent = surfaceCapabilities.currentExtent; // 图像范围
28 swapchainCreateInfo.imageArrayLayers = 1; // 图像数组层数
29 swapchainCreateInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; // 图像用途
30 swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; // 图像共享模式
31 swapchainCreateInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; // 表面变换
32 swapchainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; // 复合 alpha 模式
33 swapchainCreateInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR; // 显示模式
34 swapchainCreateInfo.clipped = VK_FALSE; // 是否剪辑
35 swapchainCreateInfo.oldSwapchain = VK_NULL_HANDLE; // 旧交换链句柄
36
37 VkSwapchainKHR swapchain;
38 VkResult result = vkCreateSwapchainKHR(device, &swapchainCreateInfo, nullptr, &swapchain); // 创建交换链
39 if (result != VK_SUCCESS) {
40    // 错误处理
41 }
42
43 // 获取交换链中的图像
44 uint32_t imageCount;
45 vkGetSwapchainImagesKHR(device, swapchain, &imageCount, nullptr);
46 std::vector<VkImage> swapChainImages(imageCount);
47 vkGetSwapchainImagesKHR(device, swapchain, &imageCount, swapChainImages.data());
48
49 // 为每个交换链图像创建图像视图,这些图像视图将会作为帧缓冲区的 attachments
50 std::vector<VkImageView> swapChainImageViews(swapChainImages.size());
51 for (size_t i = 0; i < swapChainImages.size(); i++) {
52    VkImageViewCreateInfo createInfo = {};
53    createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; // 设置结构体类型
54    createInfo.image = swapChainImages[i]; // 图像句柄
55    createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; // 视图类型
56    createInfo.format = formats[targetFormatIdx].format; // 图像格式
57    createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; // 组件重映射
58    createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
59    createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
60    createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
61    createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; // 子资源范围
62    createInfo.subresourceRange.baseMipLevel = 0;
63    createInfo.subresourceRange.levelCount = 1;
64    createInfo.subresourceRange.baseArrayLayer = 0;
65    createInfo.subresourceRange.layerCount = 1;
66
67    if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) {
68        throw std::runtime_error("failed to create image views!"); // 错误处理
69    }
70}
71
72 // 销毁交换链、释放资源
73 for (size_t i = 0; i < swapChainImages.size(); i++) {
74    vkDestroyImageView(device, swapChainImageViews[i], nullptr); // 销毁图像视图
75 }
76 vkDestroySwapchainKHR(device, swapchain, nullptr); // 销毁交换链
77 delete[] formats; // 释放格式数组内存

参考

《Vulkan学习指南》 — [新加坡] 帕敏德·辛格(Parminder Singh)
https://www.intel.com/content/www/us/en/developer/articles/training/api-without-secrets-introduction-to-vulkan-part-2.html

进技术交流群,扫码添加我的微信:Byte-Flow

Vulkan 交换链

本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/51494.html

(0)

相关推荐

发表回复

登录后才能评论