Link

让我们从添加我们需要添加到类中的新成员开始。

vk_engine.h

class VulkanEngine {
public:

	//--- other code ---
	VkRenderPass _renderPass;

	std::vector<VkFramebuffer> _framebuffers;
private:

	//--- other code ---
	void init_default_renderpass();

	void init_framebuffers();
}

我们添加一个到我们将要创建的 RenderPass 的句柄,以及一个帧缓冲区数组。这个数组将等同于 _swapchainImages 和 _swapchainImageViews。

在 cpp 方面,我们将其添加到 init 函数中,在其他函数之后。 vk_engine.cpp

void VulkanEngine::init()
{
	// SDL STUFF ----


	init_vulkan();

	init_swapchain();

	init_commands();

	init_default_renderpass();

	init_framebuffers();
}

渲染通道

我们需要在帧缓冲区之前创建渲染通道,因为帧缓冲区是为特定的渲染通道创建的。

让我们开始填充 init_default_renderpass() 函数

void VulkanEngine::init_default_renderpass()
{
	// the renderpass will use this color attachment.
	VkAttachmentDescription color_attachment = {};
	//the attachment will have the format needed by the swapchain
	color_attachment.format = _swapchainImageFormat;
	//1 sample, we won't be doing MSAA
	color_attachment.samples = VK_SAMPLE_COUNT_1_BIT;
	// we Clear when this attachment is loaded
	color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
	// we keep the attachment stored when the renderpass ends
	color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
	//we don't care about stencil
	color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
	color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;

	//we don't know or care about the starting layout of the attachment
	color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;

	//after the renderpass ends, the image has to be on a layout ready for display
	color_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
}

我们开始编写的第一件事是颜色附件。这是我们将使用渲染命令写入的图像的描述。

图像将使用我们从交换链获得的格式(因此它是兼容的),我们将在渲染通道开始时清除它。当渲染通道结束时,我们将存储图像以便稍后可以读取它。

在渲染通道之前,图像布局将是未定义的,这意味着“我们不在乎”。在渲染通道结束后,布局将准备好通过交换链显示。

现在我们的主要图像目标已定义,我们需要添加一个子通道,它将渲染到其中。这在定义附件后立即进行

	VkAttachmentReference color_attachment_ref = {};
	//attachment number will index into the pAttachments array in the parent renderpass itself
	color_attachment_ref.attachment = 0;
	color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

	//we are going to create 1 subpass, which is the minimum you can do
	VkSubpassDescription subpass = {};
	subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
	subpass.colorAttachmentCount = 1;
	subpass.pColorAttachments = &color_attachment_ref;

我们将描述 1 个子通道,它将使用上面的颜色附件。上面的颜色附件也将在布局 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL 中在此子通道期间使用。子通道 vkAttachmentReference 将在创建渲染通道时指向附件数组。

图像生命周期将如下所示

未定义 -> 渲染通道开始 -> 子通道 0 开始(转换为附件最佳) -> 子通道 0 渲染 -> 子通道 0 结束 -> 渲染通道结束(转换为呈现源)

当使用渲染通道时,Vulkan 驱动程序将为我们执行布局转换。如果我们不使用渲染通道(从计算着色器绘制),我们将需要显式地执行相同的转换。

现在主要附件和子通道已完成,我们可以创建渲染通道

	VkRenderPassCreateInfo render_pass_info = {};
	render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;

	//connect the color attachment to the info
	render_pass_info.attachmentCount = 1;
	render_pass_info.pAttachments = &color_attachment;
	//connect the subpass to the info
	render_pass_info.subpassCount = 1;
	render_pass_info.pSubpasses = &subpass;


	VK_CHECK(vkCreateRenderPass(_device, &render_pass_info, nullptr, &_renderPass));

我们将只有一个附件,这将是上面定义的 color_attachment,用于我们的主要颜色目标。然后我们还将子通道连接到它。

就这样,我们现在创建了非常基本的渲染通道。当我们添加需要定义更多附件的深度测试时,我们将稍后回到这段代码。

帧缓冲区

一旦渲染通道被创建,我们就可以使用它来创建帧缓冲区。帧缓冲区是从给定的渲染通道创建的,它们充当渲染通道的附件和它们应该渲染到的真实图像之间的链接。

void VulkanEngine::init_framebuffers()
{
	//create the framebuffers for the swapchain images. This will connect the render-pass to the images for rendering
	VkFramebufferCreateInfo fb_info = {};
	fb_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
	fb_info.pNext = nullptr;

	fb_info.renderPass = _renderPass;
	fb_info.attachmentCount = 1;
	fb_info.width = _windowExtent.width;
	fb_info.height = _windowExtent.height;
	fb_info.layers = 1;

	//grab how many images we have in the swapchain
	const uint32_t swapchain_imagecount = _swapchainImages.size();
	_framebuffers = std::vector<VkFramebuffer>(swapchain_imagecount);

	//create framebuffers for each of the swapchain image views
	for (int i = 0; i < swapchain_imagecount; i++) {

		fb_info.pAttachments = &_swapchainImageViews[i];
		VK_CHECK(vkCreateFramebuffer(_device, &fb_info, nullptr, &_framebuffers[i]));
	}
}

我们用通用参数填充 VkFrameBufferCreateInfo,然后我们为交换链的每个图像创建一个帧缓冲区。

渲染时,交换链将为我们提供要渲染到的图像的索引,因此我们将使用相同索引的帧缓冲区。

完成渲染通道设置后,我们可以继续进行渲染循环本身。

清理

像往常一样,我们将新创建的对象添加到 cleanup() 函数中。由于帧缓冲区是从图像创建的,因此它们需要与图像一起删除。我们还需要删除渲染通道。

VulkanEngine::Cleanup()

		vkDestroySwapchainKHR(_device, _swapchain, nullptr);

		//destroy the main renderpass
		vkDestroyRenderPass(_device, _renderPass, nullptr);

		//destroy swapchain resources
		for (int i = 0; i < _framebuffers.size(); i++) {
			vkDestroyFramebuffer(_device, _framebuffers[i], nullptr);

			vkDestroyImageView(_device, _swapchainImageViews[i], nullptr);
		}

下一步: 主循环