首页 > 其他分享 >Vulkan学习苦旅06:创建渲染通道(VkRenderPass)

Vulkan学习苦旅06:创建渲染通道(VkRenderPass)

时间:2024-02-02 13:45:19浏览次数:23  
标签:06 渲染 VK nullptr 苦旅 附件 VkRenderPass uint32 通道

对于一个复杂的图形应用程序,需要多个过程的配合,以生成图像的各个部分。通常,各个过程间存在着依赖关系,例如某个过程生成的图像(输出)被另一个过程使用(作为此过程的输入)。在Vulkan中,每个过程被称为一个子通道(subpass), 所有的子通道构成了一个渲染通道(VkRenderPass).

在这篇博客中,我们将定义函数createRenderPass用于创建一个渲染通道,且渲染通道中仅包含一个子通道。

1. 子通道的信息

在创建渲染通道前,我们需要每个子通道的信息,子通道的信息通过结构体VkSubpassDescription描述,此结构体的定义如下:

typedef struct VkSubpassDescription {
    VkSubpassDescriptionFlags flags;
    VkPipelineBindPoint pipelineBindPoint;
    uint32_t inputAttachmentCount;
    const VkAttachmentReference* pInputAttachments;
    uint32_t colorAttachmentCount;
    const VkAttachmentReference* pColorAttachments;
    const VkAttachmentReference* pResolveAttachments;
    const VkAttachmentReference* pDepthStencilAttachment;
    uint32_t preserveAttachmentCount;
    const uint32_t* pPreserveAttachments;
} VkSubpassDescription;

什么是附件(Attachment)?

在上面的结构体中,多次出现attachment一词,这个单词的意思是附件。什么是附件呢?顾名思义,附件就是附加的资源。在渲染一副图像时,我们还需要一些额外的资源,例如深度缓冲、模板缓冲等等。(这些之后还会详细介绍)

打个可能不是太恰当的比方:在考试时,我们的任务是在试卷上(渲染的目标)作答,而打草稿用的草稿纸就起到了附件的作用。

此结构体中,

pipelineBindPoint: 目前Vulkan仅支持图形渲染通道,因此只有一个取值VK_PIPELINE_BIND_POINT_GRAPHICS;

inputAttachmentCount, pInputAttachments: 输入附件,子通道从输入附件中读取数据;

colorAttachmentCount, pColorAttachments: 颜色附件,子通道向此附件写入输出;

pResolveAttachments: 解析附件,暂时不用管它;

pDepthStencilAttachment: 深度-模板缓冲。在图形学API中,深度缓冲与模板缓冲紧密联系在一起,一个子通道只需要一个深度缓冲和一个模板缓冲(所以此成员名没有使用复数形式,当你知道它们是做什么的,自然就明白了为什么只需要一个即可)。画一个三角形不需要设置它们,因此暂时不用管它;

preserveAttachmentCount, pPreserveAttachments: 暂时不用管它们。

目前,我们只在此结构体的颜色附件填充有效的信息(即colorAttachmentCountpColorAttachments),但在此之前,还需要搞清楚pColorAttachments指向的结构体VkAttachmentReferences是什么,这个结构体的定义如下:

typedef struct VkAttachmentReference {
    uint32_t attachment;
    VkImageLayout layout;
} VkAttachmentReference;

其中,attachment是附件的索引,是什么的索引呢?这个我们待会再说,暂时将其甚至为0; layout表示子通道中附件的图像布局,这里暂时设置为VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL. 即:

VkAttachmentReference colorAttachmentReference{
	0,  // .attachment, 上述attachment description的索引
	VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,  // .layout
};

之后,我们就可以填充描述子通道的结构体:

VkSubpassDescription subpassDescription{
	0,  // .flags
	VK_PIPELINE_BIND_POINT_GRAPHICS,  // .pipelineBindPoint
	0,  // .inputAttachmentCount
	nullptr,  // .pInputAttachments
	1,  // .colorAttachmentCount
	&colorAttachmentReference,  // .pColorAttachments
	nullptr,  // .pResolveAttachments
	nullptr,  // .pDepthStencilAttachment
	0,  // .preserveAttachmentCount
	nullptr,  // .pPreserveAttachments
};

可以看到,除了一个颜色附件外,基本上没有填入什么有效的信息。

2. 创建渲染通道

创建渲染通道的结构体为VkRenderPassCreateInfo, 其定义如下:

typedef struct VkRenderPassCreateInfo {
    VkStructureType sType;
    const void* pNext;
    VkRenderPassCreateFlags flags;
    uint32_t attachmentCount;
    const VkAttachmentDescription* pAttachments;
    uint32_t subpassCount;
    const VkSubpassDescription* pSubpasses;
    uint32_t dependencyCount;
    const VkSubpassDependency* pDependencies;
} VkRenderPassCreateInfo;

可以看到,此结构体的关键在于传递三个数组:

attachmentCount, pAttachments: 数组VkAttachmentDescription[];

subpassCount, pSubpasses: 数组VkSubpassDescription[]. 此数组正是上一节我们定义的子通道信息数组;

dependencyCount, pDependencies: 数组VkSubpassDependency[].

2.1. 对附件的操作

VkAttachmentDescription[]数组描述了对附件的操作,上一节中提到的索引正是用于索引这个数组。由于我们只定义了一个附件(颜色附件),所以只要设置如何操作此附件即可。结构体VkAttachmentDescription的定义如下:

typedef struct VkAttachmentDescription {
    VkAttachmentDescriptionFlags flags;
    VkFormat format;
    VkSampleCountFlagBits samples;
    VkAttachmentLoadOp loadOp;
    VkAttachmentStoreOp storeOp;
    VkAttachmentLoadOp stencilLoadOp;
    VkAttachmentStoreOp stencilStoreOp;
    VkImageLayout initialLayout;
    VkImageLayout finalLayout;
} VkAttachmentDescription;

其中,

format:用于指定附件的格式,之前在创建交换链时我们指定了格式,稍后我们将修改代码,将格式作为VulkanApp类的一个成员;

samples: 如果不使用多重采样,就可以设置为VK_SAMPLE_COUNT_1_BIT;

loadOp, stencilLoadOp: 在渲染通道开始时如何处理附件,如果附件是深度-模板缓冲,stencilLoadOp会指定处理模板缓冲的方式。可选的值有:VK_ATTACHMENT_LOAD_OP_LOAD(附件中已经保存了有效数据,继续对其操作)、VK_ATTACHMENT_LOAD_OP_CLEAR(清空附件的内容)、VK_ATTACHMENT_LOAD_OP_DONT_CARE(不关心附件里有什么);

storeOp, stencilStoreOp: 在渲染通道结束时如何处理附件,如果附件是深度-模板缓冲,stencilStoreOp会指定处理模板缓冲的方式。可选的值有:VK_ATTACHMENT_STORE_OP_STORE(将附件写入内存)、VK_ATTACHMENT_STORE_OP_DONT_CARE(不需要附件的内容);

initialLayout, finalLayout: 渲染通道开始与结束时图像的布局,暂时不用管它们。

首先,在VulkanApp类中添加成员表示图像的格式:

VkFormat mSwapChainImageFormat;  // 交换链的图像格式

之后,将交换链中选择的图像格式保存到上述成员中:

/*  获取物理设备对图像格式的支持  */
uint32_t formatCount = 0;
vkGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDevice, mSurface, &formatCount, nullptr);
vector<VkSurfaceFormatKHR> surfaceFormats(formatCount);
vkGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDevice, mSurface, &formatCount, surfaceFormats.data());
mSwapChainImageFormat = surfaceFormats[0].format;  // 函数createSwapChain中,选择的图像格式

这样,我们就可以填充结构体VkAttachmentDescription

VkAttachmentDescription colorAttachmentDescription{
	0,  // .flags
	mSwapChainImageFormat,  // .format
	VK_SAMPLE_COUNT_1_BIT,  // .samples
	VK_ATTACHMENT_LOAD_OP_CLEAR,  // .loadOp
	VK_ATTACHMENT_STORE_OP_STORE,  // .storeOp
	VK_ATTACHMENT_LOAD_OP_DONT_CARE,  // .stencilLoadOp
	VK_ATTACHMENT_STORE_OP_DONT_CARE,  // .stencilStoreOp
	VK_IMAGE_LAYOUT_UNDEFINED,  // .initialLayout
	VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,  // .finalLayout 
};

2.2. 子通道的依赖关系

子通道间的依赖关系通过结构体VkSubpassDependency描述:

typedef struct VkSubpassDependency {
    uint32_t srcSubpass;
    uint32_t dstSubpass;
    VkPipelineStageFlags srcStageMask;
    VkPipelineStageFlags dstStageMask;
    VkAccessFlags srcAccessMask;
    VkAccessFlags dstAccessMask;
    VkDependencyFlags dependencyFlags;
} VkSubpassDependency;

其中,

srcSubpass, dstSubpass:渲染通道中,子通道数组的索引;

srcStageMask, dstStageMask: 分别指定源子通道与目标子通道的哪些管线将使用数据;

srcAccessMask, dstAccessMask: 分别指定源子通道与目标子通道如何访问数据。

按照如下方式填充结构体:

VkSubpassDependency subpassDependency{
	VK_SUBPASS_EXTERNAL,  // .srcSubpass
	0,  // .dstSubpass
	VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,  // .srcStageMask
	VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,  // .dstStageMask
	0,  // .srcAccessMask
	VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,  // .dstAccessMask
	0,  // dependencyFlags
};

2.3. 创建渲染通道

现在,就可以填充结构体VkRenderPassCreateInfo了:

VkRenderPassCreateInfo renderPassCreateInfo{
	VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
	nullptr,
	0,
	1,  // .attachmentCount
	&colorAttachmentDescription,  // .pAttachments
	1,  // .subpassCount
	&subpassDescription,  // .pSubpasses
	1,  // dependencyCount
	&subpassDependency,  // .pDependencies
};

在VulkanApp类中定义成员mRenderPass表示渲染通道:

VkRenderPass mRenderPass;  // 渲染管线

最后,使用函数vkCreateRenderPass创建渲染通道:

if_fail(
	vkCreateRenderPass(mDevice, &renderPassCreateInfo, nullptr, &mRenderPass),
	"failed to create render pass!"
);
Log("create render pass successfully");

不要忘记在析构函数中销毁渲染通道:

vkDestroyRenderPass(mDevice, mRenderPass, nullptr);

3. 到目前为止的完整代码

这一节中,出现了许多全新的概念,有的可能还没搞清楚,有的目前并没有那么重要。在下一篇文章中,我们将介绍整个图形管线(VkPipeline)的创建,随着更加深入地学习,对于这些概念的认识也会更加清晰。

到目前为止的完整代码
#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();

		createRenderPass();
	}

	~VulkanApp() {
		vkDestroyRenderPass(mDevice, mRenderPass, nullptr);

		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;  // 交换链的图像视图
	VkFormat mSwapChainImageFormat;  // 交换链的图像格式

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

	VkRenderPass mRenderPass;  // 渲染管线

	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());
		mSwapChainImageFormat = surfaceFormats[0].format;

		/*  填充交换链结构体  */
		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!"
			);
		}
	}

	void createRenderPass() {
		VkAttachmentReference colorAttachmentReference{
			0,  // .attachment, 上述attachment description的索引
			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,  // .layout
		};

		VkSubpassDescription subpassDescription{
			0,  // .flags
			VK_PIPELINE_BIND_POINT_GRAPHICS,  // .pipelineBindPoint
			0,  // .inputAttachmentCount
			nullptr,  // .pInputAttachments
			1,  // .colorAttachmentCount
			&colorAttachmentReference,  // .pColorAttachments
			nullptr,  // .pResolveAttachments
			nullptr,  // .pDepthStencilAttachment
			0,  // .preserveAttachmentCount
			nullptr,  // .pPreserveAttachments
		};

		VkAttachmentDescription colorAttachmentDescription{
			0,  // .flags
			mSwapChainImageFormat,  // .format
			VK_SAMPLE_COUNT_1_BIT,  // .samples
			VK_ATTACHMENT_LOAD_OP_CLEAR,  // .loadOp
			VK_ATTACHMENT_STORE_OP_STORE,  // .storeOp
			VK_ATTACHMENT_LOAD_OP_DONT_CARE,  // .stencilLoadOp
			VK_ATTACHMENT_STORE_OP_DONT_CARE,  // .stencilStoreOp
			VK_IMAGE_LAYOUT_UNDEFINED,  // .initialLayout
			VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,  // .finalLayout 
		};

		VkSubpassDependency subpassDependency{
			VK_SUBPASS_EXTERNAL,  // .srcSubpass
			0,  // .dstSubpass
			VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,  // .srcStageMask
			VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,  // .dstStageMask
			0,  // .srcAccessMask
			VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,  // .dstAccessMask
			0,  // dependencyFlags
		};

		VkRenderPassCreateInfo renderPassCreateInfo{
			VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
			nullptr,
			0,
			1,  // .attachmentCount
			&colorAttachmentDescription,  // .pAttachments
			1,  // .subpassCount
			&subpassDescription,  // .pSubpasses
			1,  // dependencyCount
			&subpassDependency,  // .pDependencies
		};

		if_fail(
			vkCreateRenderPass(mDevice, &renderPassCreateInfo, nullptr, &mRenderPass),
			"failed to create render pass!"
		);
		Log("create render pass successfully");
	}
};


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);
	}
}

 

标签:06,渲染,VK,nullptr,苦旅,附件,VkRenderPass,uint32,通道
From: https://www.cnblogs.com/overxus/p/18002865/vulkan06

相关文章

  • rtthread系统用 RT1062 移植网卡功能(LAN8720A)
    RT-Thread:v4.0.2(master)SOC:i.MXRT1062Board:自研控制板背景描述1.有个控制板网口一直没人调试。2.NXPRT1xxx系列ENET_MAC调试心得。3.EthernetMAC控制器调试《终极指南》。 开整RT1062移植网卡功能(LAN8720A)1、i.MXRT系列外设驱动添加指南2、LAN8720A芯片的......
  • Java-06函数
    tip:[start]理解函数,最重要的是理解代码的执行顺序。——闫学灿tip:[end]函数基础一个典型的函数定义包括以下部分:修饰符、返回类型、函数名字、由0个或多个形参组成的形参列表以及函数体。编写函数我们来编写一个求阶乘的程序。程序如下所示:publicclassMain{ //函......
  • Vulkan学习苦旅05:马不停蹄地渲染(创建交换链VkSwapchainKHR)
    通俗地说,渲染图像就是为图像的每个像素选择合适的颜色。例如,如果图像的分辨率为1920x1080,表示图像中有1920x1080个像素,渲染的过程,就是为每个位置的像素计算出合适的颜色。具体来说,假设每种颜色具有RGBA四个通道,且每个通道用1个字节表示(可以表示255种不同的情况),那么图像应当占据192......
  • ABC306F Merge Sets
    原题链接分析观察要求的式子:\(\sum_{1\leqi\ltj\leqN}f(S_i,S_j)\),发现可以拆成每一个集合\(S_i\)的贡献的和。那么我们考虑每一个集合\(S_i\)的贡献。显然,对于每一个\(S_i\),其贡献就是\(\sum_{i\ltj\leqN}f(S_i,S_j)\),也就是它与其后每一个集合\(S_j\)的......
  • Quant-Ch06 量化调仓策略
    Ch6量化调仓策略6.1衡量投资组合的收益率 投资组合的收益率是指投资组合在一定时间内的总体收益率。投资组合的收益率可以通过加权平均每个资产的收益率来计算。具体地,假设投资组合中有n个资产,每个资产的收益率为r1,r2,...,rn,每个资产的权重为w1,w2,...,wn,则投资组合的收......
  • CPU性能跑分工具 SPEC2006
    一.工具介绍 前言   SPEC2006benchmark是SPEC新一代的行业标准化的CPU测试基准套件。重点测试系统的处理器,内存子系统和编译器。这个基准测试套件包括的SPECint基准和SPECfp基准。主要依赖于gcc,g++,gfortran并与其 版本息息相关。其中SPECint2006基准包含12个不同......
  • kali学习笔记-06-Webshell文件上传漏洞使用
    kali学习笔记-06-Webshell文件上传漏洞使用KaliLinux网络安防一、使用weevely制作一句话木马脚本在KaliLinux的终端中输入命令weevely,可以从错误提示中看到基本的使用方法。二、配置OWASP靶机三、参考文献WebShell文件上传漏洞.3......
  • 洛谷题单指南-暴力枚举-P1706 全排列问题
    原题链接:https://www.luogu.com.cn/problem/P1706题意解读:n个数全排列问题,本质上,给定n个空位,枚举每个能填入空位的数,依次填入,每个数只能填一次。解题思路:如何填入n个数呢,可以借助于递归,流程如下:dfs(填入第k个数){如果已经填满n个数输出结果返回......
  • 代码随想录算法训练营第三天 |203.移除链表元素 , 707.设计链表,206.反转链表
    206.反转链表 已解答简单 相关标签相关企业 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。 示例1:输入:head=[1,2,3,4,5]输出:[5,4,3,2,1]示例2:输入:head=[1,2]输出:[2,1]示例3:输入:head=[]输出:[] 提示:链......
  • Vulkan学习苦旅04:创建设备(逻辑设备VkDevice)
    设备是对物理设备的一种抽象,使我们更加方便地使用它。更准确地说,应该称其为“逻辑设备”,但由于逻辑设备在Vulkan中极为常用,后面几乎所有的API都需要它作为第一个参数,因此在Vulkan中直接简称为设备。1.实例、物理设备与设备的关系在之前的几篇文章中,我们依次创建了实例和物理设......