Two frames in flight
This commit is contained in:
parent
f9232f314b
commit
3088a42a07
@ -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.");
|
||||||
|
|
||||||
|
m_frameExecutors.resize(m_numFramesInFlight);
|
||||||
|
for (int i = 0; i < m_frameExecutors.size(); i++) {
|
||||||
VkCommandBufferAllocateInfo commandBufferAI = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
|
VkCommandBufferAllocateInfo commandBufferAI = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
|
||||||
commandBufferAI.commandPool = m_commandPool;
|
commandBufferAI.commandPool = m_commandPool;
|
||||||
commandBufferAI.commandBufferCount = 1;
|
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_semaphoreImageAvailable) != VK_SUCCESS ||
|
if (vkCreateSemaphore(m_device, &semaphoreCI, nullptr, &m_frameExecutors[i].semaphoreImageAvailable) != VK_SUCCESS ||
|
||||||
vkCreateSemaphore(m_device, &semaphoreCI, nullptr, &m_semaphoreRenderFinished) != VK_SUCCESS)
|
vkCreateSemaphore(m_device, &semaphoreCI, nullptr, &m_frameExecutors[i].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);
|
||||||
|
|
||||||
|
@ -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;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user