[Feat] (MG_Backend/DirectVulkan): FrameContext: command buffer lifecycle

This commit is contained in:
2026-02-17 00:59:51 +08:00
parent ebdadcecff
commit a3b440e7c5
3 changed files with 48 additions and 21 deletions
@@ -70,24 +70,51 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return m_frames[currentFrameIndex]; return m_frames[currentFrameIndex];
} }
Bool FrameContext::IsCommandRecording() const {
return GetCurrent().isCommandRecording;
}
void FrameContext::AdvanceToNext() { void FrameContext::AdvanceToNext() {
MOBILEGL_ASSERT(!m_frames.empty(), "FrameContext is not initialized"); MOBILEGL_ASSERT(!m_frames.empty(), "FrameContext is not initialized");
currentFrameIndex = (currentFrameIndex + 1) % static_cast<Uint32>(m_frames.size()); currentFrameIndex = (currentFrameIndex + 1) % static_cast<Uint32>(m_frames.size());
GetCurrent().isCommandRecording = false;
GetCurrent().hasCommandBufferRecorded = false; GetCurrent().hasCommandBufferRecorded = false;
} }
VkCommandBuffer& FrameContext::BeginCommandRecording(VkCommandBufferUsageFlags flags,
const VkCommandBufferInheritanceInfo* pInheritanceInfo) {
auto& frame = GetCurrent();
MOBILEGL_ASSERT(!frame.isCommandRecording, "BeginCommandRecording called while command buffer is already recording");
frame.hasCommandBufferRecorded = false;
VK_VERIFY(vkResetCommandBuffer(frame.commandBuffer, 0), "BeginCommandRecording, vkResetCommandBuffer");
VkCommandBufferBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = flags;
beginInfo.pInheritanceInfo = pInheritanceInfo;
VK_VERIFY(vkBeginCommandBuffer(frame.commandBuffer, &beginInfo), "BeginCommandRecording, vkBeginCommandBuffer");
frame.isCommandRecording = true;
return frame.commandBuffer;
}
void FrameContext::EndCommandRecording() {
auto& frame = GetCurrent();
MOBILEGL_ASSERT(frame.isCommandRecording, "EndCommandRecording called without active command buffer recording");
VK_VERIFY(vkEndCommandBuffer(frame.commandBuffer), "EndCommandRecording, vkEndCommandBuffer");
frame.isCommandRecording = false;
frame.hasCommandBufferRecorded = true;
}
Bool FrameContext::TransitionToPresent(VkImage image, VkImageLayout oldLayout, VkImageLayout presentLayout) { Bool FrameContext::TransitionToPresent(VkImage image, VkImageLayout oldLayout, VkImageLayout presentLayout) {
auto& frame = GetCurrent(); auto& frame = GetCurrent();
if (frame.hasCommandBufferRecorded || oldLayout == presentLayout || if (frame.hasCommandBufferRecorded || frame.isCommandRecording || oldLayout == presentLayout ||
oldLayout == VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR) { oldLayout == VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR) {
return false; return false;
} }
VK_VERIFY(vkResetCommandBuffer(frame.commandBuffer, 0), "TransitionToPresent, vkResetCommandBuffer"); auto& commandBuffer = BeginCommandRecording();
VkCommandBufferBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
VK_VERIFY(vkBeginCommandBuffer(frame.commandBuffer, &beginInfo), "TransitionToPresent, vkBeginCommandBuffer");
VkImageMemoryBarrier presentBarrier{}; VkImageMemoryBarrier presentBarrier{};
presentBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; presentBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
@@ -103,15 +130,16 @@ namespace MobileGL::MG_Backend::DirectVulkan {
presentBarrier.subresourceRange.levelCount = 1; presentBarrier.subresourceRange.levelCount = 1;
presentBarrier.subresourceRange.baseArrayLayer = 0; presentBarrier.subresourceRange.baseArrayLayer = 0;
presentBarrier.subresourceRange.layerCount = 1; presentBarrier.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(frame.commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0,
0, 0, nullptr, 0, nullptr, 1, &presentBarrier); nullptr, 0, nullptr, 1, &presentBarrier);
VK_VERIFY(vkEndCommandBuffer(frame.commandBuffer), "TransitionToPresent, vkEndCommandBuffer"); EndCommandRecording();
return true; return true;
} }
FrameContext::SubmitInfoPacket FrameContext::GetSubmitInfo(Bool shouldSubmitCommandBuffer) const { FrameContext::SubmitInfoPacket FrameContext::GetSubmitInfo(Bool shouldSubmitCommandBuffer) const {
const auto& frame = GetCurrent(); const auto& frame = GetCurrent();
MOBILEGL_ASSERT(!frame.isCommandRecording, "GetSubmitInfo called while command buffer recording is still active");
SubmitInfoPacket packet{}; SubmitInfoPacket packet{};
packet.waitSemaphore = frame.imageAvailableSemaphore; packet.waitSemaphore = frame.imageAvailableSemaphore;
packet.signalSemaphore = frame.renderFinishedSemaphore; packet.signalSemaphore = frame.renderFinishedSemaphore;
@@ -202,6 +230,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
} }
frame.hasCommandBufferRecorded = false; frame.hasCommandBufferRecorded = false;
frame.isCommandRecording = false;
return VK_SUCCESS; return VK_SUCCESS;
} }
@@ -222,6 +251,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
vkDestroySemaphore(device, frame.imageAvailableSemaphore, nullptr); vkDestroySemaphore(device, frame.imageAvailableSemaphore, nullptr);
} }
frame.imageAvailableSemaphore = VK_NULL_HANDLE; frame.imageAvailableSemaphore = VK_NULL_HANDLE;
frame.isCommandRecording = false;
frame.hasCommandBufferRecorded = false; frame.hasCommandBufferRecorded = false;
} }
} // namespace MobileGL::MG_Backend::DirectVulkan } // namespace MobileGL::MG_Backend::DirectVulkan
@@ -34,6 +34,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkSemaphore imageAvailableSemaphore = VK_NULL_HANDLE; VkSemaphore imageAvailableSemaphore = VK_NULL_HANDLE;
VkSemaphore renderFinishedSemaphore = VK_NULL_HANDLE; VkSemaphore renderFinishedSemaphore = VK_NULL_HANDLE;
VkFence imageInFlightFence = VK_NULL_HANDLE; VkFence imageInFlightFence = VK_NULL_HANDLE;
Bool isCommandRecording = false;
Bool hasCommandBufferRecorded = false; Bool hasCommandBufferRecorded = false;
}; };
@@ -43,7 +44,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// Lifecycle functions // Lifecycle functions
FrameData& GetCurrent(); FrameData& GetCurrent();
const FrameData& GetCurrent() const; const FrameData& GetCurrent() const;
Bool IsCommandRecording() const;
void AdvanceToNext(); void AdvanceToNext();
VkCommandBuffer& BeginCommandRecording(VkCommandBufferUsageFlags flags = 0,
const VkCommandBufferInheritanceInfo* pInheritanceInfo = nullptr);
void EndCommandRecording();
Bool TransitionToPresent(VkImage image, VkImageLayout oldLayout, Bool TransitionToPresent(VkImage image, VkImageLayout oldLayout,
VkImageLayout presentLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); VkImageLayout presentLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
SubmitInfoPacket GetSubmitInfo(Bool shouldSubmitCommandBuffer) const; SubmitInfoPacket GetSubmitInfo(Bool shouldSubmitCommandBuffer) const;
@@ -248,16 +248,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
} }
void VulkanRenderer::Render() { void VulkanRenderer::Render() {
auto& frame = m_frameContext.GetCurrent(); VkCommandBuffer& commandBuffer = m_frameContext.BeginCommandRecording();
VkCommandBuffer& commandBuffer = frame.commandBuffer;
VK_VERIFY(vkResetCommandBuffer(commandBuffer, 0));
// Begin command buffer
VkCommandBufferBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = 0;
beginInfo.pInheritanceInfo = nullptr;
VK_VERIFY(vkBeginCommandBuffer(commandBuffer, &beginInfo));
// Begin render pass // Begin render pass
VkRenderPassBeginInfo renderPassInfo{}; VkRenderPassBeginInfo renderPassInfo{};
@@ -295,8 +286,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
vkCmdEndRenderPass(commandBuffer); vkCmdEndRenderPass(commandBuffer);
// End command buffer // End command buffer
VK_VERIFY(vkEndCommandBuffer(commandBuffer)); m_frameContext.EndCommandRecording();
frame.hasCommandBufferRecorded = true;
} }
void VulkanRenderer::Present() { void VulkanRenderer::Present() {
@@ -311,6 +301,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// 1) Submit current frame work. // 1) Submit current frame work.
auto submitPacket = m_frameContext.GetSubmitInfo(shouldSubmitCommandBuffer); auto submitPacket = m_frameContext.GetSubmitInfo(shouldSubmitCommandBuffer);
VK_VERIFY(vkQueueSubmit(m_graphicsQueue, 1, &submitPacket.submitInfo, frame.imageInFlightFence)); VK_VERIFY(vkQueueSubmit(m_graphicsQueue, 1, &submitPacket.submitInfo, frame.imageInFlightFence));
frame.isCommandRecording = false;
frame.hasCommandBufferRecorded = false; frame.hasCommandBufferRecorded = false;
m_swapchainObject.SetImageLayout(m_imageIndexAcquired, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); m_swapchainObject.SetImageLayout(m_imageIndexAcquired, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
@@ -848,6 +839,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
CreateDefaultRenderPass(); CreateDefaultRenderPass();
CreateDefaultFramebuffers(); CreateDefaultFramebuffers();
if (m_frameContext.GetFrameCount() > 0) { if (m_frameContext.GetFrameCount() > 0) {
m_frameContext.GetCurrent().isCommandRecording = false;
m_frameContext.GetCurrent().hasCommandBufferRecorded = false; m_frameContext.GetCurrent().hasCommandBufferRecorded = false;
} }
} }