初始Vulkun(7):交换链

本文详细介绍了如何在Vulkan中创建和配置交换链,包括检查显卡是否支持VK_KHR_swapchain扩展,确保逻辑设备扩展交换链,以及获取和保持与surface兼容的设置。关键步骤包括选择表面格式、展示模式和交换范围。交换链管理的图像集合用于在窗口表面展示内容,其数量、格式和呈现模式直接影响应用程序的性能和显示效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

08/02/2020,03/22/2021

交换链

产生的VkSurfaceKHR句柄都指代窗口的Vulkan视图,为了在该表面上展示任何东西,需要创建一个特殊的图像,它可以用于在一个窗口中存储数据并且由窗口系统创建。
交换链来管理一个或者多个由窗口表面创建的图像对象,而不是创建一个普通的Vulkan图像对象。
交换链对象是用来请求窗口系统创建一个或者多个可用来代表Vulkan表面的图像。通过VK_KHR_swapchain扩展来提供至各功能。
每一个交换链对象管理一个图像集,通常以环状缓冲区的形式。交换链的本质是一个图像队列
应用程序作为生产者或获取图像进行绘制,然后将其返还给减缓年图像队列,等待屏幕消费。
交换链的具体配置信息决定了应用程序提交绘制图像到队列的条件以及图像队列的表现的效果,但交换链的通常使用目的是使绘制图像的最终呈现与屏幕的刷新频率同步。

显卡是否支持交换链(VK_KHR_swapchain)

//required device extensions
const std::vector<const char*> deviceExtensions = 
{
	VK_KHR_SWAPCHAIN_EXTENSION_NAME		//VK_KHR_swapchain 的别名
};
/*
搜索算法,检索所有支持的扩展,并在必要扩展容器中尝试删除它们,如果必要扩展容器为空表示支持所有必要扩展

*/
bool checkDeviceExtensionSupport(VkPhysicalDevice device)
{
	uint32_t extensionCount;
	vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);
	std::vector<VkExtensionProperties> availableExtensions(extensionCount);
	vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data());

	//Find or search way to use erase way
	std::set<std::string> requiredExtensions(deviceExtensions.begin(), deviceExtensions.end());
	for (const auto& extension : availableExtensions) 
	{
		requiredExtensions.erase(extension.extensionName);
	}

	return requiredExtensions.empty();
}

逻辑设备需要扩展交换链

逻辑设备的扩展一开始为空,现在需要扩展交换链,设备扩展

createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
createInfo.ppEnabledExtensionNames = deviceExtensions.data();

配置交换链环境

交换链需要保证与窗体surface兼容,或者在某些属性上保持一致。
一共需要确认三个方面:

  1. 基础窗口表面容量,比如图像最小最大数量,和最大最小尺寸
  2. 窗口表面的格式,比如像素格式,色彩空间
    • 决定颜色通道和类型
  3. 有效的展示模式

获取surface的设置细节

SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device) 
{
	SwapChainSupportDetails details;
	
	//basic surface capablities 基础surface功能设备部分
	vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device,surface,&details.capabilities);
	//query the supported surface formats  查询支持的surface格式
	uint32_t formatCount;
	vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr);
	if (formatCount != 0) 
	{
		details.formats.resize(formatCount);
		vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data());

	}
	//query the supported presentation modes work 查询支持的presentation模式
	uint32_t presentModeCount;
	vkGetPhysicalDeviceSurfacePresentModesKHR(device,surface,&presentModeCount,nullptr);

	if (presentModeCount != 0) 
	{
		details.presentModes.resize(presentModeCount);
		vkGetPhysicalDeviceSurfacePresentModesKHR(device,surface,&presentModeCount,details.presentModes.data());
	}
	return details;
}

保持与surface一致或为交换链选择正确的设置

一共三个方面:

  1. 表面格式,比如颜色深度
  2. 展示模式
  3. 交换范围,图像的分辨率

chooseSwapSurfaceFormat

选择表面的格式:

  • format: 色彩通道和类型
  • colorSpace: SRGB颜色空间
VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats) 
{
	//VkSurfaceFormatKHR contains a format and a colorSpace member
	//format: color channels and types
	//colorSpace: SRGP -- accurate color
	for (const auto& availableFormat : availableFormats) 
	{
		if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && 
			availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) 
		{
			return availableFormat;
		}
	}
	return availableFormats[0];
}

chooseSwapPresentMode

代表了在屏幕呈现图像的条件,会引起撕裂效果

//Important!  Set presentation mode
//actual condition for showing images to the screen
//4 modes
// VK_PRESENTMODE_IMMEDIATE_KHR
//FIFO
//FIFO_RELAXED
//MAILBOX
VkPresentModeKHR chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentMode) 
{
	for (const auto& availablePresentMode : availablePresentMode) 
	{
	 	//选择三重缓冲区
		if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) 
		{
			return availablePresentMode;
		}
	}
	//不然就是双重缓冲区
	return VK_PRESENT_MODE_FIFO_KHR;
}

4个模式:

  • 立即传输
  • 先进先出FIFO: 类似于垂直同步,当显示内容需要刷新的时候,显示设备从队列的前面获取图像,并却程序将渲染完成的图像插入队列的后面
  • FIFO_RELAXED
  • MAILBOX: 通常用来实现三重缓冲区,当交换链队列满了,选择新的替换旧的图像,从而代替阻塞应用程序的情形

chooseSwapExtent

交换范围:交换链图像的分辨率,几乎总是等于我们绘制窗体的分辨率。

//resolution of the window that we're drawing to
VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) 
{
	if (capabilities.currentExtent.width != UINT32_MAX) 
	{
		return capabilities.currentExtent;
	}
	else 
	{
		VkExtent2D actualExtent = { WIDTH,HEIGHT };
		actualExtent.width = std::max(capabilities.minImageExtent.width,
			std::min(capabilities.maxImageExtent.width, actualExtent.width));
		actualExtent.height = std::max(capabilities.minImageExtent.height,
			std::min(capabilities.maxImageExtent.height, actualExtent.height));
		return actualExtent;
	}

}

创建交换链

  • surface:指定窗口表面
  • minImageCount: 交换链中的图像个数,双缓冲或者三缓冲,分别用2或者3

注意:设置minImageCount为2表示你仅有一个前端缓冲区和一个后端缓冲区。出发了展示已完成的后端缓冲区之后,你无法开始渲染到另一个缓冲区,知道展示完成,为了获得最佳的性能,可能以延迟为代价,如果设备支持,建议至少为3.

  • 处理跨多个队列族的交换链图像,如果graphics和presentation队列族不同,我们将从graphics队列汇总绘制交换链的图像,然后在另一个presentation队列中提交它们。
void createSwapChain() 
{
	SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice);

	VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats);
	VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes);
	VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities);

	//number of images in swap chain, 交换链中有3张图像
	uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
	//cannot exceed maximum numbers of images
	if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) 
	{
		imageCount = swapChainSupport.capabilities.maxImageCount;
	}


	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;	//图像的颜色位域

	

	//try remove the next line  to see console
	createInfo.imageExtent = extent;						//图像大小

	createInfo.imageArrayLayers = 1;						//the amount of layers each image consists of, always 1 unless you are developing a stereoscopic 3D application
	createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;// what kind of operations we'll use the images in the swap chain for


	//how to handle swap chain images that will be used across multiple queue families
	//draw on graphics queue then submitt them on the presentation queue
	QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
	uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(),indices.presentFamily.value() };
	if (indices.graphicsFamily != indices.presentFamily) 
	{
		createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;		//multiple queue
		createInfo.queueFamilyIndexCount = 2;
		createInfo.pQueueFamilyIndices = queueFamilyIndices;
	}
	else 
	{
		createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;		//only one queue used page.87
		createInfo.queueFamilyIndexCount = 0;		//Optional
		createInfo.pQueueFamilyIndices = nullptr;	//Optional
	}

	createInfo.preTransform = swapChainSupport.capabilities.currentTransform;  //90 degree clockwish ...
	createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;				//alpha channel should be used for blending with other windows
	createInfo.presentMode = presentMode;
	createInfo.clipped = VK_TRUE;				// donot care about the color of pixels that are obscured
	createInfo.oldSwapchain = VK_NULL_HANDLE;		//resize the window, may need to recreate swap Chain


	if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) 
	{
		throw std::runtime_error("failed to create swap chain");
	}

	//retrieve VkImage, 交换链设置图像,这里可以获得交换链的图像。
	uint32_t imagesCount;
	vkGetSwapchainImagesKHR(device, swapChain, &imagesCount, nullptr);
	swapChainImages.resize(imageCount);
	vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());
	swapChainImageFormat = surfaceFormat.format;
	swapChainExtent = extent;
}
  • 交换链中应该有多少图像,图像过多按照最大值处理
  • 每个图像组成的层数
  • 图像在不同队列家族中的分享模式
  • 转换模式
  • alpha通道
  • 展示模式
  • 交换链重建由于窗口大小改变
  • 创建完交换链之后可以获取VkImage,它们跟着交换链自动销毁

知识补充

typedef struct VkSwapchainCreateInfoKHR
{
	sType,
	pNext,
	flags,
	surface, 	//将展示的平面通过surface指定
	//交换链自动创建的图像,及它的属性
	imageFormat,
	image,
	ColorSpace,
	imageExtent,
	imageArrayLayers,
	imageUsage,  		//必须是从交换链支持的用法中选取
	imageSharingMode,

	presentMode,	//控制了与窗口系统的同步,以及图像展示到表面上的速率
}VkSwapchainCreateInfoKHR;

总结

  • 交换链中的参数必须和表面的能力相匹配,所以需要先创建表面,并通过vkGetPhysicalDeviceSurfaceCapabilitiesKHR查询
  • 每个交换链对象管理一个图像集。但交换链和窗口表面匹配完,也意味着设置完生成图像VkImage的格式,大小和颜色,所以可以从交换链中获取图像VkImage
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值