前言
在 VulkanSceneGraph(VSG)中,vsg::Window 类对窗口进行了高层次的封装,为开发者提供了便捷的窗口管理接口。在上一篇文章中,我们探讨了 VkInstance、VkSurfaceKHR、VkPhysicalDevice 和 VkDevice 的创建过程,以及它们在 VSG 中的封装方式及其相互关系。本章将继续深入探讨与窗口创建密切相关的另一个核心对象——交换链 VkSwapchainKHR,并分析其与上述四个 Vulkan 对象之间的关系,同时探讨 VSG 如何对 VkSwapchainKHR 进行封装。
目录
- 1 VkSwapchainKHR的创建
- 2 vsg中对VkSwapchainKHR的封装
1 VkSwapchainKHR的创建
在Vulkan中,Swapchain是用于管理图像的核心机制,它允许应用程序将渲染结果展示到屏幕上。Swapchain本质上是一个图像队列,包含多个图像缓冲区(也可称为"交换图像"),应用程序将内容渲染到这些图像中,然后将其提交给显示设备进行呈现。VkSwapchainKHR与VkSurfaceKHR、VkPhysicalDevice联系紧密,VkSwapchainKHR的创建依赖于VkSurfaceKHR,在创建VkSwapchainKHR前必须确保VkSurfaceKHR已经创建,并且物理设备VkPhysicalDevice支持此VkSurfaceKHR。
VkSwapchainKHR包含查询表面支持、选择表面格式和呈现模式、Swapchain的创建与销毁三个步骤。
1.1 查询表面支持
VkSurfaceCapabilitiesKHR caps;
VkResult result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
app.getPhysicalDevice(), app.getSurface(), &caps);
if (result != VK_SUCCESS)
{
printf("Failed to get surface capabilities.\n");
return 1;
}
通过vkGetPhysicalDeviceSurfaceCapabilitiesKHR查询物理设备对表面的支持能力,从中可获取最小最大图象数、图像最大分辨率,代码如下:
caps.maxImageExtent.width;//最大分辨率//宽度
caps.maxImageExtent.height;//最大分辨率//高度
caps.minImageCount;//最小影像数
caps.maxImageCount;//最打影像数
1.2 选择表面格式和呈现模式
uint32_t formatCount = 0, presentModeCount = 0;
result = vkGetPhysicalDeviceSurfaceFormatsKHR(
app.getPhysicalDevice(), app.getSurface(), &formatCount, nullptr);
if (result != VK_SUCCESS || formatCount == 0)
{
return 1;
}
result = vkGetPhysicalDeviceSurfacePresentModesKHR(
app.getPhysicalDevice(), app.getSurface(), &presentModeCount, nullptr);
if (result != VK_SUCCESS || presentModeCount == 0)
{
return 1;
}
uint32_t formatIndex = 0, presentModeIndex = 0;
std::vector<VkSurfaceFormatKHR> formats(formatCount);
std::vector<VkPresentModeKHR> presentModes(presentModeCount);
vkGetPhysicalDeviceSurfaceFormatsKHR(
app.getPhysicalDevice(), app.getSurface(), &formatCount, formats.data());
for (uint32_t i = 0; i < formatCount; ++i)
{
if (formats[i].format == VK_FORMAT_B8G8R8A8_SRGB &&
formats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) formatIndex = i;
}
vkGetPhysicalDeviceSurfacePresentModesKHR(
app.getPhysicalDevice(), app.getSurface(), &presentModeCount, presentModes.data());
for (uint32_t i = 0; i < presentModeCount; ++i)
{
if (presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR) presentModeIndex = i;
}
通过vkGetPhysicalDeviceSurfaceFormatsKHR获取表面格式,以上代码中vkGetPhysicalDeviceSurfaceFormatsKHR共调用两次,第一次第四个参数传入nullptr,可获取表面格式的个数,第二次调用可获取所有表面模式。同时通过vkGetPhysicalDeviceSurfacePresentModesKHR获取呈现模式,函数共调用两次,第一次第四个参数传入nullptr,可获取呈现模式的个数,第二次调用可获取所有呈现模式。
1.3 Swapchain的创建与销毁
VkSwapchainCreateInfoKHR createInfo
{
VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
nullptr, 0,
app.getSurface(),
std::max(caps.minImageCount + caps.maxImageCount),
VK_FORMAT_B8G8R8A8_SRGB,
VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
caps.maxImageExtent,
1, // imageArrayLayers
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
VK_SHARING_MODE_EXCLUSIVE,
0, nullptr,
caps.currentTransform,
VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
VK_PRESENT_MODE_MAILBOX_KHR,
VK_TRUE,
nullptr
};
VkSwapchainKHR swapChain = nullptr;
result = vkCreateSwapchainKHR(app.getDevice(), &createInfo, nullptr, &swapChain);
if (result != VK_SUCCESS)
{
printf("Failed to create swap chain.\n");
return 1;
}
uint32_t imageCount = 0;
vkGetSwapchainImagesKHR(app.getDevice(), swapChain, &imageCount, nullptr);
std::vector<VkImage> swapChainImages(imageCount);
vkGetSwapchainImagesKHR(app.getDevice(), swapChain, &imageCount, swapChainImages.data());
if (swapChain != nullptr)
{
vkDestroySwapchainKHR(app.getDevice(), swapChain, nullptr);
}
在 Vulkan 中,交换链(VkSwapchainKHR)通过 vkCreateSwapchainKHR 函数创建,可配置表面格式为 VK_FORMAT_B8G8R8A8_SRGB、颜色空间为 VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,并选择呈现模式为 VK_PRESENT_MODE_MAILBOX_KHR。创建后,使用 vkGetSwapchainImagesKHR 查询交换链的图像缓冲区,并通过 vkDestroySwapchainKHR 销毁交换链以释放资源。
2 vsg中对VkSwapchainKHR的封装
在 VulkanSceneGraph (VSG) 中,VkSwapchainKHR 的功能被封装在 vsg::Swapchain 类中,该类提供了对 Vulkan 交换链的高层次抽象,简化了交换链的创建、管理和使用。本章主要深入探讨vsg中交换链的创建过程,并与上篇文章提及的vsg::WindowTraits关联,暂时不深入其它如图像缓冲区管理、动态重建支持等功能,这些留待后续剖析。
auto windowTraits = vsg::WindowTraits::create();
windowTraits->width = 800;
windowTraits->height = 600;
windowTraits->swapchainPreferences.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
auto window = vsg::Window::create(windowTraits);
if (!window) {
std::cerr << "Failed to create window and swapchain." << std::endl;
return -1;
}
通过vsg::WindowTraits创建窗口的属性,窗口的宽和长分别设置为800和600。传入创建vsg::Window对象,在windows系统下创建vsg::Win32_Window,在vsg::Win32_Window对象
if (_swapchain)
{
// make sure all operations on the device have stopped before we go on deleting associated resources
vkDeviceWaitIdle(*_device);
// clean up previous swap chain before we begin creating a new one.
_frames.clear();
_indices.clear();
_depthImageView.reset();
_depthImage.reset();
_multisampleImage.reset();
_multisampleImageView.reset();
}
// is width and height even required here as the surface appears to control it?
_swapchain = Swapchain::create(_physicalDevice, _device, _surface, _extent2D.width, _extent2D.height, _traits->swapchainPreferences, _swapchain);
// pass back the extents used by the swap chain.
_extent2D = _swapchain->getExtent();
vsg::Swapchain的创建传入宽高参数_extent2D.width和_extent2D.height,而变量_extent2D的初始化在vsg::Win32_Window对象的构造函数中由传入的vsg::WindowTraits对象经过一系列的转换得到。
文末:本章在上一篇文章的基础上,继续深入探讨与窗口创建密切相关的另一个核心对象——交换链 VkSwapchainKHR,并分析其与 Vulkan 核心对象之间的关系。具体而言,VkSwapchainKHR 的创建依赖于 VkSurfaceKHR,并且在创建交换链之前,必须确保 VkSurfaceKHR 已经成功创建,同时物理设备 VkPhysicalDevice 支持该表面。本章内容分为两部分:交换链的创建与销毁 和 VSG 对 VkSwapchainKHR 的封装。至此,关于 vsg::Window 创建的介绍告一段落,下一章将接着探讨 vsg::Camera 的创建与赋值过程。