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