Two frames in flight

This commit is contained in:
Grayson Riffe 2025-02-13 13:37:15 -06:00
parent f9232f314b
commit 3088a42a07
2 changed files with 58 additions and 50 deletions

View File

@ -26,10 +26,9 @@ namespace nf::client::render {
, m_pipelineLayoutOutput() , m_pipelineLayoutOutput()
, m_pipelineOutput() , m_pipelineOutput()
, m_commandPool() , m_commandPool()
, m_commandBuffer() , m_numFramesInFlight(2)
, m_semaphoreImageAvailable() , m_frameExecutors()
, m_semaphoreRenderFinished() , m_currentFrameExecutor()
, m_fenceInFlight()
{ {
NFLog("Initializing render engine"); NFLog("Initializing render engine");
m_window->setDisplay(m_display); m_window->setDisplay(m_display);
@ -41,8 +40,7 @@ namespace nf::client::render {
createSwapchain(); createSwapchain();
createOutputRenderPass(); createOutputRenderPass();
createOutputPipeline(); createOutputPipeline();
createCommandBuffer(); createFrameExecutors();
createSynchronizationObjects();
m_window->show(); m_window->show();
} }
@ -391,7 +389,7 @@ namespace nf::client::render {
} }
} }
void RenderEngine::createCommandBuffer() { void RenderEngine::createFrameExecutors() {
VkCommandPoolCreateInfo commandPoolCI = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO }; VkCommandPoolCreateInfo commandPoolCI = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };
commandPoolCI.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; commandPoolCI.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
commandPoolCI.queueFamilyIndex = m_queueFIGraphics; commandPoolCI.queueFamilyIndex = m_queueFIGraphics;
@ -399,39 +397,43 @@ namespace nf::client::render {
if (vkCreateCommandPool(m_device, &commandPoolCI, nullptr, &m_commandPool) != VK_SUCCESS) if (vkCreateCommandPool(m_device, &commandPoolCI, nullptr, &m_commandPool) != VK_SUCCESS)
NFError("Could not create command pool."); NFError("Could not create command pool.");
VkCommandBufferAllocateInfo commandBufferAI = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; m_frameExecutors.resize(m_numFramesInFlight);
commandBufferAI.commandPool = m_commandPool; for (int i = 0; i < m_frameExecutors.size(); i++) {
commandBufferAI.commandBufferCount = 1; VkCommandBufferAllocateInfo commandBufferAI = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
commandBufferAI.commandPool = m_commandPool;
commandBufferAI.commandBufferCount = 1;
if (vkAllocateCommandBuffers(m_device, &commandBufferAI, &m_commandBuffer) != VK_SUCCESS) if (vkAllocateCommandBuffers(m_device, &commandBufferAI, &m_frameExecutors[i].commandBuffer) != VK_SUCCESS)
NFError("Could not create command buffer."); NFError("Could not create command buffer.");
}
void RenderEngine::createSynchronizationObjects() { VkSemaphoreCreateInfo semaphoreCI = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
VkSemaphoreCreateInfo semaphoreCI = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; if (vkCreateSemaphore(m_device, &semaphoreCI, nullptr, &m_frameExecutors[i].semaphoreImageAvailable) != VK_SUCCESS ||
if (vkCreateSemaphore(m_device, &semaphoreCI, nullptr, &m_semaphoreImageAvailable) != VK_SUCCESS || vkCreateSemaphore(m_device, &semaphoreCI, nullptr, &m_frameExecutors[i].semaphoreRenderFinished) != VK_SUCCESS)
vkCreateSemaphore(m_device, &semaphoreCI, nullptr, &m_semaphoreRenderFinished) != VK_SUCCESS) NFError("Could not create semaphore.");
NFError("Could not create semaphore.");
VkFenceCreateInfo fenceCI = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO }; VkFenceCreateInfo fenceCI = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
fenceCI.flags = VK_FENCE_CREATE_SIGNALED_BIT; fenceCI.flags = VK_FENCE_CREATE_SIGNALED_BIT;
if (vkCreateFence(m_device, &fenceCI, nullptr, &m_fenceInFlight) != VK_SUCCESS) if (vkCreateFence(m_device, &fenceCI, nullptr, &m_frameExecutors[i].fenceFrameInFlight) != VK_SUCCESS)
NFError("Could not create fence."); NFError("Could not create fence.");
}
} }
void RenderEngine::doFrame() { void RenderEngine::doFrame() {
// Get current frame executor
auto& [commandBuffer, semaphoreImageAvailable, semaphoreRenderFinished, fenceFrameInFlight] = m_frameExecutors[m_currentFrameExecutor];
// First, wait for previous frame to complete // First, wait for previous frame to complete
vkWaitForFences(m_device, 1, &m_fenceInFlight, VK_TRUE, UINT64_MAX); vkWaitForFences(m_device, 1, &fenceFrameInFlight, VK_TRUE, UINT64_MAX);
vkResetFences(m_device, 1, &m_fenceInFlight); vkResetFences(m_device, 1, &fenceFrameInFlight);
// Get next swapchain image // Get next swapchain image
uint32_t nextImageIndex = 0; uint32_t nextImageIndex = 0;
vkAcquireNextImageKHR(m_device, m_swapchain, UINT64_MAX, m_semaphoreImageAvailable, nullptr, &nextImageIndex); vkAcquireNextImageKHR(m_device, m_swapchain, UINT64_MAX, semaphoreImageAvailable, nullptr, &nextImageIndex);
// Begin recording // Begin recording
VkCommandBufferBeginInfo commandBufferBI = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; VkCommandBufferBeginInfo commandBufferBI = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
if (vkBeginCommandBuffer(m_commandBuffer, &commandBufferBI) != VK_SUCCESS) if (vkBeginCommandBuffer(commandBuffer, &commandBufferBI) != VK_SUCCESS)
NFError("Could not begin recording command buffer."); NFError("Could not begin recording command buffer.");
// Start the render pass // Start the render pass
@ -442,59 +444,63 @@ namespace nf::client::render {
renderPassBI.renderArea.extent = { m_display.width, m_display.height }; renderPassBI.renderArea.extent = { m_display.width, m_display.height };
renderPassBI.clearValueCount = 1; renderPassBI.clearValueCount = 1;
renderPassBI.pClearValues = &black; renderPassBI.pClearValues = &black;
vkCmdBeginRenderPass(m_commandBuffer, &renderPassBI, VK_SUBPASS_CONTENTS_INLINE); vkCmdBeginRenderPass(commandBuffer, &renderPassBI, VK_SUBPASS_CONTENTS_INLINE);
// Bind output pipeline // Bind output pipeline
vkCmdBindPipeline(m_commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineOutput); vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineOutput);
// Set viewport // Set viewport
VkViewport viewport = {}; VkViewport viewport = {};
viewport.width = static_cast<float>(m_display.width), viewport.height = static_cast<float>(m_display.height); viewport.width = static_cast<float>(m_display.width), viewport.height = static_cast<float>(m_display.height);
viewport.maxDepth = 1.0; viewport.maxDepth = 1.0;
vkCmdSetViewport(m_commandBuffer, 0, 1, &viewport); vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
// Draw // Draw
vkCmdDraw(m_commandBuffer, 3, 1, 0, 0); vkCmdDraw(commandBuffer, 3, 1, 0, 0);
// End the render pass // End the render pass
vkCmdEndRenderPass(m_commandBuffer); vkCmdEndRenderPass(commandBuffer);
// Finish recording // Finish recording
if (vkEndCommandBuffer(m_commandBuffer) != VK_SUCCESS) if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS)
NFError("Could not end command buffer."); NFError("Could not end command buffer.");
// Submit to graphics queue // Submit to graphics queue
VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO };
submitInfo.waitSemaphoreCount = 1; submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = &m_semaphoreImageAvailable; submitInfo.pWaitSemaphores = &semaphoreImageAvailable;
submitInfo.pWaitDstStageMask = &waitStage; submitInfo.pWaitDstStageMask = &waitStage;
submitInfo.commandBufferCount = 1; submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &m_commandBuffer; submitInfo.pCommandBuffers = &commandBuffer;
submitInfo.signalSemaphoreCount = 1; submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = &m_semaphoreRenderFinished; submitInfo.pSignalSemaphores = &semaphoreRenderFinished;
if (vkQueueSubmit(m_queueGraphics, 1, &submitInfo, m_fenceInFlight) != VK_SUCCESS) if (vkQueueSubmit(m_queueGraphics, 1, &submitInfo, fenceFrameInFlight) != VK_SUCCESS)
NFError("Could not submit to Vulkan queue."); NFError("Could not submit to Vulkan queue.");
// And present! // And present!
VkPresentInfoKHR presentInfo = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; VkPresentInfoKHR presentInfo = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR };
presentInfo.waitSemaphoreCount = 1; presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = &m_semaphoreRenderFinished; presentInfo.pWaitSemaphores = &semaphoreRenderFinished;
presentInfo.swapchainCount = 1; presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = &m_swapchain; presentInfo.pSwapchains = &m_swapchain;
presentInfo.pImageIndices = &nextImageIndex; presentInfo.pImageIndices = &nextImageIndex;
vkQueuePresentKHR(m_queuePresent, &presentInfo); vkQueuePresentKHR(m_queuePresent, &presentInfo);
}
// Advance to next frame executor
m_currentFrameExecutor = (m_currentFrameExecutor + 1) % m_numFramesInFlight;
}
RenderEngine::~RenderEngine() { RenderEngine::~RenderEngine() {
vkDeviceWaitIdle(m_device); vkDeviceWaitIdle(m_device);
vkDestroyFence(m_device, m_fenceInFlight, nullptr); for (int i = 0; i < m_frameExecutors.size(); i++) {
vkDestroySemaphore(m_device, m_semaphoreRenderFinished, nullptr); vkDestroyFence(m_device, m_frameExecutors[i].fenceFrameInFlight, nullptr);
vkDestroySemaphore(m_device, m_semaphoreImageAvailable, nullptr); vkDestroySemaphore(m_device, m_frameExecutors[i].semaphoreRenderFinished, nullptr);
vkDestroySemaphore(m_device, m_frameExecutors[i].semaphoreImageAvailable, nullptr);
}
vkDestroyCommandPool(m_device, m_commandPool, nullptr); vkDestroyCommandPool(m_device, m_commandPool, nullptr);

View File

@ -5,6 +5,12 @@
#include "nf/config.h" #include "nf/config.h"
namespace nf::client::render { namespace nf::client::render {
struct FrameExecutor {
VkCommandBuffer commandBuffer;
VkSemaphore semaphoreImageAvailable, semaphoreRenderFinished;
VkFence fenceFrameInFlight;
};
class RenderEngine { class RenderEngine {
public: public:
RenderEngine(std::shared_ptr<Window> window, DisplayConfig display); RenderEngine(std::shared_ptr<Window> window, DisplayConfig display);
@ -20,8 +26,7 @@ namespace nf::client::render {
void createSwapchain(); void createSwapchain();
void createOutputRenderPass(); void createOutputRenderPass();
void createOutputPipeline(); void createOutputPipeline();
void createCommandBuffer(); void createFrameExecutors();
void createSynchronizationObjects();
std::shared_ptr<Window> m_window; std::shared_ptr<Window> m_window;
DisplayConfig m_display; DisplayConfig m_display;
@ -46,13 +51,10 @@ namespace nf::client::render {
VkPipelineLayout m_pipelineLayoutOutput; VkPipelineLayout m_pipelineLayoutOutput;
VkPipeline m_pipelineOutput; VkPipeline m_pipelineOutput;
// Command buffers // Execution objects
VkCommandPool m_commandPool; VkCommandPool m_commandPool;
VkCommandBuffer m_commandBuffer; const uint32_t m_numFramesInFlight;
std::vector<FrameExecutor> m_frameExecutors;
// Synchronization objects uint32_t m_currentFrameExecutor;
VkSemaphore m_semaphoreImageAvailable;
VkSemaphore m_semaphoreRenderFinished;
VkFence m_fenceInFlight;
}; };
} }