diff options
author | riperiperi <rhy3756547@hotmail.com> | 2024-07-18 00:21:32 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-07-17 20:21:32 -0300 |
commit | 1a919e99b29fff4e2158e622cb3dfbee21293b6d (patch) | |
tree | 00f1f3263411b96301be80b26a3b10967374328e | |
parent | f77bebac80bd2fcbee72b00845e56faf3de3bad6 (diff) |
Vulkan: Defer guest barriers, and improve image barrier timings (#7012)1.1.1352
* 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
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 @@ | |||
1 | using Silk.NET.Vulkan; | 1 | using Silk.NET.Vulkan; |
2 | using System; | 2 | using System; |
3 | using System.Collections.Generic; | 3 | using System.Collections.Generic; |
4 | using System.Runtime.CompilerServices; | ||
4 | 5 | ||
5 | namespace Ryujinx.Graphics.Vulkan | 6 | namespace 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 @@ | |||
1 | using Silk.NET.Vulkan; | 1 | using Silk.NET.Vulkan; |
2 | using System; | 2 | using System; |
3 | using System.Collections.Generic; | ||
4 | using System.Linq; | ||
3 | 5 | ||
4 | namespace Ryujinx.Graphics.Vulkan | 6 | namespace 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) |