From a34fb0e9392c59e5bd7a764b83f3460bf65b861b Mon Sep 17 00:00:00 2001 From: riperiperi Date: Sun, 26 Mar 2023 11:51:02 +0100 Subject: [PATCH] Vulkan: Insert barriers before clears (#4596) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Vulkan: Insert barriers before clears Newer NVIDIA GPUs seem to be able to start clearing render targets before the last rasterization task is completed, which can cause it to clear a texture while it is being sampled. This change adds a barrier from read to write when doing a clear, assuming it has been sampled in the past. It could be possible for this to be needed for sample into draw by some GPU, but it's not right now afaik. This should fix visual artifacts on newer NVIDIA GPUs and driver combos. Contrary to popular belief, TetrisĀ® Effect: Connected is not affected. Testing welcome, hopefully should fix most cases of this and not cost too much performance. * Visual Studio Moment * Address feedback * Address Feedback 2 --- .../DescriptorSetUpdater.cs | 2 +- Ryujinx.Graphics.Vulkan/EnumConversion.cs | 2 +- Ryujinx.Graphics.Vulkan/FramebufferParams.cs | 18 +++++++ Ryujinx.Graphics.Vulkan/PipelineBase.cs | 4 ++ Ryujinx.Graphics.Vulkan/TextureStorage.cs | 54 +++++++++++-------- 5 files changed, 56 insertions(+), 24 deletions(-) diff --git a/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs index 7e126e044d..ab3befd811 100644 --- a/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs +++ b/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs @@ -236,7 +236,7 @@ namespace Ryujinx.Graphics.Vulkan } else if (texture is TextureView view) { - view.Storage.InsertBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags()); + view.Storage.InsertWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags()); _textureRefs[binding] = view.GetImageView(); _samplerRefs[binding] = ((SamplerHolder)sampler)?.GetSampler(); diff --git a/Ryujinx.Graphics.Vulkan/EnumConversion.cs b/Ryujinx.Graphics.Vulkan/EnumConversion.cs index 6c273b0500..b69c64aa83 100644 --- a/Ryujinx.Graphics.Vulkan/EnumConversion.cs +++ b/Ryujinx.Graphics.Vulkan/EnumConversion.cs @@ -322,7 +322,7 @@ namespace Ryujinx.Graphics.Vulkan GAL.Format.S8Uint => ImageAspectFlags.StencilBit, GAL.Format.D24UnormS8Uint or GAL.Format.D32FloatS8Uint or - GAL.Format.S8UintD24Unorm => ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit, + GAL.Format.S8UintD24Unorm => ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit, _ => ImageAspectFlags.ColorBit }; } diff --git a/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/Ryujinx.Graphics.Vulkan/FramebufferParams.cs index 0a1cdcce36..658468d17c 100644 --- a/Ryujinx.Graphics.Vulkan/FramebufferParams.cs +++ b/Ryujinx.Graphics.Vulkan/FramebufferParams.cs @@ -218,5 +218,23 @@ namespace Ryujinx.Graphics.Vulkan AccessFlags.DepthStencilAttachmentWriteBit, PipelineStageFlags.ColorAttachmentOutputBit); } + + public void InsertClearBarrier(CommandBufferScoped cbs, int index) + { + if (_colors != null) + { + int realIndex = Array.IndexOf(AttachmentIndices, index); + + if (realIndex != -1) + { + _colors[realIndex].Storage?.InsertReadToWriteBarrier(cbs, AccessFlags.ColorAttachmentWriteBit, PipelineStageFlags.ColorAttachmentOutputBit); + } + } + } + + public void InsertClearBarrierDS(CommandBufferScoped cbs) + { + _depthStencil?.Storage?.InsertReadToWriteBarrier(cbs, AccessFlags.DepthStencilAttachmentWriteBit, PipelineStageFlags.EarlyFragmentTestsBit); + } } } diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 6c2f168492..2bec529301 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -226,6 +226,8 @@ namespace Ryujinx.Graphics.Vulkan var attachment = new ClearAttachment(ImageAspectFlags.ColorBit, (uint)index, clearValue); var clearRect = FramebufferParams.GetClearRect(ClearScissor, layer, layerCount); + FramebufferParams.InsertClearBarrier(Cbs, index); + Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect); } @@ -256,6 +258,8 @@ namespace Ryujinx.Graphics.Vulkan var attachment = new ClearAttachment(flags, 0, clearValue); var clearRect = FramebufferParams.GetClearRect(ClearScissor, layer, layerCount); + FramebufferParams.InsertClearBarrierDS(Cbs); + Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect); } diff --git a/Ryujinx.Graphics.Vulkan/TextureStorage.cs b/Ryujinx.Graphics.Vulkan/TextureStorage.cs index 03a47a091b..8ebdd4c0fd 100644 --- a/Ryujinx.Graphics.Vulkan/TextureStorage.cs +++ b/Ryujinx.Graphics.Vulkan/TextureStorage.cs @@ -46,6 +46,8 @@ namespace Ryujinx.Graphics.Vulkan private AccessFlags _lastModificationAccess; private PipelineStageFlags _lastModificationStage; + private AccessFlags _lastReadAccess; + private PipelineStageFlags _lastReadStage; private int _viewsCount; private ulong _size; @@ -440,31 +442,39 @@ namespace Ryujinx.Graphics.Vulkan _lastModificationStage = stage; } - public void InsertBarrier(CommandBufferScoped cbs, AccessFlags dstAccessFlags, PipelineStageFlags dstStageFlags) + public void InsertReadToWriteBarrier(CommandBufferScoped cbs, AccessFlags dstAccessFlags, PipelineStageFlags dstStageFlags) { + if (_lastReadAccess != AccessFlags.NoneKhr) + { + ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags(); + + TextureView.InsertImageBarrier( + _gd.Api, + cbs.CommandBuffer, + _imageAuto.Get(cbs).Value, + _lastReadAccess, + dstAccessFlags, + _lastReadStage, + dstStageFlags, + aspectFlags, + 0, + 0, + _info.GetLayers(), + _info.Levels); + + _lastReadAccess = AccessFlags.NoneKhr; + _lastReadStage = PipelineStageFlags.None; + } + } + + public void InsertWriteToReadBarrier(CommandBufferScoped cbs, AccessFlags dstAccessFlags, PipelineStageFlags dstStageFlags) + { + _lastReadAccess |= dstAccessFlags; + _lastReadStage |= dstStageFlags; + if (_lastModificationAccess != AccessFlags.NoneKhr) { - ImageAspectFlags aspectFlags; - - if (_info.Format.IsDepthOrStencil()) - { - if (_info.Format == GAL.Format.S8Uint) - { - aspectFlags = ImageAspectFlags.StencilBit; - } - else if (_info.Format == GAL.Format.D16Unorm || _info.Format == GAL.Format.D32Float) - { - aspectFlags = ImageAspectFlags.DepthBit; - } - else - { - aspectFlags = ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit; - } - } - else - { - aspectFlags = ImageAspectFlags.ColorBit; - } + ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags(); TextureView.InsertImageBarrier( _gd.Api,