diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp index 13e8e9b1..f1373f75 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp @@ -11,13 +11,10 @@ namespace MobileGL::MG_Backend::DirectVulkan { VkResult FrameContext::Initialize(VkDevice device, VkCommandPool commandPool, Uint32 frameCount) { Destroy(device, commandPool); - commandBuffers.resize(frameCount, VK_NULL_HANDLE); - imageAvailableSemaphores.resize(frameCount, VK_NULL_HANDLE); - renderFinishedSemaphores.resize(frameCount, VK_NULL_HANDLE); - imageInFlightFences.resize(frameCount, VK_NULL_HANDLE); - hasCommandBufferRecorded.assign(frameCount, false); + m_frames.assign(frameCount, {}); currentFrameIndex = 0; + Vector commandBuffers(frameCount, VK_NULL_HANDLE); VkCommandBufferAllocateInfo allocInfo{}; allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; allocInfo.commandPool = commandPool; @@ -27,6 +24,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { if (result != VK_SUCCESS) { return result; } + for (Uint32 i = 0; i < frameCount; ++i) { + m_frames[i].commandBuffer = commandBuffers[i]; + } VkSemaphoreCreateInfo semaphoreInfo{VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO}; VkFenceCreateInfo fenceInfo{VK_STRUCTURE_TYPE_FENCE_CREATE_INFO}; @@ -44,24 +44,36 @@ namespace MobileGL::MG_Backend::DirectVulkan { } void FrameContext::Destroy(VkDevice device, VkCommandPool commandPool) { - const Uint32 frameCount = static_cast(commandBuffers.size()); + const Uint32 frameCount = static_cast(m_frames.size()); + Vector commandBuffers(frameCount, VK_NULL_HANDLE); + for (Uint32 i = 0; i < frameCount; ++i) { + commandBuffers[i] = m_frames[i].commandBuffer; + } + for (Uint32 i = 0; i < frameCount; ++i) { DestroySyncObjectsForFrame(device, i); } - if (device != VK_NULL_HANDLE && commandPool != VK_NULL_HANDLE && !commandBuffers.empty()) { + if (device != VK_NULL_HANDLE && commandPool != VK_NULL_HANDLE && !m_frames.empty()) { vkFreeCommandBuffers(device, commandPool, frameCount, commandBuffers.data()); } - commandBuffers.clear(); - imageAvailableSemaphores.clear(); - renderFinishedSemaphores.clear(); - imageInFlightFences.clear(); - hasCommandBufferRecorded.clear(); + m_frames.clear(); currentFrameIndex = 0; } - void FrameContext::AdvanceFrame() { - MOBILEGL_ASSERT(!commandBuffers.empty(), "FrameContext is not initialized"); - currentFrameIndex = (currentFrameIndex + 1) % static_cast(commandBuffers.size()); + FrameContext::FrameData& FrameContext::GetCurrent() { + MOBILEGL_ASSERT(!m_frames.empty(), "FrameContext is not initialized"); + return m_frames[currentFrameIndex]; + } + + const FrameContext::FrameData& FrameContext::GetCurrent() const { + MOBILEGL_ASSERT(!m_frames.empty(), "FrameContext is not initialized"); + return m_frames[currentFrameIndex]; + } + + void FrameContext::AdvanceToNext() { + MOBILEGL_ASSERT(!m_frames.empty(), "FrameContext is not initialized"); + currentFrameIndex = (currentFrameIndex + 1) % static_cast(m_frames.size()); + GetCurrent().hasCommandBufferRecorded = false; } Uint32 FrameContext::GetCurrentFrameIndex() const { @@ -69,172 +81,63 @@ namespace MobileGL::MG_Backend::DirectVulkan { } Uint32 FrameContext::GetFrameCount() const { - return static_cast(commandBuffers.size()); - } - - VkCommandBuffer& FrameContext::GetCommandBuffer(Uint32 frameIndex) { - AssertValidFrameIndex(frameIndex); - return commandBuffers[frameIndex]; - } - - VkSemaphore& FrameContext::GetImageAvailableSemaphore(Uint32 frameIndex) { - AssertValidFrameIndex(frameIndex); - return imageAvailableSemaphores[frameIndex]; - } - - VkSemaphore& FrameContext::GetRenderFinishedSemaphore(Uint32 frameIndex) { - AssertValidFrameIndex(frameIndex); - return renderFinishedSemaphores[frameIndex]; - } - - VkFence& FrameContext::GetImageInFlightFence(Uint32 frameIndex) { - AssertValidFrameIndex(frameIndex); - return imageInFlightFences[frameIndex]; - } - - void FrameContext::SetHasCommandBufferRecorded(Uint32 frameIndex, Bool value) { - AssertValidFrameIndex(frameIndex); - hasCommandBufferRecorded[frameIndex] = value; - } - - void FrameContext::SetCurrentCommandBuffer(VkCommandBuffer value) { - SetCurrentCommandBufferRecorded(false); - GetCommandBuffer(GetCurrentFrameIndex()) = value; - } - - void FrameContext::SetCurrentImageAvailableSemaphore(VkSemaphore value) { - GetImageAvailableSemaphore(GetCurrentFrameIndex()) = value; - } - - void FrameContext::SetCurrentRenderFinishedSemaphore(VkSemaphore value) { - GetRenderFinishedSemaphore(GetCurrentFrameIndex()) = value; - } - - void FrameContext::SetCurrentImageInFlightFence(VkFence value) { - GetImageInFlightFence(GetCurrentFrameIndex()) = value; - } - - void FrameContext::SetCurrentCommandBufferRecorded(Bool value) { - SetHasCommandBufferRecorded(GetCurrentFrameIndex(), value); - } - - VkCommandBuffer& FrameContext::GetCurrentCommandBuffer() { - return GetCommandBuffer(GetCurrentFrameIndex()); - } - - VkSemaphore& FrameContext::GetCurrentImageAvailableSemaphore() { - return GetImageAvailableSemaphore(GetCurrentFrameIndex()); - } - - VkSemaphore& FrameContext::GetCurrentRenderFinishedSemaphore() { - return GetRenderFinishedSemaphore(GetCurrentFrameIndex()); - } - - VkFence& FrameContext::GetCurrentImageInFlightFence() { - return GetImageInFlightFence(GetCurrentFrameIndex()); - } - - Bool FrameContext::HasCurrentCommandBufferRecorded() const { - return HasCommandBufferRecorded(GetCurrentFrameIndex()); - } - - const VkCommandBuffer& FrameContext::GetCommandBuffer(Uint32 frameIndex) const { - AssertValidFrameIndex(frameIndex); - return commandBuffers[frameIndex]; - } - - const VkSemaphore& FrameContext::GetImageAvailableSemaphore(Uint32 frameIndex) const { - AssertValidFrameIndex(frameIndex); - return imageAvailableSemaphores[frameIndex]; - } - - const VkSemaphore& FrameContext::GetRenderFinishedSemaphore(Uint32 frameIndex) const { - AssertValidFrameIndex(frameIndex); - return renderFinishedSemaphores[frameIndex]; - } - - const VkFence& FrameContext::GetImageInFlightFence(Uint32 frameIndex) const { - AssertValidFrameIndex(frameIndex); - return imageInFlightFences[frameIndex]; - } - - Bool FrameContext::HasCommandBufferRecorded(Uint32 frameIndex) const { - AssertValidFrameIndex(frameIndex); - return hasCommandBufferRecorded[frameIndex]; - } - - const VkCommandBuffer& FrameContext::GetCurrentCommandBuffer() const { - return GetCommandBuffer(GetCurrentFrameIndex()); - } - - const VkSemaphore& FrameContext::GetCurrentImageAvailableSemaphore() const { - return GetImageAvailableSemaphore(GetCurrentFrameIndex()); - } - - const VkSemaphore& FrameContext::GetCurrentRenderFinishedSemaphore() const { - return GetRenderFinishedSemaphore(GetCurrentFrameIndex()); - } - - const VkFence& FrameContext::GetCurrentImageInFlightFence() const { - return GetImageInFlightFence(GetCurrentFrameIndex()); - } - - void FrameContext::ResetPerFrameState() { - for (SizeT i = 0; i < hasCommandBufferRecorded.size(); ++i) { - hasCommandBufferRecorded[i] = false; - } + return static_cast(m_frames.size()); } void FrameContext::AssertValidFrameIndex(Uint32 frameIndex) const { - MOBILEGL_ASSERT(frameIndex < commandBuffers.size(), "FrameContext index out of range"); + MOBILEGL_ASSERT(frameIndex < m_frames.size(), "FrameContext index out of range"); } VkResult FrameContext::CreateSyncObjectsForFrame(VkDevice device, Uint32 frameIndex, const VkSemaphoreCreateInfo& semaphoreInfo, const VkFenceCreateInfo& fenceInfo) { + AssertValidFrameIndex(frameIndex); DestroySyncObjectsForFrame(device, frameIndex); + auto& frame = m_frames[frameIndex]; VkResult result = - vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[frameIndex]); + vkCreateSemaphore(device, &semaphoreInfo, nullptr, &frame.imageAvailableSemaphore); if (result != VK_SUCCESS) { return result; } - result = vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[frameIndex]); + result = vkCreateSemaphore(device, &semaphoreInfo, nullptr, &frame.renderFinishedSemaphore); if (result != VK_SUCCESS) { - vkDestroySemaphore(device, imageAvailableSemaphores[frameIndex], nullptr); - imageAvailableSemaphores[frameIndex] = VK_NULL_HANDLE; + vkDestroySemaphore(device, frame.imageAvailableSemaphore, nullptr); + frame.imageAvailableSemaphore = VK_NULL_HANDLE; return result; } - result = vkCreateFence(device, &fenceInfo, nullptr, &imageInFlightFences[frameIndex]); + result = vkCreateFence(device, &fenceInfo, nullptr, &frame.imageInFlightFence); if (result != VK_SUCCESS) { - vkDestroySemaphore(device, renderFinishedSemaphores[frameIndex], nullptr); - vkDestroySemaphore(device, imageAvailableSemaphores[frameIndex], nullptr); - renderFinishedSemaphores[frameIndex] = VK_NULL_HANDLE; - imageAvailableSemaphores[frameIndex] = VK_NULL_HANDLE; + vkDestroySemaphore(device, frame.renderFinishedSemaphore, nullptr); + vkDestroySemaphore(device, frame.imageAvailableSemaphore, nullptr); + frame.renderFinishedSemaphore = VK_NULL_HANDLE; + frame.imageAvailableSemaphore = VK_NULL_HANDLE; return result; } - hasCommandBufferRecorded[frameIndex] = false; + frame.hasCommandBufferRecorded = false; return VK_SUCCESS; } void FrameContext::DestroySyncObjectsForFrame(VkDevice device, Uint32 frameIndex) { - if (device != VK_NULL_HANDLE && imageInFlightFences[frameIndex] != VK_NULL_HANDLE) { - vkDestroyFence(device, imageInFlightFences[frameIndex], nullptr); + AssertValidFrameIndex(frameIndex); + auto& frame = m_frames[frameIndex]; + if (device != VK_NULL_HANDLE && frame.imageInFlightFence != VK_NULL_HANDLE) { + vkDestroyFence(device, frame.imageInFlightFence, nullptr); } - imageInFlightFences[frameIndex] = VK_NULL_HANDLE; + frame.imageInFlightFence = VK_NULL_HANDLE; - if (device != VK_NULL_HANDLE && renderFinishedSemaphores[frameIndex] != VK_NULL_HANDLE) { - vkDestroySemaphore(device, renderFinishedSemaphores[frameIndex], nullptr); + if (device != VK_NULL_HANDLE && frame.renderFinishedSemaphore != VK_NULL_HANDLE) { + vkDestroySemaphore(device, frame.renderFinishedSemaphore, nullptr); } - renderFinishedSemaphores[frameIndex] = VK_NULL_HANDLE; + frame.renderFinishedSemaphore = VK_NULL_HANDLE; - if (device != VK_NULL_HANDLE && imageAvailableSemaphores[frameIndex] != VK_NULL_HANDLE) { - vkDestroySemaphore(device, imageAvailableSemaphores[frameIndex], nullptr); + if (device != VK_NULL_HANDLE && frame.imageAvailableSemaphore != VK_NULL_HANDLE) { + vkDestroySemaphore(device, frame.imageAvailableSemaphore, nullptr); } - imageAvailableSemaphores[frameIndex] = VK_NULL_HANDLE; - hasCommandBufferRecorded[frameIndex] = false; + frame.imageAvailableSemaphore = VK_NULL_HANDLE; + frame.hasCommandBufferRecorded = false; } } // namespace MobileGL::MG_Backend::DirectVulkan diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.h index feba93b7..a1f61cc0 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.h @@ -14,41 +14,23 @@ namespace MobileGL::MG_Backend::DirectVulkan { class FrameContext { public: + struct FrameData { + VkCommandBuffer commandBuffer = VK_NULL_HANDLE; + VkSemaphore imageAvailableSemaphore = VK_NULL_HANDLE; + VkSemaphore renderFinishedSemaphore = VK_NULL_HANDLE; + VkFence imageInFlightFence = VK_NULL_HANDLE; + Bool hasCommandBufferRecorded = false; + }; + VkResult Initialize(VkDevice device, VkCommandPool commandPool, Uint32 frameCount); void Destroy(VkDevice device, VkCommandPool commandPool); - void AdvanceFrame(); - void ResetPerFrameState(); + FrameData& GetCurrent(); + const FrameData& GetCurrent() const; + void AdvanceToNext(); Uint32 GetCurrentFrameIndex() const; Uint32 GetFrameCount() const; - VkCommandBuffer& GetCommandBuffer(Uint32 frameIndex); - VkSemaphore& GetImageAvailableSemaphore(Uint32 frameIndex); - VkSemaphore& GetRenderFinishedSemaphore(Uint32 frameIndex); - VkFence& GetImageInFlightFence(Uint32 frameIndex); - void SetHasCommandBufferRecorded(Uint32 frameIndex, Bool value); - void SetCurrentCommandBuffer(VkCommandBuffer value); - void SetCurrentImageAvailableSemaphore(VkSemaphore value); - void SetCurrentRenderFinishedSemaphore(VkSemaphore value); - void SetCurrentImageInFlightFence(VkFence value); - void SetCurrentCommandBufferRecorded(Bool value); - - VkCommandBuffer& GetCurrentCommandBuffer(); - VkSemaphore& GetCurrentImageAvailableSemaphore(); - VkSemaphore& GetCurrentRenderFinishedSemaphore(); - VkFence& GetCurrentImageInFlightFence(); - Bool HasCurrentCommandBufferRecorded() const; - - const VkCommandBuffer& GetCommandBuffer(Uint32 frameIndex) const; - const VkSemaphore& GetImageAvailableSemaphore(Uint32 frameIndex) const; - const VkSemaphore& GetRenderFinishedSemaphore(Uint32 frameIndex) const; - const VkFence& GetImageInFlightFence(Uint32 frameIndex) const; - Bool HasCommandBufferRecorded(Uint32 frameIndex) const; - const VkCommandBuffer& GetCurrentCommandBuffer() const; - const VkSemaphore& GetCurrentImageAvailableSemaphore() const; - const VkSemaphore& GetCurrentRenderFinishedSemaphore() const; - const VkFence& GetCurrentImageInFlightFence() const; - private: void AssertValidFrameIndex(Uint32 frameIndex) const; @@ -57,11 +39,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { const VkFenceCreateInfo& fenceInfo); void DestroySyncObjectsForFrame(VkDevice device, Uint32 frameIndex); - Vector commandBuffers; - Vector imageAvailableSemaphores; - Vector renderFinishedSemaphores; - Vector imageInFlightFences; - Vector hasCommandBufferRecorded; + Vector m_frames; Uint32 currentFrameIndex = 0; }; } // namespace MobileGL::MG_Backend::DirectVulkan diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index 6d5b0d33..2a610ee6 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp @@ -197,10 +197,11 @@ namespace MobileGL::MG_Backend::DirectVulkan { CreateFrameContexts(); // Prime the first frame so Render() always targets an acquired swapchain image. - VK_VERIFY(vkWaitForFences(m_device, 1, &m_frameContext.GetCurrentImageInFlightFence(), VK_TRUE, UINT64_MAX)); - VK_VERIFY(vkResetFences(m_device, 1, &m_frameContext.GetCurrentImageInFlightFence())); + auto& frame = m_frameContext.GetCurrent(); + VK_VERIFY(vkWaitForFences(m_device, 1, &frame.imageInFlightFence, VK_TRUE, UINT64_MAX)); + VK_VERIFY(vkResetFences(m_device, 1, &frame.imageInFlightFence)); VK_VERIFY(vkAcquireNextImageKHR(m_device, m_swapchainObject.GetHandle(), UINT64_MAX, - m_frameContext.GetCurrentImageAvailableSemaphore(), VK_NULL_HANDLE, + frame.imageAvailableSemaphore, VK_NULL_HANDLE, &m_imageIndexAcquired)); MGLOG_D("VulkanRenderer initialized"); @@ -251,7 +252,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { } void VulkanRenderer::Render() { - VkCommandBuffer& commandBuffer = m_frameContext.GetCurrentCommandBuffer(); + auto& frame = m_frameContext.GetCurrent(); + VkCommandBuffer& commandBuffer = frame.commandBuffer; VK_VERIFY(vkResetCommandBuffer(commandBuffer, 0)); // Begin command buffer @@ -298,14 +300,15 @@ namespace MobileGL::MG_Backend::DirectVulkan { // End command buffer VK_VERIFY(vkEndCommandBuffer(commandBuffer)); - m_frameContext.SetCurrentCommandBufferRecorded(true); + frame.hasCommandBufferRecorded = true; } void VulkanRenderer::Present() { MOBILEGL_ASSERT(m_imageIndexAcquired < m_swapchainObject.GetImageCount(), "Present, acquired image index out of range"); - VkCommandBuffer& commandBuffer = m_frameContext.GetCurrentCommandBuffer(); - const Bool hasRecordedWork = m_frameContext.HasCurrentCommandBufferRecorded(); + auto& frame = m_frameContext.GetCurrent(); + VkCommandBuffer& commandBuffer = frame.commandBuffer; + const Bool hasRecordedWork = frame.hasCommandBufferRecorded; Bool needsLayoutTransitionForPresent = false; if (!hasRecordedWork) { @@ -350,18 +353,18 @@ namespace MobileGL::MG_Backend::DirectVulkan { // 1) Submit current frame work. VkSubmitInfo submitInfo{}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - VkSemaphore waitSemaphores[] = {m_frameContext.GetCurrentImageAvailableSemaphore()}; + VkSemaphore waitSemaphores[] = {frame.imageAvailableSemaphore}; VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT}; submitInfo.waitSemaphoreCount = 1; submitInfo.pWaitSemaphores = waitSemaphores; submitInfo.pWaitDstStageMask = waitStages; submitInfo.commandBufferCount = shouldSubmitCommandBuffer ? 1U : 0U; submitInfo.pCommandBuffers = shouldSubmitCommandBuffer ? &commandBuffer : nullptr; - VkSemaphore signalSemaphores[] = {m_frameContext.GetCurrentRenderFinishedSemaphore()}; + VkSemaphore signalSemaphores[] = {frame.renderFinishedSemaphore}; submitInfo.signalSemaphoreCount = 1; submitInfo.pSignalSemaphores = signalSemaphores; - VK_VERIFY(vkQueueSubmit(m_graphicsQueue, 1, &submitInfo, m_frameContext.GetCurrentImageInFlightFence())); - m_frameContext.SetCurrentCommandBufferRecorded(false); + VK_VERIFY(vkQueueSubmit(m_graphicsQueue, 1, &submitInfo, frame.imageInFlightFence)); + frame.hasCommandBufferRecorded = false; m_swapchainObject.SetImageLayout(m_imageIndexAcquired, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); // 2) Present current frame. @@ -383,16 +386,17 @@ namespace MobileGL::MG_Backend::DirectVulkan { VK_VERIFY(result, "Present, vkQueuePresentKHR"); // 3) Advance frame slot. - m_frameContext.AdvanceFrame(); + m_frameContext.AdvanceToNext(); // 4) Wait/reset/acquire for next frame. - VK_VERIFY(vkWaitForFences(m_device, 1, &m_frameContext.GetCurrentImageInFlightFence(), VK_TRUE, UINT64_MAX), + auto& nextFrame = m_frameContext.GetCurrent(); + VK_VERIFY(vkWaitForFences(m_device, 1, &nextFrame.imageInFlightFence, VK_TRUE, UINT64_MAX), "Present, vkWaitForFences"); - VK_VERIFY(vkResetFences(m_device, 1, &m_frameContext.GetCurrentImageInFlightFence()), + VK_VERIFY(vkResetFences(m_device, 1, &nextFrame.imageInFlightFence), "Present, vkResetFences"); result = vkAcquireNextImageKHR(m_device, m_swapchainObject.GetHandle(), UINT64_MAX, - m_frameContext.GetCurrentImageAvailableSemaphore(), VK_NULL_HANDLE, + nextFrame.imageAvailableSemaphore, VK_NULL_HANDLE, &m_imageIndexAcquired); if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { MGLOG_D("Present, vkAcquireNextImageKHR got %d, recreating swapchain", result); @@ -912,7 +916,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { CreateSwapchain(); CreateDefaultRenderPass(); CreateDefaultFramebuffers(); - m_frameContext.ResetPerFrameState(); + if (m_frameContext.GetFrameCount() > 0) { + m_frameContext.GetCurrent().hasCommandBufferRecorded = false; + } } } // namespace MobileGL::MG_Backend::DirectVulkan