交换链 (Swap Chain) 是连接 Vulkan 和窗口系统的桥梁,主要用于管理用于显示的图像缓冲区。它的核心功能是处理渲染图像的显示时机,确保画面平滑且无撕裂。

让我详细解释:

  1. 基本概念:
// 创建交换链的基本结构
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; // 图像用途
  1. 主要功能:
  • 管理多个图像缓冲区
  • 处理显示时机
  • 支持垂直同步 (VSync)
  • 处理窗口大小改变
  1. 工作流程:
  • 获取下一个可用的图像 (vkAcquireNextImageKHR)
  • 渲染到该图像
  • 提交图像进行显示 (vkQueuePresentKHR)
  1. 呈现模式:
  • FIFO (先进先出,支持垂直同步)
  • MAILBOX (三重缓冲)
  • IMMEDIATE (立即显示,可能撕裂)
  • FIFO_RELAXED (当延迟时立即显示)
  1. 交换链的重建:
  • 窗口大小改变时
  • 设备丢失时
  • 其他需要重新创建的场景

Q&A

游戏设置中的 2 倍缓存 3 倍缓存是不是就是指的交换链相关参数

是的,2 倍缓存(Double Buffering)和 3 倍缓存(Triple Buffering)就是指的交换链中的图像缓冲数量。让我详细解释:

  1. 2 倍缓存(Double Buffering)
  • 使用两个缓冲区:前缓冲 (Front Buffer) 和后缓冲 (Back Buffer)

  • 前缓冲用于显示,后缓冲用于渲染

  • 工作流程:

    后缓冲:正在渲染帧 N+1
    前缓冲:显示帧 N
    完成后交换位置
    
  • 优点:减少撕裂

  • 缺点:可能造成等待,因为需要等前缓冲显示完才能交换

  1. 3 倍缓存(Triple Buffering)
  • 使用三个缓冲区

  • 一个用于显示,一个用于渲染,一个作为备用

  • 工作流程:

    缓冲A:显示帧 N
    缓冲B:渲染帧 N+1
    缓冲C:已完成帧 N+2,等待显示
    
  • 优点:

    • 减少等待时间
    • 提高帧率
    • 更流畅的画面
  • 缺点:

    • 占用更多显存
    • 可能增加输入延迟

在 Vulkan 中,你可以通过设置交换链的 minImageCount 来控制缓冲数量:

VkSwapchainCreateInfoKHR createInfo = {};
createInfo.minImageCount = 3; // 3倍缓存

为什么要引入一个 交换链的概念,不能直接将 vkImage 的内容显示到窗口么

引入交换链概念主要是为了解决显示同步和性能问题。让我解释为什么不能直接显示:

  1. 显示器刷新机制
  • 显示器按固定频率 (如 60Hz) 更新画面

  • 直接写入显示内容会导致画面撕裂 (Tearing):

    显示器正在显示帧A的上半部分
    GPU 突然写入了新的帧B
    导致上半部分显示A,下半部分显示B
    
  1. 性能考虑
  • GPU 渲染速度和显示器刷新率通常不同步

  • 直接显示会导致 GPU 等待显示器准备好,或显示器等待 GPU 完成渲染

  • 交换链通过维护多个缓冲区来解决这个问题:

    GPU 可以渲染到缓冲区A
    显示器正在显示缓冲区B的内容
    互不干扰,提高效率
    
  1. 平台兼容性
  • 不同操作系统有不同的显示机制
  • 交换链提供了统一的抽象层,处理平台特定的显示细节
  • 开发者只需要和交换链交互,不用关心底层实现

所以交换链实际上是一个必要的 ” 中间层 “,用于协调 GPU 渲染和显示器显示之间的关系。

交换链本质是一个 vkImage 池?

交换链本质上就是一个管理多个 VkImage 的池子。

  1. 交换链结构
// 获取交换链图像
std::vector<VkImage> swapChainImages;
uint32_t imageCount;
vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr);
swapChainImages.resize(imageCount);
vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());
  1. 主要特点
  • 其中的每个 VkImage 都是由系统管理的
  • 这些图像可以直接显示到窗口系统
  • 系统自动处理这些图像和显示器之间的同步
  1. 与普通 VkImage 的区别
  • 交换链中的图像是由系统创建和管理的
  • 这些图像有特殊的内存要求,确保能高效地显示到屏幕
  • 不能像普通 VkImage 那样直接创建和销毁
  1. 使用流程
// 获取下一个可用的图像索引
uint32_t imageIndex;
vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, 
                      imageAvailableSemaphore, VK_NULL_HANDLE, &imageIndex);
 
// 使用这个图像进行渲染
// ...
 
// 提交显示
VkPresentInfoKHR presentInfo = {};
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = &swapChain;
presentInfo.pImageIndices = &imageIndex;
vkQueuePresentKHR(presentQueue, &presentInfo);