天天看點

Vulkan學習(五): Command buffers & Rendering & PresentationFramebuffersCommand buffersRendering and presentationCode

目錄

  • Framebuffers
  • Command buffers
    • Command pools
    • Command buffer allocation
    • Starting command buffer recording
    • Starting a render pass
    • Basic drawing commands & Finishing up
  • Rendering and presentation
    • Synchronization
    • Semaphores
    • Acquiring an image from the swap chain
    • Submitting the command buffer
    • Subpass dependencies
    • Presentation
    • Frames in flight
  • Code

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類似,因為它們可以發出信号并等待

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