Fix Vulkan swapchain synchronization

It is necessary to use a semaphore to synchronize rendering and
presenting even when the graphics and present queues are the same.

This fixes rendering artifacts encountered on a Mali-G78 device.

Bug: dawn:269
Change-Id: Ieb0240d3668b7f9f68857ed3cf8d3bfea18cf33a
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/130221
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Albin Bernhardsson <albin.bernhardsson@arm.com>
Reviewed-by: Austin Eng <enga@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Albin Bernhardsson 2023-05-03 17:06:13 +00:00 committed by Dawn LUCI CQ
parent 1f0aa9b1f5
commit 42b688bcff
4 changed files with 48 additions and 11 deletions

View File

@ -39,6 +39,7 @@ struct CommandPoolAndBuffer {
struct CommandRecordingContext { struct CommandRecordingContext {
VkCommandBuffer commandBuffer = VK_NULL_HANDLE; VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
std::vector<VkSemaphore> waitSemaphores = {}; std::vector<VkSemaphore> waitSemaphores = {};
std::vector<VkSemaphore> signalSemaphores = {};
// The internal buffers used in the workaround of texture-to-texture copies with compressed // The internal buffers used in the workaround of texture-to-texture copies with compressed
// formats. // formats.

View File

@ -314,11 +314,11 @@ MaybeError Device::SubmitPendingCommands() {
fn, &mRecordingContext, mRecordingContext.mappableBuffersForEagerTransition); fn, &mRecordingContext, mRecordingContext.mappableBuffersForEagerTransition);
} }
ScopedSignalSemaphore scopedSignalSemaphore(this, VK_NULL_HANDLE); ScopedSignalSemaphore externalTextureSemaphore(this, VK_NULL_HANDLE);
if (mRecordingContext.externalTexturesForEagerTransition.size() > 0) { if (mRecordingContext.externalTexturesForEagerTransition.size() > 0) {
// Create an external semaphore for all external textures that have been used in the pending // Create an external semaphore for all external textures that have been used in the pending
// submit. // submit.
DAWN_TRY_ASSIGN(*scopedSignalSemaphore.InitializeInto(), DAWN_TRY_ASSIGN(*externalTextureSemaphore.InitializeInto(),
mExternalSemaphoreService->CreateExportableSemaphore()); mExternalSemaphoreService->CreateExportableSemaphore());
} }
@ -336,6 +336,10 @@ MaybeError Device::SubmitPendingCommands() {
std::vector<VkPipelineStageFlags> dstStageMasks(mRecordingContext.waitSemaphores.size(), std::vector<VkPipelineStageFlags> dstStageMasks(mRecordingContext.waitSemaphores.size(),
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
if (externalTextureSemaphore.Get() != VK_NULL_HANDLE) {
mRecordingContext.signalSemaphores.push_back(externalTextureSemaphore.Get());
}
VkSubmitInfo submitInfo; VkSubmitInfo submitInfo;
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.pNext = nullptr; submitInfo.pNext = nullptr;
@ -344,8 +348,8 @@ MaybeError Device::SubmitPendingCommands() {
submitInfo.pWaitDstStageMask = dstStageMasks.data(); submitInfo.pWaitDstStageMask = dstStageMasks.data();
submitInfo.commandBufferCount = mRecordingContext.commandBufferList.size(); submitInfo.commandBufferCount = mRecordingContext.commandBufferList.size();
submitInfo.pCommandBuffers = mRecordingContext.commandBufferList.data(); submitInfo.pCommandBuffers = mRecordingContext.commandBufferList.data();
submitInfo.signalSemaphoreCount = (scopedSignalSemaphore.Get() == VK_NULL_HANDLE ? 0 : 1); submitInfo.signalSemaphoreCount = mRecordingContext.signalSemaphores.size();
submitInfo.pSignalSemaphores = AsVkArray(scopedSignalSemaphore.InitializeInto()); submitInfo.pSignalSemaphores = AsVkArray(mRecordingContext.signalSemaphores.data());
VkFence fence = VK_NULL_HANDLE; VkFence fence = VK_NULL_HANDLE;
DAWN_TRY_ASSIGN(fence, GetUnusedFence()); DAWN_TRY_ASSIGN(fence, GetUnusedFence());
@ -376,7 +380,7 @@ MaybeError Device::SubmitPendingCommands() {
// Export the signal semaphore. // Export the signal semaphore.
ExternalSemaphoreHandle semaphoreHandle; ExternalSemaphoreHandle semaphoreHandle;
DAWN_TRY_ASSIGN(semaphoreHandle, DAWN_TRY_ASSIGN(semaphoreHandle,
mExternalSemaphoreService->ExportSemaphore(scopedSignalSemaphore.Get())); mExternalSemaphoreService->ExportSemaphore(externalTextureSemaphore.Get()));
// Update all external textures, eagerly transitioned in the submit, with the exported // Update all external textures, eagerly transitioned in the submit, with the exported
// handle, and the duplicated handles. // handle, and the duplicated handles.
@ -1086,6 +1090,7 @@ void Device::DestroyImpl() {
fn.DestroySemaphore(mVkDevice, semaphore, nullptr); fn.DestroySemaphore(mVkDevice, semaphore, nullptr);
} }
mRecordingContext.waitSemaphores.clear(); mRecordingContext.waitSemaphores.clear();
mRecordingContext.signalSemaphores.clear();
// Some commands might still be marked as in-flight if we shut down because of a device // Some commands might still be marked as in-flight if we shut down because of a device
// loss. Recycle them as unused so that we free them below. // loss. Recycle them as unused so that we free them below.

View File

@ -291,6 +291,15 @@ MaybeError SwapChain::Initialize(SwapChainBase* previousSwapChain) {
ToBackend(previousSwapChain->GetDevice()) ToBackend(previousSwapChain->GetDevice())
->GetFencedDeleter() ->GetFencedDeleter()
->DeleteWhenUnused(previousVkSwapChain); ->DeleteWhenUnused(previousVkSwapChain);
// Delete the previous swapchain's semaphores once they are not in use.
// TODO(crbug.com/dawn/269): Wait for presentation to finish rather than submission.
for (VkSemaphore semaphore : previousVulkanSwapChain->mSwapChainSemaphores) {
ToBackend(previousSwapChain->GetDevice())
->GetFencedDeleter()
->DeleteWhenUnused(semaphore);
}
previousVulkanSwapChain->mSwapChainSemaphores.clear();
} }
if (mVkSurface == VK_NULL_HANDLE) { if (mVkSurface == VK_NULL_HANDLE) {
@ -340,6 +349,21 @@ MaybeError SwapChain::Initialize(SwapChainBase* previousSwapChain) {
AsVkArray(mSwapChainImages.data())), AsVkArray(mSwapChainImages.data())),
"GetSwapChainImages2")); "GetSwapChainImages2"));
// Create one semaphore per swapchain image.
mSwapChainSemaphores.resize(count);
VkSemaphoreCreateInfo semaphoreCreateInfo;
semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
semaphoreCreateInfo.pNext = nullptr;
semaphoreCreateInfo.flags = 0;
for (std::size_t i = 0; i < mSwapChainSemaphores.size(); i++) {
DAWN_TRY(
CheckVkSuccess(device->fn.CreateSemaphore(device->GetVkDevice(), &semaphoreCreateInfo,
nullptr, &*mSwapChainSemaphores[i]),
"CreateSemaphore"));
}
return {}; return {};
} }
@ -546,17 +570,17 @@ MaybeError SwapChain::PresentImpl() {
mTexture->TransitionUsageNow(recordingContext, kPresentTextureUsage, mTexture->TransitionUsageNow(recordingContext, kPresentTextureUsage,
mTexture->GetAllSubresources()); mTexture->GetAllSubresources());
// Use a semaphore to make sure all rendering has finished before presenting.
VkSemaphore currentSemaphore = mSwapChainSemaphores[mLastImageIndex];
recordingContext->signalSemaphores.push_back(currentSemaphore);
DAWN_TRY(device->SubmitPendingCommands()); DAWN_TRY(device->SubmitPendingCommands());
// Assuming that the present queue is the same as the graphics queue, the proper
// synchronization has already been done on the queue so we don't need to wait on any
// semaphores.
// TODO(crbug.com/dawn/269): Support the present queue not being the main queue.
VkPresentInfoKHR presentInfo; VkPresentInfoKHR presentInfo;
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.pNext = nullptr; presentInfo.pNext = nullptr;
presentInfo.waitSemaphoreCount = 0; presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = nullptr; presentInfo.pWaitSemaphores = AsVkArray(&currentSemaphore);
presentInfo.swapchainCount = 1; presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = &*mSwapChain; presentInfo.pSwapchains = &*mSwapChain;
presentInfo.pImageIndices = &mLastImageIndex; presentInfo.pImageIndices = &mLastImageIndex;
@ -681,6 +705,12 @@ void SwapChain::DetachFromSurfaceImpl() {
mBlitTexture = nullptr; mBlitTexture = nullptr;
} }
for (VkSemaphore semaphore : mSwapChainSemaphores) {
// TODO(crbug.com/dawn/269): Wait for presentation to finish rather than submission.
ToBackend(GetDevice())->GetFencedDeleter()->DeleteWhenUnused(semaphore);
}
mSwapChainSemaphores.clear();
// The swapchain images are destroyed with the swapchain. // The swapchain images are destroyed with the swapchain.
if (mSwapChain != VK_NULL_HANDLE) { if (mSwapChain != VK_NULL_HANDLE) {
ToBackend(GetDevice())->GetFencedDeleter()->DeleteWhenUnused(mSwapChain); ToBackend(GetDevice())->GetFencedDeleter()->DeleteWhenUnused(mSwapChain);

View File

@ -76,6 +76,7 @@ class SwapChain : public SwapChainBase {
VkSurfaceKHR mVkSurface = VK_NULL_HANDLE; VkSurfaceKHR mVkSurface = VK_NULL_HANDLE;
VkSwapchainKHR mSwapChain = VK_NULL_HANDLE; VkSwapchainKHR mSwapChain = VK_NULL_HANDLE;
std::vector<VkImage> mSwapChainImages; std::vector<VkImage> mSwapChainImages;
std::vector<VkSemaphore> mSwapChainSemaphores;
uint32_t mLastImageIndex = 0; uint32_t mLastImageIndex = 0;
Ref<Texture> mBlitTexture; Ref<Texture> mBlitTexture;