From 1a919e99b29fff4e2158e622cb3dfbee21293b6d Mon Sep 17 00:00:00 2001 From: riperiperi Date: Thu, 18 Jul 2024 00:21:32 +0100 Subject: [PATCH] Vulkan: Defer guest barriers, and improve image barrier timings (#7012) * More guarantees for buffer correct placement, defer guest requested buffers * Split RP on indirect barrier rn * Better handling for feedback loops. * Qualcomm barriers suck too * Fix condition * Remove unused field * Allow render pass barriers on turnip for now --- src/Ryujinx.Graphics.GAL/ResourceLayout.cs | 4 +- .../Shader/ShaderInfoBuilder.cs | 24 +- src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs | 260 ++++++++++++++++-- .../Effects/FsrScalingFilter.cs | 4 +- .../Effects/FxaaPostProcessingEffect.cs | 2 +- .../Effects/SmaaPostProcessingEffect.cs | 6 +- .../FramebufferParams.cs | 17 +- src/Ryujinx.Graphics.Vulkan/HelperShader.cs | 10 +- src/Ryujinx.Graphics.Vulkan/PipelineBase.cs | 101 ++----- .../PipelineConverter.cs | 33 +-- src/Ryujinx.Graphics.Vulkan/PipelineFull.cs | 2 +- .../RenderPassHolder.cs | 40 ++- .../ResourceLayoutBuilder.cs | 4 +- .../ShaderCollection.cs | 71 +++++ src/Ryujinx.Graphics.Vulkan/TextureCopy.cs | 2 +- src/Ryujinx.Graphics.Vulkan/TextureStorage.cs | 19 +- src/Ryujinx.Graphics.Vulkan/TextureView.cs | 4 +- src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 5 + 18 files changed, 452 insertions(+), 156 deletions(-) diff --git a/src/Ryujinx.Graphics.GAL/ResourceLayout.cs b/src/Ryujinx.Graphics.GAL/ResourceLayout.cs index 998c046f19..b7464ee12e 100644 --- a/src/Ryujinx.Graphics.GAL/ResourceLayout.cs +++ b/src/Ryujinx.Graphics.GAL/ResourceLayout.cs @@ -74,13 +74,15 @@ namespace Ryujinx.Graphics.GAL public int ArrayLength { get; } public ResourceType Type { get; } public ResourceStages Stages { get; } + public bool Write { get; } - public ResourceUsage(int binding, int arrayLength, ResourceType type, ResourceStages stages) + public ResourceUsage(int binding, int arrayLength, ResourceType type, ResourceStages stages, bool write) { Binding = binding; ArrayLength = arrayLength; Type = type; Stages = stages; + Write = write; } public override int GetHashCode() diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs index 42b2cbb59b..49823562f2 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs @@ -78,9 +78,9 @@ namespace Ryujinx.Graphics.Gpu.Shader ResourceStages stages = vertexAsCompute ? ResourceStages.Compute | ResourceStages.Vertex : VtgStages; PopulateDescriptorAndUsages(stages, ResourceType.UniformBuffer, uniformSetIndex, 1, rrc.ReservedConstantBuffers - 1); - PopulateDescriptorAndUsages(stages, ResourceType.StorageBuffer, storageSetIndex, 0, rrc.ReservedStorageBuffers); + PopulateDescriptorAndUsages(stages, ResourceType.StorageBuffer, storageSetIndex, 0, rrc.ReservedStorageBuffers, true); PopulateDescriptorAndUsages(stages, ResourceType.BufferTexture, textureSetIndex, 0, rrc.ReservedTextures); - PopulateDescriptorAndUsages(stages, ResourceType.BufferImage, imageSetIndex, 0, rrc.ReservedImages); + PopulateDescriptorAndUsages(stages, ResourceType.BufferImage, imageSetIndex, 0, rrc.ReservedImages, true); } /// @@ -91,10 +91,11 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Resource set index where the resources are used /// First binding number /// Amount of bindings - private void PopulateDescriptorAndUsages(ResourceStages stages, ResourceType type, int setIndex, int start, int count) + /// True if the binding is written from the shader, false otherwise + private void PopulateDescriptorAndUsages(ResourceStages stages, ResourceType type, int setIndex, int start, int count, bool write = false) { AddDescriptor(stages, type, setIndex, start, count); - AddUsage(stages, type, setIndex, start, count); + AddUsage(stages, type, setIndex, start, count, write); } /// @@ -216,11 +217,12 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Descriptor set number where the resource will be bound /// Binding number where the resource will be bound /// Number of resources bound at the binding location - private void AddUsage(ResourceStages stages, ResourceType type, int setIndex, int binding, int count) + /// True if the binding is written from the shader, false otherwise + private void AddUsage(ResourceStages stages, ResourceType type, int setIndex, int binding, int count, bool write = false) { for (int index = 0; index < count; index++) { - _resourceUsages[setIndex].Add(new ResourceUsage(binding + index, 1, type, stages)); + _resourceUsages[setIndex].Add(new ResourceUsage(binding + index, 1, type, stages, write)); } } @@ -238,7 +240,8 @@ namespace Ryujinx.Graphics.Gpu.Shader buffer.Binding, 1, isStorage ? ResourceType.StorageBuffer : ResourceType.UniformBuffer, - stages)); + stages, + buffer.Flags.HasFlag(BufferUsageFlags.Write))); } } @@ -254,7 +257,12 @@ namespace Ryujinx.Graphics.Gpu.Shader { ResourceType type = GetTextureResourceType(texture, isImage); - GetUsages(texture.Set).Add(new ResourceUsage(texture.Binding, texture.ArrayLength, type, stages)); + GetUsages(texture.Set).Add(new ResourceUsage( + texture.Binding, + texture.ArrayLength, + type, + stages, + texture.Flags.HasFlag(TextureUsageFlags.ImageStore))); } } diff --git a/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs b/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs index 24642af2d4..a6a006bb9e 100644 --- a/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs +++ b/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs @@ -1,6 +1,7 @@ using Silk.NET.Vulkan; using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; namespace Ryujinx.Graphics.Vulkan { @@ -8,22 +9,64 @@ namespace Ryujinx.Graphics.Vulkan { private const int MaxBarriersPerCall = 16; + private const AccessFlags BaseAccess = AccessFlags.ShaderReadBit | AccessFlags.ShaderWriteBit; + private const AccessFlags BufferAccess = AccessFlags.IndexReadBit | AccessFlags.VertexAttributeReadBit | AccessFlags.UniformReadBit; + private const AccessFlags CommandBufferAccess = AccessFlags.IndirectCommandReadBit; + private readonly VulkanRenderer _gd; private readonly NativeArray _memoryBarrierBatch = new(MaxBarriersPerCall); private readonly NativeArray _bufferBarrierBatch = new(MaxBarriersPerCall); private readonly NativeArray _imageBarrierBatch = new(MaxBarriersPerCall); - private readonly List> _memoryBarriers = new(); - private readonly List> _bufferBarriers = new(); - private readonly List> _imageBarriers = new(); + private readonly List> _memoryBarriers = new(); + private readonly List> _bufferBarriers = new(); + private readonly List> _imageBarriers = new(); private int _queuedBarrierCount; + private enum IncoherentBarrierType + { + None, + Texture, + All, + CommandBuffer + } + + private PipelineStageFlags _incoherentBufferWriteStages; + private PipelineStageFlags _incoherentTextureWriteStages; + private PipelineStageFlags _extraStages; + private IncoherentBarrierType _queuedIncoherentBarrier; + public BarrierBatch(VulkanRenderer gd) { _gd = gd; } + public static (AccessFlags Access, PipelineStageFlags Stages) GetSubpassAccessSuperset(VulkanRenderer gd) + { + AccessFlags access = BufferAccess; + PipelineStageFlags stages = PipelineStageFlags.AllGraphicsBit; + + if (gd.TransformFeedbackApi != null) + { + access |= AccessFlags.TransformFeedbackWriteBitExt; + stages |= PipelineStageFlags.TransformFeedbackBitExt; + } + + if (!gd.IsTBDR) + { + // Desktop GPUs can transform image barriers into memory barriers. + + access |= AccessFlags.DepthStencilAttachmentWriteBit | AccessFlags.ColorAttachmentWriteBit; + access |= AccessFlags.DepthStencilAttachmentReadBit | AccessFlags.ColorAttachmentReadBit; + + stages |= PipelineStageFlags.EarlyFragmentTestsBit | PipelineStageFlags.LateFragmentTestsBit; + stages |= PipelineStageFlags.ColorAttachmentOutputBit; + } + + return (access, stages); + } + private readonly record struct StageFlags : IEquatable { public readonly PipelineStageFlags Source; @@ -36,47 +79,130 @@ namespace Ryujinx.Graphics.Vulkan } } - private readonly struct BarrierWithStageFlags where T : unmanaged + private readonly struct BarrierWithStageFlags where T : unmanaged { public readonly StageFlags Flags; public readonly T Barrier; + public readonly T2 Resource; public BarrierWithStageFlags(StageFlags flags, T barrier) { Flags = flags; Barrier = barrier; + Resource = default; } - public BarrierWithStageFlags(PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags, T barrier) + public BarrierWithStageFlags(PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags, T barrier, T2 resource) { Flags = new StageFlags(srcStageFlags, dstStageFlags); Barrier = barrier; + Resource = resource; } } - private void QueueBarrier(List> list, T barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags) where T : unmanaged + private void QueueBarrier(List> list, T barrier, T2 resource, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags) where T : unmanaged { - list.Add(new BarrierWithStageFlags(srcStageFlags, dstStageFlags, barrier)); + list.Add(new BarrierWithStageFlags(srcStageFlags, dstStageFlags, barrier, resource)); _queuedBarrierCount++; } public void QueueBarrier(MemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags) { - QueueBarrier(_memoryBarriers, barrier, srcStageFlags, dstStageFlags); + QueueBarrier(_memoryBarriers, barrier, default, srcStageFlags, dstStageFlags); } public void QueueBarrier(BufferMemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags) { - QueueBarrier(_bufferBarriers, barrier, srcStageFlags, dstStageFlags); + QueueBarrier(_bufferBarriers, barrier, default, srcStageFlags, dstStageFlags); } - public void QueueBarrier(ImageMemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags) + public void QueueBarrier(ImageMemoryBarrier barrier, TextureStorage resource, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags) { - QueueBarrier(_imageBarriers, barrier, srcStageFlags, dstStageFlags); + QueueBarrier(_imageBarriers, barrier, resource, srcStageFlags, dstStageFlags); } - public unsafe void Flush(CommandBuffer cb, bool insideRenderPass, Action endRenderPass) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void FlushMemoryBarrier(ShaderCollection program, bool inRenderPass) { + if (_queuedIncoherentBarrier > IncoherentBarrierType.None) + { + // We should emit a memory barrier if there's a write access in the program (current program, or program since last barrier) + bool hasTextureWrite = _incoherentTextureWriteStages != PipelineStageFlags.None; + bool hasBufferWrite = _incoherentBufferWriteStages != PipelineStageFlags.None; + bool hasBufferBarrier = _queuedIncoherentBarrier > IncoherentBarrierType.Texture; + + if (hasTextureWrite || (hasBufferBarrier && hasBufferWrite)) + { + AccessFlags access = BaseAccess; + + PipelineStageFlags stages = inRenderPass ? PipelineStageFlags.AllGraphicsBit : PipelineStageFlags.AllCommandsBit; + + if (hasBufferBarrier && hasBufferWrite) + { + access |= BufferAccess; + + if (_gd.TransformFeedbackApi != null) + { + access |= AccessFlags.TransformFeedbackWriteBitExt; + stages |= PipelineStageFlags.TransformFeedbackBitExt; + } + } + + if (_queuedIncoherentBarrier == IncoherentBarrierType.CommandBuffer) + { + access |= CommandBufferAccess; + stages |= PipelineStageFlags.DrawIndirectBit; + } + + MemoryBarrier barrier = new MemoryBarrier() + { + SType = StructureType.MemoryBarrier, + SrcAccessMask = access, + DstAccessMask = access + }; + + QueueBarrier(barrier, stages, stages); + + _incoherentTextureWriteStages = program?.IncoherentTextureWriteStages ?? PipelineStageFlags.None; + + if (_queuedIncoherentBarrier > IncoherentBarrierType.Texture) + { + if (program != null) + { + _incoherentBufferWriteStages = program.IncoherentBufferWriteStages | _extraStages; + } + else + { + _incoherentBufferWriteStages = PipelineStageFlags.None; + } + } + + _queuedIncoherentBarrier = IncoherentBarrierType.None; + } + } + } + + public unsafe void Flush(CommandBufferScoped cbs, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass) + { + Flush(cbs, null, inRenderPass, rpHolder, endRenderPass); + } + + public unsafe void Flush(CommandBufferScoped cbs, ShaderCollection program, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass) + { + if (program != null) + { + _incoherentBufferWriteStages |= program.IncoherentBufferWriteStages | _extraStages; + _incoherentTextureWriteStages |= program.IncoherentTextureWriteStages; + } + + FlushMemoryBarrier(program, inRenderPass); + + if (!inRenderPass && rpHolder != null) + { + // Render pass is about to begin. Queue any fences that normally interrupt the pass. + rpHolder.InsertForcedFences(cbs); + } + while (_queuedBarrierCount > 0) { int memoryCount = 0; @@ -86,20 +212,20 @@ namespace Ryujinx.Graphics.Vulkan bool hasBarrier = false; StageFlags flags = default; - static void AddBarriers( + static void AddBarriers( Span target, ref int queuedBarrierCount, ref bool hasBarrier, ref StageFlags flags, ref int count, - List> list) where T : unmanaged + List> list) where T : unmanaged { int firstMatch = -1; int end = list.Count; for (int i = 0; i < list.Count; i++) { - BarrierWithStageFlags barrier = list[i]; + BarrierWithStageFlags barrier = list[i]; if (!hasBarrier) { @@ -162,21 +288,60 @@ namespace Ryujinx.Graphics.Vulkan } } - if (insideRenderPass) + if (inRenderPass && _imageBarriers.Count > 0) { // Image barriers queued in the batch are meant to be globally scoped, // but inside a render pass they're scoped to just the range of the render pass. // On MoltenVK, we just break the rules and always use image barrier. // On desktop GPUs, all barriers are globally scoped, so we just replace it with a generic memory barrier. - // TODO: On certain GPUs, we need to split render pass so the barrier scope is global. When this is done, - // notify the resource that it should add a barrier as soon as a render pass ends to avoid this in future. + // 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. - if (!_gd.IsMoltenVk) + bool anyIsNonAttachment = false; + + foreach (BarrierWithStageFlags barrier in _imageBarriers) { + // 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) + { + 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 + { + // Generic pipeline memory barriers will work for desktop GPUs. + // They do require a few more access flags on the subpass dependency, though. foreach (var barrier in _imageBarriers) { - _memoryBarriers.Add(new BarrierWithStageFlags( + _memoryBarriers.Add(new BarrierWithStageFlags( barrier.Flags, new MemoryBarrier() { @@ -190,6 +355,22 @@ namespace Ryujinx.Graphics.Vulkan } } + if (inRenderPass && _memoryBarriers.Count > 0) + { + PipelineStageFlags allFlags = PipelineStageFlags.None; + + foreach (var barrier in _memoryBarriers) + { + allFlags |= barrier.Flags.Dest; + } + + if (allFlags.HasFlag(PipelineStageFlags.DrawIndirectBit) || !_gd.SupportsRenderPassBarrier(allFlags)) + { + endRenderPass(); + inRenderPass = false; + } + } + AddBarriers(_memoryBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref memoryCount, _memoryBarriers); AddBarriers(_bufferBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref bufferCount, _bufferBarriers); AddBarriers(_imageBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref imageCount, _imageBarriers); @@ -198,14 +379,14 @@ namespace Ryujinx.Graphics.Vulkan { PipelineStageFlags srcStageFlags = flags.Source; - if (insideRenderPass) + if (inRenderPass) { // Inside a render pass, barrier stages can only be from rasterization. srcStageFlags &= ~PipelineStageFlags.ComputeShaderBit; } _gd.Api.CmdPipelineBarrier( - cb, + cbs.CommandBuffer, srcStageFlags, flags.Dest, 0, @@ -219,6 +400,41 @@ namespace Ryujinx.Graphics.Vulkan } } + private void QueueIncoherentBarrier(IncoherentBarrierType type) + { + if (type > _queuedIncoherentBarrier) + { + _queuedIncoherentBarrier = type; + } + } + + public void QueueTextureBarrier() + { + QueueIncoherentBarrier(IncoherentBarrierType.Texture); + } + + public void QueueMemoryBarrier() + { + QueueIncoherentBarrier(IncoherentBarrierType.All); + } + + public void QueueCommandBufferBarrier() + { + QueueIncoherentBarrier(IncoherentBarrierType.CommandBuffer); + } + + public void EnableTfbBarriers(bool enable) + { + if (enable) + { + _extraStages |= PipelineStageFlags.TransformFeedbackBitExt; + } + else + { + _extraStages &= ~PipelineStageFlags.TransformFeedbackBitExt; + } + } + public void Dispose() { _memoryBarrierBatch.Dispose(); diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs b/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs index 5a5ddf8c8d..c4501ca17f 100644 --- a/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs +++ b/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs @@ -59,14 +59,14 @@ namespace Ryujinx.Graphics.Vulkan.Effects var scalingResourceLayout = new ResourceLayoutBuilder() .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) - .Add(ResourceStages.Compute, ResourceType.Image, 0).Build(); + .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build(); var sharpeningResourceLayout = new ResourceLayoutBuilder() .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 3) .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 4) .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) - .Add(ResourceStages.Compute, ResourceType.Image, 0).Build(); + .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build(); _sampler = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs index c129333354..70b3b32a74 100644 --- a/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs +++ b/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs @@ -42,7 +42,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects var resourceLayout = new ResourceLayoutBuilder() .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) - .Add(ResourceStages.Compute, ResourceType.Image, 0).Build(); + .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build(); _samplerLinear = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs index 08e07f256b..6d80f4a491 100644 --- a/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs +++ b/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs @@ -81,20 +81,20 @@ namespace Ryujinx.Graphics.Vulkan.Effects var edgeResourceLayout = new ResourceLayoutBuilder() .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) - .Add(ResourceStages.Compute, ResourceType.Image, 0).Build(); + .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build(); var blendResourceLayout = new ResourceLayoutBuilder() .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3) .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 4) - .Add(ResourceStages.Compute, ResourceType.Image, 0).Build(); + .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build(); var neighbourResourceLayout = new ResourceLayoutBuilder() .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3) - .Add(ResourceStages.Compute, ResourceType.Image, 0).Build(); + .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build(); _samplerLinear = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); diff --git a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs index ea0fd42e56..5c5a8f3ad4 100644 --- a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs +++ b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs @@ -286,10 +286,23 @@ namespace Ryujinx.Graphics.Vulkan _depthStencil?.Storage?.QueueLoadOpBarrier(cbs, true); - gd.Barriers.Flush(cbs.CommandBuffer, false, null); + gd.Barriers.Flush(cbs, false, null, null); } - public (Auto renderPass, Auto framebuffer) GetPassAndFramebuffer( + 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, CommandBufferScoped cbs) diff --git a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs index 3efb1119f4..73aa95c74c 100644 --- a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs +++ b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -115,7 +115,7 @@ namespace Ryujinx.Graphics.Vulkan var strideChangeResourceLayout = new ResourceLayoutBuilder() .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) - .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build(); + .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build(); _programStrideChange = gd.CreateProgramWithMinimalLayout(new[] { @@ -125,7 +125,7 @@ namespace Ryujinx.Graphics.Vulkan var colorCopyResourceLayout = new ResourceLayoutBuilder() .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 0) - .Add(ResourceStages.Compute, ResourceType.Image, 0).Build(); + .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build(); _programColorCopyShortening = gd.CreateProgramWithMinimalLayout(new[] { @@ -155,7 +155,7 @@ namespace Ryujinx.Graphics.Vulkan var convertD32S8ToD24S8ResourceLayout = new ResourceLayoutBuilder() .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) - .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build(); + .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build(); _programConvertD32S8ToD24S8 = gd.CreateProgramWithMinimalLayout(new[] { @@ -165,7 +165,7 @@ namespace Ryujinx.Graphics.Vulkan var convertIndexBufferResourceLayout = new ResourceLayoutBuilder() .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) - .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build(); + .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build(); _programConvertIndexBuffer = gd.CreateProgramWithMinimalLayout(new[] { @@ -175,7 +175,7 @@ namespace Ryujinx.Graphics.Vulkan var convertIndirectDataResourceLayout = new ResourceLayoutBuilder() .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) - .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2) + .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true) .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 3).Build(); _programConvertIndirectData = gd.CreateProgramWithMinimalLayout(new[] diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 2b2caeaec9..bda6167d7b 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -55,6 +55,7 @@ namespace Ryujinx.Graphics.Vulkan protected FramebufferParams FramebufferParams; private Auto _framebuffer; + private RenderPassHolder _rpHolder; private Auto _renderPass; private RenderPassHolder _nullRenderPass; private int _writtenAttachmentCount; @@ -85,8 +86,6 @@ namespace Ryujinx.Graphics.Vulkan private bool _tfActive; private readonly PipelineColorBlendAttachmentState[] _storedBlend; - - private ulong _drawCountSinceBarrier; public ulong DrawCount { get; private set; } public bool RenderPassActive { get; private set; } @@ -135,48 +134,7 @@ namespace Ryujinx.Graphics.Vulkan public unsafe void Barrier() { - if (_drawCountSinceBarrier != DrawCount) - { - _drawCountSinceBarrier = DrawCount; - - // Barriers are not supported inside a render pass on Apple GPUs. - // As a workaround, end the render pass. - if (Gd.Vendor == Vendor.Apple) - { - EndRenderPass(); - } - } - - MemoryBarrier memoryBarrier = new() - { - SType = StructureType.MemoryBarrier, - SrcAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit, - DstAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit, - }; - - PipelineStageFlags pipelineStageFlags = PipelineStageFlags.VertexShaderBit | PipelineStageFlags.FragmentShaderBit; - - if (Gd.Capabilities.SupportsGeometryShader) - { - pipelineStageFlags |= PipelineStageFlags.GeometryShaderBit; - } - - if (Gd.Capabilities.SupportsTessellationShader) - { - pipelineStageFlags |= PipelineStageFlags.TessellationControlShaderBit | PipelineStageFlags.TessellationEvaluationShaderBit; - } - - Gd.Api.CmdPipelineBarrier( - CommandBuffer, - pipelineStageFlags, - pipelineStageFlags, - 0, - 1, - memoryBarrier, - 0, - null, - 0, - null); + Gd.Barriers.QueueMemoryBarrier(); } public void ComputeBarrier() @@ -203,6 +161,7 @@ namespace Ryujinx.Graphics.Vulkan public void BeginTransformFeedback(PrimitiveTopology topology) { + Gd.Barriers.EnableTfbBarriers(true); _tfEnabled = true; } @@ -249,7 +208,7 @@ namespace Ryujinx.Graphics.Vulkan CreateRenderPass(); } - Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate); + Gd.Barriers.Flush(Cbs, RenderPassActive, _rpHolder, EndRenderPassDelegate); BeginRenderPass(); @@ -287,7 +246,7 @@ namespace Ryujinx.Graphics.Vulkan CreateRenderPass(); } - Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate); + Gd.Barriers.Flush(Cbs, RenderPassActive, _rpHolder, EndRenderPassDelegate); BeginRenderPass(); @@ -299,24 +258,7 @@ namespace Ryujinx.Graphics.Vulkan public unsafe void CommandBufferBarrier() { - MemoryBarrier memoryBarrier = new() - { - SType = StructureType.MemoryBarrier, - SrcAccessMask = BufferHolder.DefaultAccessFlags, - DstAccessMask = AccessFlags.IndirectCommandReadBit, - }; - - Gd.Api.CmdPipelineBarrier( - CommandBuffer, - PipelineStageFlags.AllCommandsBit, - PipelineStageFlags.DrawIndirectBit, - 0, - 1, - memoryBarrier, - 0, - null, - 0, - null); + Gd.Barriers.QueueCommandBufferBarrier(); } public void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size) @@ -722,6 +664,7 @@ namespace Ryujinx.Graphics.Vulkan public void EndTransformFeedback() { + Gd.Barriers.EnableTfbBarriers(false); PauseTransformFeedbackInternal(); _tfEnabled = false; } @@ -1408,24 +1351,7 @@ namespace Ryujinx.Graphics.Vulkan public unsafe void TextureBarrier() { - MemoryBarrier memoryBarrier = new() - { - SType = StructureType.MemoryBarrier, - SrcAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit, - DstAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit, - }; - - Gd.Api.CmdPipelineBarrier( - CommandBuffer, - PipelineStageFlags.FragmentShaderBit, - PipelineStageFlags.FragmentShaderBit, - 0, - 1, - memoryBarrier, - 0, - null, - 0, - null); + Gd.Barriers.QueueTextureBarrier(); } public void TextureBarrierTiled() @@ -1532,12 +1458,15 @@ namespace Ryujinx.Graphics.Vulkan // Use the null framebuffer. _nullRenderPass ??= new RenderPassHolder(Gd, Device, new RenderPassCacheKey(), FramebufferParams); + _rpHolder = _nullRenderPass; _renderPass = _nullRenderPass.GetRenderPass(); _framebuffer = _nullRenderPass.GetFramebuffer(Gd, Cbs, FramebufferParams); } else { - (_renderPass, _framebuffer) = FramebufferParams.GetPassAndFramebuffer(Gd, Device, Cbs); + (_rpHolder, _framebuffer) = FramebufferParams.GetPassAndFramebuffer(Gd, Device, Cbs); + + _renderPass = _rpHolder.GetRenderPass(); } } @@ -1564,7 +1493,7 @@ namespace Ryujinx.Graphics.Vulkan } } - Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate); + Gd.Barriers.Flush(Cbs, _program, RenderPassActive, _rpHolder, EndRenderPassDelegate); _descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Compute); } @@ -1629,7 +1558,7 @@ namespace Ryujinx.Graphics.Vulkan } } - Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate); + Gd.Barriers.Flush(Cbs, _program, RenderPassActive, _rpHolder, EndRenderPassDelegate); _descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Graphics); @@ -1708,6 +1637,8 @@ namespace Ryujinx.Graphics.Vulkan { if (RenderPassActive) { + FramebufferParams.AddStoreOpUsage(); + PauseTransformFeedbackInternal(); Gd.Api.CmdEndRenderPass(CommandBuffer); SignalRenderPassEnd(); diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs index 7d124c830a..89ce10b0aa 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs @@ -9,13 +9,6 @@ namespace Ryujinx.Graphics.Vulkan { static class PipelineConverter { - private const AccessFlags SubpassAccessMask = - AccessFlags.MemoryReadBit | - AccessFlags.MemoryWriteBit | - AccessFlags.ShaderReadBit | - AccessFlags.ColorAttachmentWriteBit | - AccessFlags.DepthStencilAttachmentWriteBit; - public static unsafe DisposableRenderPass ToRenderPass(this ProgramPipelineState state, VulkanRenderer gd, Device device) { const int MaxAttachments = Constants.MaxRenderTargets + 1; @@ -108,7 +101,7 @@ namespace Ryujinx.Graphics.Vulkan } } - var subpassDependency = CreateSubpassDependency(); + var subpassDependency = CreateSubpassDependency(gd); fixed (AttachmentDescription* pAttachmentDescs = attachmentDescs) { @@ -129,29 +122,33 @@ namespace Ryujinx.Graphics.Vulkan } } - public static SubpassDependency CreateSubpassDependency() + public static SubpassDependency CreateSubpassDependency(VulkanRenderer gd) { + var (access, stages) = BarrierBatch.GetSubpassAccessSuperset(gd); + return new SubpassDependency( 0, 0, - PipelineStageFlags.AllGraphicsBit, - PipelineStageFlags.AllGraphicsBit, - SubpassAccessMask, - SubpassAccessMask, + stages, + stages, + access, + access, 0); } - public unsafe static SubpassDependency2 CreateSubpassDependency2() + public unsafe static SubpassDependency2 CreateSubpassDependency2(VulkanRenderer gd) { + var (access, stages) = BarrierBatch.GetSubpassAccessSuperset(gd); + return new SubpassDependency2( StructureType.SubpassDependency2, null, 0, 0, - PipelineStageFlags.AllGraphicsBit, - PipelineStageFlags.AllGraphicsBit, - SubpassAccessMask, - SubpassAccessMask, + stages, + stages, + access, + access, 0); } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs b/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs index 5808406dca..cf65eefb0d 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs @@ -257,7 +257,7 @@ namespace Ryujinx.Graphics.Vulkan PreloadCbs = null; } - Gd.Barriers.Flush(Cbs.CommandBuffer, false, null); + Gd.Barriers.Flush(Cbs, false, null, null); CommandBuffer = (Cbs = Gd.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer; Gd.RegisterFlush(); diff --git a/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs b/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs index 9edea5788d..b2dd0dd874 100644 --- a/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs +++ b/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs @@ -1,5 +1,7 @@ using Silk.NET.Vulkan; using System; +using System.Collections.Generic; +using System.Linq; namespace Ryujinx.Graphics.Vulkan { @@ -29,10 +31,13 @@ namespace Ryujinx.Graphics.Vulkan } } + private readonly record struct ForcedFence(TextureStorage Texture, PipelineStageFlags StageFlags); + private readonly TextureView[] _textures; private readonly Auto _renderPass; private readonly HashTableSlim> _framebuffers; private readonly RenderPassCacheKey _key; + private readonly List _forcedFences; public unsafe RenderPassHolder(VulkanRenderer gd, Device device, RenderPassCacheKey key, FramebufferParams fb) { @@ -105,7 +110,7 @@ namespace Ryujinx.Graphics.Vulkan } } - var subpassDependency = PipelineConverter.CreateSubpassDependency(); + var subpassDependency = PipelineConverter.CreateSubpassDependency(gd); fixed (AttachmentDescription* pAttachmentDescs = attachmentDescs) { @@ -138,6 +143,8 @@ namespace Ryujinx.Graphics.Vulkan _textures = textures; _key = key; + + _forcedFences = new List(); } public Auto GetFramebuffer(VulkanRenderer gd, CommandBufferScoped cbs, FramebufferParams fb) @@ -159,6 +166,37 @@ namespace Ryujinx.Graphics.Vulkan return _renderPass; } + public void AddForcedFence(TextureStorage storage, PipelineStageFlags stageFlags) + { + if (!_forcedFences.Any(fence => fence.Texture == storage)) + { + _forcedFences.Add(new ForcedFence(storage, stageFlags)); + } + } + + public void InsertForcedFences(CommandBufferScoped cbs) + { + if (_forcedFences.Count > 0) + { + _forcedFences.RemoveAll((entry) => + { + if (entry.Texture.Disposed) + { + return true; + } + + entry.Texture.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, entry.StageFlags); + + return false; + }); + } + } + + 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/ResourceLayoutBuilder.cs b/src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs index 76a5ef4f95..730a0a2f91 100644 --- a/src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs +++ b/src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs @@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Vulkan } } - public ResourceLayoutBuilder Add(ResourceStages stages, ResourceType type, int binding) + public ResourceLayoutBuilder Add(ResourceStages stages, ResourceType type, int binding, bool write = false) { int setIndex = type switch { @@ -35,7 +35,7 @@ namespace Ryujinx.Graphics.Vulkan }; _resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding, 1, type, stages)); - _resourceUsages[setIndex].Add(new ResourceUsage(binding, 1, type, stages)); + _resourceUsages[setIndex].Add(new ResourceUsage(binding, 1, type, stages, write)); return this; } diff --git a/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs b/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs index b1547b795a..c9aab4018b 100644 --- a/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs +++ b/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs @@ -27,6 +27,9 @@ namespace Ryujinx.Graphics.Vulkan public uint Stages { get; } + public PipelineStageFlags IncoherentBufferWriteStages { get; } + public PipelineStageFlags IncoherentTextureWriteStages { get; } + public ResourceBindingSegment[][] ClearSegments { get; } public ResourceBindingSegment[][] BindingSegments { get; } public DescriptorSetTemplate[] Templates { get; } @@ -131,6 +134,7 @@ namespace Ryujinx.Graphics.Vulkan ClearSegments = BuildClearSegments(sets); BindingSegments = BuildBindingSegments(resourceLayout.SetUsages, out bool usesBufferTextures); Templates = BuildTemplates(usePushDescriptors); + (IncoherentBufferWriteStages, IncoherentTextureWriteStages) = BuildIncoherentStages(resourceLayout.SetUsages); // Updating buffer texture bindings using template updates crashes the Adreno driver on Windows. UpdateTexturesWithoutTemplate = gd.IsQualcommProprietary && usesBufferTextures; @@ -377,6 +381,73 @@ namespace Ryujinx.Graphics.Vulkan return templates; } + private PipelineStageFlags GetPipelineStages(ResourceStages stages) + { + PipelineStageFlags result = 0; + + if ((stages & ResourceStages.Compute) != 0) + { + result |= PipelineStageFlags.ComputeShaderBit; + } + + if ((stages & ResourceStages.Vertex) != 0) + { + result |= PipelineStageFlags.VertexShaderBit; + } + + if ((stages & ResourceStages.Fragment) != 0) + { + result |= PipelineStageFlags.FragmentShaderBit; + } + + if ((stages & ResourceStages.Geometry) != 0) + { + result |= PipelineStageFlags.GeometryShaderBit; + } + + if ((stages & ResourceStages.TessellationControl) != 0) + { + result |= PipelineStageFlags.TessellationControlShaderBit; + } + + if ((stages & ResourceStages.TessellationEvaluation) != 0) + { + result |= PipelineStageFlags.TessellationEvaluationShaderBit; + } + + return result; + } + + private (PipelineStageFlags Buffer, PipelineStageFlags Texture) BuildIncoherentStages(ReadOnlyCollection setUsages) + { + PipelineStageFlags buffer = PipelineStageFlags.None; + PipelineStageFlags texture = PipelineStageFlags.None; + + foreach (var set in setUsages) + { + foreach (var range in set.Usages) + { + if (range.Write) + { + PipelineStageFlags stages = GetPipelineStages(range.Stages); + + switch (range.Type) + { + case ResourceType.Image: + texture |= stages; + break; + case ResourceType.StorageBuffer: + case ResourceType.BufferImage: + buffer |= stages; + break; + } + } + } + } + + return (buffer, texture); + } + private async Task BackgroundCompilation() { await Task.WhenAll(_shaders.Select(shader => shader.CompileTask)); diff --git a/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs b/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs index 7c06a5df64..fdc0a248bd 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs @@ -407,7 +407,7 @@ namespace Ryujinx.Graphics.Vulkan ImageLayout.General, ImageLayout.General); - var subpassDependency = PipelineConverter.CreateSubpassDependency2(); + var subpassDependency = PipelineConverter.CreateSubpassDependency2(gd); fixed (AttachmentDescription2* pAttachmentDescs = attachmentDescs) { diff --git a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs index 1aaf2fbbee..f36db68de3 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs @@ -38,6 +38,8 @@ namespace Ryujinx.Graphics.Vulkan public TextureCreateInfo Info => _info; + public bool Disposed { get; private set; } + private readonly Image _image; private readonly Auto _imageAuto; private readonly Auto _allocationAuto; @@ -433,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; @@ -458,7 +471,7 @@ namespace Ryujinx.Graphics.Vulkan _info.GetLayers(), _info.Levels); - _gd.Barriers.QueueBarrier(barrier, srcStageFlags, dstStageFlags); + _gd.Barriers.QueueBarrier(barrier, this, srcStageFlags, dstStageFlags); _lastReadStage = PipelineStageFlags.None; _lastReadAccess = AccessFlags.None; @@ -491,7 +504,7 @@ namespace Ryujinx.Graphics.Vulkan _info.GetLayers(), _info.Levels); - _gd.Barriers.QueueBarrier(barrier, _lastModificationStage, dstStageFlags); + _gd.Barriers.QueueBarrier(barrier, this, _lastModificationStage, dstStageFlags); _lastModificationAccess = AccessFlags.None; } @@ -514,6 +527,8 @@ namespace Ryujinx.Graphics.Vulkan public void Dispose() { + Disposed = true; + if (_aliasedStorages != null) { foreach (var storage in _aliasedStorages.Values) diff --git a/src/Ryujinx.Graphics.Vulkan/TextureView.cs b/src/Ryujinx.Graphics.Vulkan/TextureView.cs index 5206680280..eb612da796 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureView.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureView.cs @@ -993,7 +993,7 @@ namespace Ryujinx.Graphics.Vulkan throw new NotImplementedException(); } - public (Auto renderPass, Auto framebuffer) GetPassAndFramebuffer( + public (RenderPassHolder rpHolder, Auto framebuffer) GetPassAndFramebuffer( VulkanRenderer gd, Device device, CommandBufferScoped cbs, @@ -1006,7 +1006,7 @@ namespace Ryujinx.Graphics.Vulkan rpHolder = new RenderPassHolder(gd, device, key, fb); } - return (rpHolder.GetRenderPass(), rpHolder.GetFramebuffer(gd, cbs, fb)); + return (rpHolder, rpHolder.GetFramebuffer(gd, cbs, fb)); } public void AddRenderPass(RenderPassCacheKey key, RenderPassHolder renderPass) diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index e46eac95ff..c9ce678b77 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -939,6 +939,11 @@ namespace Ryujinx.Graphics.Vulkan ScreenCaptured?.Invoke(this, bitmap); } + public bool SupportsRenderPassBarrier(PipelineStageFlags flags) + { + return !(IsMoltenVk || IsQualcommProprietary); + } + public unsafe void Dispose() { if (!_initialized)