mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-09 04:38:30 +09:00
[Feat] (MG_Backend/DirectVulkan): implement some FrameContext lifecycle function
This commit is contained in:
@@ -76,6 +76,90 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
GetCurrent().hasCommandBufferRecorded = false;
|
GetCurrent().hasCommandBufferRecorded = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Bool FrameContext::TransitionToPresent(VkImage image, VkImageLayout oldLayout, VkImageLayout presentLayout) {
|
||||||
|
auto& frame = GetCurrent();
|
||||||
|
if (frame.hasCommandBufferRecorded || 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");
|
||||||
|
|
||||||
|
VkImageMemoryBarrier presentBarrier{};
|
||||||
|
presentBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||||
|
presentBarrier.srcAccessMask = 0;
|
||||||
|
presentBarrier.dstAccessMask = 0;
|
||||||
|
presentBarrier.oldLayout = oldLayout;
|
||||||
|
presentBarrier.newLayout = presentLayout;
|
||||||
|
presentBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
|
presentBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
|
presentBarrier.image = image;
|
||||||
|
presentBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
presentBarrier.subresourceRange.baseMipLevel = 0;
|
||||||
|
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);
|
||||||
|
|
||||||
|
VK_VERIFY(vkEndCommandBuffer(frame.commandBuffer), "TransitionToPresent, vkEndCommandBuffer");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
FrameContext::SubmitInfoPacket FrameContext::GetSubmitInfo(Bool shouldSubmitCommandBuffer) const {
|
||||||
|
const auto& frame = GetCurrent();
|
||||||
|
SubmitInfoPacket packet{};
|
||||||
|
packet.waitSemaphore = frame.imageAvailableSemaphore;
|
||||||
|
packet.signalSemaphore = frame.renderFinishedSemaphore;
|
||||||
|
packet.commandBuffer = frame.commandBuffer;
|
||||||
|
|
||||||
|
packet.submitInfo.waitSemaphoreCount = 1;
|
||||||
|
packet.submitInfo.pWaitSemaphores = &packet.waitSemaphore;
|
||||||
|
packet.submitInfo.pWaitDstStageMask = &packet.waitDstStageMask;
|
||||||
|
packet.submitInfo.commandBufferCount = shouldSubmitCommandBuffer ? 1U : 0U;
|
||||||
|
packet.submitInfo.pCommandBuffers = shouldSubmitCommandBuffer ? &packet.commandBuffer : nullptr;
|
||||||
|
packet.submitInfo.signalSemaphoreCount = 1;
|
||||||
|
packet.submitInfo.pSignalSemaphores = &packet.signalSemaphore;
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
FrameContext::PresentInfoPacket FrameContext::GetPresentInfo(VkSwapchainKHR swapchain, const Uint32& imageIndex) const {
|
||||||
|
const auto& frame = GetCurrent();
|
||||||
|
PresentInfoPacket packet{};
|
||||||
|
packet.waitSemaphore = frame.renderFinishedSemaphore;
|
||||||
|
packet.swapchain = swapchain;
|
||||||
|
packet.imageIndex = &imageIndex;
|
||||||
|
|
||||||
|
packet.presentInfo.waitSemaphoreCount = 1;
|
||||||
|
packet.presentInfo.pWaitSemaphores = &packet.waitSemaphore;
|
||||||
|
packet.presentInfo.swapchainCount = 1;
|
||||||
|
packet.presentInfo.pSwapchains = &packet.swapchain;
|
||||||
|
packet.presentInfo.pImageIndices = packet.imageIndex;
|
||||||
|
packet.presentInfo.pResults = nullptr;
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkResult FrameContext::WaitAndAcquireNextImage(VkDevice device, VkSwapchainKHR swapchain, Uint32& outImageIndex,
|
||||||
|
Uint64 timeout, VkFence acquireFence) {
|
||||||
|
auto& frame = GetCurrent();
|
||||||
|
VkResult result = vkWaitForFences(device, 1, &frame.imageInFlightFence, VK_TRUE, timeout);
|
||||||
|
if (result != VK_SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = vkResetFences(device, 1, &frame.imageInFlightFence);
|
||||||
|
if (result != VK_SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vkAcquireNextImageKHR(device, swapchain, timeout, frame.imageAvailableSemaphore, acquireFence,
|
||||||
|
&outImageIndex);
|
||||||
|
}
|
||||||
|
|
||||||
Uint32 FrameContext::GetCurrentFrameIndex() const {
|
Uint32 FrameContext::GetCurrentFrameIndex() const {
|
||||||
return currentFrameIndex;
|
return currentFrameIndex;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,21 @@
|
|||||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||||
class FrameContext {
|
class FrameContext {
|
||||||
public:
|
public:
|
||||||
|
struct SubmitInfoPacket {
|
||||||
|
VkPipelineStageFlags waitDstStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||||
|
VkSemaphore waitSemaphore = VK_NULL_HANDLE;
|
||||||
|
VkSemaphore signalSemaphore = VK_NULL_HANDLE;
|
||||||
|
VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
|
||||||
|
VkSubmitInfo submitInfo{VK_STRUCTURE_TYPE_SUBMIT_INFO};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PresentInfoPacket {
|
||||||
|
VkSemaphore waitSemaphore = VK_NULL_HANDLE;
|
||||||
|
VkSwapchainKHR swapchain = VK_NULL_HANDLE;
|
||||||
|
const Uint32* imageIndex = nullptr;
|
||||||
|
VkPresentInfoKHR presentInfo{VK_STRUCTURE_TYPE_PRESENT_INFO_KHR};
|
||||||
|
};
|
||||||
|
|
||||||
struct FrameData {
|
struct FrameData {
|
||||||
VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
|
VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
|
||||||
VkSemaphore imageAvailableSemaphore = VK_NULL_HANDLE;
|
VkSemaphore imageAvailableSemaphore = VK_NULL_HANDLE;
|
||||||
@@ -24,9 +39,17 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
|
|
||||||
VkResult Initialize(VkDevice device, VkCommandPool commandPool, Uint32 frameCount);
|
VkResult Initialize(VkDevice device, VkCommandPool commandPool, Uint32 frameCount);
|
||||||
void Destroy(VkDevice device, VkCommandPool commandPool);
|
void Destroy(VkDevice device, VkCommandPool commandPool);
|
||||||
|
|
||||||
|
// Lifecycle functions
|
||||||
FrameData& GetCurrent();
|
FrameData& GetCurrent();
|
||||||
const FrameData& GetCurrent() const;
|
const FrameData& GetCurrent() const;
|
||||||
void AdvanceToNext();
|
void AdvanceToNext();
|
||||||
|
Bool TransitionToPresent(VkImage image, VkImageLayout oldLayout,
|
||||||
|
VkImageLayout presentLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
|
||||||
|
SubmitInfoPacket GetSubmitInfo(Bool shouldSubmitCommandBuffer) const;
|
||||||
|
PresentInfoPacket GetPresentInfo(VkSwapchainKHR swapchain, const Uint32& imageIndex) const;
|
||||||
|
VkResult WaitAndAcquireNextImage(VkDevice device, VkSwapchainKHR swapchain, Uint32& outImageIndex,
|
||||||
|
Uint64 timeout = UINT64_MAX, VkFence acquireFence = VK_NULL_HANDLE);
|
||||||
|
|
||||||
Uint32 GetCurrentFrameIndex() const;
|
Uint32 GetCurrentFrameIndex() const;
|
||||||
Uint32 GetFrameCount() const;
|
Uint32 GetFrameCount() const;
|
||||||
|
|||||||
@@ -197,12 +197,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
CreateFrameContexts();
|
CreateFrameContexts();
|
||||||
|
|
||||||
// Prime the first frame so Render() always targets an acquired swapchain image.
|
// Prime the first frame so Render() always targets an acquired swapchain image.
|
||||||
auto& frame = m_frameContext.GetCurrent();
|
VK_VERIFY(m_frameContext.WaitAndAcquireNextImage(m_device, m_swapchainObject.GetHandle(), m_imageIndexAcquired),
|
||||||
VK_VERIFY(vkWaitForFences(m_device, 1, &frame.imageInFlightFence, VK_TRUE, UINT64_MAX));
|
"Initialize, WaitAndAcquireNextImage");
|
||||||
VK_VERIFY(vkResetFences(m_device, 1, &frame.imageInFlightFence));
|
|
||||||
VK_VERIFY(vkAcquireNextImageKHR(m_device, m_swapchainObject.GetHandle(), UINT64_MAX,
|
|
||||||
frame.imageAvailableSemaphore, VK_NULL_HANDLE,
|
|
||||||
&m_imageIndexAcquired));
|
|
||||||
|
|
||||||
MGLOG_D("VulkanRenderer initialized");
|
MGLOG_D("VulkanRenderer initialized");
|
||||||
}
|
}
|
||||||
@@ -307,77 +303,20 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
MOBILEGL_ASSERT(m_imageIndexAcquired < m_swapchainObject.GetImageCount(),
|
MOBILEGL_ASSERT(m_imageIndexAcquired < m_swapchainObject.GetImageCount(),
|
||||||
"Present, acquired image index out of range");
|
"Present, acquired image index out of range");
|
||||||
auto& frame = m_frameContext.GetCurrent();
|
auto& frame = m_frameContext.GetCurrent();
|
||||||
VkCommandBuffer& commandBuffer = frame.commandBuffer;
|
const auto acquiredImageLayout = m_swapchainObject.GetImageLayout(m_imageIndexAcquired);
|
||||||
const Bool hasRecordedWork = frame.hasCommandBufferRecorded;
|
const Bool needsLayoutTransitionForPresent =
|
||||||
Bool needsLayoutTransitionForPresent = false;
|
m_frameContext.TransitionToPresent(m_swapchainObject.GetImage(m_imageIndexAcquired), acquiredImageLayout);
|
||||||
|
const Bool shouldSubmitCommandBuffer = frame.hasCommandBufferRecorded || needsLayoutTransitionForPresent;
|
||||||
if (!hasRecordedWork) {
|
|
||||||
const auto acquiredImageLayout = m_swapchainObject.GetImageLayout(m_imageIndexAcquired);
|
|
||||||
if (acquiredImageLayout != VK_IMAGE_LAYOUT_PRESENT_SRC_KHR &&
|
|
||||||
acquiredImageLayout != VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR) {
|
|
||||||
needsLayoutTransitionForPresent = true;
|
|
||||||
|
|
||||||
VK_VERIFY(vkResetCommandBuffer(commandBuffer, 0),
|
|
||||||
"Present, vkResetCommandBuffer(layout transition)");
|
|
||||||
|
|
||||||
VkCommandBufferBeginInfo beginInfo{};
|
|
||||||
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
||||||
VK_VERIFY(vkBeginCommandBuffer(commandBuffer, &beginInfo),
|
|
||||||
"Present, vkBeginCommandBuffer(layout transition)");
|
|
||||||
|
|
||||||
VkImageMemoryBarrier presentBarrier{};
|
|
||||||
presentBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
|
||||||
presentBarrier.srcAccessMask = 0;
|
|
||||||
presentBarrier.dstAccessMask = 0;
|
|
||||||
presentBarrier.oldLayout = acquiredImageLayout;
|
|
||||||
presentBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
|
||||||
presentBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
||||||
presentBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
||||||
presentBarrier.image = m_swapchainObject.GetImage(m_imageIndexAcquired);
|
|
||||||
presentBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
||||||
presentBarrier.subresourceRange.baseMipLevel = 0;
|
|
||||||
presentBarrier.subresourceRange.levelCount = 1;
|
|
||||||
presentBarrier.subresourceRange.baseArrayLayer = 0;
|
|
||||||
presentBarrier.subresourceRange.layerCount = 1;
|
|
||||||
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(commandBuffer),
|
|
||||||
"Present, vkEndCommandBuffer(layout transition)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Bool shouldSubmitCommandBuffer = hasRecordedWork || needsLayoutTransitionForPresent;
|
|
||||||
|
|
||||||
// 1) Submit current frame work.
|
// 1) Submit current frame work.
|
||||||
VkSubmitInfo submitInfo{};
|
auto submitPacket = m_frameContext.GetSubmitInfo(shouldSubmitCommandBuffer);
|
||||||
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
VK_VERIFY(vkQueueSubmit(m_graphicsQueue, 1, &submitPacket.submitInfo, frame.imageInFlightFence));
|
||||||
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[] = {frame.renderFinishedSemaphore};
|
|
||||||
submitInfo.signalSemaphoreCount = 1;
|
|
||||||
submitInfo.pSignalSemaphores = signalSemaphores;
|
|
||||||
VK_VERIFY(vkQueueSubmit(m_graphicsQueue, 1, &submitInfo, frame.imageInFlightFence));
|
|
||||||
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);
|
||||||
|
|
||||||
// 2) Present current frame.
|
// 2) Present current frame.
|
||||||
VkPresentInfoKHR presentInfo{};
|
auto presentPacket = m_frameContext.GetPresentInfo(m_swapchainObject.GetHandle(), m_imageIndexAcquired);
|
||||||
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
|
auto result = vkQueuePresentKHR(m_presentQueue, &presentPacket.presentInfo);
|
||||||
presentInfo.waitSemaphoreCount = 1;
|
|
||||||
presentInfo.pWaitSemaphores = signalSemaphores;
|
|
||||||
VkSwapchainKHR swapChains[] = {m_swapchainObject.GetHandle()};
|
|
||||||
presentInfo.swapchainCount = 1;
|
|
||||||
presentInfo.pSwapchains = swapChains;
|
|
||||||
presentInfo.pImageIndices = &m_imageIndexAcquired;
|
|
||||||
presentInfo.pResults = nullptr;
|
|
||||||
auto result = vkQueuePresentKHR(m_presentQueue, &presentInfo);
|
|
||||||
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
|
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
|
||||||
MGLOG_D("Present, vkQueuePresentKHR got %d, recreating swapchain", result);
|
MGLOG_D("Present, vkQueuePresentKHR got %d, recreating swapchain", result);
|
||||||
RecreateSwapchain();
|
RecreateSwapchain();
|
||||||
@@ -389,15 +328,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
m_frameContext.AdvanceToNext();
|
m_frameContext.AdvanceToNext();
|
||||||
|
|
||||||
// 4) Wait/reset/acquire for next frame.
|
// 4) Wait/reset/acquire for next frame.
|
||||||
auto& nextFrame = m_frameContext.GetCurrent();
|
result = m_frameContext.WaitAndAcquireNextImage(m_device, m_swapchainObject.GetHandle(), m_imageIndexAcquired);
|
||||||
VK_VERIFY(vkWaitForFences(m_device, 1, &nextFrame.imageInFlightFence, VK_TRUE, UINT64_MAX),
|
|
||||||
"Present, vkWaitForFences");
|
|
||||||
VK_VERIFY(vkResetFences(m_device, 1, &nextFrame.imageInFlightFence),
|
|
||||||
"Present, vkResetFences");
|
|
||||||
result =
|
|
||||||
vkAcquireNextImageKHR(m_device, m_swapchainObject.GetHandle(), UINT64_MAX,
|
|
||||||
nextFrame.imageAvailableSemaphore, VK_NULL_HANDLE,
|
|
||||||
&m_imageIndexAcquired);
|
|
||||||
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
|
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
|
||||||
MGLOG_D("Present, vkAcquireNextImageKHR got %d, recreating swapchain", result);
|
MGLOG_D("Present, vkAcquireNextImageKHR got %d, recreating swapchain", result);
|
||||||
RecreateSwapchain();
|
RecreateSwapchain();
|
||||||
|
|||||||
Reference in New Issue
Block a user