Skip to content

Commit

Permalink
Vulkan: return to more conventional swapchain sync method, encapsulat…
Browse files Browse the repository at this point in the history
…e more code (#525)
  • Loading branch information
goeiecool9999 committed Nov 25, 2022
1 parent 8162477 commit bc10485
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 39 deletions.
81 changes: 72 additions & 9 deletions src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ void SwapchainInfoVk::Create(VkPhysicalDevice physicalDevice, VkDevice logicalDe
m_surfaceFormat = ChooseSurfaceFormat(details.formats);
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);
create_info.oldSwapchain = nullptr;
Expand Down Expand Up @@ -103,32 +108,47 @@ void SwapchainInfoVk::Create(VkPhysicalDevice physicalDevice, VkDevice logicalDe
if (result != VK_SUCCESS)
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 = {};
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)
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 = {};
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
result = vkCreateFence(logicalDevice, &fenceInfo, nullptr, &m_imageAvailableFence);
if (result != VK_SUCCESS)
UnrecoverableError("Failed to create fence for swapchain");

m_acquireIndex = 0;
hasDefinedSwapchainImage = false;
}

void SwapchainInfoVk::Cleanup()
{
m_swapchainImages.clear();

for (auto& sem: m_swapchainPresentSemaphores)
for (auto& sem: m_acquireSemaphores)
vkDestroySemaphore(m_logicalDevice, sem, nullptr);
m_acquireSemaphores.clear();

for (auto& sem: m_presentSemaphores)
vkDestroySemaphore(m_logicalDevice, sem, nullptr);
m_swapchainPresentSemaphores.clear();
m_presentSemaphores.clear();

if (m_swapchainRenderPass)
{
Expand Down Expand Up @@ -159,12 +179,55 @@ void SwapchainInfoVk::Cleanup()

bool SwapchainInfoVk::IsValid() const
{
return swapchain && m_imageAvailableFence;
return swapchain && !m_acquireSemaphores.empty();
}

void SwapchainInfoVk::WaitAvailableFence()
{
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;
}

void SwapchainInfoVk::WaitAvailableFence() const
bool SwapchainInfoVk::AcquireImage(uint64 timeout)
{
vkWaitForFences(m_logicalDevice, 1, &m_imageAvailableFence, VK_TRUE, UINT64_MAX);
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)
Expand Down
20 changes: 16 additions & 4 deletions src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,14 @@ struct SwapchainInfoVk

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);

Expand Down Expand Up @@ -76,19 +83,24 @@ struct SwapchainInfoVk
VkSurfaceFormatKHR m_surfaceFormat{};
VkSwapchainKHR swapchain{};
Vector2i m_desiredExtent{};
VkFence m_imageAvailableFence{};
uint32 swapchainImageIndex = (uint32)-1;


// swapchain image ringbuffer (indexed by swapchainImageIndex)
std::vector<VkImage> m_swapchainImages;
std::vector<VkImageView> m_swapchainImageViews;
std::vector<VkFramebuffer> m_swapchainFramebuffers;
std::vector<VkSemaphore> m_swapchainPresentSemaphores;
std::array<uint32, 2> m_swapchainQueueFamilyIndices;
std::vector<VkSemaphore> m_presentSemaphores; // indexed by swapchainImageIndex

VkRenderPass m_swapchainRenderPass = nullptr;

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{};
};
38 changes: 13 additions & 25 deletions src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1665,7 +1665,6 @@ bool VulkanRenderer::ImguiBegin(bool mainWindow)
draw_endRenderPass();
m_state.currentPipeline = VK_NULL_HANDLE;

chainInfo.WaitAvailableFence();
ImGui_ImplVulkan_CreateFontsTexture(m_state.currentCommandBuffer);
ImGui_ImplVulkan_NewFrame(m_state.currentCommandBuffer, chainInfo.m_swapchainFramebuffers[chainInfo.swapchainImageIndex], chainInfo.getExtent());
ImGui_UpdateWindowInformation(mainWindow);
Expand Down Expand Up @@ -1722,7 +1721,6 @@ bool VulkanRenderer::BeginFrame(bool mainWindow)

auto& chainInfo = GetChainInfo(mainWindow);

chainInfo.WaitAvailableFence();
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);

Expand Down Expand Up @@ -1848,7 +1846,7 @@ void VulkanRenderer::WaitForNextFinishedCommandBuffer()
ProcessFinishedCommandBuffers();
}

void VulkanRenderer::SubmitCommandBuffer(VkSemaphore* signalSemaphore, VkSemaphore* waitSemaphore)
void VulkanRenderer::SubmitCommandBuffer(VkSemaphore signalSemaphore, VkSemaphore waitSemaphore)
{
draw_endRenderPass();

Expand All @@ -1863,11 +1861,11 @@ void VulkanRenderer::SubmitCommandBuffer(VkSemaphore* signalSemaphore, VkSemapho

// signal current command buffer semaphore
VkSemaphore signalSemArray[2];
if (signalSemaphore)
if (signalSemaphore != VK_NULL_HANDLE)
{
submitInfo.signalSemaphoreCount = 2;
signalSemArray[0] = m_commandBufferSemaphores[m_commandBufferIndex]; // signal current
signalSemArray[1] = *signalSemaphore; // signal current
signalSemArray[1] = signalSemaphore; // signal current
submitInfo.pSignalSemaphores = signalSemArray;
}
else
Expand All @@ -1883,8 +1881,8 @@ void VulkanRenderer::SubmitCommandBuffer(VkSemaphore* signalSemaphore, VkSemapho
submitInfo.waitSemaphoreCount = 0;
if (m_numSubmittedCmdBuffers > 0)
waitSemArray[submitInfo.waitSemaphoreCount++] = prevSem; // wait on semaphore from previous submit
if (waitSemaphore)
waitSemArray[submitInfo.waitSemaphoreCount++] = *waitSemaphore;
if (waitSemaphore != VK_NULL_HANDLE)
waitSemArray[submitInfo.waitSemaphoreCount++] = waitSemaphore;
submitInfo.pWaitDstStageMask = semWaitStageMask;
submitInfo.pWaitSemaphores = waitSemArray;

Expand Down Expand Up @@ -2546,20 +2544,11 @@ bool VulkanRenderer::AcquireNextSwapchainImage(bool mainWindow)
if (!UpdateSwapchainProperties(mainWindow))
return false;

vkResetFences(m_logicalDevice, 1, &chainInfo.m_imageAvailableFence);
VkResult result = vkAcquireNextImageKHR(m_logicalDevice, chainInfo.swapchain, std::numeric_limits<uint64_t>::max(), VK_NULL_HANDLE, chainInfo.m_imageAvailableFence, &chainInfo.swapchainImageIndex);
if (result != VK_SUCCESS)
{
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));
}
bool result = chainInfo.AcquireImage(UINT64_MAX);
if (!result)
return false;

SubmitCommandBuffer(VK_NULL_HANDLE, chainInfo.ConsumeAcquireSemaphore());
return true;
}

Expand All @@ -2568,6 +2557,8 @@ void VulkanRenderer::RecreateSwapchain(bool mainWindow, bool skipCreate)
SubmitCommandBuffer();
WaitDeviceIdle();
auto& chainInfo = GetChainInfo(mainWindow);
// make sure fence has no signal operation submitted
chainInfo.WaitAvailableFence();

Vector2i size;
if (mainWindow)
Expand Down Expand Up @@ -2633,14 +2624,13 @@ void VulkanRenderer::SwapBuffer(bool mainWindow)

if (!chainInfo.hasDefinedSwapchainImage)
{
chainInfo.WaitAvailableFence();
// set the swapchain image to a defined state
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);
}

VkSemaphore presentSemaphore = chainInfo.m_swapchainPresentSemaphores[chainInfo.swapchainImageIndex];
SubmitCommandBuffer(&presentSemaphore); // submit all command and signal semaphore
VkSemaphore presentSemaphore = chainInfo.m_presentSemaphores[chainInfo.swapchainImageIndex];
SubmitCommandBuffer(presentSemaphore); // submit all command and signal semaphore

cemu_assert_debug(m_numSubmittedCmdBuffers > 0);

Expand Down Expand Up @@ -2701,7 +2691,6 @@ void VulkanRenderer::ClearColorbuffer(bool padView)
if (chainInfo.swapchainImageIndex == -1)
return;

chainInfo.WaitAvailableFence();
VkClearColorValue clearColor{ 0, 0, 0, 0 };
ClearColorImageRaw(chainInfo.m_swapchainImages[chainInfo.swapchainImageIndex], 0, 0, clearColor, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL);
}
Expand Down Expand Up @@ -2792,7 +2781,6 @@ void VulkanRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutpu
LatteTextureViewVk* texViewVk = (LatteTextureViewVk*)texView;
draw_endRenderPass();

chainInfo.WaitAvailableFence();
if (clearBackground)
ClearColorbuffer(padView);

Expand Down
2 changes: 1 addition & 1 deletion src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ class VulkanRenderer : public Renderer
void InitFirstCommandBuffer();
void ProcessFinishedCommandBuffers();
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 RequestSubmitOnIdle();

Expand Down

0 comments on commit bc10485

Please sign in to comment.