From e599ae37c31b0beb1594b3e664faa0a50db91690 Mon Sep 17 00:00:00 2001 From: TellowKrinkle Date: Sat, 18 Oct 2025 05:15:04 -0500 Subject: [PATCH] VideoBackends:Vulkan: Prevent hazards when restarting render passes --- .../VideoBackends/Vulkan/StateTracker.cpp | 2 + .../Core/VideoBackends/Vulkan/VKTexture.cpp | 68 +++++++++++++++++-- Source/Core/VideoBackends/Vulkan/VKTexture.h | 3 + 3 files changed, 67 insertions(+), 6 deletions(-) diff --git a/Source/Core/VideoBackends/Vulkan/StateTracker.cpp b/Source/Core/VideoBackends/Vulkan/StateTracker.cpp index 575ba9a352..5ba70c5845 100644 --- a/Source/Core/VideoBackends/Vulkan/StateTracker.cpp +++ b/Source/Core/VideoBackends/Vulkan/StateTracker.cpp @@ -288,6 +288,7 @@ void StateTracker::BeginRenderPass() m_current_render_pass = m_framebuffer->GetLoadRenderPass(); m_framebuffer_render_area = m_framebuffer->GetRect(); + m_framebuffer->PrepareForRenderPass(); VkRenderPassBeginInfo begin_info = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, nullptr, @@ -337,6 +338,7 @@ void StateTracker::BeginClearRenderPass(const VkRect2D& area, const VkClearValue m_current_render_pass = m_framebuffer->GetClearRenderPass(); m_framebuffer_render_area = area; + m_framebuffer->PrepareForRenderPass(); VkRenderPassBeginInfo begin_info = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, nullptr, diff --git a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp index edccc9da14..ee6a1b77b3 100644 --- a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp @@ -466,6 +466,8 @@ void VKTexture::TransitionToLayout(VkCommandBuffer command_buffer, VkImageLayout if (m_layout == new_layout) return; + m_written_since_last_layout_change = false; + VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType nullptr, // const void* pNext @@ -613,6 +615,8 @@ void VKTexture::TransitionToLayout(VkCommandBuffer command_buffer, if (m_compute_layout == new_layout) return; + m_written_since_last_layout_change = false; + VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType nullptr, // const void* pNext @@ -709,6 +713,49 @@ void VKTexture::TransitionToLayout(VkCommandBuffer command_buffer, &barrier); } +void VKTexture::PrepareForRenderPass(VkCommandBuffer command_buffer) const +{ + // Should only be used on images that are already in the layout for being rendered to + ASSERT(m_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL || + m_layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + if (m_written_since_last_layout_change) + { + // If the image has already been written, we need a barrier to prevent WaW or RaW hazards. + VkPipelineStageFlags srcStage = 0; + VkPipelineStageFlags dstStage = 0; + VkImageMemoryBarrier barrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER}; + barrier.image = m_image; + barrier.oldLayout = m_layout; + barrier.newLayout = m_layout; + barrier.subresourceRange.aspectMask = GetImageAspectForFormat(GetFormat()); + barrier.subresourceRange.layerCount = GetLayers(); + barrier.subresourceRange.levelCount = GetLevels(); + if (barrier.subresourceRange.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) + { + barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | // + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; + srcStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dstStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + } + else + { + barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT; + srcStage = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + dstStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; + } + vkCmdPipelineBarrier(command_buffer, srcStage, dstStage, 0, // + 0, nullptr, 0, nullptr, 1, &barrier); + } + else + { + // Otherwise, it's about to be written right now. + m_written_since_last_layout_change = true; + } +} + VKStagingTexture::VKStagingTexture(PrivateTag, StagingTextureType type, const TextureConfig& config, std::unique_ptr buffer, VkImage linear_image, VmaAllocation linear_image_alloc) @@ -1117,27 +1164,36 @@ void VKFramebuffer::Unbind() void VKFramebuffer::TransitionForRender() { + VkCommandBuffer cb = g_command_buffer_mgr->GetCurrentCommandBuffer(); if (m_color_attachment) { static_cast(m_color_attachment) - ->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + ->TransitionToLayout(cb, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); } for (auto* attachment : m_additional_color_attachments) { static_cast(attachment) - ->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + ->TransitionToLayout(cb, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); } if (m_depth_attachment) { static_cast(m_depth_attachment) - ->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + ->TransitionToLayout(cb, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); } } +void VKFramebuffer::PrepareForRenderPass() +{ + VkCommandBuffer cb = g_command_buffer_mgr->GetCurrentCommandBuffer(); + if (m_color_attachment) + static_cast(m_color_attachment)->PrepareForRenderPass(cb); + if (m_depth_attachment) + static_cast(m_depth_attachment)->PrepareForRenderPass(cb); + for (auto* attachment : m_additional_color_attachments) + static_cast(attachment)->PrepareForRenderPass(cb); +} + void VKFramebuffer::SetAndClear(const VkRect2D& rect, const VkClearValue& color_value, const VkClearValue& depth_value) { diff --git a/Source/Core/VideoBackends/Vulkan/VKTexture.h b/Source/Core/VideoBackends/Vulkan/VKTexture.h index c868087dae..456b4fcd33 100644 --- a/Source/Core/VideoBackends/Vulkan/VKTexture.h +++ b/Source/Core/VideoBackends/Vulkan/VKTexture.h @@ -69,6 +69,7 @@ public: void TransitionToLayout(VkCommandBuffer command_buffer, VkImageLayout new_layout) const; void TransitionToLayout(VkCommandBuffer command_buffer, ComputeImageLayout new_layout) const; + void PrepareForRenderPass(VkCommandBuffer command_buffer) const; private: bool CreateView(VkImageViewType type); @@ -78,6 +79,7 @@ private: VkImageView m_view = VK_NULL_HANDLE; mutable VkImageLayout m_layout = VK_IMAGE_LAYOUT_UNDEFINED; mutable ComputeImageLayout m_compute_layout = ComputeImageLayout::Undefined; + mutable bool m_written_since_last_layout_change = false; std::string m_name; }; @@ -141,6 +143,7 @@ public: void Unbind(); void TransitionForRender(); + void PrepareForRenderPass(); void SetAndClear(const VkRect2D& rect, const VkClearValue& color_value, const VkClearValue& depth_value);