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

View File

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