Vulkan学习(五): Command buffers & Rendering & Presentation

Framebuffers

  在render pass创建期间指定attachments ,并绑定到VkFramebuffer对象。 framebuffer引用所有VkImageView 对象。 使用检索到swap chain返回的需展示的图像作为图像attachments。 这意味着我们必须为swap chain中的所有图像创建一个framebuffer,并在绘制时使用检索到的图像对应的framebuffer。

	//调整容器大小以容纳所有帧缓冲区
	swapChainFramebuffers.resize(swapChainImageViews.size());

	//create framebuffers
	for (size_t i = 0; i < swapChainImageViews.size(); i++) {

		VkImageView attachments[] = { swapChainImageViews[i] };

		VkFramebufferCreateInfo framebufferInfo = {};
		framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
		framebufferInfo.renderPass = renderPass;
		framebufferInfo.attachmentCount = 1;
		framebufferInfo.pAttachments = attachments;
		framebufferInfo.width = swapChainExtent.width;
		framebufferInfo.height = swapChainExtent.height;
		framebufferInfo.layers = 1;

		if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) {
			throw std::runtime_error("failed to create framebuffer!");
		}
	}

Command buffers

  Vulkan 中的命令,如绘图操作和内存传输,不是直接使用函数调用执行。 而是在命令缓冲区对象中记录要执行的所有操作。 这样做的好处是,所有设置绘图命令的繁重工作都可以提前在子线程中(多线程)完成。 之后,你只需要告诉 Vulkan 执行主循环中的命令。

Command pools

  在创建command buffers前,先创建Command pools, Command pools管理用于存储buffers的内存,并从中分配command buffers。

    QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice);	
    VkCommandPoolCreateInfo poolInfo = {};
    poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
    poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
    //There are two possible flags for command pools
    //VK_COMMAND_POOL_CREATE_TRANSIENT_BIT 提示CommandPool经常用新命令重新记录(可能会改变内存分配行为)
    //VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT 允许单独重新记录命CommandPool,否则都必须一起重置
    poolInfo.flags = 0; //不使用flag
    if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) {
		throw std::runtime_error("failed to create command pool!");
	}

Command buffer allocation

  我们必须为swap chain 中的每个图像记录一个command buffe。

	VkCommandBufferAllocateInfo allocInfo = {};
	allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
	allocInfo.commandPool = commandPool;
	//指定primary 或 secondary command buffers.
	//VK_COMMAND_BUFFER_LEVEL_PRIMARY 可以提交到队列执行,但不能从其他命令缓冲区调用。
	//VK_COMMAND_BUFFER_LEVEL_SECONDARY 不能直接提交,但可以从主命令缓冲区调用
	allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
	allocInfo.commandBufferCount = (uint32_t)commandBuffers.size();

	if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) {
	 	throw std::runtime_error("failed to allocate command buffers!");
	}

Starting command buffer recording

	//recording a command buffer
	for (size_t i = 0; i < commandBuffers.size(); i++) {
		VkCommandBufferBeginInfo beginInfo = {};
		beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
		//指定指定我们将如何使用command buffers.
		//VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT 在执行一次后立即rerecord。
		//VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT 这是一个辅助command buffers, 将在单个渲染通道中使用
		//VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT command buffer 可以在已经挂起执行时重新提交。
		beginInfo.flags = 0; // Optional
		//仅与secondary command buffers 相关。 它指定从primarycommand buffers 继承哪些状态。
		beginInfo.pInheritanceInfo = nullptr; // Optional
		
		if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) {
			throw std::runtime_error("failed to begin recording command buffer!");
		}
	}

Starting a render pass

	//Starting a render pass
	VkRenderPassBeginInfo renderPassInfo = {};
	//the render pass itself and the attachments to bind
	renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
	renderPassInfo.renderPass = renderPass;
	renderPassInfo.framebuffer = swapChainFramebuffers[i];
	//the size of the render area
	renderPassInfo.renderArea.offset = { 0, 0 }; //It should match the size of the attachments for best performance
	renderPassInfo.renderArea.extent = swapChainExtent;

	VkClearValue clearColor = { 0.0f, 0.0f, 0.0f, 1.0f };
	renderPassInfo.clearValueCount = 1;
	renderPassInfo.pClearValues = &clearColor;
	//最后一个参数:how the drawing commands within the render pass will be provided
	//VK_SUBPASS_CONTENTS_INLINE render pass commands 将嵌入 primary command buffer, 并且不会执行 secondary command buffers
	//VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERSrender pass commands 将从 secondary command buffers 执行
	vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);

Basic drawing commands & Finishing up

	//[6]Basic drawing commands
	//[6]第二个参数指定管线对象是图形管线还是计算管线。
	vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
	//[6]vertexCount 即使没有顶点缓冲区,从技术上讲,仍然有3个顶点要绘制
	//[6]instanceCount 用于实例化渲染,如果不进行实例化使用1
	//[6]firstVertex 顶点缓冲区的偏移量,定义gl_VertexIndex的最小值
	//[6]firstInstance 用作实例渲染的偏移量,定义gl_InstanceIndex的最小值
	vkCmdDraw(commandBuffers[i], 3, 1, 0, 0);
	
	//[7]The render pass can now be ended
	if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) {
		throw std::runtime_error("failed to record command buffer!");
	}

Rendering and presentation

Synchronization

drawFrame 函数将执行以下操作:

  • 从swap chain中获取图像
  • 使用在 framebuffer 中的 image 作为 attachment,执行 command buffer
  • 将图像返回到swap chain进行展示

  drawFrame 是异步执行的。 而且未定义执行顺序。但是每一个操作都依赖于前一个完成。 有两种同步swap chain事件的方法:fences 和 semaphores。 它们都是可用于协调操作的对象,方法是让一个操作信号和另一个操作等待fences 或semaphores从无信号状态变为有信号状态。 不同之处在于,可以使用如 vkWaitForFences 之类的调用从程序中访问fences 的状态,而semaphores则不能。fences 主要用于同步应用程序本身与渲染操作,而semaphores用于同步命令队列内或跨命令队列的操作。 我们希望同步绘制命令和显示的队列操作,emaphores最适合。

Semaphores

	//Vulkan API 会扩展 flags 和 pNext 参数。
	VkSemaphoreCreateInfo semaphoreInfo = {};
	semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
	if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphore) != VK_SUCCESS ||
		vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphore) != VK_SUCCESS) {
			throw std::runtime_error("failed to create semaphores!");
	}
	-------------------------------------
	vkDestroySemaphore(device, renderFinishedSemaphore, nullptr);
	vkDestroySemaphore(device, imageAvailableSemaphore, nullptr);

Acquiring an image from the swap chain

	uint32_t imageIndex;
	//第三个参数指定可获得图像的超时时间(以纳秒为单位)。
	//接下来的两个参数指定了同步对象,这些对象将在引擎使用完图像时发出信号。 可以指定semaphore、fence或都指定
	//用于输出可用swap chain中图像的索引。
	vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphore, VK_NULL_HANDLE, &imageIndex);

Submitting the command buffer

	VkSubmitInfo submitInfo = {};
	submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
		
	VkSemaphore waitSemaphores[] = { imageAvailableSemaphore };
	VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
	//前三个参数指定在执行开始之前等待哪些Semaphore,以及在管线的哪个阶段等待。
	submitInfo.waitSemaphoreCount = 1;
	submitInfo.pWaitSemaphores = waitSemaphores;
	submitInfo.pWaitDstStageMask = waitStages;

	//指定实际提交执行的commandBuffer。
	submitInfo.commandBufferCount = 1;
	submitInfo.pCommandBuffers = &commandBuffers[imageIndex];

	//指定在commandBuffer完成执行后发送信号的Semaphore。
	VkSemaphore signalSemaphores[] = { renderFinishedSemaphore };
	submitInfo.signalSemaphoreCount = 1;	
	submitInfo.pSignalSemaphores = signalSemaphores;

	//最后一个参数引用了可选的 fenc, 当 command buffer 执行完成时,它将发出信号。
	//我们使用信号量进行同步,所以我们传递VK_NULL_HANDLE。
	if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE) != VK_SUCCESS) {
		throw std::runtime_error("failed to submit draw command buffer!");
	}

Subpass dependencies

  有两个内置依赖项负责在渲染过程开始时和渲染过程结束时的过渡,但前者不会在正确的时间发生。它假设转换发生在管线的开始,但是我们还没有获得图像!有两种方法来处理这个问题。我们可以将imageAvailableSemaphore的waitStages更改为VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT ,以确保渲染过程在图像可用之前不会开始,或者我们可以使渲染过程等待VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT 阶段。这里使用第二个选项,因为这是查看子通道依赖项及其工作方式的最佳方式。

	//前两个字段指定依赖项和依赖subpass的索引
	VkSubpassDependency dependency = {};
	//根据在渲染过程之前或之后引用subpass, 决定是否在srcsubass或dstsubass中指定。
	dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
	//The index 0 refers to our subpass
	//The dstSubpass must always be higher than srcSubpass,unless one of the subpasses is VK_SUBPASS_EXTERNAL
	dependency.dstSubpass = 0;

	//指定操作发生的阶段
	dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
	dependency.srcAccessMask = 0;
	//wait on this are in the color attachment stage
	//involve the reading and writing of the color attachment
	//These settings will prevent the transition from happening until it’s actually necessary
	dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
	dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;

Presentation

	VkPresentInfoKHR presentInfo = {};
	presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
	//在呈现(Present)之前等待哪些信号量
	presentInfo.waitSemaphoreCount = 1;
	presentInfo.pWaitSemaphores = signalSemaphores;

	VkSwapchainKHR swapChains[] = { swapChain };
	presentInfo.swapchainCount = 1;
	//为每个swapChain指定呈现的图像和图像索引
	presentInfo.pSwapchains = swapChains;
	presentInfo.pImageIndices = &imageIndex;
	//check for every individual swap chain if presentation was successful. (array of VkResult)
	presentInfo.pResults = nullptr; // Optional

	//submits the request to present an image to the swap chain.
	vkQueuePresentKHR(presentQueue, &presentInfo);
	-------------------------
	//同步执行方法.
	vkDeviceWaitIdle(device);

Frames in flight

  虽然现在已经设置了所需的对象以便于同时处理多个帧,但实际上我们仍然没有阻止提交超过MAX_FRAMES_IN_FLIGHT的帧。现在只有GPU-GPU同步,没有CPU-GPU同步来跟踪工作进展。程序可能正在使用 frame #0对象,而 frame #0仍在运行中!为了执行CPU-GPU同步,Vulkan提供了第二种称为fences的msynchronization原语。fence与semaphores类似,因为它们可以发出信号并等待

	代码为[13]

Code

#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>

#include <vulkan/vulkan.h>

#include <iostream>
#include <GLFW/glfw3.h>

#include <vulkan/vulkan.h>

#include <iostream>  //[1]
#include <stdexcept> //[1]异常处理函数
#include <functional>//[1]提供 EXIT_SUCCESS and EXIT_FAILURE 宏指令
#include <cstdlib>
#include <vector>
#include <map>
#include <optional>
#include <set>
#include <cstdint> // Necessary for UINT32_MAX
#include <fstream>
const int WIDTH = 800;
const int HEIGHT = 600;


struct QueueFamilyIndices {
	std::optional<uint32_t> graphicsFamily;
	std::optional<uint32_t> presentFamily;
	//[8]为了方便起见,我们还将向结构本身添加一个泛型检查
	bool isComplete() {
		return graphicsFamily.has_value() && presentFamily.has_value();
	}
};
//[12]
const int MAX_FRAMES_IN_FLIGHT = 2;

class HelloTriangleApplication
{

public:
	void run()
	{
		initVulkan(); //[1]初始化Vulakn相关
		//[8]
		mainLoop();
		cleanup();    //[1]释放资源
	}
private:

	void initVulkan()
	{
		//...
		createRenderPass();

		createGraphicsPipeline();
		//[1]
		createFramebuffers();
		//[2]
		createCommandPool();
		//[3]
		createCommandBuffers();
		//[9]
		createSemaphores();
	}

	void cleanup()
	{
		//[9]
		//vkDestroySemaphore(device, renderFinishedSemaphore, nullptr);
		//vkDestroySemaphore(device, imageAvailableSemaphore, nullptr);
		// or
		//[12]
		for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
			vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr);
			vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr);
			//[13]
			vkDestroyFence(device, inFlightFences[i], nullptr);
		}
		//[2]
		vkDestroyCommandPool(device, commandPool, nullptr);
		//[1]
		for (auto framebuffer : swapChainFramebuffers) {
			vkDestroyFramebuffer(device, framebuffer, nullptr);
		}
		vkDestroyPipeline(device, graphicsPipeline, nullptr);
		//...
	}

	void createGraphicsPipeline() {

	}
	QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) {
		QueueFamilyIndices indices;
		
		return indices;
	}

	void createFramebuffers()
	{
		//[1]调整容器大小以容纳所有帧缓冲区
		swapChainFramebuffers.resize(swapChainImageViews.size());

		//[1] create framebuffers
		for (size_t i = 0; i < swapChainImageViews.size(); i++) {

			VkImageView attachments[] = { swapChainImageViews[i] };

			VkFramebufferCreateInfo framebufferInfo = {};
			framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
			framebufferInfo.renderPass = renderPass;
			framebufferInfo.attachmentCount = 1;
			framebufferInfo.pAttachments = attachments;
			framebufferInfo.width = swapChainExtent.width;
			framebufferInfo.height = swapChainExtent.height;
			framebufferInfo.layers = 1;

			if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) {
				throw std::runtime_error("failed to create framebuffer!");
			}
		}

	}
	void createCommandPool() {
		 QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice);

		
		 VkCommandPoolCreateInfo poolInfo = {};
		 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
		 poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
		 //[2]There are two possible flags for command pools
		 //[2]VK_COMMAND_POOL_CREATE_TRANSIENT_BIT 提示CommandPool经常用新命令重新记录(可能会改变内存分配行为)
		 //[2]VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT 允许单独重新记录命CommandPool,否则都必须一起重置
		 poolInfo.flags = 0; //不使用flag

		 if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) {
			 throw std::runtime_error("failed to create command pool!");
		 }


	
	}

	void createCommandBuffers() {
		commandBuffers.resize(swapChainFramebuffers.size());

		VkCommandBufferAllocateInfo allocInfo = {};
		allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
		allocInfo.commandPool = commandPool;
		//[3]指定primary 或 secondary command buffers.
		//[3]VK_COMMAND_BUFFER_LEVEL_PRIMARY 可以提交到队列执行,但不能从其他命令缓冲区调用。
		//[3]VK_COMMAND_BUFFER_LEVEL_SECONDARY 不能直接提交,但可以从主命令缓冲区调用
		allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
		allocInfo.commandBufferCount = (uint32_t)commandBuffers.size();

		if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) {
			 throw std::runtime_error("failed to allocate command buffers!");
		}

		//[4]recording a command buffer
		for (size_t i = 0; i < commandBuffers.size(); i++) {

			VkCommandBufferBeginInfo beginInfo = {};
			beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
			//[4]指定指定我们将如何使用command buffers.
			//[4]VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT 在执行一次后立即rerecord。
			//[4]VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT 这是一个辅助command buffers, 将在单个渲染通道中使用
			//[4] VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT command buffer 可以在已经挂起执行时重新提交。
			beginInfo.flags = 0; // Optional
			//[4]仅与secondary command buffers 相关。 它指定从primarycommand buffers 继承哪些状态。
			beginInfo.pInheritanceInfo = nullptr; // Optional
			
			if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) {
				throw std::runtime_error("failed to begin recording command buffer!");
			}
			
			//[5]Starting a render pass
			VkRenderPassBeginInfo renderPassInfo = {};
			//[5]the render pass itself and the attachments to bind
			renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
			renderPassInfo.renderPass = renderPass;
			renderPassInfo.framebuffer = swapChainFramebuffers[i];
			//[5]the size of the render area
			renderPassInfo.renderArea.offset = { 0, 0 }; //It should match the size of the attachments for best performance
			renderPassInfo.renderArea.extent = swapChainExtent;

			VkClearValue clearColor = { 0.0f, 0.0f, 0.0f, 1.0f };
			renderPassInfo.clearValueCount = 1;
			renderPassInfo.pClearValues = &clearColor;
			//[5]最后一个参数:how the drawing commands within the render pass will be provided
			//[5]VK_SUBPASS_CONTENTS_INLINE render pass commands 将嵌入 primary command buffer, 并且不会执行 secondary command buffers
			//[5]VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERSrender pass commands 将从 secondary command buffers 执行
			vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
			
			//[6]Basic drawing commands
			//[6]第二个参数指定管线对象是图形管线还是计算管线。
			vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
			//[6]vertexCount 即使没有顶点缓冲区,从技术上讲,仍然有3个顶点要绘制
			//[6]instanceCount 用于实例化渲染,如果不进行实例化使用1
			//[6]firstVertex 顶点缓冲区的偏移量,定义gl_VertexIndex的最小值
			//[6]firstInstance 用作实例渲染的偏移量,定义gl_InstanceIndex的最小值
			vkCmdDraw(commandBuffers[i], 3, 1, 0, 0);
			
			//[7]The render pass can now be ended
			if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) {
				throw std::runtime_error("failed to record command buffer!");
			}


		}

		




	}

	void mainLoop() {
		while (!glfwWindowShouldClose(window))
		{
			glfwPollEvents();
			//[8]
			drawFrame();
		}

		//[12]可以用作执行同步的基本的方法.
		vkDeviceWaitIdle(device);
	}

	void createRenderPass() {
		VkAttachmentDescription colorAttachment = {};
		VkAttachmentReference colorAttachmentRef = {};
		VkSubpassDescription subpass = {};
		VkRenderPassCreateInfo renderPassInfo = {};

		//[10]前两个字段指定依赖项和依赖subpass的索引
		VkSubpassDependency dependency = {};
		//[10]根据在渲染过程之前或之后引用subpass, 决定是否在srcsubass或dstsubass中指定。
		dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
		//[10]The index 0 refers to our subpass
		//[10]The dstSubpass must always be higher than srcSubpass,unless one of the subpasses is VK_SUBPASS_EXTERNAL
		dependency.dstSubpass = 0;

		//[10]指定操作发生的阶段
		dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
		dependency.srcAccessMask = 0;
		//[10]wait on this are in the color attachment stage
		//[10]involve the reading and writing of the color attachment
		//[10]These settings will prevent the transition from happening until it’s actually necessary
		dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
		dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;

		renderPassInfo.dependencyCount = 1;
		renderPassInfo.pDependencies = &dependency;

		if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS)
		{

			throw std::runtime_error("failed to create render pass!");
		}


	}

	//[8]异步执行的
	//[8]acquire an image from the swap chain
	void drawFrame() {

		//[13]wait for the frame to be finished
		//[13]vkWaitForFences函数接受一个fences数组,并等待其中任何一个或所有fences在返回之前发出信号。
		//[13]这里传递的VK_TRUE表示要等待所有的fences,但是对于单个fences来说,这显然无关紧要。
		//[13]就像vkAcquireNextImageKHR一样,这个函数也需要超时。与信号量不同,我们需要通过vkresetfines调用将fence重置为unsignaled状态。
		//vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);
		//or	

		uint32_t imageIndex;
		//[8]第三个参数指定可获得图像的超时时间(以纳秒为单位)。
		//[8]接下来的两个参数指定了同步对象,这些对象将在引擎使用完图像时发出信号。 可以指定semaphore、fence或都指定
		//[8]用于输出可用swap chain中图像的索引。
		//vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphore, VK_NULL_HANDLE, &imageIndex);
		//or
		//[12]
		vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);

		//[13]Check if a previous frame is using this image (i.e. there is its fence to wait on)
		if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) {
			vkWaitForFences(device, 1, &imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX);
		}
		//[13] Mark the image as now being in use by this frame
		imagesInFlight[imageIndex] = inFlightFences[currentFrame];
		
		VkSubmitInfo submitInfo = {};
		submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
		
		//VkSemaphore waitSemaphores[] = { imageAvailableSemaphore };
		//or
		//[12]
		VkSemaphore waitSemaphores[] = { imageAvailableSemaphores[currentFrame] };
		VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
		//[9]前三个参数指定在执行开始之前等待哪些Semaphore,以及在管线的哪个阶段等待。
		submitInfo.waitSemaphoreCount = 1;
		submitInfo.pWaitSemaphores = waitSemaphores;
		submitInfo.pWaitDstStageMask = waitStages;

		//[9]指定实际提交执行的commandBuffer。
		submitInfo.commandBufferCount = 1;
		submitInfo.pCommandBuffers = &commandBuffers[imageIndex];

		//[9]指定在commandBuffer完成执行后发送信号的Semaphore。
		//VkSemaphore signalSemaphores[] = { renderFinishedSemaphore};
		//or
		//[12]
		VkSemaphore signalSemaphores[] = { renderFinishedSemaphores[currentFrame] };
		submitInfo.signalSemaphoreCount = 1;	
		submitInfo.pSignalSemaphores = signalSemaphores;
		

		//[13] 
		vkResetFences(device, 1, &inFlightFences[currentFrame]);

		//[9]最后一个参数引用了可选的 fenc, 当 command buffer 执行完成时,它将发出信号。
		//[9]我们使用信号量进行同步,所以我们传递VK_NULL_HANDLE。
		//if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE) != VK_SUCCESS) {
		//	throw std::runtime_error("failed to submit draw command buffer!");
		//}
		//or
		//[13]
		if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) {
			throw std::runtime_error("failed to submit draw command buffer!");
		}

		VkPresentInfoKHR presentInfo = {};
		presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
		//[11]在呈现(Present)之前等待哪些信号量
		presentInfo.waitSemaphoreCount = 1;
		presentInfo.pWaitSemaphores = signalSemaphores;

		VkSwapchainKHR swapChains[] = { swapChain };
		presentInfo.swapchainCount = 1;
		//[11]为每个swapChain指定呈现的图像和图像索引
		presentInfo.pSwapchains = swapChains;
		presentInfo.pImageIndices = &imageIndex;
		//[11]check for every individual swap chain if presentation was successful. (array of VkResult)
		presentInfo.pResults = nullptr; // Optional

		//[11]submits the request to present an image to the swap chain.
		vkQueuePresentKHR(presentQueue, &presentInfo);
		//[12]
		vkQueueWaitIdle(presentQueue);

		//[12]
		currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;

		

	}
	//[13]renamed createSemaphores to createSyncObjects :
	void createSemaphores() {

		//[12]
		imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
		renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
		//[13]
		inFlightFences.resize(MAX_FRAMES_IN_FLIGHT);
		imagesInFlight.resize(swapChainImages.size(), VK_NULL_HANDLE);


		//[9] Vulkan API 会扩展 flags 和 pNext 参数。
		VkSemaphoreCreateInfo semaphoreInfo = {};
		semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
		//[13]
		VkFenceCreateInfo fenceInfo = {};
		fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
		//[13]if we had rendered an initial frame that finished
		fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;

		//[12]
		for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
			//[9] 
			if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]) != VK_SUCCESS ||
				vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]) != VK_SUCCESS ||
				//[13]
				vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS) {
				throw std::runtime_error("failed to create semaphores for a frame!");
			}
		}


	}
private:
	GLFWwindow* window;
	VkDevice device;
	VkSwapchainKHR swapChain;
	VkExtent2D swapChainExtent;
	VkRenderPass renderPass;
	VkPipeline graphicsPipeline;
	VkQueue graphicsQueue;
	std::vector<VkImage> swapChainImages;
	VkQueue presentQueue;
	VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
	std::vector<VkImageView> swapChainImageViews;
	//[1]
	std::vector<VkFramebuffer> swapChainFramebuffers;
	//[2]
	VkCommandPool commandPool;
	//[3] Command buffers will be automatically freed when their command pool is destroyed
	std::vector<VkCommandBuffer> commandBuffers;
	//[9]
	VkSemaphore imageAvailableSemaphore;
	VkSemaphore renderFinishedSemaphore;

	//[12]
	std::vector<VkSemaphore> imageAvailableSemaphores;
	std::vector<VkSemaphore> renderFinishedSemaphores;

	//[12]
	size_t currentFrame = 0;

	//[13]
	std::vector<VkFence> inFlightFences;
	std::vector<VkFence> imagesInFlight;

};


int main() {
	HelloTriangleApplication app;
	//[1]捕获异常
	try {
		app.run();
	}
	catch (const std::exception& e) {
		std::cerr << e.what() << std::endl;
		return EXIT_FAILURE;
	}

	return EXIT_SUCCESS;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值