首页 > 其他分享 >Vulkan学习苦旅05:马不停蹄地渲染(创建交换链VkSwapchainKHR)

Vulkan学习苦旅05:马不停蹄地渲染(创建交换链VkSwapchainKHR)

时间:2024-02-01 15:57:58浏览次数:18  
标签:创建 05 队列 交换 VK nullptr 图像 VkSwapchainKHR Vulkan

通俗地说,渲染图像就是为图像的每个像素选择合适的颜色。例如,如果图像的分辨率为1920x1080,表示图像中有1920x1080个像素,渲染的过程,就是为每个位置的像素计算出合适的颜色。具体来说,假设每种颜色具有RGBA四个通道,且每个通道用1个字节表示(可以表示255种不同的情况),那么图像应当占据1920x1080x4个字节的内存。

渲染完一幅图片后,就可以将其显示到窗口上。从而,整个工作流程可以描述为:

渲染图像 → 显示图像 → 渲染图像 → 显示图像 → …… 

然而,渲染需要将颜色写入内存,而显示需要从内存中读取数据。也就是说,当内存被用于显示图像时,渲染应当停止工作。显而易见的是,这样并没有充分利用资源。

这个问题很容易解决:只要使用两块或更多的内存。例如,有两块内存A和B,渲染的结果写到内存A中,显示从内存B读取数据;在下一次,交换A和B,即显示从内存A读取数据,渲染的结果写到内存B中……之后反复交换A和B。这也是交换链中“交换”一词的由来。

本文将要介绍的内容位于“地图”第三层的右上方:

1. 创建交换链前的准备

交换链用于管理多个图像对象,在创建交换链前需要查询物理设备对交换链的支持程度:例如交换链中可以创建多少张图像、图像的最大尺寸与格式等。为此,定义函数createSwapChain,本节的代码都写在这个函数中。

 

首先查询物理设备对图像大小与数量的支持:

VkSurfaceCapabilitiesKHR surfaceCapabilities;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(mPhysicalDevice, mSurface, &surfaceCapabilities);

查询需要物理设备mPhysicalDevice和表面mSurface作为参数,查询结果保存在结构体VkSurfaceCapabilitiesKHR中。目前,我们需要此结构体的以下成员:

minImageCount, maxImageCount: 交换链中的图像数必须在minImageCount ~ maxImageCount内;

currentImageExtent: 一个VkExtent2D对象,VkExtent2D只有两个成员widthheight, 所以它表示什么就不用我多说了吧;

minImageExtent, maxImageExtent: 同样为VkExtent2D对象,如果表面支持改变大小,它们会被依次设置为表面的最小和最大尺寸。

supportedTransforms, currentTransforms: 是一个VkSurfaceTransformFlagBitsKHR对象。有的表面可以在展示图像前对图像加以变换,例如翻转图像等等,如果物理设备支持某项功能,supportedTransforms相应的位会被置为1. currentTransforms表示当前使用的变换类型。

 

接下来,查询物理设备对图像格式的支持:

uint32_t formatCount = 0;
vkGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDevice, mSurface, &formatCount, nullptr);
vector<VkSurfaceFormatKHR> surfaceFormats(formatCount);
vkGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDevice, mSurface, &formatCount, surfaceFormats.data());

从前面的文章一直看过来的小伙伴们,想必不用我再过多解释上面这段代码的含义了吧,这段代码返回一个VkSurfaceFormatKHR类型的数组。VkSurfaceFormatKHR结构体仅有两个成员:

format: 像素在内存中的保存格式,例如VK_FORMAT_R8G8B8_UINT(无符号的RGB格式)、VK_FORMAT_R8G8B8_SINT(有符号的RGB格式);

colorSpace: 支持的颜色空间,目前只有VK_COLORSPACE_SRGB_NONLINEAR_KHR这一个取值。

2. 创建交换链对象

查找好物理设备支持的参数后,就可以创建一个交换链对象啦。不过在此之前,首先设置交换链图像的数目。前面提到过,交换链的图像数量在minImageCount ~ maxImageCount内,这里设置图像的数量为min(minImageCount + 1, maxImageCount), 代码如下:

int imageCount = surfaceCapabilities.minImageCount + 1 <= surfaceCapabilities.maxImageCount ?
	surfaceCapabilities.minImageCount + 1 : surfaceCapabilities.maxImageCount;  // 设置图像的数量

为什么minImageCount要+1?

在博主的物理设备中,支持的图像数量为2~64. 如果设置为2,当渲染完成而显示仍未结束时,渲染将不得不等待显示的完成。因此,只要物理设备支持,图像数量至少设置为3.

之后,填充结构体VkSwapchainCreateInfoKHR:

VkSwapchainCreateInfoKHR swapchainCreateInfo{
	VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,  // .sType
	nullptr,  // .pNext
	0,  // .flags
	mSurface,  // .surface
	imageCount,  // .minImageCount
	surfaceFormats[0].format,  // .imageFormat
	surfaceFormats[0].colorSpace,  // .imageColorSpace
	surfaceCapabilities.currentExtent,  // imageExtent
	1,  // .imageArrayLayers
	VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,  // .imageUsage
	VK_SHARING_MODE_EXCLUSIVE,  // .imageSharingMode
	0,  // .queueFamilyIndexCount
	nullptr,  // .pQueueFamilyIndices
	surfaceCapabilities.currentTransform,  // .preTransform
	VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,  // .compositeAlpha
	VK_PRESENT_MODE_FIFO_KHR,  // .presentMode
	VK_TRUE,  // .clipped
	VK_NULL_HANDLE,  // .oldSwapChain
};

其中:

surface:之前创建的表面,这里设置为mSurface;

minImageCount: 交换链中的图像数量,这里设置为刚才计算出的值imageCount;

imageFormat, imageColorSpace: 支持的格式,此处使用查询到的格式数组的第一个元素;

imageExtent: 图像大小,此处设置为查询到的值;

imageArrayLayers: 每张图像可以有多层,暂时设置为1,只使用一层;

imageUsage: 暂时不用管它,按照上面的设置即可;

imageSharingMode, queueFamilyIndexCount, pQueueFamilyIndices: 这三个成员与之前多次提到的、图形队列与显示队列有关。如果二者不是同一个队列,就会涉及到图像在不同队列见的共享问题,此时需要告诉交换链两个队列所在队列族的索引;

preTransform:图像在展示给用户前所作的变换,此处设置为查询到的currentTransform;

compositeAlpha: 如何处理透明通道,此处暂时忽略透明通道;

presentMode: 图像如何与窗口同步,例如显示到窗口的速率,此处设置为VK_PRESENT_MODE_FIFO_KHR,图像存储在一个队列中,并依次向用户展示;

clipped, oldSwapChain: 暂时不管它们。

上面的设置有一个问题:imageSharingMode, queueFamilyIndexCountpQueueFamilyIndices的取值假设图形队列和显示队列是同一个队列,如果它们不是同一个队列,那么就应该另外设置这些成员的值:

if (mGraphicsQueueFamilyIndex != mPresentQueueFamilyIndex) {
	swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
	swapchainCreateInfo.queueFamilyIndexCount = 2;
	uint32_t queueFamilyIndices[] = {
		mGraphicsQueueFamilyIndex, mPresentQueueFamilyIndex
	};
	swapchainCreateInfo.pQueueFamilyIndices = queueFamilyIndices;
}

一切准备就绪后,就可以创建交换链对象了。首先在VulkanApp类中定义交换链成员:

VkSwapchainKHR mSwapChain;

之后调用函数vkCreateSwapchainKHR创建它:

if_fail(
	vkCreateSwapchainKHR(mDevice, &swapchainCreateInfo, nullptr, &mSwapChain),
	"failed to create swapchain!"
);
Log("create swapchain successfully");

此函数中,第一个参数为之前创建的设备(之后创建的许多对象都需要设备作为第一个参数);第二个参数为描述交换链信息的结构体;第三个参数与交换链的内存分配有关,一律设置为空指针;最后一个参数返回创建好的交换链对象。

 

最后,不要忘记在析构函数中销毁此对象:

vkDestroySwapchainKHR(mDevice, mSwapChain, nullptr);

创建交换链时需要设备,销毁它时同样也需要传入设备。

3. 获取图像

很可惜,到目前创建交换链的过程还没有结束,我们还需要获取交换链创建好的图像,就像从设备对象中获取命令队列那样,幸好这个过程也很简单。

首先在VulkanApp类中定义成员变量:

vector<VkImage> mSwapChainImages;  // 交换链的图像

接下来通过函数vkGetSwapchainImagesKHR获取图像:

uint32_t swapChainImagesCount = 0;
vkGetSwapchainImagesKHR(mDevice, mSwapChain, &swapChainImagesCount, nullptr);
mSwapChainImages.resize(swapChainImagesCount);
vkGetSwapchainImagesKHR(mDevice, mSwapChain, &swapChainImagesCount, mSwapChainImages.data());

此处我们已经知道了交换链中图像的数量,所以第一次查询图像数量的操作完全可以不写,这里为了追求代码的一致性,多写了一步。

4. 创建图像视图

很可惜,到这里仍然没有结束,还需要为获取到的每一张图像创建相应的图像视图。

什么是视图?

视图这个概念很常见,例如在数据库甚至是numpy中都有类似的概念。在Vulkan中,图像(VkImage)描述了底层的数据,而图像的视图(VkImageView)描述了对图像的操纵方式,可以为一张图像创建多个视图。

首先,在VulkanApp类中添加相应的成员:

vector<VkImageView> mSwapChainImageViews;

接下来,为每一个图像创建对应的图像视图:

mSwapChainImageViews.resize(mSwapChainImages.size());
for (size_t i = 0; i < mSwapChainImages.size(); i++) {
	VkImageViewCreateInfo imageViewCreateInfo{
		VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,  // .sType
		nullptr,  // .pNext
		0,  // .flags
		mSwapChainImages[i],  // .image
		VK_IMAGE_VIEW_TYPE_2D,  // .viewType
		surfaceFormats[0].format,  // .format
		{
			VK_COMPONENT_SWIZZLE_IDENTITY,
			VK_COMPONENT_SWIZZLE_IDENTITY,
			VK_COMPONENT_SWIZZLE_IDENTITY,
			VK_COMPONENT_SWIZZLE_IDENTITY,
		},   // .components
		{
			VK_IMAGE_ASPECT_COLOR_BIT,  // .aspectMask
			0,  // .baseMipLevel
			1,  // .levelCount
			0,  // .baseArrayLayer
			1,  // .layerCount
		},  // .subresourceRange
	};

	if_fail(
		vkCreateImageView(mDevice, &imageViewCreateInfo, nullptr, &mSwapChainImageViews[i]),
		"failed to create image views!"
	);
}

由于目前不涉及内存相关的内容,因此暂时不去解释结构体VkImageViewCreateInfo中各成员的含义。

 

同样地,记得在析构函数中销毁这些视图:

for (auto swapChainImageView : mSwapChainImageViews) {
	vkDestroyImageView(mDevice, swapChainImageView, nullptr);
}

图像(VkImage)不需要销毁,它们由交换链创建,亦由交换链负责销毁。与之类似的还有队列(VkQueue),它们由设备创建,无需我们手动销毁。

5. 目前为止的完整代码

这一次,我们创建了交换链对象,然而这并不是终点。由于窗口的大小可以被改变,一旦大小发生变换,这里的许多设置都需要作出相应的改变,例如调整底层图像内存的大小等。因此,当时机成熟时,我们还会再回过头来,调整这里的代码。

下一次介绍完整的渲染流程,你将看到代码量的井喷式增长。

到目前为止的完整代码
 #define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>

#include <iostream>
#include <vector>
using std::vector;
#include <cstring>

#define Log(message) std::cout << "[INFO] " << message << std::endl
#define Error(message) std::cerr << "[ERROR] " << message << std::endl; exit(-1)

static void if_fail(VkResult result, const char* message);


class VulkanApp {
public:
	VulkanApp() {
		glfwInit();  // 初始化glfw库
		glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);  // 禁用OpenGL相关的API
		glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);  // 禁止调整窗口大小

		createInstance();
		createSurface();

		selectPhysicalDevice();
		createDevice();

		createSwapChain();
	}

	~VulkanApp() {
		for (auto swapChainImageView : mSwapChainImageViews) {
			vkDestroyImageView(mDevice, swapChainImageView, nullptr);
		}
		vkDestroySwapchainKHR(mDevice, mSwapChain, nullptr);
		vkDestroyDevice(mDevice, nullptr);

		vkDestroySurfaceKHR(mInstance, mSurface, nullptr);
		vkDestroyInstance(mInstance, nullptr);

		glfwDestroyWindow(mWindow);
		glfwTerminate();
	}

	void Run() {
		while (!glfwWindowShouldClose(mWindow)) {
			glfwPollEvents();
		}
	}

private:
	const vector<const char*> mRequiredLayers = {
		"VK_LAYER_KHRONOS_validation"
	};
	const vector<const char*> mRequiredExtensions = {
		VK_KHR_SWAPCHAIN_EXTENSION_NAME,  // 等价于字符串"VK_KHR_swapchain"
	};
	VkInstance mInstance;  // 实例
	VkPhysicalDevice mPhysicalDevice;  // 物理设备
	int mGraphicsQueueFamilyIndex = -1;  // 支持图形功能的队列族索引
	int mPresentQueueFamilyIndex = -1;  // 支持显示功能的队列族索引

	int mWidth = 800;  // 窗口宽度
	int mHeight = 600;  // 窗口高度
	GLFWwindow* mWindow = nullptr;  // glfw窗口指针
	VkSurfaceKHR mSurface;  // 表面
	VkSwapchainKHR mSwapChain;  // 交换链
	vector<VkImage> mSwapChainImages;  // 交换链的图像
	vector<VkImageView> mSwapChainImageViews;  // 交换链的图像视图

	VkDevice mDevice;  // (逻辑)设备
	VkQueue mGraphicsQueue;  // 支持图形的队列
	VkQueue mPresentQueue;  // 支持显示的队列

	void createInstance() {
		/* 填充VkApplicationInfo结构体 */
		VkApplicationInfo appInfo{
			VK_STRUCTURE_TYPE_APPLICATION_INFO,  // .sType
			nullptr,  // .pNext
			"I don't care",  // .pApplicationName
			VK_MAKE_VERSION(1, 0, 0),  // .applicationVersion
			"I don't care",  // .pEngineName
			VK_MAKE_VERSION(1, 0, 0),  // .engineVersion
			VK_API_VERSION_1_0,  // .apiVersion
		};

		/* 获取glfw要求支持的扩展 */
		uint32_t glfwExtensionCount = 0; 
		const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);

		/* 输出glfw所需的扩展 */
		std::cout << "[INFO] glfw needs the following extensions:\n";
		for (int i = 0; i < glfwExtensionCount; i++) {
			std::cout << "    " << glfwExtensions[i] << std::endl;
		}

		/* 填充VkInstanceCreateInfo结构体 */
		VkInstanceCreateInfo instanceCreateInfo{
			VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,  // .sType
			nullptr,  // .pNext
			0,  // .flags
			&appInfo,  // .pApplicationInfo
			mRequiredLayers.size(),  // .enabledLayerCount
			mRequiredLayers.data(),  // .ppEnabledLayerNames
			glfwExtensionCount,  // .enabledExtensioncount
			glfwExtensions,  // .ppEnabledExtensionNames
		};

		/* 如果创建实例失败,终止程序 */
		if_fail(
			vkCreateInstance(&instanceCreateInfo, nullptr, &mInstance),
			"failed to create instance"
		);
	}

	void createSurface() {
		mWindow = glfwCreateWindow(mWidth, mHeight, "Vulkan App", nullptr, nullptr);  // 创建glfw窗口
		if (mWindow == nullptr) {
			std::cerr << "failed to create window\n";
			exit(-1);
		}

		/* 创建VkSurfaceKHR对象 */
		if_fail(
			glfwCreateWindowSurface(mInstance, mWindow, nullptr, &mSurface),
			"failed to create surface"
		);
	}

	void selectPhysicalDevice() {
		/* 查找所有可选的物理设备 */
		uint32_t physicalDeviceCount = 0;
		vkEnumeratePhysicalDevices(mInstance, &physicalDeviceCount, nullptr);
		vector<VkPhysicalDevice> physicalDevices(physicalDeviceCount);
		vkEnumeratePhysicalDevices(mInstance, &physicalDeviceCount, physicalDevices.data());

		mPhysicalDevice = VK_NULL_HANDLE;

		for (VkPhysicalDevice physicalDevice : physicalDevices) {
			/* 1. 检查物理设备是否支持扩展 */
			/* 获取物理设备支持的扩展信息 */
			uint32_t extensionCount = 0;
			vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extensionCount, nullptr);
			vector<VkExtensionProperties> availableExtensions(extensionCount);
			vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extensionCount, availableExtensions.data());

			bool isAllRequiredExtensionsSupported = true;  // 检查此物理设备是否支持所有的扩展
			for (const char* requiredExtensionName : mRequiredExtensions) {
				bool isSupported = false;
				for (const auto& availableExtension : availableExtensions) {
					if (strcmp(requiredExtensionName, availableExtension.extensionName) == 0) {
						isSupported = true;
						break;
					}
				}
				if (isSupported == false) {
					isAllRequiredExtensionsSupported = false;
					break;
				}
			}
			if (isAllRequiredExtensionsSupported) {
				Log("all required extensions are supported");
			}
			else {
				continue;
			}

			/* 2. 检查物理设备是否支持几何着色器 */
			VkPhysicalDeviceFeatures physicalDeviceFeatures;
			vkGetPhysicalDeviceFeatures(physicalDevice, &physicalDeviceFeatures);
			if (physicalDeviceFeatures.geometryShader) {
				Log("geometry shader is supported");
			}
			else {
				continue;
			}

			/* 获取队列族的信息 */
			uint32_t queueFamilyCount = 0;
			vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr);
			vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
			vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilies.data());

			for (int i = 0; i < queueFamilyCount; i++) {
				/*  5.3. 检查是否支持图形功能 */
				if (mGraphicsQueueFamilyIndex < 0 && (queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)) {
					Log("find graphics queue family index " << i);
					mGraphicsQueueFamilyIndex = i;  // 保留队列族的索引
				}

				/*  5.4. 检查是否支持显示功能  */
				if (mPresentQueueFamilyIndex < 0) {
					VkBool32 isPresentSupport = false;
					vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, i, mSurface, &isPresentSupport);
					if (isPresentSupport) {
						mPresentQueueFamilyIndex = i;
						Log("find present queue family index " << i);
					}
					else {
						Log("present is not supported");
					}
				}
			}

			if (mGraphicsQueueFamilyIndex >= 0 && mPresentQueueFamilyIndex >= 0) {
				mPhysicalDevice = physicalDevice;

				/*  获取物理设备的属性  */
				VkPhysicalDeviceProperties physicalDeviceProperties;
				vkGetPhysicalDeviceProperties(mPhysicalDevice, &physicalDeviceProperties);
				Log("select physical device: " << physicalDeviceProperties.deviceName);
			}
		}

		/* 如果没找到合适的物理设备 */
		if (mPhysicalDevice == VK_NULL_HANDLE) {
			Error("can't find suitable physical device");
		}
	}

	void createDevice() {
		/*  填充VkDeviceQueueCreateInfo结构体  */
		vector<VkDeviceQueueCreateInfo> deviceQueueCreateInfos;
		float queuePriority = 1.0f;  // 必须指定优先级,如果pQueuePriorities设置为nullptr会报错

		VkDeviceQueueCreateInfo deviceGraphicsQueueCreateInfo{
			VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,  // .sType
			nullptr,  // .pNext
			0,  // .flags
			mGraphicsQueueFamilyIndex,  // .queueFamilyIndex
			1,  // .queueCount
			&queuePriority,  // .pQueuePriorities
		};
		deviceQueueCreateInfos.push_back(deviceGraphicsQueueCreateInfo);

		if (mPresentQueueFamilyIndex != mGraphicsQueueFamilyIndex) {
			VkDeviceQueueCreateInfo devicePresentQueueCreateInfo{
				VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,  // .sType
				nullptr,  // .pNext
				0,  // .flags
				mPresentQueueFamilyIndex,  // .queueFamilyIndex
				1,  // .queueCount
				&queuePriority,  // .pQueuePriorities
			};
			deviceQueueCreateInfos.push_back(devicePresentQueueCreateInfo);
		}

		/*  填充VkDeviceCreateInfo结构体  */
		VkDeviceCreateInfo deviceCreateInfo{
			VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,  // .sType
			nullptr,  // .pNext
			0,  // .flags
			deviceQueueCreateInfos.size(),  // .queueCreateInfoCount
			deviceQueueCreateInfos.data(),  // .pQueueCreateInfos
			mRequiredLayers.size(),  // .enabledLayerCount
			mRequiredLayers.data(),  // .ppEnabledLayerNames
			mRequiredExtensions.size(),  // .enabledExtensionCount
			mRequiredExtensions.data(),  // .ppEnabledExtensionNames
			nullptr,  // .pEnabledFeatureks
		};

		if_fail(
			vkCreateDevice(mPhysicalDevice, &deviceCreateInfo, nullptr, &mDevice),
			"failed to create device!"
		);
		Log("create device successfully");

		vkGetDeviceQueue(mDevice, mGraphicsQueueFamilyIndex, 0, &mGraphicsQueue);
		vkGetDeviceQueue(mDevice, mPresentQueueFamilyIndex, 0, &mPresentQueue);
	}

	void createSwapChain() {
		/*  获取物理设备对图像大小、数量的支持  */
		VkSurfaceCapabilitiesKHR surfaceCapabilities;
		vkGetPhysicalDeviceSurfaceCapabilitiesKHR(mPhysicalDevice, mSurface, &surfaceCapabilities);
		
		/*  获取物理设备对图像格式的支持  */
		uint32_t formatCount = 0;
		vkGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDevice, mSurface, &formatCount, nullptr);
		vector<VkSurfaceFormatKHR> surfaceFormats(formatCount);
		vkGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDevice, mSurface, &formatCount, surfaceFormats.data());

		/*  填充交换链结构体  */
		int imageCount = surfaceCapabilities.minImageCount + 1 <= surfaceCapabilities.maxImageCount ?
			surfaceCapabilities.minImageCount + 1 : surfaceCapabilities.maxImageCount;  // 设置图像的数量

		VkSwapchainCreateInfoKHR swapchainCreateInfo{
			VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,  // .sType
			nullptr,  // .pNext
			0,  // .flags
			mSurface,  // .surface
			imageCount,  // .minImageCount
			surfaceFormats[0].format,  // .imageFormat
			surfaceFormats[0].colorSpace,  // .imageColorSpace
			surfaceCapabilities.currentExtent,  // imageExtent
			1,  // .imageArrayLayers
			VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,  // .imageUsage
			VK_SHARING_MODE_EXCLUSIVE,  // .imageSharingMode
			0,  // .queueFamilyIndexCount
			nullptr,  // .pQueueFamilyIndices
			surfaceCapabilities.currentTransform,  // .preTransform
			VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,  // .compositeAlpha
			VK_PRESENT_MODE_FIFO_KHR,  // .presentMode
			VK_TRUE,  // .clipped
			VK_NULL_HANDLE,  // .oldSwapChain
		};

		/*  如果图形队列和展示队列不是同一个队列  */
		if (mGraphicsQueueFamilyIndex != mPresentQueueFamilyIndex) {
			swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
			swapchainCreateInfo.queueFamilyIndexCount = 2;
			uint32_t queueFamilyIndices[] = {
				mGraphicsQueueFamilyIndex, mPresentQueueFamilyIndex
			};
			swapchainCreateInfo.pQueueFamilyIndices = queueFamilyIndices;
		}

		/*  创建交换链  */
		if_fail(
			vkCreateSwapchainKHR(mDevice, &swapchainCreateInfo, nullptr, &mSwapChain),
			"failed to create swapchain!"
		);
		Log("create swapchain successfully");

		/*  获取交换链的图像  */
		uint32_t swapChainImagesCount = 0;
		vkGetSwapchainImagesKHR(mDevice, mSwapChain, &swapChainImagesCount, nullptr);
		mSwapChainImages.resize(swapChainImagesCount);
		vkGetSwapchainImagesKHR(mDevice, mSwapChain, &swapChainImagesCount, mSwapChainImages.data());

		/*  为每张图像创建对应的图像视图  */
		mSwapChainImageViews.resize(mSwapChainImages.size());
		for (size_t i = 0; i < mSwapChainImages.size(); i++) {
			VkImageViewCreateInfo imageViewCreateInfo{
				VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,  // .sType
				nullptr,  // .pNext
				0,  // .flags
				mSwapChainImages[i],  // .image
				VK_IMAGE_VIEW_TYPE_2D,  // .viewType
				surfaceFormats[0].format,  // .format
				{
					VK_COMPONENT_SWIZZLE_IDENTITY,
					VK_COMPONENT_SWIZZLE_IDENTITY,
					VK_COMPONENT_SWIZZLE_IDENTITY,
					VK_COMPONENT_SWIZZLE_IDENTITY,
				},   // .components
				{
					VK_IMAGE_ASPECT_COLOR_BIT,  // .aspectMask
					0,  // .baseMipLevel
					1,  // .levelCount
					0,  // .baseArrayLayer
					1,  // .layerCount
				},  // .subresourceRange
			};

			if_fail(
				vkCreateImageView(mDevice, &imageViewCreateInfo, nullptr, &mSwapChainImageViews[i]),
				"failed to create image views!"
			);
		}
	}
};


int main() {
	VulkanApp app;
	app.Run();
}

static void if_fail(VkResult result, const char* message) {
	if (result != VK_SUCCESS) {
		std::cerr << "[error] " << message << std::endl;
		exit(-1);
	}
}

标签:创建,05,队列,交换,VK,nullptr,图像,VkSwapchainKHR,Vulkan
From: https://www.cnblogs.com/overxus/p/17997410/vulkan05

相关文章

  • 一个简单的Vulkan渲染器管线组织
    目录预计算管线逐帧管线逐帧预计算部分ShadowDepthPass逐帧计算部分G-BufferPassAOPassSSAOShadowPassCSMLightingPassPBR-IBLPassCompositePass总结问题参考资料延迟渲染管线核心在于先记录一遍每个像素上的信息,如Material、Normal、Albedo等,即G-Buffer,世界空间坐标可通......
  • kali学习笔记-05-DVWA XSS跨站脚本攻击
    kali学习笔记-05-DVWA XSS跨站脚本攻击KaliLinux网络安防一、反射型XSS攻击在OWASP的DVWA上,选中XSSreflected页面,在输入框内输入张三,页面反应正常。尝试输入一句script脚本。<script>alert('xss')</script>出现了如下的系统弹框,也就意味着后端服务器没有对特殊字符做......
  • 05-大厂咋解决技术债的?
    在构建可扩展的软件时,它是最关键的团队。现实没有技术债管理团队,也没人愿意加入这样队伍。这种团队每天就是给其他开发人员收拾烂摊子,谁愿意给别人擦屁股呢,毕竟又不是年薪百万?但确实有一些名字听起来更专业的团队,如基础设施团队、架构团队、核心团队,这听起来是不是就吊炸天了?......
  • datawhale-leetcode打卡:038~050题
    两数相加(leetcode002)#Definitionforsingly-linkedlist.#classListNode:#def__init__(self,val=0,next=None):#self.val=val#self.next=nextclassSolution:defaddTwoNumbers(self,l1:Optional[ListNode],l2:Optional[List......
  • Arduino - Arduino/AVR/8051 和 ARM/STM32
    8051,AVR和ARM架构80518051是由Intel于1980年代初推出的一款8位单片机。它的架构基于哈佛结构(分离的程序和数据存储器),并使用8位数据总线和16位地址总线。8051内核是一个8位CISC(复杂指令集计算机)处理器,具有不同的寻址模式和指令。该内核具有四个寄存器组、两个16位计数器/定时器、一......
  • Vulkan学习苦旅04:创建设备(逻辑设备VkDevice)
    设备是对物理设备的一种抽象,使我们更加方便地使用它。更准确地说,应该称其为“逻辑设备”,但由于逻辑设备在Vulkan中极为常用,后面几乎所有的API都需要它作为第一个参数,因此在Vulkan中直接简称为设备。1.实例、物理设备与设备的关系在之前的几篇文章中,我们依次创建了实例和物理设......
  • A+B问题1+105种解法
    个人写法应该没有更短的了吧,挑战世界最短a,b;main(){scanf("%d%d",&a,&b);printf("%d",a+b);}要测试点这里以下转自\(AcWing\)=================原作者为Conan15。要测试点温馨提示:此题解适合人群为算法学习者,不那么适合语法基础课还没学完的学生为了不误导初学者先......
  • Quant-Ch05 量化择时策略
    Ch5量化择时策略 量化择时策略,就是采用数量化分析方法,利用单个或多个技术指标的组合,来对交易标的股票或股票指数进行低买高卖的操作,期望获得超越简单买入持有策略的收益风险表现。 量化择时策略的核心是技术分析,更准确地来说,是客观型技术分析。客观型技术分析,是指其分析过程中所......
  • 洛谷题单指南-排序-P1059 [NOIP2006 普及组] 明明的随机数
    原题链接:https://www.luogu.com.cn/problem/P1059题意解读:此题主要做两件事:排序+去重,用计数排序即可解决,直接给出代码。100分代码:#include<bits/stdc++.h>usingnamespacestd;constintN=1005;inta[N];intn;intmain(){cin>>n;intx;intcnt......
  • 英语一课一练一年级扩展阅读05The World of Alice-爱丽丝的世界
    PDF格式公众号回复关键字:YYYKYLY05记忆树1Hello!MynameisAlice.翻译你好,我的名字是爱丽丝。简化记忆名字2Thisismyface.It'sround.翻译这是我的脸,它是圆的。简化记忆圆脸3Lookatmyeyes.They'rebig.翻译看我的眼睛,它们很大。简化记忆眼睛4......