[Feat] (MG_Test/Backend/DirectVulkan): use per-frame resources

This commit is contained in:
2026-02-14 21:16:27 +08:00
parent 7bd4d74b2c
commit 81125dd06d
2 changed files with 49 additions and 38 deletions
@@ -222,9 +222,16 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkSemaphoreCreateInfo semaphoreInfo{VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO}; VkSemaphoreCreateInfo semaphoreInfo{VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
VkFenceCreateInfo fenceInfo{VK_STRUCTURE_TYPE_FENCE_CREATE_INFO}; VkFenceCreateInfo fenceInfo{VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
VK_VERIFY(vkCreateSemaphore(m_device, &semaphoreInfo, nullptr, &m_imageAvailableSemaphore));
VK_VERIFY(vkCreateSemaphore(m_device, &semaphoreInfo, nullptr, &m_renderFinishedSemaphore)); m_imageAvailableSemaphores.resize(m_swapchainImages.size(), VK_NULL_HANDLE);
VK_VERIFY(vkCreateFence(m_device, &fenceInfo, nullptr, &m_imageInFlightFence)); m_renderFinishedSemaphores.resize(m_swapchainImages.size(), VK_NULL_HANDLE);
m_imageInFlightFences.resize(m_swapchainImages.size(), VK_NULL_HANDLE);
for (SizeT i = 0; i < m_swapchainImages.size(); i++) {
VK_VERIFY(vkCreateSemaphore(m_device, &semaphoreInfo, nullptr, &m_imageAvailableSemaphores[i]));
VK_VERIFY(vkCreateSemaphore(m_device, &semaphoreInfo, nullptr, &m_renderFinishedSemaphores[i]));
VK_VERIFY(vkCreateFence(m_device, &fenceInfo, nullptr, &m_imageInFlightFences[i]));
}
MGLOG_I("CreateSyncObjects completed"); MGLOG_I("CreateSyncObjects completed");
} }
@@ -237,7 +244,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
CreateSwapchain(); CreateSwapchain();
CreateSwapchainImageViews(); CreateSwapchainImageViews();
CreateCommandPool(); CreateCommandPool();
CreateCommandBuffer(); CreateCommandBuffers();
CreateDefaultRenderPass(); CreateDefaultRenderPass();
PrepareDemoPipeline(); PrepareDemoPipeline();
CreateSyncObjects(); CreateSyncObjects();
@@ -247,18 +254,20 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void VulkanRenderer::Shutdown() { void VulkanRenderer::Shutdown() {
VK_VERIFY(vkDeviceWaitIdle(m_device)); VK_VERIFY(vkDeviceWaitIdle(m_device));
if (m_imageInFlightFence != VK_NULL_HANDLE) { for (auto fence: m_imageInFlightFences) {
vkDestroyFence(m_device, m_imageInFlightFence, nullptr); vkDestroyFence(m_device, fence, nullptr);
m_imageInFlightFence = VK_NULL_HANDLE;
} }
m_imageInFlightFences.clear();
if (m_renderFinishedSemaphore != VK_NULL_HANDLE) { for (auto s: m_renderFinishedSemaphores) {
vkDestroySemaphore(m_device, m_renderFinishedSemaphore, nullptr); vkDestroySemaphore(m_device, s, nullptr);
} }
m_renderFinishedSemaphores.clear();
if (m_imageAvailableSemaphore != VK_NULL_HANDLE) { for (auto s: m_imageAvailableSemaphores) {
vkDestroySemaphore(m_device, m_imageAvailableSemaphore, nullptr); vkDestroySemaphore(m_device, s, nullptr);
} }
m_imageAvailableSemaphores.clear();
if (m_pipeline != VK_NULL_HANDLE) { if (m_pipeline != VK_NULL_HANDLE) {
vkDestroyPipeline(m_device, m_pipeline, nullptr); vkDestroyPipeline(m_device, m_pipeline, nullptr);
@@ -316,14 +325,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
} }
void VulkanRenderer::Render() { void VulkanRenderer::Render() {
VK_VERIFY(vkResetCommandBuffer(m_commandBuffer, 0)); VK_VERIFY(vkResetCommandBuffer(m_commandBuffers[m_currentFrameIndex], 0));
// Begin command buffer // Begin command buffer
VkCommandBufferBeginInfo beginInfo{}; VkCommandBufferBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = 0; beginInfo.flags = 0;
beginInfo.pInheritanceInfo = nullptr; beginInfo.pInheritanceInfo = nullptr;
VK_VERIFY(vkBeginCommandBuffer(m_commandBuffer, &beginInfo)); VK_VERIFY(vkBeginCommandBuffer(m_commandBuffers[m_currentFrameIndex], &beginInfo));
// Begin render pass // Begin render pass
VkRenderPassBeginInfo renderPassInfo{}; VkRenderPassBeginInfo renderPassInfo{};
@@ -335,10 +344,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
renderPassInfo.clearValueCount = 1; renderPassInfo.clearValueCount = 1;
renderPassInfo.pClearValues = &clearColor; renderPassInfo.pClearValues = &clearColor;
vkCmdBeginRenderPass(m_commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); vkCmdBeginRenderPass(m_commandBuffers[m_currentFrameIndex], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
// Render commands // Render commands
vkCmdBindPipeline(m_commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline); vkCmdBindPipeline(m_commandBuffers[m_currentFrameIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline);
VkViewport viewport{}; VkViewport viewport{};
viewport.x = 0.0f; viewport.x = 0.0f;
@@ -347,44 +356,44 @@ namespace MobileGL::MG_Backend::DirectVulkan {
viewport.height = static_cast<float>(m_swapChainExtent.height); viewport.height = static_cast<float>(m_swapChainExtent.height);
viewport.minDepth = 0.0f; viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f; viewport.maxDepth = 1.0f;
vkCmdSetViewport(m_commandBuffer, 0, 1, &viewport); vkCmdSetViewport(m_commandBuffers[m_currentFrameIndex], 0, 1, &viewport);
VkRect2D scissor{}; VkRect2D scissor{};
scissor.offset = {0, 0}; scissor.offset = {0, 0};
scissor.extent = m_swapChainExtent; scissor.extent = m_swapChainExtent;
vkCmdSetScissor(m_commandBuffer, 0, 1, &scissor); vkCmdSetScissor(m_commandBuffers[m_currentFrameIndex], 0, 1, &scissor);
vkCmdDraw(m_commandBuffer, 3, 1, 0, 0); vkCmdDraw(m_commandBuffers[m_currentFrameIndex], 3, 1, 0, 0);
// End render pass // End render pass
vkCmdEndRenderPass(m_commandBuffer); vkCmdEndRenderPass(m_commandBuffers[m_currentFrameIndex]);
// End command buffer // End command buffer
VK_VERIFY(vkEndCommandBuffer(m_commandBuffer)); VK_VERIFY(vkEndCommandBuffer(m_commandBuffers[m_currentFrameIndex]));
} }
void VulkanRenderer::Present() { void VulkanRenderer::Present() {
VK_VERIFY(vkWaitForFences(m_device, 1, &m_imageInFlightFence, VK_TRUE, UINT64_MAX)); VK_VERIFY(vkWaitForFences(m_device, 1, &m_imageInFlightFences[m_currentFrameIndex], VK_TRUE, UINT64_MAX));
VK_VERIFY(vkResetFences(m_device, 1, &m_imageInFlightFence)); VK_VERIFY(vkResetFences(m_device, 1, &m_imageInFlightFences[m_currentFrameIndex]));
VK_VERIFY(vkAcquireNextImageKHR(m_device, m_swapchain, UINT64_MAX, m_imageAvailableSemaphore, VK_NULL_HANDLE, &m_imageIndexAcquired)); VK_VERIFY(vkAcquireNextImageKHR(m_device, m_swapchain, UINT64_MAX, m_imageAvailableSemaphores[m_currentFrameIndex], VK_NULL_HANDLE, &m_imageIndexAcquired));
// Do demo drawing // Do demo drawing
VK_VERIFY(vkResetCommandBuffer(m_commandBuffer, 0)); VK_VERIFY(vkResetCommandBuffer(m_commandBuffers[m_currentFrameIndex], 0));
Render(); Render();
VkSubmitInfo submitInfo{}; VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
VkSemaphore waitSemaphores[] = {m_imageAvailableSemaphore}; VkSemaphore waitSemaphores[] = {m_imageAvailableSemaphores[m_currentFrameIndex]};
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT}; VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT};
submitInfo.waitSemaphoreCount = 1; submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = waitSemaphores; submitInfo.pWaitSemaphores = waitSemaphores;
submitInfo.pWaitDstStageMask = waitStages; submitInfo.pWaitDstStageMask = waitStages;
submitInfo.commandBufferCount = 1; submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &m_commandBuffer; submitInfo.pCommandBuffers = &m_commandBuffers[m_currentFrameIndex];
VkSemaphore signalSemaphores[] = {m_renderFinishedSemaphore}; VkSemaphore signalSemaphores[] = {m_renderFinishedSemaphores[m_currentFrameIndex]};
submitInfo.signalSemaphoreCount = 1; submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = signalSemaphores; submitInfo.pSignalSemaphores = signalSemaphores;
VK_VERIFY(vkQueueSubmit(m_graphicsQueue, 1, &submitInfo, m_imageInFlightFence)); VK_VERIFY(vkQueueSubmit(m_graphicsQueue, 1, &submitInfo, m_imageInFlightFences[m_currentFrameIndex]));
VkPresentInfoKHR presentInfo{}; VkPresentInfoKHR presentInfo{};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
@@ -398,6 +407,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
presentInfo.pImageIndices = &m_imageIndexAcquired; presentInfo.pImageIndices = &m_imageIndexAcquired;
presentInfo.pResults = nullptr; presentInfo.pResults = nullptr;
VK_VERIFY(vkQueuePresentKHR(m_presentQueue, &presentInfo)); VK_VERIFY(vkQueuePresentKHR(m_presentQueue, &presentInfo));
m_currentFrameIndex = (m_currentFrameIndex + 1) % m_swapchainImages.size();
} }
void VulkanRenderer::CreateInstance() { void VulkanRenderer::CreateInstance() {
@@ -1102,7 +1112,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Uint32 gotImageCount = 0; Uint32 gotImageCount = 0;
VK_VERIFY(vkGetSwapchainImagesKHR(m_device, m_swapchain, &gotImageCount, nullptr)); VK_VERIFY(vkGetSwapchainImagesKHR(m_device, m_swapchain, &gotImageCount, nullptr));
m_swapchainImages.resize(gotImageCount); m_swapchainImages.resize(gotImageCount, VK_NULL_HANDLE);
VK_VERIFY(vkGetSwapchainImagesKHR(m_device, m_swapchain, &gotImageCount, m_swapchainImages.data())); VK_VERIFY(vkGetSwapchainImagesKHR(m_device, m_swapchain, &gotImageCount, m_swapchainImages.data()));
m_swapChainExtent = swapchainCaps.currentExtent; m_swapChainExtent = swapchainCaps.currentExtent;
@@ -1110,7 +1120,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
} }
void VulkanRenderer::CreateSwapchainImageViews() { void VulkanRenderer::CreateSwapchainImageViews() {
m_swapchainImageViews.resize(m_swapchainImages.size()); m_swapchainImageViews.resize(m_swapchainImages.size(), VK_NULL_HANDLE);
for (size_t i = 0; i < m_swapchainImageViews.size(); i++) { for (size_t i = 0; i < m_swapchainImageViews.size(); i++) {
VkImageViewCreateInfo createInfo{}; VkImageViewCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
@@ -1139,13 +1149,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
MGLOG_I("Command pool created"); MGLOG_I("Command pool created");
} }
void VulkanRenderer::CreateCommandBuffer() { void VulkanRenderer::CreateCommandBuffers() {
m_commandBuffers.resize(m_swapchainImages.size(), VK_NULL_HANDLE);
VkCommandBufferAllocateInfo allocInfo{}; VkCommandBufferAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.commandPool = m_commandPool; allocInfo.commandPool = m_commandPool;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandBufferCount = 1; allocInfo.commandBufferCount = m_swapchainImages.size();
VK_VERIFY(vkAllocateCommandBuffers(m_device, &allocInfo, &m_commandBuffer)); VK_VERIFY(vkAllocateCommandBuffers(m_device, &allocInfo, m_commandBuffers.data()));
MGLOG_I("Command buffer created"); MGLOG_I("Command buffer created");
} }
@@ -94,7 +94,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkQueue m_presentQueue = VK_NULL_HANDLE; VkQueue m_presentQueue = VK_NULL_HANDLE;
VkCommandPool m_commandPool = VK_NULL_HANDLE; VkCommandPool m_commandPool = VK_NULL_HANDLE;
VkCommandBuffer m_commandBuffer = VK_NULL_HANDLE; Vector<VkCommandBuffer> m_commandBuffers;
VkRenderPass m_renderPass = VK_NULL_HANDLE; VkRenderPass m_renderPass = VK_NULL_HANDLE;
Vector<VkFramebuffer> m_framebuffers; Vector<VkFramebuffer> m_framebuffers;
@@ -104,9 +104,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Uint m_imageIndexAcquired = 0; Uint m_imageIndexAcquired = 0;
Uint m_currentFrameIndex = 0; Uint m_currentFrameIndex = 0;
VkSemaphore m_imageAvailableSemaphore; Vector<VkSemaphore> m_imageAvailableSemaphores;
VkSemaphore m_renderFinishedSemaphore; Vector<VkSemaphore> m_renderFinishedSemaphores;
VkFence m_imageInFlightFence; Vector<VkFence> m_imageInFlightFences;
void CreateInstance(); void CreateInstance();
VkResult SetupDebugMessenger(); VkResult SetupDebugMessenger();
@@ -118,7 +118,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void CreateSwapchain(); void CreateSwapchain();
void CreateSwapchainImageViews(); void CreateSwapchainImageViews();
void CreateCommandPool(); void CreateCommandPool();
void CreateCommandBuffer(); void CreateCommandBuffers();
void CreateDefaultRenderPass(); void CreateDefaultRenderPass();
void PrepareDemoPipeline(); void PrepareDemoPipeline();
void CreateSyncObjects(); void CreateSyncObjects();