Vulkan Swapchain 通俗入门:渲染结果是怎么显示到屏幕上的?
2026/6/8 19:36:55 网站建设 项目流程

一、什么是 Swapchain?

在 Vulkan 中,Swapchain可以理解为:

一组用来显示画面的图像。

GPU 渲染出来的画面,并不是直接显示到屏幕上的,而是先画到一张图像里,然后再把这张图像交给窗口系统显示。

这个“管理多张显示图像”的东西,就是 Swapchain。

简单来说:

GPU 渲染画面 ↓ 写入 Swapchain 图像 ↓ 提交给窗口系统 ↓ 显示到屏幕

二、为什么需要 Swapchain?

如果 GPU 正在画一张图,而屏幕也正在显示同一张图,就可能出现画面撕裂、闪烁等问题。

所以 Vulkan 不会只使用一张图像,而是使用多张图像轮流工作。

例如三缓冲可以理解为:

图像 A:正在屏幕上显示 图像 B:GPU 正在渲染 图像 C:等待下一次使用

这样可以让显示和渲染互不干扰。


三、Surface 和 Swapchain 的关系

在 Vulkan 中,窗口本身不能直接拿来渲染。

我们需要先创建一个Surface,它表示一个窗口表面。

然后基于这个 Surface 创建 Swapchain。

关系如下:

窗口 ↓ VkSurfaceKHR ↓ VkSwapchainKHR ↓ Swapchain Images ↓ 屏幕显示

可以这样理解:

  • VkSurfaceKHR:我要显示到哪个窗口;

  • VkSwapchainKHR:我要用哪些图像轮流显示;

  • VkImage:真正存放画面的图像。


四、Swapchain 每一帧做了什么?

Vulkan 每一帧的显示流程大致是:

1. 从 Swapchain 取一张可用图像 2. GPU 把画面渲染到这张图像上 3. 渲染完成后,把图像交给显示系统 4. 屏幕显示这张图像

对应的核心 Vulkan 函数是:

vkAcquireNextImageKHR(); // 从 Swapchain 获取图像 vkQueueSubmit(); // 提交 GPU 渲染命令 vkQueuePresentKHR(); // 把渲染好的图像显示到屏幕

可以记成一句话:

acquire 拿图,submit 渲染,present 显示。


五、创建 Swapchain 之前要检查什么?

创建 Swapchain 之前,一般要检查三件事。

1. 显卡是否支持 Swapchain 扩展

Swapchain 需要启用这个扩展:

VK_KHR_SWAPCHAIN_EXTENSION_NAME

创建设备时要把它加入扩展列表:

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

2. 队列是否支持显示到窗口

Vulkan 中有不同类型的队列。

我们通常至少需要:

Graphics Queue:负责渲染 Present Queue :负责显示

有些显卡上这两个队列可能是同一个,有些平台上可能不是。

所以创建 Swapchain 前,需要确认当前设备既能渲染,又能显示到这个窗口。


3. Surface 是否支持可用格式

Swapchain 需要选择图像格式,例如:

VK_FORMAT_B8G8R8A8_SRGB

也要选择色彩空间,例如:

VK_COLOR_SPACE_SRGB_NONLINEAR_KHR

通俗理解:

这一步是在决定屏幕图像的颜色格式。


六、Swapchain 的几个重要参数

创建 Swapchain 时,最重要的是下面几个参数。

1. 图像格式 Format

决定每个像素怎么存颜色。

常见选择:

VK_FORMAT_B8G8R8A8_SRGB

它表示每个像素包含:

B:蓝色 G:绿色 R:红色 A:透明度

2. 显示模式 Present Mode

Present Mode 决定图像怎么显示到屏幕。

常见的有:

VK_PRESENT_MODE_FIFO_KHR VK_PRESENT_MODE_MAILBOX_KHR VK_PRESENT_MODE_IMMEDIATE_KHR

简单理解:

模式特点
FIFO类似垂直同步,稳定,不撕裂
MAILBOX延迟较低,适合实时渲染
IMMEDIATE立即显示,但可能画面撕裂

一般可以优先选择:

VK_PRESENT_MODE_MAILBOX_KHR

如果不支持,就使用:

VK_PRESENT_MODE_FIFO_KHR

因为 FIFO 是 Vulkan 保证支持的模式。


3. 图像大小 Extent

Extent 表示 Swapchain 图像的宽和高。

一般应该和窗口的 framebuffer 大小一致。

使用 GLFW 时,推荐这样获取:

glfwGetFramebufferSize(window, &width, &height);

不要简单使用窗口逻辑大小,因为高 DPI 屏幕上两者可能不一样。


4. 图像数量 Image Count

Swapchain 里面有多少张图像。

常见做法是:

uint32_t imageCount = minImageCount + 1;

这样比最少数量多一张,可以减少等待。

常见情况:

2 张图像:双缓冲 3 张图像:三缓冲

七、创建 Swapchain 的核心代码

下面是一个简化版创建流程:

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; createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; createInfo.preTransform = 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("failed to create swap chain!"); }

这里最重要的几个字段是:

surface

表示显示到哪个窗口。

imageFormat

表示图像格式。

imageExtent

表示图像大小。

presentMode

表示显示方式。

imageUsage

表示这张图像要用来做什么。

如果我们要把它作为渲染目标,一般使用:

VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT

八、获取 Swapchain 图像

创建 Swapchain 后,需要获取它内部的图像:

vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); swapChainImages.resize(imageCount); vkGetSwapchainImagesKHR( device, swapChain, &imageCount, swapChainImages.data() );

这些图像是 Swapchain 自己创建的。

我们只需要使用它们,不需要自己销毁它们。


九、为什么还要创建 Image View?

在 Vulkan 中,VkImage不能直接拿来当渲染目标。

通常需要为每张 Swapchain Image 创建一个VkImageView

可以理解为:

VkImage :真正的图像数据 VkImageView :访问这张图像的方式

创建 Image View 后,Swapchain 图像才能更方便地被 Render Pass 或 Framebuffer 使用。


十、Swapchain 和 Framebuffer 的关系

如果使用传统 Render Pass,一般每张 Swapchain Image 都会对应一个 Framebuffer。

关系如下:

Swapchain Image 0 → Image View 0 → Framebuffer 0 Swapchain Image 1 → Image View 1 → Framebuffer 1 Swapchain Image 2 → Image View 2 → Framebuffer 2

每一帧获取到哪张 Swapchain Image,就使用对应的 Framebuffer 渲染。


十一、每帧渲染流程

每一帧一般这样写:

uint32_t imageIndex; vkAcquireNextImageKHR( device, swapChain, UINT64_MAX, imageAvailableSemaphore, VK_NULL_HANDLE, &imageIndex ); // 记录命令,把画面渲染到 imageIndex 对应的 framebuffer vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFence); vkQueuePresentKHR(presentQueue, &presentInfo);

核心逻辑是:

获取图像 → 渲染图像 → 显示图像

十二、Swapchain 里的同步对象

Swapchain 渲染通常会用到三个同步对象。

1. imageAvailableSemaphore

表示:

Swapchain 图像已经可以用了

也就是说,GPU 可以开始往这张图像上渲染了。


2. renderFinishedSemaphore

表示:

GPU 已经渲染完成了

只有渲染完成后,图像才能交给屏幕显示。


3. inFlightFence

Fence 是给 CPU 用的。

它的作用是:

防止 CPU 太快,重复使用 GPU 还没处理完的资源

例如 command buffer、uniform buffer 等。


十三、窗口大小变化怎么办?

窗口大小变化时,Swapchain 通常需要重建。

因为 Swapchain 图像大小要和窗口大小匹配。

常见触发情况:

窗口 resize 窗口最小化后恢复 屏幕旋转 vkAcquireNextImageKHR 返回 VK_ERROR_OUT_OF_DATE_KHR vkQueuePresentKHR 返回 VK_ERROR_OUT_OF_DATE_KHR

重建流程一般是:

等待设备空闲 ↓ 销毁旧 Swapchain 相关资源 ↓ 重新创建 Swapchain ↓ 重新创建 Image View ↓ 重新创建 Framebuffer ↓ 继续渲染

简化代码:

void recreateSwapChain() { vkDeviceWaitIdle(device); cleanupSwapChain(); createSwapChain(); createImageViews(); createFramebuffers(); }

十四、常见错误

1. 忘记启用 Swapchain 扩展

必须启用:

VK_KHR_SWAPCHAIN_EXTENSION_NAME

否则无法创建 Swapchain。


2. 忘记处理窗口大小变化

窗口 resize 后,旧 Swapchain 可能不能继续使用,需要重建。


3. 把 currentFrame 和 imageIndex 搞混

这是初学者常见错误。

currentFrame

表示当前是第几帧资源。

imageIndex

表示当前拿到的是第几张 Swapchain 图像。

二者不是同一个东西。

正确做法是:

recordCommandBuffer(commandBuffers[currentFrame], imageIndex);

命令缓冲区可以按currentFrame使用,但 framebuffer 要根据imageIndex选择。


4. 没有正确同步

如果没有正确使用 semaphore 和 fence,可能出现:

图像还没准备好就开始渲染 图像还没渲染完就拿去显示 CPU 重复使用 GPU 正在用的资源

所以 Swapchain 渲染一定要处理同步。


十五、总结

Swapchain 是 Vulkan 中负责“把画面显示到屏幕上”的核心机制。

可以把它理解成:

Vulkan 准备了一组图像,GPU 轮流往这些图像上画画,画完之后交给屏幕显示。

它的基本流程是:

Acquire Image ↓ Render ↓ Present

也就是:

拿图像 → 渲染 → 显示

Swapchain 需要关注几个重点:

  1. 它依赖VkSurfaceKHR

  2. 它内部包含多张VkImage

  3. 每张图像通常需要创建VkImageView

  4. 使用传统 Render Pass 时,每张图像一般对应一个 Framebuffer;

  5. 每帧需要先 acquire,再 submit,最后 present;

  6. 渲染和显示之间必须正确同步;

  7. 窗口大小变化时需要重建 Swapchain。

如果说 Pipeline 负责“怎么画”,Shader 负责“画什么效果”,那么 Swapchain 负责的就是:

把 GPU 画好的最终结果送到屏幕上。

掌握 Swapchain 后,Vulkan 的窗口显示流程就会清晰很多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询