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