Vulkan教程翻译之六 创建 Swapchain
原文链接:https://vulkan.lunarg.com/doc/sdk/1.2.131.2/windows/tutorial/html/05-init_swapchain.html
创建 Swapchain
这一章节的代码文件是 05-init_swapchain.cpp
本章节描述了如何创建 swapchain,它是最终显示给用户的图像缓冲区列表。这是设置渲染需要的所有缓冲区的第一步。
这是一张 Vulkan swapchain 的视图,它关联着系统的其他各个部分。其中一些部分我们很熟悉,剩下的你将会在这一节里进行学习。
Vulkan 和 窗口系统
像其他的图形API一样,Vulkan 保持窗口系统和核心图形API分离。在Vulkan里,窗口系统通过 WSI(Window System Integration) extension 部分暴露出来。你可以在包含WSI内容的 Vulkan 规范文档里找到这些 extension 的文档。在 Vulkan SDK download website 和 Khronos Vulkan Registry 中可以找到包含这些内容的 Vulkan 规范。
WSI extension 支持各种不同平台。可以通过以下定义激活特定平台的 extension :
VK_USE_PLATFORM_ANDROID_KHR
- AndroidVK_USE_PLATFORM_WAYLAND_KHR
- WaylandVK_USE_PLATFORM_WIN32_KHR
- Microsoft WindowsVK_USE_PLATFORM_XCB_KHR
- X Window System, using the XCB libraryVK_USE_PLATFORM_XLIB_KHR
- X Window System, using the Xlib library
名称里的 "KHR" 部分表明该符号是在 Khronos extension 里定义的。
Surface 抽象
Vulkan 使用 VkSurfaceKHR 对象来抽象出原生平台的 surface 或 window。该符号定义为 VK_KHR_surface 扩展的一部分。WSI extension 的各种函数用来创建,操作和销毁这些 surface 对象。
回顾 Instance 和 Device Extensions
因为你在本教程前面的章节中跳过了使用 extension,现在是回顾它们的时候了,好让你可以激活接入窗口系统所要用的WSI extension。
Instance Extensions
为了使用 WSI extension,你需要激活通用的 surface 扩展。在该示例中 init_instance_extension_name() 函数里,找到把 VK_KHR_SURFACE_EXTENSION_NAME
插入要加载的 instance extension 列表的代码。
info.instance_extension_names.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
也要注意到,相同的函数在特定平台的 extension 里,依赖于代码要编译的平台。例如,如果为 Windows 编译,该函数就要把 VK_KHR_WIN32_SURFACE_EXTENSION_NAME
插入到要加载的 instance extension 列表里。
info.instance_extension_names.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
当创建 instance 的时候,在 init_instance() 函数里加载这些 extension。
Device Extensionss
swapchain 是一个 GPU 用来绘制的图像缓存区的列表,并且提供给显示硬件来扫描到显示屏上。因为是GPU硬件在写这些 image,所以设备层面的 extension 需要和 swapchain 协同工作。因此,示例代码把 VK_KHR_SWAPCHAIN_EXTENSION_NAME
添加到 init_device_extension_names() 加载的 device extension 列表里。
info.device_extension_names.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
这个 extension 稍后在本节中用来创建 swapchain。
回顾一下:
- 本示例调用 init_instance_extension_names() 函数来加载常用的和特定平台的 surface extension 作为 instance extension。
- 本示例调用 init_device_extension_names() 函数来加载 swapchain extension 作为 device extension。
通过查看在 Vulkan SDK download site 里的文档,你可以学习更多关于 instance extension 和 device extension。
Queue Family 和 Present
"present" 操作包括把其中一个 swapchain image 传给物理显示器以使它可以被看见。当应用想要显示一张 image 到显示器上,调用 vkQueuePresentKHR() 函数把一个 present 请求放到其中一个GPU queue里。因此,该函数引用的 queue 必须能支持 present 请求,或者是 graphics 和 present 请求。示例为此做了如下的检查:
// Iterate over each queue to learn whether it supports presenting:
VkBool32 *pSupportsPresent =(VkBool32 *)malloc(info.queue_family_count * sizeof(VkBool32));
for (uint32_t i = 0; i < info.queue_family_count; i++) {vkGetPhysicalDeviceSurfaceSupportKHR(info.gpus[0], i, info.surface,&pSupportsPresent[i]);
}// Search for a graphics and a present queue in the array of queue
// families, try to find one that supports both
info.graphics_queue_family_index = UINT32_MAX;
info.present_queue_family_index = UINT32_MAX;
for (uint32_t i = 0; i < info.queue_family_count; ++i) {if ((info.queue_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) {if (info.graphics_queue_family_index == UINT32_MAX)info.graphics_queue_family_index = i;if (pSupportsPresent[i] == VK_TRUE) {info.graphics_queue_family_index = i;info.present_queue_family_index = i;break;}}
}if (info.present_queue_family_index == UINT32_MAX) {// If didn't find a queue that supports both graphics and present, then// find a separate present queue.for (size_t i = 0; i < info.queue_family_count; ++i)if (pSupportsPresent[i] == VK_TRUE) {info.present_queue_family_index = i;break;}
}
free(pSupportsPresent);
这段代码里复用了之前获取的 info.queue_family_count,因为 vkGetPhysicalDeviceSurfaceSupportKHR() 为每一个 queue family 返回一个标记。然后搜索同时支持 present 和 graphics 的 queue family。如果没有 queue family 能同时支持两个,程序就会先记录一个支持 graphics 的 queue family,然后再搜索一个支持 present 的。因为这段代码里同时设置了 graphics_queue_family_index
和 present_queue_family_index,后续示例必须使用 graphics_queue_family_index
里的队列放 graphics 命令,用 present_queue_family_index
里的队列放 present 命令。
是的,前面在 init_device 里已经执行了搜索仅支持 graphics 的 queue family,这里有点多余,这么做仅仅是出于说明的目的。真正的应用也许用不同的顺序做这些步骤来避免重复。
如果找不到这些 queue family,应用就会退出。
Swapchain 创建信息
本节中其余大部分工作都是为了填充这个创建信息的结构体,用来创建 swapchain:
typedef struct VkSwapchainCreateInfoKHR {VkStructureType sType;const void* pNext;VkSwapchainCreateFlagsKHR flags;VkSurfaceKHR surface;uint32_t minImageCount;VkFormat imageFormat;VkColorSpaceKHR imageColorSpace;VkExtent2D imageExtent;uint32_t imageArrayLayers;VkImageUsageFlags imageUsage;VkSharingMode imageSharingMode;uint32_t queueFamilyIndexCount;const uint32_t* pQueueFamilyIndices;VkSurfaceTransformFlagBitsKHR preTransform;VkCompositeAlphaFlagBitsKHR compositeAlpha;VkPresentModeKHR presentMode;VkBool32 clipped;VkSwapchainKHR oldSwapchain;
} VkSwapchainCreateInfoKHR;
创建 Surface
有了在 instance 和 device 里加载的 WSI extension,你就可以创建一个 VkSurface 来使你可以继续创建 swapchain。你需要再次使用特定平台的代码,在 05-init_swapchain.cpp 文件顶部显示的:
#ifdef _WIN32VkWin32SurfaceCreateInfoKHR createInfo = {};createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;createInfo.pNext = NULL;createInfo.hinstance = info.connection;createInfo.hwnd = info.window;res = vkCreateWin32SurfaceKHR(info.inst, &createInfo, NULL, &info.surface);
#endif
info.connection 和 info.window 值是被包含在 init_connection() 和 inti_window()里的更多的特定平台代码设置的,这些你也可以在示例代码里找到。
请注意,init_connection() 和 inti_window() 函数还负责特定平台操作,包括连接到显示层和创建实际窗口。你刚用 vkCreateWin32SurfaceKHR() 函数创建的 VkSurfaceKHR surface 用句柄表示,该句柄可通过 Vulkan 用于平台窗口对象。
然后你把返回的 surface 加到 swapchain 创建信息结构体里:
VkSwapchainCreateInfoKHR swapchain_ci = {};
swapchain_ci.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
swapchain_ci.pNext = NULL;
swapchain_ci.surface = info.surface;
设备 Surface Format
在创建 swapchain 的时候,你还需要指定 format。在当前上下文中,“format”指如 VkFormat 枚举所描述的像素格式,在这里 VK_FORMAT_B8G8R8A8_UNORM
是设备 surface format 的一个常用例子。
示例代码中的下一段获取一个 VkSurfaceFormatKHR 结构体列表,它包含了显示器支持的 VkFormat 格式和其他信息。因为示例不用关心显示屏 image 和 surface 使用什么 format,示例就选取了第一个可用的 format,如果未指定 format,会回退到一个通用任意的。
查看示例中的代码,可以看到它最后在创建信息结构体里设置了 format:
swapchain_ci.imageFormat = info.format;
Surface Capabilities
为了继续填充 swapchain 创建信息的结构体,示例里调用了 vkGetPhysicalDeviceSurfaceCapabilitiesKHR()
和vkGetPhysicalDeviceSurfacePresentModesKHR() 来获取所需的信息。然后填到以下字段里:
uint32_t desiredNumberOfSwapChainImages = surfCapabilities.minImageCount;swapchain_ci.minImageCount = desiredNumberOfSwapChainImages;
swapchain_ci.imageExtent.width = swapChainExtent.width;
swapchain_ci.imageExtent.height = swapChainExtent.height;
swapchain_ci.preTransform = preTransform;
swapchain_ci.presentMode = swapchainPresentMode;
你应该给 minImageCount 成员设置一个表明你的应用使用的缓冲区策略的值,比如双缓冲或者三重缓冲。该示例调用 vkGetPhysicalDeviceSurfaceCapabilitiesKHR() 函数请求可以用在 swapchain 里的最小 image 数量,结果存在 surfCapabilities 里。请求 image 的最小数量值,保证了我们在尝试申请另一个 image 之前,可以只获取一个可显示的 image。这表示的就是双缓冲配置,因为你将能够申请一个image来渲染,同时另一个image正在显示。如果你想要三重缓冲,那么你就得再申请一个image,然后在你显示之前,能够申请两个缓冲区来渲染。
Graphics 和 Present 的不同 Queue Family
你之前确定了graphics 和 present 队列的 queue family。如果它们是不同的,你需要做些额外的工作来允许 image 在 queue family 之间能共享。
swapchain_ci.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
swapchain_ci.queueFamilyIndexCount = 0;
swapchain_ci.pQueueFamilyIndices = NULL;
uint32_t queueFamilyIndices[2] = {(uint32_t)info.graphics_queue_family_index,(uint32_t)info.present_queue_family_index};
if (info.graphics_queue_family_index != info.present_queue_family_index) {// If the graphics and present queues are from different queue families,// we either have to explicitly transfer ownership of images between the// queues, or we have to create the swapchain with imageSharingMode// as VK_SHARING_MODE_CONCURRENTswapchain_ci.imageSharingMode = VK_SHARING_MODE_CONCURRENT;swapchain_ci.queueFamilyIndexCount = 2;swapchain_ci.pQueueFamilyIndices = queueFamilyIndices;
}
以上字段提供了更多的创建 swapchain 所需基本信息。你可以检查示例中其余代码,来看看这些信息是如何获取的,以及创建信息结构体里其他部分是如何填充的。
创建 Swapchain
填完了 swapchain 创建信息结构体,现在你可以创建 swapchain 了:
res = vkCreateSwapchainKHR(info.device, &swapchain_ci, NULL, &info.swap_chain);
该调用创建了一个 image 的集合并构建成了 swapchain。在某些时刻,你需要各个 image 的句柄,来告诉GPU用哪一个 image 来渲染。vkCreateSwapchainKHR() 函数本身创建了 image,所以就一直追踪着句柄。示例使用了熟悉模式来获取 image 句柄,先查询存在多少个句柄,通过调用
vkGetSwapchainImagesKHR(info.device, info.swap_chain,&info.swapchainImageCount, NULL);
获取了计数,然后再次调用函数获取 image 句柄的列表,并且把它们保存在 info.buffers[]。现在你拥有了一个目标 image 的句柄列表。
创建 Image View
你需要通过创建 image view 告诉 Vulkan 你想如何使用这些 swapchain。"view" 是附加给资源的必须的额外信息,描述这些资源如何使用。
属于一个 image 的内存区域可以被安排成多种方式,这取决于该 image 的用途。例如,一个 image 可以是1D,2D或3D。或者也可以是一个 image 数组等等。描述 image 格式(例如 VK_FORMAT_R8G8B8A8_UNORM),组件的顺序 和 layer 信息也很有用。所有这些信息是包含在 VkImageView 里的 image 元数据。
创建一个 image view 很简单。找到 05-init_swapchain.cpp 文件里的 VkImageViewCreateInfo
结构体,看一下它是用 你所期望的2D framebuffer 值来填充的。注意,image 句柄本身是存在 image view 里的。
然后,你可以存储 image view 句柄到 info 数据结构体以备之后使用,以此结束 swapchain 的创建。
Vulkan教程翻译之六 创建 Swapchain相关推荐
- [Vulkan教程] 一: 创建VkDevice
这个系列的文章是写给对已有的D3D11和GL比较熟悉并理解多线程.资源暂存.同步等概念但想进一步了解它们是如何以Vulkan实现的读者. 文章并不追求通俗易懂,里面有很多术语(为了解里面的晦涩内容,建 ...
- [Vulkan教程]绘制一个三角形/呈现/交换链(Swip chain)
文章目录 检查交换链支持 启用设备扩展 查询交换链支持详情 为交换链选择正确的设置 表面格式 呈现模式 交换范围 创建交换链 检索交换链中的图像 Vulkan没有默认缓冲区的概念,因此需要一个东西提供 ...
- [译]Vulkan教程(20)重建交换链
[译]Vulkan教程(20)重建交换链 Swap chain recreation 重建交换链 Introduction 入门 The application we have now success ...
- Vulkan教程(10): Swap chain(交换链)
Vulkan官方英文原文: https://vulkan-tutorial.com/Drawing_a_triangle/Presentation/Swap_chain 对应的Vulkan技术规格说明 ...
- Vulkan教程 - 06 交换链
Swap chain(交换链) 现在到了Vulkan教程第十章了,学习交换链.Vulkan没有默认帧缓冲的概念,因此它需要一个基础设施,能够在我们通过屏幕看到内容之前,持有我们想要渲染的东西的缓冲.该 ...
- [译]Vulkan教程(05)Instance
[译]Vulkan教程(05)Instance Creating an instance 创建一个instance The very first thing you need to do is ini ...
- [译]Vulkan教程(04)基础代码
[译]Vulkan教程(04)基础代码 General structure 通用结构 In the previous chapter you've created a Vulkan project w ...
- caffe教程翻译:在caffe上训练与测试数据
本文为caffe官网上ImageNet教程翻译,地址:http://caffe.berkeleyvision.org/gathered/examples/imagenet.html 本教程旨在教我们用 ...
- [译]Vulkan教程(32)生成mipmap
[译]Vulkan教程(32)生成mipmap Generating Mipmaps 生成mipmap Introduction 入门 Our program can now load and ren ...
- SAP UI5 初学者教程之九 - 创建第一个 Component 的试读版
一套适合 SAP UI5 初学者循序渐进的学习教程 教程目录 SAP UI5 本地开发环境的搭建 SAP UI5 初学者教程之一:Hello World SAP UI5 初学者教程之二:SAP UI5 ...
最新文章
- 结巴分词和自然语言处理HanLP处理手记
- TCPView for Windows
- Centos7单用户模式修改root密码
- centenos安装mysql,Cento7 安装mycat和mycat-eye
- BENET3.0 Linux系统管理(S2)
- 软考中级数据库系统工程师复习资料
- 工业级ERPS环网协议交换机千兆4光8电工业级冗余环网以太网交换机工业光纤环网交换机
- 小红书4大主要人群的消费特征,你占了几个?
- 云原生时代的镜像分发工具——Dragonfly简介
- Qt调节电脑屏幕亮度
- 公共关系与人际交往能力
- 网络编程之OSI七层模型,讲解tcp/ip五层涉及的网络协议,网络通信实现,结合协议来看网络通信流程...
- qpython3可以画图吗_手机qpython3如何画图
- [FTP] ftp通信协议抓包分析
- 错排公式的理解与推导(转载)
- 聚美自建的“真品联盟”被京东捅破的窗户纸
- 今日金融词汇---应收,是什么?
- 多线程对共享资源的访问
- 【实用】罗斯蒙特3051压力变送器如何进行调试校准?
- LoRaWAN介绍3 优点
热门文章
- 用c语言编程心形,用c语言编写心形图案
- edge启动页北hao123流氓绑定的其中一个解决方法
- 西游记中出现的八位菩萨
- js+php+mysql写的rpg网页游戏引擎源码-DotK改进版
- C语言编程,将26个英文字母大小写输出
- 老庙黄金2016春晚抢红包活动技术架构详解
- 论文:轨迹路线生成算法
- 服务器winsxs文件夹怎么清理工具,win10系统winsxs文件夹该如何删除?win10删除winsxs文件夹的两种方法...
- BAT的医疗春秋大梦
- 解决报错: ‘v-slot‘ directive must be owned by a custom element, but ‘div‘ is not