aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Ryujinx.Graphics.GAL/ResourceLayout.cs4
-rw-r--r--src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs24
-rw-r--r--src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs260
-rw-r--r--src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs4
-rw-r--r--src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs2
-rw-r--r--src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs6
-rw-r--r--src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs17
-rw-r--r--src/Ryujinx.Graphics.Vulkan/HelperShader.cs10
-rw-r--r--src/Ryujinx.Graphics.Vulkan/PipelineBase.cs101
-rw-r--r--src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs33
-rw-r--r--src/Ryujinx.Graphics.Vulkan/PipelineFull.cs2
-rw-r--r--src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs40
-rw-r--r--src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs4
-rw-r--r--src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs71
-rw-r--r--src/Ryujinx.Graphics.Vulkan/TextureCopy.cs2
-rw-r--r--src/Ryujinx.Graphics.Vulkan/TextureStorage.cs19
-rw-r--r--src/Ryujinx.Graphics.Vulkan/TextureView.cs4
-rw-r--r--src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs5
18 files changed, 452 insertions, 156 deletions
diff --git a/src/Ryujinx.Graphics.GAL/ResourceLayout.cs b/src/Ryujinx.Graphics.GAL/ResourceLayout.cs
index 998c046f1..b7464ee12 100644
--- a/src/Ryujinx.Graphics.GAL/ResourceLayout.cs
+++ b/src/Ryujinx.Graphics.GAL/ResourceLayout.cs
@@ -74,13 +74,15 @@ namespace Ryujinx.Graphics.GAL
74 public int ArrayLength { get; } 74 public int ArrayLength { get; }
75 public ResourceType Type { get; } 75 public ResourceType Type { get; }
76 public ResourceStages Stages { get; } 76 public ResourceStages Stages { get; }
77 public bool Write { get; }
77 78
78 public ResourceUsage(int binding, int arrayLength, ResourceType type, ResourceStages stages) 79 public ResourceUsage(int binding, int arrayLength, ResourceType type, ResourceStages stages, bool write)
79 { 80 {
80 Binding = binding; 81 Binding = binding;
81 ArrayLength = arrayLength; 82 ArrayLength = arrayLength;
82 Type = type; 83 Type = type;
83 Stages = stages; 84 Stages = stages;
85 Write = write;
84 } 86 }
85 87
86 public override int GetHashCode() 88 public override int GetHashCode()
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs
index 42b2cbb59..49823562f 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
78 ResourceStages stages = vertexAsCompute ? ResourceStages.Compute | ResourceStages.Vertex : VtgStages; 78 ResourceStages stages = vertexAsCompute ? ResourceStages.Compute | ResourceStages.Vertex : VtgStages;
79 79
80 PopulateDescriptorAndUsages(stages, ResourceType.UniformBuffer, uniformSetIndex, 1, rrc.ReservedConstantBuffers - 1); 80 PopulateDescriptorAndUsages(stages, ResourceType.UniformBuffer, uniformSetIndex, 1, rrc.ReservedConstantBuffers - 1);
81 PopulateDescriptorAndUsages(stages, ResourceType.StorageBuffer, storageSetIndex, 0, rrc.ReservedStorageBuffers); 81 PopulateDescriptorAndUsages(stages, ResourceType.StorageBuffer, storageSetIndex, 0, rrc.ReservedStorageBuffers, true);
82 PopulateDescriptorAndUsages(stages, ResourceType.BufferTexture, textureSetIndex, 0, rrc.ReservedTextures); 82 PopulateDescriptorAndUsages(stages, ResourceType.BufferTexture, textureSetIndex, 0, rrc.ReservedTextures);
83 PopulateDescriptorAndUsages(stages, ResourceType.BufferImage, imageSetIndex, 0, rrc.ReservedImages); 83 PopulateDescriptorAndUsages(stages, ResourceType.BufferImage, imageSetIndex, 0, rrc.ReservedImages, true);
84 } 84 }
85 85
86 /// <summary> 86 /// <summary>
@@ -91,10 +91,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
91 /// <param name="setIndex">Resource set index where the resources are used</param> 91 /// <param name="setIndex">Resource set index where the resources are used</param>
92 /// <param name="start">First binding number</param> 92 /// <param name="start">First binding number</param>
93 /// <param name="count">Amount of bindings</param> 93 /// <param name="count">Amount of bindings</param>
94 private void PopulateDescriptorAndUsages(ResourceStages stages, ResourceType type, int setIndex, int start, int count) 94 /// <param name="write">True if the binding is written from the shader, false otherwise</param>
95 private void PopulateDescriptorAndUsages(ResourceStages stages, ResourceType type, int setIndex, int start, int count, bool write = false)
95 { 96 {
96 AddDescriptor(stages, type, setIndex, start, count); 97 AddDescriptor(stages, type, setIndex, start, count);
97 AddUsage(stages, type, setIndex, start, count); 98 AddUsage(stages, type, setIndex, start, count, write);
98 } 99 }
99 100
100 /// <summary> 101 /// <summary>
@@ -216,11 +217,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
216 /// <param name="setIndex">Descriptor set number where the resource will be bound</param> 217 /// <param name="setIndex">Descriptor set number where the resource will be bound</param>
217 /// <param name="binding">Binding number where the resource will be bound</param> 218 /// <param name="binding">Binding number where the resource will be bound</param>
218 /// <param name="count">Number of resources bound at the binding location</param> 219 /// <param name="count">Number of resources bound at the binding location</param>
219 private void AddUsage(ResourceStages stages, ResourceType type, int setIndex, int binding, int count) 220 /// <param name="write">True if the binding is written from the shader, false otherwise</param>
221 private void AddUsage(ResourceStages stages, ResourceType type, int setIndex, int binding, int count, bool write = false)
220 { 222 {
221 for (int index = 0; index < count; index++) 223 for (int index = 0; index < count; index++)
222 { 224 {
223 _resourceUsages[setIndex].Add(new ResourceUsage(binding + index, 1, type, stages)); 225 _resourceUsages[setIndex].Add(new ResourceUsage(binding + index, 1, type, stages, write));
224 } 226 }
225 } 227 }
226 228
@@ -238,7 +240,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
238 buffer.Binding, 240 buffer.Binding,
239 1, 241 1,
240 isStorage ? ResourceType.StorageBuffer : ResourceType.UniformBuffer, 242 isStorage ? ResourceType.StorageBuffer : ResourceType.UniformBuffer,
241 stages)); 243 stages,
244 buffer.Flags.HasFlag(BufferUsageFlags.Write)));
242 } 245 }
243 } 246 }
244 247
@@ -254,7 +257,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
254 { 257 {
255 ResourceType type = GetTextureResourceType(texture, isImage); 258 ResourceType type = GetTextureResourceType(texture, isImage);
256 259
257 GetUsages(texture.Set).Add(new ResourceUsage(texture.Binding, texture.ArrayLength, type, stages)); 260 GetUsages(texture.Set).Add(new ResourceUsage(
261 texture.Binding,
262 texture.ArrayLength,
263 type,
264 stages,
265 texture.Flags.HasFlag(TextureUsageFlags.ImageStore)));
258 } 266 }
259 } 267 }
260 268
diff --git a/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs b/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs
index 24642af2d..a6a006bb9 100644
--- a/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs
+++ b/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs
@@ -1,6 +1,7 @@
1using Silk.NET.Vulkan; 1using Silk.NET.Vulkan;
2using System; 2using System;
3using System.Collections.Generic; 3using System.Collections.Generic;
4using System.Runtime.CompilerServices;
4 5
5namespace Ryujinx.Graphics.Vulkan 6namespace Ryujinx.Graphics.Vulkan
6{ 7{
@@ -8,22 +9,64 @@ namespace Ryujinx.Graphics.Vulkan
8 { 9 {
9 private const int MaxBarriersPerCall = 16; 10 private const int MaxBarriersPerCall = 16;
10 11
12 private const AccessFlags BaseAccess = AccessFlags.ShaderReadBit | AccessFlags.ShaderWriteBit;
13 private const AccessFlags BufferAccess = AccessFlags.IndexReadBit | AccessFlags.VertexAttributeReadBit | AccessFlags.UniformReadBit;
14 private const AccessFlags CommandBufferAccess = AccessFlags.IndirectCommandReadBit;
15
11 private readonly VulkanRenderer _gd; 16 private readonly VulkanRenderer _gd;
12 17
13 private readonly NativeArray<MemoryBarrier> _memoryBarrierBatch = new(MaxBarriersPerCall); 18 private readonly NativeArray<MemoryBarrier> _memoryBarrierBatch = new(MaxBarriersPerCall);
14 private readonly NativeArray<BufferMemoryBarrier> _bufferBarrierBatch = new(MaxBarriersPerCall); 19 private readonly NativeArray<BufferMemoryBarrier> _bufferBarrierBatch = new(MaxBarriersPerCall);
15 private readonly NativeArray<ImageMemoryBarrier> _imageBarrierBatch = new(MaxBarriersPerCall); 20 private readonly NativeArray<ImageMemoryBarrier> _imageBarrierBatch = new(MaxBarriersPerCall);
16 21
17 private readonly List<BarrierWithStageFlags<MemoryBarrier>> _memoryBarriers = new(); 22 private readonly List<BarrierWithStageFlags<MemoryBarrier, int>> _memoryBarriers = new();
18 private readonly List<BarrierWithStageFlags<BufferMemoryBarrier>> _bufferBarriers = new(); 23 private readonly List<BarrierWithStageFlags<BufferMemoryBarrier, int>> _bufferBarriers = new();
19 private readonly List<BarrierWithStageFlags<ImageMemoryBarrier>> _imageBarriers = new(); 24 private readonly List<BarrierWithStageFlags<ImageMemoryBarrier, TextureStorage>> _imageBarriers = new();
20 private int _queuedBarrierCount; 25 private int _queuedBarrierCount;
21 26
27 private enum IncoherentBarrierType
28 {
29 None,
30 Texture,
31 All,
32 CommandBuffer
33 }
34
35 private PipelineStageFlags _incoherentBufferWriteStages;
36 private PipelineStageFlags _incoherentTextureWriteStages;
37 private PipelineStageFlags _extraStages;
38 private IncoherentBarrierType _queuedIncoherentBarrier;
39
22 public BarrierBatch(VulkanRenderer gd) 40 public BarrierBatch(VulkanRenderer gd)
23 { 41 {
24 _gd = gd; 42 _gd = gd;
25 } 43 }
26 44
45 public static (AccessFlags Access, PipelineStageFlags Stages) GetSubpassAccessSuperset(VulkanRenderer gd)
46 {
47 AccessFlags access = BufferAccess;
48 PipelineStageFlags stages = PipelineStageFlags.AllGraphicsBit;
49
50 if (gd.TransformFeedbackApi != null)
51 {
52 access |= AccessFlags.TransformFeedbackWriteBitExt;
53 stages |= PipelineStageFlags.TransformFeedbackBitExt;
54 }
55
56 if (!gd.IsTBDR)
57 {
58 // Desktop GPUs can transform image barriers into memory barriers.
59
60 access |= AccessFlags.DepthStencilAttachmentWriteBit | AccessFlags.ColorAttachmentWriteBit;
61 access |= AccessFlags.DepthStencilAttachmentReadBit | AccessFlags.ColorAttachmentReadBit;
62
63 stages |= PipelineStageFlags.EarlyFragmentTestsBit | PipelineStageFlags.LateFragmentTestsBit;
64 stages |= PipelineStageFlags.ColorAttachmentOutputBit;
65 }
66
67 return (access, stages);
68 }
69
27 private readonly record struct StageFlags : IEquatable<StageFlags> 70 private readonly record struct StageFlags : IEquatable<StageFlags>
28 { 71 {
29 public readonly PipelineStageFlags Source; 72 public readonly PipelineStageFlags Source;
@@ -36,47 +79,130 @@ namespace Ryujinx.Graphics.Vulkan
36 } 79 }
37 } 80 }
38 81
39 private readonly struct BarrierWithStageFlags<T> where T : unmanaged 82 private readonly struct BarrierWithStageFlags<T, T2> where T : unmanaged
40 { 83 {
41 public readonly StageFlags Flags; 84 public readonly StageFlags Flags;
42 public readonly T Barrier; 85 public readonly T Barrier;
86 public readonly T2 Resource;
43 87
44 public BarrierWithStageFlags(StageFlags flags, T barrier) 88 public BarrierWithStageFlags(StageFlags flags, T barrier)
45 { 89 {
46 Flags = flags; 90 Flags = flags;
47 Barrier = barrier; 91 Barrier = barrier;
92 Resource = default;
48 } 93 }
49 94
50 public BarrierWithStageFlags(PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags, T barrier) 95 public BarrierWithStageFlags(PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags, T barrier, T2 resource)
51 { 96 {
52 Flags = new StageFlags(srcStageFlags, dstStageFlags); 97 Flags = new StageFlags(srcStageFlags, dstStageFlags);
53 Barrier = barrier; 98 Barrier = barrier;
99 Resource = resource;
54 } 100 }
55 } 101 }
56 102
57 private void QueueBarrier<T>(List<BarrierWithStageFlags<T>> list, T barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags) where T : unmanaged 103 private void QueueBarrier<T, T2>(List<BarrierWithStageFlags<T, T2>> list, T barrier, T2 resource, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags) where T : unmanaged
58 { 104 {
59 list.Add(new BarrierWithStageFlags<T>(srcStageFlags, dstStageFlags, barrier)); 105 list.Add(new BarrierWithStageFlags<T, T2>(srcStageFlags, dstStageFlags, barrier, resource));
60 _queuedBarrierCount++; 106 _queuedBarrierCount++;
61 } 107 }
62 108
63 public void QueueBarrier(MemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags) 109 public void QueueBarrier(MemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags)
64 { 110 {
65 QueueBarrier(_memoryBarriers, barrier, srcStageFlags, dstStageFlags); 111 QueueBarrier(_memoryBarriers, barrier, default, srcStageFlags, dstStageFlags);
66 } 112 }
67 113
68 public void QueueBarrier(BufferMemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags) 114 public void QueueBarrier(BufferMemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags)
69 { 115 {
70 QueueBarrier(_bufferBarriers, barrier, srcStageFlags, dstStageFlags); 116 QueueBarrier(_bufferBarriers, barrier, default, srcStageFlags, dstStageFlags);
71 } 117 }
72 118
73 public void QueueBarrier(ImageMemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags) 119 public void QueueBarrier(ImageMemoryBarrier barrier, TextureStorage resource, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags)
74 { 120 {
75 QueueBarrier(_imageBarriers, barrier, srcStageFlags, dstStageFlags); 121 QueueBarrier(_imageBarriers, barrier, resource, srcStageFlags, dstStageFlags);
76 } 122 }
77 123
78 public unsafe void Flush(CommandBuffer cb, bool insideRenderPass, Action endRenderPass) 124 [MethodImpl(MethodImplOptions.AggressiveInlining)]
125 public unsafe void FlushMemoryBarrier(ShaderCollection program, bool inRenderPass)
79 { 126 {
127 if (_queuedIncoherentBarrier > IncoherentBarrierType.None)
128 {
129 // We should emit a memory barrier if there's a write access in the program (current program, or program since last barrier)
130 bool hasTextureWrite = _incoherentTextureWriteStages != PipelineStageFlags.None;
131 bool hasBufferWrite = _incoherentBufferWriteStages != PipelineStageFlags.None;
132 bool hasBufferBarrier = _queuedIncoherentBarrier > IncoherentBarrierType.Texture;
133
134 if (hasTextureWrite || (hasBufferBarrier && hasBufferWrite))
135 {
136 AccessFlags access = BaseAccess;
137
138 PipelineStageFlags stages = inRenderPass ? PipelineStageFlags.AllGraphicsBit : PipelineStageFlags.AllCommandsBit;
139
140 if (hasBufferBarrier && hasBufferWrite)
141 {
142 access |= BufferAccess;
143
144 if (_gd.TransformFeedbackApi != null)
145 {
146 access |= AccessFlags.TransformFeedbackWriteBitExt;
147 stages |= PipelineStageFlags.TransformFeedbackBitExt;
148 }
149 }
150
151 if (_queuedIncoherentBarrier == IncoherentBarrierType.CommandBuffer)
152 {
153 access |= CommandBufferAccess;
154 stages |= PipelineStageFlags.DrawIndirectBit;
155 }
156
157 MemoryBarrier barrier = new MemoryBarrier()
158 {
159 SType = StructureType.MemoryBarrier,
160 SrcAccessMask = access,
161 DstAccessMask = access
162 };
163
164 QueueBarrier(barrier, stages, stages);
165
166 _incoherentTextureWriteStages = program?.IncoherentTextureWriteStages ?? PipelineStageFlags.None;
167
168 if (_queuedIncoherentBarrier > IncoherentBarrierType.Texture)
169 {
170 if (program != null)
171 {
172 _incoherentBufferWriteStages = program.IncoherentBufferWriteStages | _extraStages;
173 }
174 else
175 {
176 _incoherentBufferWriteStages = PipelineStageFlags.None;
177 }
178 }
179
180 _queuedIncoherentBarrier = IncoherentBarrierType.None;
181 }
182 }
183 }
184
185 public unsafe void Flush(CommandBufferScoped cbs, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass)
186 {
187 Flush(cbs, null, inRenderPass, rpHolder, endRenderPass);
188 }
189
190 public unsafe void Flush(CommandBufferScoped cbs, ShaderCollection program, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass)
191 {
192 if (program != null)
193 {
194 _incoherentBufferWriteStages |= program.IncoherentBufferWriteStages | _extraStages;
195 _incoherentTextureWriteStages |= program.IncoherentTextureWriteStages;
196 }
197
198 FlushMemoryBarrier(program, inRenderPass);
199
200 if (!inRenderPass && rpHolder != null)
201 {
202 // Render pass is about to begin. Queue any fences that normally interrupt the pass.
203 rpHolder.InsertForcedFences(cbs);
204 }
205
80 while (_queuedBarrierCount > 0) 206 while (_queuedBarrierCount > 0)
81 { 207 {
82 int memoryCount = 0; 208 int memoryCount = 0;
@@ -86,20 +212,20 @@ namespace Ryujinx.Graphics.Vulkan
86 bool hasBarrier = false; 212 bool hasBarrier = false;
87 StageFlags flags = default; 213 StageFlags flags = default;
88 214
89 static void AddBarriers<T>( 215 static void AddBarriers<T, T2>(
90 Span<T> target, 216 Span<T> target,
91 ref int queuedBarrierCount, 217 ref int queuedBarrierCount,
92 ref bool hasBarrier, 218 ref bool hasBarrier,
93 ref StageFlags flags, 219 ref StageFlags flags,
94 ref int count, 220 ref int count,
95 List<BarrierWithStageFlags<T>> list) where T : unmanaged 221 List<BarrierWithStageFlags<T, T2>> list) where T : unmanaged
96 { 222 {
97 int firstMatch = -1; 223 int firstMatch = -1;
98 int end = list.Count; 224 int end = list.Count;
99 225
100 for (int i = 0; i < list.Count; i++) 226 for (int i = 0; i < list.Count; i++)
101 { 227 {
102 BarrierWithStageFlags<T> barrier = list[i]; 228 BarrierWithStageFlags<T, T2> barrier = list[i];
103 229
104 if (!hasBarrier) 230 if (!hasBarrier)
105 { 231 {
@@ -162,21 +288,60 @@ namespace Ryujinx.Graphics.Vulkan
162 } 288 }
163 } 289 }
164 290
165 if (insideRenderPass) 291 if (inRenderPass && _imageBarriers.Count > 0)
166 { 292 {
167 // Image barriers queued in the batch are meant to be globally scoped, 293 // Image barriers queued in the batch are meant to be globally scoped,
168 // but inside a render pass they're scoped to just the range of the render pass. 294 // but inside a render pass they're scoped to just the range of the render pass.
169 295
170 // On MoltenVK, we just break the rules and always use image barrier. 296 // On MoltenVK, we just break the rules and always use image barrier.
171 // On desktop GPUs, all barriers are globally scoped, so we just replace it with a generic memory barrier. 297 // On desktop GPUs, all barriers are globally scoped, so we just replace it with a generic memory barrier.
172 // TODO: On certain GPUs, we need to split render pass so the barrier scope is global. When this is done, 298 // Generally, we want to avoid this from happening in the future, so flag the texture to immediately
173 // notify the resource that it should add a barrier as soon as a render pass ends to avoid this in future. 299 // emit a barrier whenever the current render pass is bound again.
300
301 bool anyIsNonAttachment = false;
302
303 foreach (BarrierWithStageFlags<ImageMemoryBarrier, TextureStorage> barrier in _imageBarriers)
304 {
305 // If the binding is an attachment, don't add it as a forced fence.
306 bool isAttachment = rpHolder.ContainsAttachment(barrier.Resource);
307
308 if (!isAttachment)
309 {
310 rpHolder.AddForcedFence(barrier.Resource, barrier.Flags.Dest);
311 anyIsNonAttachment = true;
312 }
313 }
314
315 if (_gd.IsTBDR)
316 {
317 if (!_gd.IsMoltenVk)
318 {
319 if (!anyIsNonAttachment)
320 {
321 // This case is a feedback loop. To prevent this from causing an absolute performance disaster,
322 // remove the barriers entirely.
323 // If this is not here, there will be a lot of single draw render passes.
324 // TODO: explicit handling for feedback loops, likely outside this class.
174 325
175 if (!_gd.IsMoltenVk) 326 _queuedBarrierCount -= _imageBarriers.Count;
327 _imageBarriers.Clear();
328 }
329 else
330 {
331 // TBDR GPUs are sensitive to barriers, so we need to end the pass to ensure the data is available.
332 // Metal already has hazard tracking so MVK doesn't need this.
333 endRenderPass();
334 inRenderPass = false;
335 }
336 }
337 }
338 else
176 { 339 {
340 // Generic pipeline memory barriers will work for desktop GPUs.
341 // They do require a few more access flags on the subpass dependency, though.
177 foreach (var barrier in _imageBarriers) 342 foreach (var barrier in _imageBarriers)
178 { 343 {
179 _memoryBarriers.Add(new BarrierWithStageFlags<MemoryBarrier>( 344 _memoryBarriers.Add(new BarrierWithStageFlags<MemoryBarrier, int>(
180 barrier.Flags, 345 barrier.Flags,
181 new MemoryBarrier() 346 new MemoryBarrier()
182 { 347 {
@@ -190,6 +355,22 @@ namespace Ryujinx.Graphics.Vulkan
190 } 355 }
191 } 356 }
192 357
358 if (inRenderPass && _memoryBarriers.Count > 0)
359 {
360 PipelineStageFlags allFlags = PipelineStageFlags.None;
361
362 foreach (var barrier in _memoryBarriers)
363 {
364 allFlags |= barrier.Flags.Dest;
365 }
366
367 if (allFlags.HasFlag(PipelineStageFlags.DrawIndirectBit) || !_gd.SupportsRenderPassBarrier(allFlags))
368 {
369 endRenderPass();
370 inRenderPass = false;
371 }
372 }
373
193 AddBarriers(_memoryBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref memoryCount, _memoryBarriers); 374 AddBarriers(_memoryBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref memoryCount, _memoryBarriers);
194 AddBarriers(_bufferBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref bufferCount, _bufferBarriers); 375 AddBarriers(_bufferBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref bufferCount, _bufferBarriers);
195 AddBarriers(_imageBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref imageCount, _imageBarriers); 376 AddBarriers(_imageBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref imageCount, _imageBarriers);
@@ -198,14 +379,14 @@ namespace Ryujinx.Graphics.Vulkan
198 { 379 {
199 PipelineStageFlags srcStageFlags = flags.Source; 380 PipelineStageFlags srcStageFlags = flags.Source;
200 381
201 if (insideRenderPass) 382 if (inRenderPass)
202 { 383 {
203 // Inside a render pass, barrier stages can only be from rasterization. 384 // Inside a render pass, barrier stages can only be from rasterization.
204 srcStageFlags &= ~PipelineStageFlags.ComputeShaderBit; 385 srcStageFlags &= ~PipelineStageFlags.ComputeShaderBit;
205 } 386 }
206 387
207 _gd.Api.CmdPipelineBarrier( 388 _gd.Api.CmdPipelineBarrier(
208 cb, 389 cbs.CommandBuffer,
209 srcStageFlags, 390 srcStageFlags,
210 flags.Dest, 391 flags.Dest,
211 0, 392 0,
@@ -219,6 +400,41 @@ namespace Ryujinx.Graphics.Vulkan
219 } 400 }
220 } 401 }
221 402
403 private void QueueIncoherentBarrier(IncoherentBarrierType type)
404 {
405 if (type > _queuedIncoherentBarrier)
406 {
407 _queuedIncoherentBarrier = type;
408 }
409 }
410
411 public void QueueTextureBarrier()
412 {
413 QueueIncoherentBarrier(IncoherentBarrierType.Texture);
414 }
415
416 public void QueueMemoryBarrier()
417 {
418 QueueIncoherentBarrier(IncoherentBarrierType.All);
419 }
420
421 public void QueueCommandBufferBarrier()
422 {
423 QueueIncoherentBarrier(IncoherentBarrierType.CommandBuffer);
424 }
425
426 public void EnableTfbBarriers(bool enable)
427 {
428 if (enable)
429 {
430 _extraStages |= PipelineStageFlags.TransformFeedbackBitExt;
431 }
432 else
433 {
434 _extraStages &= ~PipelineStageFlags.TransformFeedbackBitExt;
435 }
436 }
437
222 public void Dispose() 438 public void Dispose()
223 { 439 {
224 _memoryBarrierBatch.Dispose(); 440 _memoryBarrierBatch.Dispose();
diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs b/src/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs
index 5a5ddf8c8..c4501ca17 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
59 var scalingResourceLayout = new ResourceLayoutBuilder() 59 var scalingResourceLayout = new ResourceLayoutBuilder()
60 .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) 60 .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
61 .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) 61 .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
62 .Add(ResourceStages.Compute, ResourceType.Image, 0).Build(); 62 .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
63 63
64 var sharpeningResourceLayout = new ResourceLayoutBuilder() 64 var sharpeningResourceLayout = new ResourceLayoutBuilder()
65 .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) 65 .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
66 .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 3) 66 .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 3)
67 .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 4) 67 .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 4)
68 .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) 68 .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
69 .Add(ResourceStages.Compute, ResourceType.Image, 0).Build(); 69 .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
70 70
71 _sampler = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); 71 _sampler = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
72 72
diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs
index c12933335..70b3b32a7 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
42 var resourceLayout = new ResourceLayoutBuilder() 42 var resourceLayout = new ResourceLayoutBuilder()
43 .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) 43 .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
44 .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) 44 .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
45 .Add(ResourceStages.Compute, ResourceType.Image, 0).Build(); 45 .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
46 46
47 _samplerLinear = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); 47 _samplerLinear = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
48 48
diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs
index 08e07f256..6d80f4a49 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
81 var edgeResourceLayout = new ResourceLayoutBuilder() 81 var edgeResourceLayout = new ResourceLayoutBuilder()
82 .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) 82 .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
83 .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) 83 .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
84 .Add(ResourceStages.Compute, ResourceType.Image, 0).Build(); 84 .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
85 85
86 var blendResourceLayout = new ResourceLayoutBuilder() 86 var blendResourceLayout = new ResourceLayoutBuilder()
87 .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) 87 .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
88 .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) 88 .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
89 .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3) 89 .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3)
90 .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 4) 90 .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 4)
91 .Add(ResourceStages.Compute, ResourceType.Image, 0).Build(); 91 .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
92 92
93 var neighbourResourceLayout = new ResourceLayoutBuilder() 93 var neighbourResourceLayout = new ResourceLayoutBuilder()
94 .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) 94 .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
95 .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) 95 .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
96 .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3) 96 .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3)
97 .Add(ResourceStages.Compute, ResourceType.Image, 0).Build(); 97 .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
98 98
99 _samplerLinear = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); 99 _samplerLinear = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
100 100
diff --git a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs
index ea0fd42e5..5c5a8f3ad 100644
--- a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs
+++ b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs
@@ -286,10 +286,23 @@ namespace Ryujinx.Graphics.Vulkan
286 286
287 _depthStencil?.Storage?.QueueLoadOpBarrier(cbs, true); 287 _depthStencil?.Storage?.QueueLoadOpBarrier(cbs, true);
288 288
289 gd.Barriers.Flush(cbs.CommandBuffer, false, null); 289 gd.Barriers.Flush(cbs, false, null, null);
290 } 290 }
291 291
292 public (Auto<DisposableRenderPass> renderPass, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer( 292 public void AddStoreOpUsage()
293 {
294 if (_colors != null)
295 {
296 foreach (var color in _colors)
297 {
298 color.Storage?.AddStoreOpUsage(false);
299 }
300 }
301
302 _depthStencil?.Storage?.AddStoreOpUsage(true);
303 }
304
305 public (RenderPassHolder rpHolder, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
293 VulkanRenderer gd, 306 VulkanRenderer gd,
294 Device device, 307 Device device,
295 CommandBufferScoped cbs) 308 CommandBufferScoped cbs)
diff --git a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs
index 3efb1119f..73aa95c74 100644
--- a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs
+++ b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs
@@ -115,7 +115,7 @@ namespace Ryujinx.Graphics.Vulkan
115 var strideChangeResourceLayout = new ResourceLayoutBuilder() 115 var strideChangeResourceLayout = new ResourceLayoutBuilder()
116 .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) 116 .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
117 .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) 117 .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
118 .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build(); 118 .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build();
119 119
120 _programStrideChange = gd.CreateProgramWithMinimalLayout(new[] 120 _programStrideChange = gd.CreateProgramWithMinimalLayout(new[]
121 { 121 {
@@ -125,7 +125,7 @@ namespace Ryujinx.Graphics.Vulkan
125 var colorCopyResourceLayout = new ResourceLayoutBuilder() 125 var colorCopyResourceLayout = new ResourceLayoutBuilder()
126 .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) 126 .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
127 .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 0) 127 .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 0)
128 .Add(ResourceStages.Compute, ResourceType.Image, 0).Build(); 128 .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
129 129
130 _programColorCopyShortening = gd.CreateProgramWithMinimalLayout(new[] 130 _programColorCopyShortening = gd.CreateProgramWithMinimalLayout(new[]
131 { 131 {
@@ -155,7 +155,7 @@ namespace Ryujinx.Graphics.Vulkan
155 var convertD32S8ToD24S8ResourceLayout = new ResourceLayoutBuilder() 155 var convertD32S8ToD24S8ResourceLayout = new ResourceLayoutBuilder()
156 .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) 156 .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
157 .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) 157 .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
158 .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build(); 158 .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build();
159 159
160 _programConvertD32S8ToD24S8 = gd.CreateProgramWithMinimalLayout(new[] 160 _programConvertD32S8ToD24S8 = gd.CreateProgramWithMinimalLayout(new[]
161 { 161 {
@@ -165,7 +165,7 @@ namespace Ryujinx.Graphics.Vulkan
165 var convertIndexBufferResourceLayout = new ResourceLayoutBuilder() 165 var convertIndexBufferResourceLayout = new ResourceLayoutBuilder()
166 .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) 166 .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
167 .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) 167 .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
168 .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build(); 168 .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build();
169 169
170 _programConvertIndexBuffer = gd.CreateProgramWithMinimalLayout(new[] 170 _programConvertIndexBuffer = gd.CreateProgramWithMinimalLayout(new[]
171 { 171 {
@@ -175,7 +175,7 @@ namespace Ryujinx.Graphics.Vulkan
175 var convertIndirectDataResourceLayout = new ResourceLayoutBuilder() 175 var convertIndirectDataResourceLayout = new ResourceLayoutBuilder()
176 .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) 176 .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
177 .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) 177 .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
178 .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2) 178 .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true)
179 .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 3).Build(); 179 .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 3).Build();
180 180
181 _programConvertIndirectData = gd.CreateProgramWithMinimalLayout(new[] 181 _programConvertIndirectData = gd.CreateProgramWithMinimalLayout(new[]
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs
index 2b2caeaec..bda6167d7 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs
@@ -55,6 +55,7 @@ namespace Ryujinx.Graphics.Vulkan
55 55
56 protected FramebufferParams FramebufferParams; 56 protected FramebufferParams FramebufferParams;
57 private Auto<DisposableFramebuffer> _framebuffer; 57 private Auto<DisposableFramebuffer> _framebuffer;
58 private RenderPassHolder _rpHolder;
58 private Auto<DisposableRenderPass> _renderPass; 59 private Auto<DisposableRenderPass> _renderPass;
59 private RenderPassHolder _nullRenderPass; 60 private RenderPassHolder _nullRenderPass;
60 private int _writtenAttachmentCount; 61 private int _writtenAttachmentCount;
@@ -85,8 +86,6 @@ namespace Ryujinx.Graphics.Vulkan
85 private bool _tfActive; 86 private bool _tfActive;
86 87
87 private readonly PipelineColorBlendAttachmentState[] _storedBlend; 88 private readonly PipelineColorBlendAttachmentState[] _storedBlend;
88
89 private ulong _drawCountSinceBarrier;
90 public ulong DrawCount { get; private set; } 89 public ulong DrawCount { get; private set; }
91 public bool RenderPassActive { get; private set; } 90 public bool RenderPassActive { get; private set; }
92 91
@@ -135,48 +134,7 @@ namespace Ryujinx.Graphics.Vulkan
135 134
136 public unsafe void Barrier() 135 public unsafe void Barrier()
137 { 136 {
138 if (_drawCountSinceBarrier != DrawCount) 137 Gd.Barriers.QueueMemoryBarrier();
139 {
140 _drawCountSinceBarrier = DrawCount;
141
142 // Barriers are not supported inside a render pass on Apple GPUs.
143 // As a workaround, end the render pass.
144 if (Gd.Vendor == Vendor.Apple)
145 {
146 EndRenderPass();
147 }
148 }
149
150 MemoryBarrier memoryBarrier = new()
151 {
152 SType = StructureType.MemoryBarrier,
153 SrcAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit,
154 DstAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit,
155 };
156
157 PipelineStageFlags pipelineStageFlags = PipelineStageFlags.VertexShaderBit | PipelineStageFlags.FragmentShaderBit;
158
159 if (Gd.Capabilities.SupportsGeometryShader)
160 {
161 pipelineStageFlags |= PipelineStageFlags.GeometryShaderBit;
162 }
163
164 if (Gd.Capabilities.SupportsTessellationShader)
165 {
166 pipelineStageFlags |= PipelineStageFlags.TessellationControlShaderBit | PipelineStageFlags.TessellationEvaluationShaderBit;
167 }
168
169 Gd.Api.CmdPipelineBarrier(
170 CommandBuffer,
171 pipelineStageFlags,
172 pipelineStageFlags,
173 0,
174 1,
175 memoryBarrier,
176 0,
177 null,
178 0,
179 null);
180 } 138 }
181 139
182 public void ComputeBarrier() 140 public void ComputeBarrier()
@@ -203,6 +161,7 @@ namespace Ryujinx.Graphics.Vulkan
203 161
204 public void BeginTransformFeedback(PrimitiveTopology topology) 162 public void BeginTransformFeedback(PrimitiveTopology topology)
205 { 163 {
164 Gd.Barriers.EnableTfbBarriers(true);
206 _tfEnabled = true; 165 _tfEnabled = true;
207 } 166 }
208 167
@@ -249,7 +208,7 @@ namespace Ryujinx.Graphics.Vulkan
249 CreateRenderPass(); 208 CreateRenderPass();
250 } 209 }
251 210
252 Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate); 211 Gd.Barriers.Flush(Cbs, RenderPassActive, _rpHolder, EndRenderPassDelegate);
253 212
254 BeginRenderPass(); 213 BeginRenderPass();
255 214
@@ -287,7 +246,7 @@ namespace Ryujinx.Graphics.Vulkan
287 CreateRenderPass(); 246 CreateRenderPass();
288 } 247 }
289 248
290 Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate); 249 Gd.Barriers.Flush(Cbs, RenderPassActive, _rpHolder, EndRenderPassDelegate);
291 250
292 BeginRenderPass(); 251 BeginRenderPass();
293 252
@@ -299,24 +258,7 @@ namespace Ryujinx.Graphics.Vulkan
299 258
300 public unsafe void CommandBufferBarrier() 259 public unsafe void CommandBufferBarrier()
301 { 260 {
302 MemoryBarrier memoryBarrier = new() 261 Gd.Barriers.QueueCommandBufferBarrier();
303 {
304 SType = StructureType.MemoryBarrier,
305 SrcAccessMask = BufferHolder.DefaultAccessFlags,
306 DstAccessMask = AccessFlags.IndirectCommandReadBit,
307 };
308
309 Gd.Api.CmdPipelineBarrier(
310 CommandBuffer,
311 PipelineStageFlags.AllCommandsBit,
312 PipelineStageFlags.DrawIndirectBit,
313 0,
314 1,
315 memoryBarrier,
316 0,
317 null,
318 0,
319 null);
320 } 262 }
321 263
322 public void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size) 264 public void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size)
@@ -722,6 +664,7 @@ namespace Ryujinx.Graphics.Vulkan
722 664
723 public void EndTransformFeedback() 665 public void EndTransformFeedback()
724 { 666 {
667 Gd.Barriers.EnableTfbBarriers(false);
725 PauseTransformFeedbackInternal(); 668 PauseTransformFeedbackInternal();
726 _tfEnabled = false; 669 _tfEnabled = false;
727 } 670 }
@@ -1408,24 +1351,7 @@ namespace Ryujinx.Graphics.Vulkan
1408 1351
1409 public unsafe void TextureBarrier() 1352 public unsafe void TextureBarrier()
1410 { 1353 {
1411 MemoryBarrier memoryBarrier = new() 1354 Gd.Barriers.QueueTextureBarrier();
1412 {
1413 SType = StructureType.MemoryBarrier,
1414 SrcAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit,
1415 DstAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit,
1416 };
1417
1418 Gd.Api.CmdPipelineBarrier(
1419 CommandBuffer,
1420 PipelineStageFlags.FragmentShaderBit,
1421 PipelineStageFlags.FragmentShaderBit,
1422 0,
1423 1,
1424 memoryBarrier,
1425 0,
1426 null,
1427 0,
1428 null);
1429 } 1355 }
1430 1356
1431 public void TextureBarrierTiled() 1357 public void TextureBarrierTiled()
@@ -1532,12 +1458,15 @@ namespace Ryujinx.Graphics.Vulkan
1532 // Use the null framebuffer. 1458 // Use the null framebuffer.
1533 _nullRenderPass ??= new RenderPassHolder(Gd, Device, new RenderPassCacheKey(), FramebufferParams); 1459 _nullRenderPass ??= new RenderPassHolder(Gd, Device, new RenderPassCacheKey(), FramebufferParams);
1534 1460
1461 _rpHolder = _nullRenderPass;
1535 _renderPass = _nullRenderPass.GetRenderPass(); 1462 _renderPass = _nullRenderPass.GetRenderPass();
1536 _framebuffer = _nullRenderPass.GetFramebuffer(Gd, Cbs, FramebufferParams); 1463 _framebuffer = _nullRenderPass.GetFramebuffer(Gd, Cbs, FramebufferParams);
1537 } 1464 }
1538 else 1465 else
1539 { 1466 {
1540 (_renderPass, _framebuffer) = FramebufferParams.GetPassAndFramebuffer(Gd, Device, Cbs); 1467 (_rpHolder, _framebuffer) = FramebufferParams.GetPassAndFramebuffer(Gd, Device, Cbs);
1468
1469 _renderPass = _rpHolder.GetRenderPass();
1541 } 1470 }
1542 } 1471 }
1543 1472
@@ -1564,7 +1493,7 @@ namespace Ryujinx.Graphics.Vulkan
1564 } 1493 }
1565 } 1494 }
1566 1495
1567 Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate); 1496 Gd.Barriers.Flush(Cbs, _program, RenderPassActive, _rpHolder, EndRenderPassDelegate);
1568 1497
1569 _descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Compute); 1498 _descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Compute);
1570 } 1499 }
@@ -1629,7 +1558,7 @@ namespace Ryujinx.Graphics.Vulkan
1629 } 1558 }
1630 } 1559 }
1631 1560
1632 Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate); 1561 Gd.Barriers.Flush(Cbs, _program, RenderPassActive, _rpHolder, EndRenderPassDelegate);
1633 1562
1634 _descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Graphics); 1563 _descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Graphics);
1635 1564
@@ -1708,6 +1637,8 @@ namespace Ryujinx.Graphics.Vulkan
1708 { 1637 {
1709 if (RenderPassActive) 1638 if (RenderPassActive)
1710 { 1639 {
1640 FramebufferParams.AddStoreOpUsage();
1641
1711 PauseTransformFeedbackInternal(); 1642 PauseTransformFeedbackInternal();
1712 Gd.Api.CmdEndRenderPass(CommandBuffer); 1643 Gd.Api.CmdEndRenderPass(CommandBuffer);
1713 SignalRenderPassEnd(); 1644 SignalRenderPassEnd();
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs
index 7d124c830..89ce10b0a 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs
@@ -9,13 +9,6 @@ namespace Ryujinx.Graphics.Vulkan
9{ 9{
10 static class PipelineConverter 10 static class PipelineConverter
11 { 11 {
12 private const AccessFlags SubpassAccessMask =
13 AccessFlags.MemoryReadBit |
14 AccessFlags.MemoryWriteBit |
15 AccessFlags.ShaderReadBit |
16 AccessFlags.ColorAttachmentWriteBit |
17 AccessFlags.DepthStencilAttachmentWriteBit;
18
19 public static unsafe DisposableRenderPass ToRenderPass(this ProgramPipelineState state, VulkanRenderer gd, Device device) 12 public static unsafe DisposableRenderPass ToRenderPass(this ProgramPipelineState state, VulkanRenderer gd, Device device)
20 { 13 {
21 const int MaxAttachments = Constants.MaxRenderTargets + 1; 14 const int MaxAttachments = Constants.MaxRenderTargets + 1;
@@ -108,7 +101,7 @@ namespace Ryujinx.Graphics.Vulkan
108 } 101 }
109 } 102 }
110 103
111 var subpassDependency = CreateSubpassDependency(); 104 var subpassDependency = CreateSubpassDependency(gd);
112 105
113 fixed (AttachmentDescription* pAttachmentDescs = attachmentDescs) 106 fixed (AttachmentDescription* pAttachmentDescs = attachmentDescs)
114 { 107 {
@@ -129,29 +122,33 @@ namespace Ryujinx.Graphics.Vulkan
129 } 122 }
130 } 123 }
131 124
132 public static SubpassDependency CreateSubpassDependency() 125 public static SubpassDependency CreateSubpassDependency(VulkanRenderer gd)
133 { 126 {
127 var (access, stages) = BarrierBatch.GetSubpassAccessSuperset(gd);
128
134 return new SubpassDependency( 129 return new SubpassDependency(
135 0, 130 0,
136 0, 131 0,
137 PipelineStageFlags.AllGraphicsBit, 132 stages,
138 PipelineStageFlags.AllGraphicsBit, 133 stages,
139 SubpassAccessMask, 134 access,
140 SubpassAccessMask, 135 access,
141 0); 136 0);
142 } 137 }
143 138
144 public unsafe static SubpassDependency2 CreateSubpassDependency2() 139 public unsafe static SubpassDependency2 CreateSubpassDependency2(VulkanRenderer gd)
145 { 140 {
141 var (access, stages) = BarrierBatch.GetSubpassAccessSuperset(gd);
142
146 return new SubpassDependency2( 143 return new SubpassDependency2(
147 StructureType.SubpassDependency2, 144 StructureType.SubpassDependency2,
148 null, 145 null,
149 0, 146 0,
150 0, 147 0,
151 PipelineStageFlags.AllGraphicsBit, 148 stages,
152 PipelineStageFlags.AllGraphicsBit, 149 stages,
153 SubpassAccessMask, 150 access,
154 SubpassAccessMask, 151 access,
155 0); 152 0);
156 } 153 }
157 154
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs b/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs
index 5808406dc..cf65eefb0 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs
@@ -257,7 +257,7 @@ namespace Ryujinx.Graphics.Vulkan
257 PreloadCbs = null; 257 PreloadCbs = null;
258 } 258 }
259 259
260 Gd.Barriers.Flush(Cbs.CommandBuffer, false, null); 260 Gd.Barriers.Flush(Cbs, false, null, null);
261 CommandBuffer = (Cbs = Gd.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer; 261 CommandBuffer = (Cbs = Gd.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer;
262 Gd.RegisterFlush(); 262 Gd.RegisterFlush();
263 263
diff --git a/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs b/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs
index 9edea5788..b2dd0dd87 100644
--- a/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs
+++ b/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs
@@ -1,5 +1,7 @@
1using Silk.NET.Vulkan; 1using Silk.NET.Vulkan;
2using System; 2using System;
3using System.Collections.Generic;
4using System.Linq;
3 5
4namespace Ryujinx.Graphics.Vulkan 6namespace Ryujinx.Graphics.Vulkan
5{ 7{
@@ -29,10 +31,13 @@ namespace Ryujinx.Graphics.Vulkan
29 } 31 }
30 } 32 }
31 33
34 private readonly record struct ForcedFence(TextureStorage Texture, PipelineStageFlags StageFlags);
35
32 private readonly TextureView[] _textures; 36 private readonly TextureView[] _textures;
33 private readonly Auto<DisposableRenderPass> _renderPass; 37 private readonly Auto<DisposableRenderPass> _renderPass;
34 private readonly HashTableSlim<FramebufferCacheKey, Auto<DisposableFramebuffer>> _framebuffers; 38 private readonly HashTableSlim<FramebufferCacheKey, Auto<DisposableFramebuffer>> _framebuffers;
35 private readonly RenderPassCacheKey _key; 39 private readonly RenderPassCacheKey _key;
40 private readonly List<ForcedFence> _forcedFences;
36 41
37 public unsafe RenderPassHolder(VulkanRenderer gd, Device device, RenderPassCacheKey key, FramebufferParams fb) 42 public unsafe RenderPassHolder(VulkanRenderer gd, Device device, RenderPassCacheKey key, FramebufferParams fb)
38 { 43 {
@@ -105,7 +110,7 @@ namespace Ryujinx.Graphics.Vulkan
105 } 110 }
106 } 111 }
107 112
108 var subpassDependency = PipelineConverter.CreateSubpassDependency(); 113 var subpassDependency = PipelineConverter.CreateSubpassDependency(gd);
109 114
110 fixed (AttachmentDescription* pAttachmentDescs = attachmentDescs) 115 fixed (AttachmentDescription* pAttachmentDescs = attachmentDescs)
111 { 116 {
@@ -138,6 +143,8 @@ namespace Ryujinx.Graphics.Vulkan
138 143
139 _textures = textures; 144 _textures = textures;
140 _key = key; 145 _key = key;
146
147 _forcedFences = new List<ForcedFence>();
141 } 148 }
142 149
143 public Auto<DisposableFramebuffer> GetFramebuffer(VulkanRenderer gd, CommandBufferScoped cbs, FramebufferParams fb) 150 public Auto<DisposableFramebuffer> GetFramebuffer(VulkanRenderer gd, CommandBufferScoped cbs, FramebufferParams fb)
@@ -159,6 +166,37 @@ namespace Ryujinx.Graphics.Vulkan
159 return _renderPass; 166 return _renderPass;
160 } 167 }
161 168
169 public void AddForcedFence(TextureStorage storage, PipelineStageFlags stageFlags)
170 {
171 if (!_forcedFences.Any(fence => fence.Texture == storage))
172 {
173 _forcedFences.Add(new ForcedFence(storage, stageFlags));
174 }
175 }
176
177 public void InsertForcedFences(CommandBufferScoped cbs)
178 {
179 if (_forcedFences.Count > 0)
180 {
181 _forcedFences.RemoveAll((entry) =>
182 {
183 if (entry.Texture.Disposed)
184 {
185 return true;
186 }
187
188 entry.Texture.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, entry.StageFlags);
189
190 return false;
191 });
192 }
193 }
194
195 public bool ContainsAttachment(TextureStorage storage)
196 {
197 return _textures.Any(view => view.Storage == storage);
198 }
199
162 public void Dispose() 200 public void Dispose()
163 { 201 {
164 // Dispose all framebuffers. 202 // Dispose all framebuffers.
diff --git a/src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs b/src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs
index 76a5ef4f9..730a0a2f9 100644
--- a/src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs
+++ b/src/Ryujinx.Graphics.Vulkan/ResourceLayoutBuilder.cs
@@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Vulkan
23 } 23 }
24 } 24 }
25 25
26 public ResourceLayoutBuilder Add(ResourceStages stages, ResourceType type, int binding) 26 public ResourceLayoutBuilder Add(ResourceStages stages, ResourceType type, int binding, bool write = false)
27 { 27 {
28 int setIndex = type switch 28 int setIndex = type switch
29 { 29 {
@@ -35,7 +35,7 @@ namespace Ryujinx.Graphics.Vulkan
35 }; 35 };
36 36
37 _resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding, 1, type, stages)); 37 _resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding, 1, type, stages));
38 _resourceUsages[setIndex].Add(new ResourceUsage(binding, 1, type, stages)); 38 _resourceUsages[setIndex].Add(new ResourceUsage(binding, 1, type, stages, write));
39 39
40 return this; 40 return this;
41 } 41 }
diff --git a/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs b/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs
index b1547b795..c9aab4018 100644
--- a/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs
+++ b/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs
@@ -27,6 +27,9 @@ namespace Ryujinx.Graphics.Vulkan
27 27
28 public uint Stages { get; } 28 public uint Stages { get; }
29 29
30 public PipelineStageFlags IncoherentBufferWriteStages { get; }
31 public PipelineStageFlags IncoherentTextureWriteStages { get; }
32
30 public ResourceBindingSegment[][] ClearSegments { get; } 33 public ResourceBindingSegment[][] ClearSegments { get; }
31 public ResourceBindingSegment[][] BindingSegments { get; } 34 public ResourceBindingSegment[][] BindingSegments { get; }
32 public DescriptorSetTemplate[] Templates { get; } 35 public DescriptorSetTemplate[] Templates { get; }
@@ -131,6 +134,7 @@ namespace Ryujinx.Graphics.Vulkan
131 ClearSegments = BuildClearSegments(sets); 134 ClearSegments = BuildClearSegments(sets);
132 BindingSegments = BuildBindingSegments(resourceLayout.SetUsages, out bool usesBufferTextures); 135 BindingSegments = BuildBindingSegments(resourceLayout.SetUsages, out bool usesBufferTextures);
133 Templates = BuildTemplates(usePushDescriptors); 136 Templates = BuildTemplates(usePushDescriptors);
137 (IncoherentBufferWriteStages, IncoherentTextureWriteStages) = BuildIncoherentStages(resourceLayout.SetUsages);
134 138
135 // Updating buffer texture bindings using template updates crashes the Adreno driver on Windows. 139 // Updating buffer texture bindings using template updates crashes the Adreno driver on Windows.
136 UpdateTexturesWithoutTemplate = gd.IsQualcommProprietary && usesBufferTextures; 140 UpdateTexturesWithoutTemplate = gd.IsQualcommProprietary && usesBufferTextures;
@@ -377,6 +381,73 @@ namespace Ryujinx.Graphics.Vulkan
377 return templates; 381 return templates;
378 } 382 }
379 383
384 private PipelineStageFlags GetPipelineStages(ResourceStages stages)
385 {
386 PipelineStageFlags result = 0;
387
388 if ((stages & ResourceStages.Compute) != 0)
389 {
390 result |= PipelineStageFlags.ComputeShaderBit;
391 }
392
393 if ((stages & ResourceStages.Vertex) != 0)
394 {
395 result |= PipelineStageFlags.VertexShaderBit;
396 }
397
398 if ((stages & ResourceStages.Fragment) != 0)
399 {
400 result |= PipelineStageFlags.FragmentShaderBit;
401 }
402
403 if ((stages & ResourceStages.Geometry) != 0)
404 {
405 result |= PipelineStageFlags.GeometryShaderBit;
406 }
407
408 if ((stages & ResourceStages.TessellationControl) != 0)
409 {
410 result |= PipelineStageFlags.TessellationControlShaderBit;
411 }
412
413 if ((stages & ResourceStages.TessellationEvaluation) != 0)
414 {
415 result |= PipelineStageFlags.TessellationEvaluationShaderBit;
416 }
417
418 return result;
419 }
420
421 private (PipelineStageFlags Buffer, PipelineStageFlags Texture) BuildIncoherentStages(ReadOnlyCollection<ResourceUsageCollection> setUsages)
422 {
423 PipelineStageFlags buffer = PipelineStageFlags.None;
424 PipelineStageFlags texture = PipelineStageFlags.None;
425
426 foreach (var set in setUsages)
427 {
428 foreach (var range in set.Usages)
429 {
430 if (range.Write)
431 {
432 PipelineStageFlags stages = GetPipelineStages(range.Stages);
433
434 switch (range.Type)
435 {
436 case ResourceType.Image:
437 texture |= stages;
438 break;
439 case ResourceType.StorageBuffer:
440 case ResourceType.BufferImage:
441 buffer |= stages;
442 break;
443 }
444 }
445 }
446 }
447
448 return (buffer, texture);
449 }
450
380 private async Task BackgroundCompilation() 451 private async Task BackgroundCompilation()
381 { 452 {
382 await Task.WhenAll(_shaders.Select(shader => shader.CompileTask)); 453 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 7c06a5df6..fdc0a248b 100644
--- a/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs
+++ b/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs
@@ -407,7 +407,7 @@ namespace Ryujinx.Graphics.Vulkan
407 ImageLayout.General, 407 ImageLayout.General,
408 ImageLayout.General); 408 ImageLayout.General);
409 409
410 var subpassDependency = PipelineConverter.CreateSubpassDependency2(); 410 var subpassDependency = PipelineConverter.CreateSubpassDependency2(gd);
411 411
412 fixed (AttachmentDescription2* pAttachmentDescs = attachmentDescs) 412 fixed (AttachmentDescription2* pAttachmentDescs = attachmentDescs)
413 { 413 {
diff --git a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs
index 1aaf2fbbe..f36db68de 100644
--- a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs
+++ b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs
@@ -38,6 +38,8 @@ namespace Ryujinx.Graphics.Vulkan
38 38
39 public TextureCreateInfo Info => _info; 39 public TextureCreateInfo Info => _info;
40 40
41 public bool Disposed { get; private set; }
42
41 private readonly Image _image; 43 private readonly Image _image;
42 private readonly Auto<DisposableImage> _imageAuto; 44 private readonly Auto<DisposableImage> _imageAuto;
43 private readonly Auto<MemoryAllocation> _allocationAuto; 45 private readonly Auto<MemoryAllocation> _allocationAuto;
@@ -433,6 +435,17 @@ namespace Ryujinx.Graphics.Vulkan
433 return FormatCapabilities.IsD24S8(Info.Format) && VkFormat == VkFormat.D32SfloatS8Uint; 435 return FormatCapabilities.IsD24S8(Info.Format) && VkFormat == VkFormat.D32SfloatS8Uint;
434 } 436 }
435 437
438 public void AddStoreOpUsage(bool depthStencil)
439 {
440 _lastModificationStage = depthStencil ?
441 PipelineStageFlags.LateFragmentTestsBit :
442 PipelineStageFlags.ColorAttachmentOutputBit;
443
444 _lastModificationAccess = depthStencil ?
445 AccessFlags.DepthStencilAttachmentWriteBit :
446 AccessFlags.ColorAttachmentWriteBit;
447 }
448
436 public void QueueLoadOpBarrier(CommandBufferScoped cbs, bool depthStencil) 449 public void QueueLoadOpBarrier(CommandBufferScoped cbs, bool depthStencil)
437 { 450 {
438 PipelineStageFlags srcStageFlags = _lastReadStage | _lastModificationStage; 451 PipelineStageFlags srcStageFlags = _lastReadStage | _lastModificationStage;
@@ -458,7 +471,7 @@ namespace Ryujinx.Graphics.Vulkan
458 _info.GetLayers(), 471 _info.GetLayers(),
459 _info.Levels); 472 _info.Levels);
460 473
461 _gd.Barriers.QueueBarrier(barrier, srcStageFlags, dstStageFlags); 474 _gd.Barriers.QueueBarrier(barrier, this, srcStageFlags, dstStageFlags);
462 475
463 _lastReadStage = PipelineStageFlags.None; 476 _lastReadStage = PipelineStageFlags.None;
464 _lastReadAccess = AccessFlags.None; 477 _lastReadAccess = AccessFlags.None;
@@ -491,7 +504,7 @@ namespace Ryujinx.Graphics.Vulkan
491 _info.GetLayers(), 504 _info.GetLayers(),
492 _info.Levels); 505 _info.Levels);
493 506
494 _gd.Barriers.QueueBarrier(barrier, _lastModificationStage, dstStageFlags); 507 _gd.Barriers.QueueBarrier(barrier, this, _lastModificationStage, dstStageFlags);
495 508
496 _lastModificationAccess = AccessFlags.None; 509 _lastModificationAccess = AccessFlags.None;
497 } 510 }
@@ -514,6 +527,8 @@ namespace Ryujinx.Graphics.Vulkan
514 527
515 public void Dispose() 528 public void Dispose()
516 { 529 {
530 Disposed = true;
531
517 if (_aliasedStorages != null) 532 if (_aliasedStorages != null)
518 { 533 {
519 foreach (var storage in _aliasedStorages.Values) 534 foreach (var storage in _aliasedStorages.Values)
diff --git a/src/Ryujinx.Graphics.Vulkan/TextureView.cs b/src/Ryujinx.Graphics.Vulkan/TextureView.cs
index 520668028..eb612da79 100644
--- a/src/Ryujinx.Graphics.Vulkan/TextureView.cs
+++ b/src/Ryujinx.Graphics.Vulkan/TextureView.cs
@@ -993,7 +993,7 @@ namespace Ryujinx.Graphics.Vulkan
993 throw new NotImplementedException(); 993 throw new NotImplementedException();
994 } 994 }
995 995
996 public (Auto<DisposableRenderPass> renderPass, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer( 996 public (RenderPassHolder rpHolder, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
997 VulkanRenderer gd, 997 VulkanRenderer gd,
998 Device device, 998 Device device,
999 CommandBufferScoped cbs, 999 CommandBufferScoped cbs,
@@ -1006,7 +1006,7 @@ namespace Ryujinx.Graphics.Vulkan
1006 rpHolder = new RenderPassHolder(gd, device, key, fb); 1006 rpHolder = new RenderPassHolder(gd, device, key, fb);
1007 } 1007 }
1008 1008
1009 return (rpHolder.GetRenderPass(), rpHolder.GetFramebuffer(gd, cbs, fb)); 1009 return (rpHolder, rpHolder.GetFramebuffer(gd, cbs, fb));
1010 } 1010 }
1011 1011
1012 public void AddRenderPass(RenderPassCacheKey key, RenderPassHolder renderPass) 1012 public void AddRenderPass(RenderPassCacheKey key, RenderPassHolder renderPass)
diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
index e46eac95f..c9ce678b7 100644
--- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
+++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
@@ -939,6 +939,11 @@ namespace Ryujinx.Graphics.Vulkan
939 ScreenCaptured?.Invoke(this, bitmap); 939 ScreenCaptured?.Invoke(this, bitmap);
940 } 940 }
941 941
942 public bool SupportsRenderPassBarrier(PipelineStageFlags flags)
943 {
944 return !(IsMoltenVk || IsQualcommProprietary);
945 }
946
942 public unsafe void Dispose() 947 public unsafe void Dispose()
943 { 948 {
944 if (!_initialized) 949 if (!_initialized)