diff --git a/NothinFancy/src/client/render/RenderEngine.cpp b/NothinFancy/src/client/render/RenderEngine.cpp index 74840d0..076a884 100644 --- a/NothinFancy/src/client/render/RenderEngine.cpp +++ b/NothinFancy/src/client/render/RenderEngine.cpp @@ -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."); - VkCommandBufferAllocateInfo commandBufferAI = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; - commandBufferAI.commandPool = m_commandPool; - commandBufferAI.commandBufferCount = 1; + 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) - NFError("Could not create command buffer."); - } + 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) - NFError("Could not create semaphore."); + VkSemaphoreCreateInfo semaphoreCI = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; + 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) - NFError("Could not create fence."); + VkFenceCreateInfo fenceCI = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO }; + fenceCI.flags = VK_FENCE_CREATE_SIGNALED_BIT; + 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(m_display.width), viewport.height = static_cast(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); diff --git a/NothinFancy/src/client/render/RenderEngine.h b/NothinFancy/src/client/render/RenderEngine.h index 5fdde7c..85aa5b9 100644 --- a/NothinFancy/src/client/render/RenderEngine.h +++ b/NothinFancy/src/client/render/RenderEngine.h @@ -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, 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 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 m_frameExecutors; + uint32_t m_currentFrameExecutor; }; }