2.4 平台表面与交换链构建


文档摘要

2.4 平台表面与交换链构建 本节摘要:把原生窗口接入 Vulkan 需要两份证件:实例级的 VkSurfaceKHR(窗口在大厅里的登记位)与设备级的 VkSwapchainKHR(一串可轮换展示的图像)。本节讲清表面的平台差异如何被统一接口吸收、交换链参数为什么必须从三张查询清单里选,并交付完整的构建代码。 「Swapchain」这个行话直译是「交换链」,业内更贴切的理解是「一串轮换的展台」——程序在后台展台上作画,画好的一帧被推上前台展示,展台数量与轮换规则决定了画面能否丝滑衔接。本节是第 2 章第四道手续:先办表面(窗口与渲染的接头),再办交换链(真正可绘制的图像来源),它直接为 2.5 节的呈现循环供货。

2.4 平台表面与交换链构建

本节摘要:把原生窗口接入 Vulkan 需要两份证件:实例级的 VkSurfaceKHR(窗口在大厅里的登记位)与设备级的 VkSwapchainKHR(一串可轮换展示的图像)。本节讲清表面的平台差异如何被统一接口吸收、交换链参数为什么必须从三张查询清单里选,并交付完整的构建代码。

「Swapchain」这个行话直译是「交换链」,业内更贴切的理解是「一串轮换的展台」——程序在后台展台上作画,画好的一帧被推上前台展示,展台数量与轮换规则决定了画面能否丝滑衔接。本节是第 2 章第四道手续:先办表面(窗口与渲染的接头),再办交换链(真正可绘制的图像来源),它直接为 2.5 节的呈现循环供货。

一、表面:平台差异的收口点

Windows 的 HWND、Linux 的 XCB 窗口、Android 的 ANativeWindow,形态各异。Vulkan 的处理方式是在实例级放一个统一抽象 VkSurfaceKHR,再由各平台表面扩展负责从原生句柄到抽象的转换。以 GLFW 为例,平台差异被库抹平:

// GLFW 汇总了当前平台所需的实例扩展(1.2 节已启用) VkSurfaceKHR surface; VK_CHECK(glfwCreateWindowSurface(instance, window, NULL, &surface));

若不用窗口库,就得按平台手写。Windows 上启用 VK_KHR_win32_surface 后填 VkWin32SurfaceCreateInfoKHR(hinstance 与 hwnd 两项);Linux 上按窗口系统在 xcb 与 wayland 扩展里二选一。表面创建后要注意归属:它由实例派生,销毁顺序上必须在 vkDestroyInstance 之前调用 vkDestroySurfaceKHR。

表面还有一个必须确认的关系:呈现支持。某个队列族能不能把图像送上这个表面,要用查询回答,不能假设:

VkBool32 presentable = VK_FALSE; vkGetPhysicalDeviceSurfaceSupportKHR(physDev, graphicsFamily, surface, &presentable); // 多数桌面平台的图形族支持呈现;部分移动平台需要单独的呈现族

二、三张查询清单

表面办好后,它会交给你三张清单,交换链的每一项参数都必须从清单里选。这三种查询对应三类约束:

capabilities(能力边界):minImageCount 与 maxImageCount(展台数量上下限)、currentExtent(当前尺寸,特殊值 0xFFFFFFFF 表示由你决定)、maxImageArrayLayers、supportedTransforms、supportedUsageFlags。它约束「有什么可挑」。

formats(格式清单):VkSurfaceFormatKHR 数组,每项是「格式加色彩空间」的组合。绝大多数平台至少含 B8G8R8A8 类格式;是否含 SRGB 线性化格式因设备而异。

presentModes(呈现模式清单):FIFO(垂直同步队列,规范保证必存在)、MAILBOX(信箱式,等着被替换的最新帧)、IMMEDIATE(立即上屏,可能撕裂)、FIFO_RELAXED(垂直同步失守时降级为立即)。

VkSurfaceCapabilitiesKHR caps; vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physDev, surface, &caps); uint32_t fmtCount = 0; vkGetPhysicalDeviceSurfaceFormatsKHR(physDev, surface, &fmtCount, NULL); std::vector<VkSurfaceFormatKHR> formats(fmtCount); vkGetPhysicalDeviceSurfaceFormatsKHR(physDev, surface, &fmtCount, formats.data()); uint32_t pmCount = 0; vkGetPhysicalDeviceSurfacePresentModesKHR(physDev, surface, &pmCount, NULL); std::vector<VkPresentModeKHR> modes(pmCount); vkGetPhysicalDeviceSurfacePresentModesKHR(physDev, surface, &pmCount, modes.data());

三项选择的常用策略:格式优先挑 SRGB 非线性(色彩正确性),没有再退 B8G8R8A8_UNORM;呈现模式在支持 MAILBOX 的低延迟场景选它,一般应用选 FIFO;展台数量取 caps.minImageCount 与你想要的值(建议至少 3 做三缓冲)之间较大者,且不超过 maxImageCount(该值为 0 表示无上限)。

图 2-3:从查询到交换链的参数推导

图 2-3:从查询到交换链的参数推导

三、组装交换链

参数齐了,创建是一次性调用,但有两个字段值得解释。imageSharingMode:图像若只被一个族使用填 EXCLUSIVE(最优性能),跨族共享要填 CONCURRENT 并把族编号都列上——2.3 节开的多柜台如果涉及跨族呈现,这里就是接线处。clipped:设为 VK_TRUE 允许驱动丢弃被其他窗口遮挡部分的渲染,是白送的优化。

VkSwapchainCreateInfoKHR sci = {VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR}; sci.surface = surface; sci.minImageCount = imageCount; // 从 caps 推导 sci.imageFormat = chosenFormat.format; // 从 formats 挑选 sci.imageColorSpace = chosenFormat.colorSpace; sci.imageExtent = extent; // 与 currentExtent 对齐 sci.imageArrayLayers = 1; sci.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; sci.preTransform = caps.currentTransform; sci.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; sci.presentMode = chosenPresentMode; // 从 modes 挑选 sci.clipped = VK_TRUE; sci.oldSwapchain = VK_NULL_HANDLE; // 重建时传旧链句柄 VkSwapchainKHR swapchain; VK_CHECK(vkCreateSwapchainKHR(device, &sci, NULL, &swapchain)); uint32_t imgCount = 0; vkGetSwapchainImagesKHR(device, swapchain, &imgCount, NULL); swapchainImages.resize(imgCount); vkGetSwapchainImagesKHR(device, swapchain, &imgCount, swapchainImages.data());

最后领取的那组 VkImage 属于交换链——不需要也无法由你销毁,它们的生命周期跟着交换链走。要让管线使用这些图像,还要为每张创建 VkImageView(「视图」概念在第 3 章展开)。

四、案例:尺寸突变引发的参数失效

背景:一个桌面应用支持用户拖拽缩放窗口。缩放后画面偶尔拉伸变形,拖到另一台显示器(不同分辨率、不同缩放比)后直接渲染错乱,且概率性出现验证层报错「imageExtent 不匹配」。

操作:在窗口尺寸回调里重跑三张查询清单,发现 currentExtent 已经变成新值,而交换链仍是旧尺寸——诊断成立:尺寸属于「能力边界」类参数,过期即失效。改造为标准重建流程:等设备空闲(vkDeviceWaitIdle),用 oldSwapchain 字段挂上旧链创建新链,销毁旧链与旧视图,重建帧缓冲(第 4 章的相纸)。

结果:缩放与跨屏拖动后画面始终正确。oldSwapchain 的使用还让驱动能复用未变的分配,重建开销远低于先销毁再创建。

解读:交换链不是创建完就一劳永逸的对象——它的参数与「当前窗口状态」绑定,窗口状态会变。把「查询、比对、按需重建」做成对尺寸变化的反射,是所有窗口应用的标准动作,2.5 节会把触发条件补全。

变式:移动端还有 preTransform 变化(横竖屏旋转)这条额外触发路径,处理方式一致:把 supportedTransforms 与 currentTransform 的比对加进重建条件。老的嵌入式设备偶尔要求 preTransform 用特定旋转而非 currentTransform,需要矩阵补偿——查询清单支持什么,决定你能怎么写。

本节要点回顾

  • 表面是收口:平台差异被 VkSurfaceKHR 统一吸收,实例级创建、窗口句柄转换、销毁顺序有讲究。
  • 呈现支持要查:队列族与表面的配对关系用 vkGetPhysicalDeviceSurfaceSupportKHR 确认,不假设全能族必支持。
  • 三张清单定参数:capabilities、formats、presentModes 分别约束数量尺寸、颜色、节奏,每项参数都有查询出处。
  • 共享模式接多柜台:单族 EXCLUSIVE 最优,跨族 CONCURRENT 是 2.3 节多队列架构的接线点。
  • 交换链会过期:尺寸、变换等参数绑定窗口状态,重建流程用 oldSwapchain 平滑过渡。

作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U