Skip to content

Commit 9fe64f9

Browse files
Edit tutorial to reset & record command buffers each frame (#255)
* Update tutorial for command buffer re-recording Means the code now calls recordCommandBuffer every frame instead of ahead of time Many changes to the structure of chapters 14 & 15, with small changes to subsequent chapters. Primary focus was shifting everything away from 'swapchain image count' to MAX_FRAMES_IN_FLIGHT. This has caused a lot of chapter 15 to need to be rewritten. Such as: Introducing fences alongside semaphores and not later; Waiting on a fence at the start of the frame before introducing acquireNextImage; consolidating the Frames In Flight concepts to apply to command buffers, fences, and semaphores at the same time. Chapter 14 saw command buffer allocation reduced to 1 at a time. This allows the concept of Frames in Flight to not need introduction before having a triangle drawing on the screen. * Update introduction to semaphores and fences Greatly improve the descriptions of semaphores and fences before their introduction into the code. Provide examples with psuedo code. By using max_frames_in_flight command buffers & semaphores, we dont need to keep track of previously sumitted frames' fences and wait on them "just in case". This removes a lot of the confusion I had when I first was trying to understand the vulkan update loop. * Remove accidental code changes Un-comment vkDeviceWaitIdle and remove resizing of imagesInFlight (which was removed) * Use uint32_t instead of size_t for currentFrame * Address typos and fixup changes for re-recording command buffers PR * Add description of fences needing explicit resetting while semaphores are automatic * Elaborate on why 2 frames in flight are chosen * Fix deadlock in resizing from resetting the fence too early Because acquiring the swapchain image index may cause drawFrame to return early, it was possible to cause the next vkWaitForFences to deadlock. By delaying fence resetting till after acquiring, it prevents the deadlock.
1 parent 10118cb commit 9fe64f9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+2333
-1433
lines changed

code/14_command_buffers.cpp

+28-29
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ class HelloTriangleApplication {
9494
VkPipeline graphicsPipeline;
9595

9696
VkCommandPool commandPool;
97-
std::vector<VkCommandBuffer> commandBuffers;
97+
VkCommandBuffer commandBuffer;
9898

9999
void initWindow() {
100100
glfwInit();
@@ -117,7 +117,7 @@ class HelloTriangleApplication {
117117
createGraphicsPipeline();
118118
createFramebuffers();
119119
createCommandPool();
120-
createCommandBuffers();
120+
createCommandBuffer();
121121
}
122122

123123
void mainLoop() {
@@ -537,56 +537,55 @@ class HelloTriangleApplication {
537537

538538
VkCommandPoolCreateInfo poolInfo{};
539539
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
540+
poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
540541
poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
541542

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

547-
void createCommandBuffers() {
548-
commandBuffers.resize(swapChainFramebuffers.size());
549-
548+
void createCommandBuffer() {
550549
VkCommandBufferAllocateInfo allocInfo{};
551550
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
552551
allocInfo.commandPool = commandPool;
553552
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
554-
allocInfo.commandBufferCount = (uint32_t) commandBuffers.size();
553+
allocInfo.commandBufferCount = 1;
555554

556-
if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) {
555+
if (vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer) != VK_SUCCESS) {
557556
throw std::runtime_error("failed to allocate command buffers!");
558557
}
558+
}
559559

560-
for (size_t i = 0; i < commandBuffers.size(); i++) {
561-
VkCommandBufferBeginInfo beginInfo{};
562-
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
560+
void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) {
561+
VkCommandBufferBeginInfo beginInfo{};
562+
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
563563

564-
if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) {
565-
throw std::runtime_error("failed to begin recording command buffer!");
566-
}
564+
if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) {
565+
throw std::runtime_error("failed to begin recording command buffer!");
566+
}
567567

568-
VkRenderPassBeginInfo renderPassInfo{};
569-
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
570-
renderPassInfo.renderPass = renderPass;
571-
renderPassInfo.framebuffer = swapChainFramebuffers[i];
572-
renderPassInfo.renderArea.offset = {0, 0};
573-
renderPassInfo.renderArea.extent = swapChainExtent;
568+
VkRenderPassBeginInfo renderPassInfo{};
569+
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
570+
renderPassInfo.renderPass = renderPass;
571+
renderPassInfo.framebuffer = swapChainFramebuffers[imageIndex];
572+
renderPassInfo.renderArea.offset = {0, 0};
573+
renderPassInfo.renderArea.extent = swapChainExtent;
574574

575-
VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
576-
renderPassInfo.clearValueCount = 1;
577-
renderPassInfo.pClearValues = &clearColor;
575+
VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
576+
renderPassInfo.clearValueCount = 1;
577+
renderPassInfo.pClearValues = &clearColor;
578578

579-
vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
579+
vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
580580

581-
vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
581+
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
582582

583-
vkCmdDraw(commandBuffers[i], 3, 1, 0, 0);
583+
vkCmdDraw(commandBuffer, 3, 1, 0, 0);
584584

585-
vkCmdEndRenderPass(commandBuffers[i]);
585+
vkCmdEndRenderPass(commandBuffer);
586586

587-
if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) {
588-
throw std::runtime_error("failed to record command buffer!");
589-
}
587+
if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) {
588+
throw std::runtime_error("failed to record command buffer!");
590589
}
591590
}
592591

code/15_hello_triangle.cpp

+48-64
Original file line numberDiff line numberDiff line change
@@ -96,13 +96,11 @@ class HelloTriangleApplication {
9696
VkPipeline graphicsPipeline;
9797

9898
VkCommandPool commandPool;
99-
std::vector<VkCommandBuffer> commandBuffers;
99+
VkCommandBuffer commandBuffer;
100100

101-
std::vector<VkSemaphore> imageAvailableSemaphores;
102-
std::vector<VkSemaphore> renderFinishedSemaphores;
103-
std::vector<VkFence> inFlightFences;
104-
std::vector<VkFence> imagesInFlight;
105-
size_t currentFrame = 0;
101+
VkSemaphore imageAvailableSemaphores;
102+
VkSemaphore renderFinishedSemaphores;
103+
VkFence inFlightFence;
106104

107105
void initWindow() {
108106
glfwInit();
@@ -125,7 +123,7 @@ class HelloTriangleApplication {
125123
createGraphicsPipeline();
126124
createFramebuffers();
127125
createCommandPool();
128-
createCommandBuffers();
126+
createCommandBuffer();
129127
createSyncObjects();
130128
}
131129

@@ -139,11 +137,9 @@ class HelloTriangleApplication {
139137
}
140138

141139
void cleanup() {
142-
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
143-
vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr);
144-
vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr);
145-
vkDestroyFence(device, inFlightFences[i], nullptr);
146-
}
140+
vkDestroySemaphore(device, renderFinishedSemaphores, nullptr);
141+
vkDestroySemaphore(device, imageAvailableSemaphores, nullptr);
142+
vkDestroyFence(device, inFlightFence, nullptr);
147143

148144
vkDestroyCommandPool(device, commandPool, nullptr);
149145

@@ -565,111 +561,101 @@ class HelloTriangleApplication {
565561

566562
VkCommandPoolCreateInfo poolInfo{};
567563
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
564+
poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
568565
poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
569566

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

575-
void createCommandBuffers() {
576-
commandBuffers.resize(swapChainFramebuffers.size());
577-
572+
void createCommandBuffer() {
578573
VkCommandBufferAllocateInfo allocInfo{};
579574
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
580575
allocInfo.commandPool = commandPool;
581576
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
582-
allocInfo.commandBufferCount = (uint32_t) commandBuffers.size();
577+
allocInfo.commandBufferCount = 1;
583578

584-
if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) {
579+
if (vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer) != VK_SUCCESS) {
585580
throw std::runtime_error("failed to allocate command buffers!");
586581
}
582+
}
587583

588-
for (size_t i = 0; i < commandBuffers.size(); i++) {
589-
VkCommandBufferBeginInfo beginInfo{};
590-
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
584+
void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) {
585+
VkCommandBufferBeginInfo beginInfo{};
586+
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
591587

592-
if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) {
593-
throw std::runtime_error("failed to begin recording command buffer!");
594-
}
588+
if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) {
589+
throw std::runtime_error("failed to begin recording command buffer!");
590+
}
595591

596-
VkRenderPassBeginInfo renderPassInfo{};
597-
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
598-
renderPassInfo.renderPass = renderPass;
599-
renderPassInfo.framebuffer = swapChainFramebuffers[i];
600-
renderPassInfo.renderArea.offset = {0, 0};
601-
renderPassInfo.renderArea.extent = swapChainExtent;
592+
VkRenderPassBeginInfo renderPassInfo{};
593+
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
594+
renderPassInfo.renderPass = renderPass;
595+
renderPassInfo.framebuffer = swapChainFramebuffers[imageIndex];
596+
renderPassInfo.renderArea.offset = {0, 0};
597+
renderPassInfo.renderArea.extent = swapChainExtent;
602598

603-
VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
604-
renderPassInfo.clearValueCount = 1;
605-
renderPassInfo.pClearValues = &clearColor;
599+
VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
600+
renderPassInfo.clearValueCount = 1;
601+
renderPassInfo.pClearValues = &clearColor;
606602

607-
vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
603+
vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
608604

609-
vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
605+
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
610606

611-
vkCmdDraw(commandBuffers[i], 3, 1, 0, 0);
607+
vkCmdDraw(commandBuffer, 3, 1, 0, 0);
612608

613-
vkCmdEndRenderPass(commandBuffers[i]);
609+
vkCmdEndRenderPass(commandBuffer);
614610

615-
if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) {
616-
throw std::runtime_error("failed to record command buffer!");
617-
}
611+
if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) {
612+
throw std::runtime_error("failed to record command buffer!");
618613
}
619614
}
620615

621616
void createSyncObjects() {
622-
imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
623-
renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
624-
inFlightFences.resize(MAX_FRAMES_IN_FLIGHT);
625-
imagesInFlight.resize(swapChainImages.size(), VK_NULL_HANDLE);
626-
627617
VkSemaphoreCreateInfo semaphoreInfo{};
628618
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
629619

630620
VkFenceCreateInfo fenceInfo{};
631621
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
632622
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
633623

634-
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
635-
if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]) != VK_SUCCESS ||
636-
vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]) != VK_SUCCESS ||
637-
vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS) {
638-
throw std::runtime_error("failed to create synchronization objects for a frame!");
639-
}
624+
if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores) != VK_SUCCESS ||
625+
vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores) != VK_SUCCESS ||
626+
vkCreateFence(device, &fenceInfo, nullptr, &inFlightFence) != VK_SUCCESS) {
627+
throw std::runtime_error("failed to create synchronization objects for a frame!");
640628
}
629+
641630
}
642631

643632
void drawFrame() {
644-
vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);
633+
vkWaitForFences(device, 1, &inFlightFence, VK_TRUE, UINT64_MAX);
634+
vkResetFences(device, 1, &inFlightFence);
645635

646636
uint32_t imageIndex;
647-
vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
637+
vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores, VK_NULL_HANDLE, &imageIndex);
648638

649-
if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) {
650-
vkWaitForFences(device, 1, &imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX);
651-
}
652-
imagesInFlight[imageIndex] = inFlightFences[currentFrame];
639+
vkResetCommandBuffer(commandBuffer, /*VkCommandBufferResetFlagBits*/ 0);
640+
recordCommandBuffer(commandBuffer, imageIndex);
653641

654642
VkSubmitInfo submitInfo{};
655643
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
656644

657-
VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[currentFrame]};
645+
VkSemaphore waitSemaphores[] = {imageAvailableSemaphores};
658646
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
659647
submitInfo.waitSemaphoreCount = 1;
660648
submitInfo.pWaitSemaphores = waitSemaphores;
661649
submitInfo.pWaitDstStageMask = waitStages;
662650

663651
submitInfo.commandBufferCount = 1;
664-
submitInfo.pCommandBuffers = &commandBuffers[imageIndex];
652+
submitInfo.pCommandBuffers = &commandBuffer;
665653

666-
VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]};
654+
VkSemaphore signalSemaphores[] = {renderFinishedSemaphores};
667655
submitInfo.signalSemaphoreCount = 1;
668656
submitInfo.pSignalSemaphores = signalSemaphores;
669657

670-
vkResetFences(device, 1, &inFlightFences[currentFrame]);
671-
672-
if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) {
658+
if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFence) != VK_SUCCESS) {
673659
throw std::runtime_error("failed to submit draw command buffer!");
674660
}
675661

@@ -686,8 +672,6 @@ class HelloTriangleApplication {
686672
presentInfo.pImageIndices = &imageIndex;
687673

688674
vkQueuePresentKHR(presentQueue, &presentInfo);
689-
690-
currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
691675
}
692676

693677
VkShaderModule createShaderModule(const std::vector<char>& code) {

0 commit comments

Comments
 (0)