Vulkan: return to more conventional swapchain sync method, encapsulate more code (#525)

This commit is contained in:
goeiecool9999 2022-11-25 09:51:47 +01:00 committed by GitHub
parent 8162477dc1
commit bc104859f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 102 additions and 39 deletions

View File

@ -13,7 +13,12 @@ void SwapchainInfoVk::Create(VkPhysicalDevice physicalDevice, VkDevice logicalDe
m_surfaceFormat = ChooseSurfaceFormat(details.formats); m_surfaceFormat = ChooseSurfaceFormat(details.formats);
m_actualExtent = ChooseSwapExtent(details.capabilities); m_actualExtent = ChooseSwapExtent(details.capabilities);
uint32_t image_count = details.capabilities.minImageCount; // use at least two swapchain images. fewer than that causes problems on some drivers
uint32_t image_count = std::max(2u, details.capabilities.minImageCount);
if(details.capabilities.maxImageCount > 0)
image_count = std::min(image_count, details.capabilities.maxImageCount);
if(image_count < 2)
cemuLog_force("Vulkan: Swapchain image count less than 2 may cause problems");
VkSwapchainCreateInfoKHR create_info = CreateSwapchainCreateInfo(surface, details, m_surfaceFormat, image_count, m_actualExtent); VkSwapchainCreateInfoKHR create_info = CreateSwapchainCreateInfo(surface, details, m_surfaceFormat, image_count, m_actualExtent);
create_info.oldSwapchain = nullptr; create_info.oldSwapchain = nullptr;
@ -103,15 +108,25 @@ void SwapchainInfoVk::Create(VkPhysicalDevice physicalDevice, VkDevice logicalDe
if (result != VK_SUCCESS) if (result != VK_SUCCESS)
UnrecoverableError("Failed to create framebuffer for swapchain"); UnrecoverableError("Failed to create framebuffer for swapchain");
} }
m_swapchainPresentSemaphores.resize(m_swapchainImages.size());
// create present semaphore m_presentSemaphores.resize(m_swapchainImages.size());
// create present semaphores
VkSemaphoreCreateInfo info = {}; VkSemaphoreCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
for (auto& semaphore : m_swapchainPresentSemaphores){ for (auto& semaphore : m_presentSemaphores){
if (vkCreateSemaphore(logicalDevice, &info, nullptr, &semaphore) != VK_SUCCESS) if (vkCreateSemaphore(logicalDevice, &info, nullptr, &semaphore) != VK_SUCCESS)
UnrecoverableError("Failed to create semaphore for swapchain present"); UnrecoverableError("Failed to create semaphore for swapchain present");
} }
m_acquireSemaphores.resize(m_swapchainImages.size());
// create acquire semaphores
info = {};
info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
for (auto& semaphore : m_acquireSemaphores){
if (vkCreateSemaphore(logicalDevice, &info, nullptr, &semaphore) != VK_SUCCESS)
UnrecoverableError("Failed to create semaphore for swapchain acquire");
}
VkFenceCreateInfo fenceInfo = {}; VkFenceCreateInfo fenceInfo = {};
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
@ -119,6 +134,7 @@ void SwapchainInfoVk::Create(VkPhysicalDevice physicalDevice, VkDevice logicalDe
if (result != VK_SUCCESS) if (result != VK_SUCCESS)
UnrecoverableError("Failed to create fence for swapchain"); UnrecoverableError("Failed to create fence for swapchain");
m_acquireIndex = 0;
hasDefinedSwapchainImage = false; hasDefinedSwapchainImage = false;
} }
@ -126,9 +142,13 @@ void SwapchainInfoVk::Cleanup()
{ {
m_swapchainImages.clear(); m_swapchainImages.clear();
for (auto& sem: m_swapchainPresentSemaphores) for (auto& sem: m_acquireSemaphores)
vkDestroySemaphore(m_logicalDevice, sem, nullptr); vkDestroySemaphore(m_logicalDevice, sem, nullptr);
m_swapchainPresentSemaphores.clear(); m_acquireSemaphores.clear();
for (auto& sem: m_presentSemaphores)
vkDestroySemaphore(m_logicalDevice, sem, nullptr);
m_presentSemaphores.clear();
if (m_swapchainRenderPass) if (m_swapchainRenderPass)
{ {
@ -159,12 +179,55 @@ void SwapchainInfoVk::Cleanup()
bool SwapchainInfoVk::IsValid() const bool SwapchainInfoVk::IsValid() const
{ {
return swapchain && m_imageAvailableFence; return swapchain && !m_acquireSemaphores.empty();
} }
void SwapchainInfoVk::WaitAvailableFence() const void SwapchainInfoVk::WaitAvailableFence()
{ {
vkWaitForFences(m_logicalDevice, 1, &m_imageAvailableFence, VK_TRUE, UINT64_MAX); if(m_awaitableFence != VK_NULL_HANDLE)
vkWaitForFences(m_logicalDevice, 1, &m_awaitableFence, VK_TRUE, UINT64_MAX);
m_awaitableFence = VK_NULL_HANDLE;
}
void SwapchainInfoVk::ResetAvailableFence() const
{
vkResetFences(m_logicalDevice, 1, &m_imageAvailableFence);
}
VkSemaphore SwapchainInfoVk::ConsumeAcquireSemaphore()
{
VkSemaphore ret = m_currentSemaphore;
m_currentSemaphore = VK_NULL_HANDLE;
return ret;
}
bool SwapchainInfoVk::AcquireImage(uint64 timeout)
{
WaitAvailableFence();
ResetAvailableFence();
VkSemaphore acquireSemaphore = m_acquireSemaphores[m_acquireIndex];
VkResult result = vkAcquireNextImageKHR(m_logicalDevice, swapchain, timeout, acquireSemaphore, m_imageAvailableFence, &swapchainImageIndex);
if (result == VK_TIMEOUT)
{
return false;
}
else if (result != VK_SUCCESS)
{
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR)
m_shouldRecreate = true;
if (result == VK_ERROR_OUT_OF_DATE_KHR)
return false;
if (result != VK_ERROR_OUT_OF_DATE_KHR && result != VK_SUBOPTIMAL_KHR)
throw std::runtime_error(fmt::format("Failed to acquire next image: {}", result));
}
m_currentSemaphore = acquireSemaphore;
m_awaitableFence = m_imageAvailableFence;
m_acquireIndex = (m_acquireIndex + 1) % m_swapchainImages.size();
return true;
} }
void SwapchainInfoVk::UnrecoverableError(const char* errMsg) void SwapchainInfoVk::UnrecoverableError(const char* errMsg)

View File

@ -34,7 +34,14 @@ struct SwapchainInfoVk
bool IsValid() const; bool IsValid() const;
void WaitAvailableFence() const; void WaitAvailableFence();
void ResetAvailableFence() const;
bool AcquireImage(uint64 timeout);
// retrieve semaphore of last acquire for submitting a wait operation
// only one wait operation must be submitted per acquire (which submits a single signal operation)
// therefore subsequent calls will return a NULL handle
VkSemaphore ConsumeAcquireSemaphore();
static void UnrecoverableError(const char* errMsg); static void UnrecoverableError(const char* errMsg);
@ -76,7 +83,6 @@ struct SwapchainInfoVk
VkSurfaceFormatKHR m_surfaceFormat{}; VkSurfaceFormatKHR m_surfaceFormat{};
VkSwapchainKHR swapchain{}; VkSwapchainKHR swapchain{};
Vector2i m_desiredExtent{}; Vector2i m_desiredExtent{};
VkFence m_imageAvailableFence{};
uint32 swapchainImageIndex = (uint32)-1; uint32 swapchainImageIndex = (uint32)-1;
@ -84,11 +90,17 @@ struct SwapchainInfoVk
std::vector<VkImage> m_swapchainImages; std::vector<VkImage> m_swapchainImages;
std::vector<VkImageView> m_swapchainImageViews; std::vector<VkImageView> m_swapchainImageViews;
std::vector<VkFramebuffer> m_swapchainFramebuffers; std::vector<VkFramebuffer> m_swapchainFramebuffers;
std::vector<VkSemaphore> m_swapchainPresentSemaphores; std::vector<VkSemaphore> m_presentSemaphores; // indexed by swapchainImageIndex
std::array<uint32, 2> m_swapchainQueueFamilyIndices;
VkRenderPass m_swapchainRenderPass = nullptr; VkRenderPass m_swapchainRenderPass = nullptr;
private: private:
uint32 m_acquireIndex = 0;
std::vector<VkSemaphore> m_acquireSemaphores; // indexed by m_acquireIndex
VkFence m_imageAvailableFence{};
VkSemaphore m_currentSemaphore = VK_NULL_HANDLE;
VkFence m_awaitableFence = VK_NULL_HANDLE;
std::array<uint32, 2> m_swapchainQueueFamilyIndices;
VkExtent2D m_actualExtent{}; VkExtent2D m_actualExtent{};
}; };

View File

@ -1665,7 +1665,6 @@ bool VulkanRenderer::ImguiBegin(bool mainWindow)
draw_endRenderPass(); draw_endRenderPass();
m_state.currentPipeline = VK_NULL_HANDLE; m_state.currentPipeline = VK_NULL_HANDLE;
chainInfo.WaitAvailableFence();
ImGui_ImplVulkan_CreateFontsTexture(m_state.currentCommandBuffer); ImGui_ImplVulkan_CreateFontsTexture(m_state.currentCommandBuffer);
ImGui_ImplVulkan_NewFrame(m_state.currentCommandBuffer, chainInfo.m_swapchainFramebuffers[chainInfo.swapchainImageIndex], chainInfo.getExtent()); ImGui_ImplVulkan_NewFrame(m_state.currentCommandBuffer, chainInfo.m_swapchainFramebuffers[chainInfo.swapchainImageIndex], chainInfo.getExtent());
ImGui_UpdateWindowInformation(mainWindow); ImGui_UpdateWindowInformation(mainWindow);
@ -1722,7 +1721,6 @@ bool VulkanRenderer::BeginFrame(bool mainWindow)
auto& chainInfo = GetChainInfo(mainWindow); auto& chainInfo = GetChainInfo(mainWindow);
chainInfo.WaitAvailableFence();
VkClearColorValue clearColor{ 0, 0, 0, 0 }; VkClearColorValue clearColor{ 0, 0, 0, 0 };
ClearColorImageRaw(chainInfo.m_swapchainImages[chainInfo.swapchainImageIndex], 0, 0, clearColor, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); ClearColorImageRaw(chainInfo.m_swapchainImages[chainInfo.swapchainImageIndex], 0, 0, clearColor, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
@ -1848,7 +1846,7 @@ void VulkanRenderer::WaitForNextFinishedCommandBuffer()
ProcessFinishedCommandBuffers(); ProcessFinishedCommandBuffers();
} }
void VulkanRenderer::SubmitCommandBuffer(VkSemaphore* signalSemaphore, VkSemaphore* waitSemaphore) void VulkanRenderer::SubmitCommandBuffer(VkSemaphore signalSemaphore, VkSemaphore waitSemaphore)
{ {
draw_endRenderPass(); draw_endRenderPass();
@ -1863,11 +1861,11 @@ void VulkanRenderer::SubmitCommandBuffer(VkSemaphore* signalSemaphore, VkSemapho
// signal current command buffer semaphore // signal current command buffer semaphore
VkSemaphore signalSemArray[2]; VkSemaphore signalSemArray[2];
if (signalSemaphore) if (signalSemaphore != VK_NULL_HANDLE)
{ {
submitInfo.signalSemaphoreCount = 2; submitInfo.signalSemaphoreCount = 2;
signalSemArray[0] = m_commandBufferSemaphores[m_commandBufferIndex]; // signal current signalSemArray[0] = m_commandBufferSemaphores[m_commandBufferIndex]; // signal current
signalSemArray[1] = *signalSemaphore; // signal current signalSemArray[1] = signalSemaphore; // signal current
submitInfo.pSignalSemaphores = signalSemArray; submitInfo.pSignalSemaphores = signalSemArray;
} }
else else
@ -1883,8 +1881,8 @@ void VulkanRenderer::SubmitCommandBuffer(VkSemaphore* signalSemaphore, VkSemapho
submitInfo.waitSemaphoreCount = 0; submitInfo.waitSemaphoreCount = 0;
if (m_numSubmittedCmdBuffers > 0) if (m_numSubmittedCmdBuffers > 0)
waitSemArray[submitInfo.waitSemaphoreCount++] = prevSem; // wait on semaphore from previous submit waitSemArray[submitInfo.waitSemaphoreCount++] = prevSem; // wait on semaphore from previous submit
if (waitSemaphore) if (waitSemaphore != VK_NULL_HANDLE)
waitSemArray[submitInfo.waitSemaphoreCount++] = *waitSemaphore; waitSemArray[submitInfo.waitSemaphoreCount++] = waitSemaphore;
submitInfo.pWaitDstStageMask = semWaitStageMask; submitInfo.pWaitDstStageMask = semWaitStageMask;
submitInfo.pWaitSemaphores = waitSemArray; submitInfo.pWaitSemaphores = waitSemArray;
@ -2546,20 +2544,11 @@ bool VulkanRenderer::AcquireNextSwapchainImage(bool mainWindow)
if (!UpdateSwapchainProperties(mainWindow)) if (!UpdateSwapchainProperties(mainWindow))
return false; return false;
vkResetFences(m_logicalDevice, 1, &chainInfo.m_imageAvailableFence); bool result = chainInfo.AcquireImage(UINT64_MAX);
VkResult result = vkAcquireNextImageKHR(m_logicalDevice, chainInfo.swapchain, std::numeric_limits<uint64_t>::max(), VK_NULL_HANDLE, chainInfo.m_imageAvailableFence, &chainInfo.swapchainImageIndex); if (!result)
if (result != VK_SUCCESS) return false;
{
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR)
chainInfo.m_shouldRecreate = true;
if (result == VK_ERROR_OUT_OF_DATE_KHR)
return false;
if (result != VK_ERROR_OUT_OF_DATE_KHR && result != VK_SUBOPTIMAL_KHR)
throw std::runtime_error(fmt::format("Failed to acquire next image: {}", result));
}
SubmitCommandBuffer(VK_NULL_HANDLE, chainInfo.ConsumeAcquireSemaphore());
return true; return true;
} }
@ -2568,6 +2557,8 @@ void VulkanRenderer::RecreateSwapchain(bool mainWindow, bool skipCreate)
SubmitCommandBuffer(); SubmitCommandBuffer();
WaitDeviceIdle(); WaitDeviceIdle();
auto& chainInfo = GetChainInfo(mainWindow); auto& chainInfo = GetChainInfo(mainWindow);
// make sure fence has no signal operation submitted
chainInfo.WaitAvailableFence();
Vector2i size; Vector2i size;
if (mainWindow) if (mainWindow)
@ -2633,14 +2624,13 @@ void VulkanRenderer::SwapBuffer(bool mainWindow)
if (!chainInfo.hasDefinedSwapchainImage) if (!chainInfo.hasDefinedSwapchainImage)
{ {
chainInfo.WaitAvailableFence();
// set the swapchain image to a defined state // set the swapchain image to a defined state
VkClearColorValue clearColor{ 0, 0, 0, 0 }; VkClearColorValue clearColor{ 0, 0, 0, 0 };
ClearColorImageRaw(chainInfo.m_swapchainImages[chainInfo.swapchainImageIndex], 0, 0, clearColor, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); ClearColorImageRaw(chainInfo.m_swapchainImages[chainInfo.swapchainImageIndex], 0, 0, clearColor, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
} }
VkSemaphore presentSemaphore = chainInfo.m_swapchainPresentSemaphores[chainInfo.swapchainImageIndex]; VkSemaphore presentSemaphore = chainInfo.m_presentSemaphores[chainInfo.swapchainImageIndex];
SubmitCommandBuffer(&presentSemaphore); // submit all command and signal semaphore SubmitCommandBuffer(presentSemaphore); // submit all command and signal semaphore
cemu_assert_debug(m_numSubmittedCmdBuffers > 0); cemu_assert_debug(m_numSubmittedCmdBuffers > 0);
@ -2701,7 +2691,6 @@ void VulkanRenderer::ClearColorbuffer(bool padView)
if (chainInfo.swapchainImageIndex == -1) if (chainInfo.swapchainImageIndex == -1)
return; return;
chainInfo.WaitAvailableFence();
VkClearColorValue clearColor{ 0, 0, 0, 0 }; VkClearColorValue clearColor{ 0, 0, 0, 0 };
ClearColorImageRaw(chainInfo.m_swapchainImages[chainInfo.swapchainImageIndex], 0, 0, clearColor, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL); ClearColorImageRaw(chainInfo.m_swapchainImages[chainInfo.swapchainImageIndex], 0, 0, clearColor, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL);
} }
@ -2792,7 +2781,6 @@ void VulkanRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutpu
LatteTextureViewVk* texViewVk = (LatteTextureViewVk*)texView; LatteTextureViewVk* texViewVk = (LatteTextureViewVk*)texView;
draw_endRenderPass(); draw_endRenderPass();
chainInfo.WaitAvailableFence();
if (clearBackground) if (clearBackground)
ClearColorbuffer(padView); ClearColorbuffer(padView);

View File

@ -233,7 +233,7 @@ public:
void InitFirstCommandBuffer(); void InitFirstCommandBuffer();
void ProcessFinishedCommandBuffers(); void ProcessFinishedCommandBuffers();
void WaitForNextFinishedCommandBuffer(); void WaitForNextFinishedCommandBuffer();
void SubmitCommandBuffer(VkSemaphore* signalSemaphore = nullptr, VkSemaphore* waitSemaphore = nullptr); void SubmitCommandBuffer(VkSemaphore signalSemaphore = VK_NULL_HANDLE, VkSemaphore waitSemaphore = VK_NULL_HANDLE);
void RequestSubmitSoon(); void RequestSubmitSoon();
void RequestSubmitOnIdle(); void RequestSubmitOnIdle();