From 7ab99e8ea1a848582609fb3ec81aa3cbb7e782f1 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Tue, 9 Jul 2024 00:14:44 +0100 Subject: [PATCH] Better handling for feedback loops. --- src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs | 32 ++++++++++++++++--- .../FramebufferParams.cs | 13 ++++++++ src/Ryujinx.Graphics.Vulkan/PipelineBase.cs | 2 ++ .../RenderPassHolder.cs | 5 +++ src/Ryujinx.Graphics.Vulkan/TextureStorage.cs | 11 +++++++ 5 files changed, 58 insertions(+), 5 deletions(-) diff --git a/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs b/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs index e3daf4c14c..a6a006bb9e 100644 --- a/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs +++ b/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs @@ -298,19 +298,41 @@ namespace Ryujinx.Graphics.Vulkan // Generally, we want to avoid this from happening in the future, so flag the texture to immediately // emit a barrier whenever the current render pass is bound again. + bool anyIsNonAttachment = false; + foreach (BarrierWithStageFlags barrier in _imageBarriers) { - rpHolder.AddForcedFence(barrier.Resource, barrier.Flags.Dest); + // If the binding is an attachment, don't add it as a forced fence. + bool isAttachment = rpHolder.ContainsAttachment(barrier.Resource); + + if (!isAttachment) + { + rpHolder.AddForcedFence(barrier.Resource, barrier.Flags.Dest); + anyIsNonAttachment = true; + } } if (_gd.IsTBDR) { if (!_gd.IsMoltenVk) { - // TBDR GPUs are sensitive to barriers, so we need to end the pass to ensure the data is available. - // Metal already has hazard tracking so MVK doesn't need this. - endRenderPass(); - inRenderPass = false; + if (!anyIsNonAttachment) + { + // This case is a feedback loop. To prevent this from causing an absolute performance disaster, + // remove the barriers entirely. + // If this is not here, there will be a lot of single draw render passes. + // TODO: explicit handling for feedback loops, likely outside this class. + + _queuedBarrierCount -= _imageBarriers.Count; + _imageBarriers.Clear(); + } + else + { + // TBDR GPUs are sensitive to barriers, so we need to end the pass to ensure the data is available. + // Metal already has hazard tracking so MVK doesn't need this. + endRenderPass(); + inRenderPass = false; + } } } else diff --git a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs index acaa8beeec..5c5a8f3ad4 100644 --- a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs +++ b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs @@ -289,6 +289,19 @@ namespace Ryujinx.Graphics.Vulkan gd.Barriers.Flush(cbs, false, null, null); } + public void AddStoreOpUsage() + { + if (_colors != null) + { + foreach (var color in _colors) + { + color.Storage?.AddStoreOpUsage(false); + } + } + + _depthStencil?.Storage?.AddStoreOpUsage(true); + } + public (RenderPassHolder rpHolder, Auto framebuffer) GetPassAndFramebuffer( VulkanRenderer gd, Device device, diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index ace98fb04d..5cc68bd3fa 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -1639,6 +1639,8 @@ namespace Ryujinx.Graphics.Vulkan { if (RenderPassActive) { + FramebufferParams.AddStoreOpUsage(); + PauseTransformFeedbackInternal(); Gd.Api.CmdEndRenderPass(CommandBuffer); SignalRenderPassEnd(); diff --git a/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs b/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs index e9a59d1dbc..b2dd0dd874 100644 --- a/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs +++ b/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs @@ -192,6 +192,11 @@ namespace Ryujinx.Graphics.Vulkan } } + public bool ContainsAttachment(TextureStorage storage) + { + return _textures.Any(view => view.Storage == storage); + } + public void Dispose() { // Dispose all framebuffers. diff --git a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs index be15646ee9..f36db68de3 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs @@ -435,6 +435,17 @@ namespace Ryujinx.Graphics.Vulkan return FormatCapabilities.IsD24S8(Info.Format) && VkFormat == VkFormat.D32SfloatS8Uint; } + public void AddStoreOpUsage(bool depthStencil) + { + _lastModificationStage = depthStencil ? + PipelineStageFlags.LateFragmentTestsBit : + PipelineStageFlags.ColorAttachmentOutputBit; + + _lastModificationAccess = depthStencil ? + AccessFlags.DepthStencilAttachmentWriteBit : + AccessFlags.ColorAttachmentWriteBit; + } + public void QueueLoadOpBarrier(CommandBufferScoped cbs, bool depthStencil) { PipelineStageFlags srcStageFlags = _lastReadStage | _lastModificationStage;