aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorriperiperi <rhy3756547@hotmail.com>2024-09-02 01:28:16 +0100
committerGitHub <noreply@github.com>2024-09-01 21:28:16 -0300
commitca59c3f4998e2d1beb3b0d0214611e3332238557 (patch)
tree38d1a2e2c0b4a906b258ee2e988256299e3e866d
parentfdd7ee791cd37546390856f38eab16ea78451742 (diff)
Vulkan: Feedback loop detection and barriers (#7226)1.1.1385
* Vulkan: Feedback loop improvements This PR allows the Vulkan backend to detect attachment feedback loops. These are currently used in the following ways: - Partial use of VK_EXT_attachment_feedback_loop_layout - All renderable textures have AttachmentFeedbackLoopBitExt - Compile pipelines with Color/DepthStencil feedback loop flags when present - Support using FragmentBarrier for feedback loops (fixes regressions from https://github.com/Ryujinx/Ryujinx/pull/7012 ) TODO: - AMD GPUs may need layout transitions for it to properly allow textures to be used in feedback loops. - Use dynamic state for feedback loops. The background pipeline will always miss since feedback loop state isn't known on the GPU project. - How is the barrier dependency flag used? (DXVK just ignores it, there's no vulkan validation...) - Improve subpass dependencies to fix validation errors * Mark field readonly * Add feedback loop dynamic state * fix: add MoltenVK resolver workaround fix: add MoltenVK resolver workaround * Formatting * Fix more complaints * RADV dcc workaround * Use dynamic state properly, cleanup. * Use aspects flags in more places
-rw-r--r--src/Ryujinx.Common/GraphicsDriver/DriverUtilities.cs24
-rw-r--r--src/Ryujinx.Common/Utilities/OsUtils.cs24
-rw-r--r--src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs39
-rw-r--r--src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs64
-rw-r--r--src/Ryujinx.Graphics.Vulkan/FeedbackLoopAspects.cs12
-rw-r--r--src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs21
-rw-r--r--src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs6
-rw-r--r--src/Ryujinx.Graphics.Vulkan/PipelineBase.cs122
-rw-r--r--src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs34
-rw-r--r--src/Ryujinx.Graphics.Vulkan/PipelineFull.cs4
-rw-r--r--src/Ryujinx.Graphics.Vulkan/PipelineState.cs38
-rw-r--r--src/Ryujinx.Graphics.Vulkan/TextureStorage.cs74
-rw-r--r--src/Ryujinx.Graphics.Vulkan/TextureView.cs32
-rw-r--r--src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs54
-rw-r--r--src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs34
-rw-r--r--src/Ryujinx.Gtk3/Program.cs23
-rw-r--r--src/Ryujinx.Headless.SDL2/Program.cs3
-rw-r--r--src/Ryujinx/Program.cs4
18 files changed, 538 insertions, 74 deletions
diff --git a/src/Ryujinx.Common/GraphicsDriver/DriverUtilities.cs b/src/Ryujinx.Common/GraphicsDriver/DriverUtilities.cs
index 7fe2a4f02..a9163f348 100644
--- a/src/Ryujinx.Common/GraphicsDriver/DriverUtilities.cs
+++ b/src/Ryujinx.Common/GraphicsDriver/DriverUtilities.cs
@@ -1,13 +1,33 @@
1using Ryujinx.Common.Utilities;
1using System; 2using System;
2 3
3namespace Ryujinx.Common.GraphicsDriver 4namespace Ryujinx.Common.GraphicsDriver
4{ 5{
5 public static class DriverUtilities 6 public static class DriverUtilities
6 { 7 {
8 private static void AddMesaFlags(string envVar, string newFlags)
9 {
10 string existingFlags = Environment.GetEnvironmentVariable(envVar);
11
12 string flags = existingFlags == null ? newFlags : $"{existingFlags},{newFlags}";
13
14 OsUtils.SetEnvironmentVariableNoCaching(envVar, flags);
15 }
16
17 public static void InitDriverConfig(bool oglThreading)
18 {
19 if (OperatingSystem.IsLinux())
20 {
21 AddMesaFlags("RADV_DEBUG", "nodcc");
22 }
23
24 ToggleOGLThreading(oglThreading);
25 }
26
7 public static void ToggleOGLThreading(bool enabled) 27 public static void ToggleOGLThreading(bool enabled)
8 { 28 {
9 Environment.SetEnvironmentVariable("mesa_glthread", enabled.ToString().ToLower()); 29 OsUtils.SetEnvironmentVariableNoCaching("mesa_glthread", enabled.ToString().ToLower());
10 Environment.SetEnvironmentVariable("__GL_THREADED_OPTIMIZATIONS", enabled ? "1" : "0"); 30 OsUtils.SetEnvironmentVariableNoCaching("__GL_THREADED_OPTIMIZATIONS", enabled ? "1" : "0");
11 31
12 try 32 try
13 { 33 {
diff --git a/src/Ryujinx.Common/Utilities/OsUtils.cs b/src/Ryujinx.Common/Utilities/OsUtils.cs
new file mode 100644
index 000000000..a0791b092
--- /dev/null
+++ b/src/Ryujinx.Common/Utilities/OsUtils.cs
@@ -0,0 +1,24 @@
1using System;
2using System.Diagnostics;
3using System.Runtime.InteropServices;
4
5namespace Ryujinx.Common.Utilities
6{
7 public partial class OsUtils
8 {
9 [LibraryImport("libc", SetLastError = true)]
10 private static partial int setenv([MarshalAs(UnmanagedType.LPStr)] string name, [MarshalAs(UnmanagedType.LPStr)] string value, int overwrite);
11
12 public static void SetEnvironmentVariableNoCaching(string key, string value)
13 {
14 // Set the value in the cached environment variables, too.
15 Environment.SetEnvironmentVariable(key, value);
16
17 if (!OperatingSystem.IsWindows())
18 {
19 int res = setenv(key, value, 1);
20 Debug.Assert(res != -1);
21 }
22 }
23 }
24}
diff --git a/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs b/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs
index a6a006bb9..bcfb3dbfe 100644
--- a/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs
+++ b/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs
@@ -32,10 +32,12 @@ namespace Ryujinx.Graphics.Vulkan
32 CommandBuffer 32 CommandBuffer
33 } 33 }
34 34
35 private bool _feedbackLoopActive;
35 private PipelineStageFlags _incoherentBufferWriteStages; 36 private PipelineStageFlags _incoherentBufferWriteStages;
36 private PipelineStageFlags _incoherentTextureWriteStages; 37 private PipelineStageFlags _incoherentTextureWriteStages;
37 private PipelineStageFlags _extraStages; 38 private PipelineStageFlags _extraStages;
38 private IncoherentBarrierType _queuedIncoherentBarrier; 39 private IncoherentBarrierType _queuedIncoherentBarrier;
40 private bool _queuedFeedbackLoopBarrier;
39 41
40 public BarrierBatch(VulkanRenderer gd) 42 public BarrierBatch(VulkanRenderer gd)
41 { 43 {
@@ -53,17 +55,6 @@ namespace Ryujinx.Graphics.Vulkan
53 stages |= PipelineStageFlags.TransformFeedbackBitExt; 55 stages |= PipelineStageFlags.TransformFeedbackBitExt;
54 } 56 }
55 57
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); 58 return (access, stages);
68 } 59 }
69 60
@@ -178,16 +169,34 @@ namespace Ryujinx.Graphics.Vulkan
178 } 169 }
179 170
180 _queuedIncoherentBarrier = IncoherentBarrierType.None; 171 _queuedIncoherentBarrier = IncoherentBarrierType.None;
172 _queuedFeedbackLoopBarrier = false;
181 } 173 }
174 else if (_feedbackLoopActive && _queuedFeedbackLoopBarrier)
175 {
176 // Feedback loop barrier.
177
178 MemoryBarrier barrier = new MemoryBarrier()
179 {
180 SType = StructureType.MemoryBarrier,
181 SrcAccessMask = AccessFlags.ShaderWriteBit,
182 DstAccessMask = AccessFlags.ShaderReadBit
183 };
184
185 QueueBarrier(barrier, PipelineStageFlags.FragmentShaderBit, PipelineStageFlags.AllGraphicsBit);
186
187 _queuedFeedbackLoopBarrier = false;
188 }
189
190 _feedbackLoopActive = false;
182 } 191 }
183 } 192 }
184 193
185 public unsafe void Flush(CommandBufferScoped cbs, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass) 194 public unsafe void Flush(CommandBufferScoped cbs, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass)
186 { 195 {
187 Flush(cbs, null, inRenderPass, rpHolder, endRenderPass); 196 Flush(cbs, null, false, inRenderPass, rpHolder, endRenderPass);
188 } 197 }
189 198
190 public unsafe void Flush(CommandBufferScoped cbs, ShaderCollection program, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass) 199 public unsafe void Flush(CommandBufferScoped cbs, ShaderCollection program, bool feedbackLoopActive, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass)
191 { 200 {
192 if (program != null) 201 if (program != null)
193 { 202 {
@@ -195,6 +204,8 @@ namespace Ryujinx.Graphics.Vulkan
195 _incoherentTextureWriteStages |= program.IncoherentTextureWriteStages; 204 _incoherentTextureWriteStages |= program.IncoherentTextureWriteStages;
196 } 205 }
197 206
207 _feedbackLoopActive |= feedbackLoopActive;
208
198 FlushMemoryBarrier(program, inRenderPass); 209 FlushMemoryBarrier(program, inRenderPass);
199 210
200 if (!inRenderPass && rpHolder != null) 211 if (!inRenderPass && rpHolder != null)
@@ -406,6 +417,8 @@ namespace Ryujinx.Graphics.Vulkan
406 { 417 {
407 _queuedIncoherentBarrier = type; 418 _queuedIncoherentBarrier = type;
408 } 419 }
420
421 _queuedFeedbackLoopBarrier = true;
409 } 422 }
410 423
411 public void QueueTextureBarrier() 424 public void QueueTextureBarrier()
diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs
index 563fdafd3..298526d51 100644
--- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs
+++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs
@@ -4,6 +4,7 @@ using Ryujinx.Graphics.Shader;
4using Silk.NET.Vulkan; 4using Silk.NET.Vulkan;
5using System; 5using System;
6using System.Buffers; 6using System.Buffers;
7using System.Collections.Generic;
7using System.Runtime.CompilerServices; 8using System.Runtime.CompilerServices;
8using System.Runtime.InteropServices; 9using System.Runtime.InteropServices;
9using CompareOp = Ryujinx.Graphics.GAL.CompareOp; 10using CompareOp = Ryujinx.Graphics.GAL.CompareOp;
@@ -42,15 +43,15 @@ namespace Ryujinx.Graphics.Vulkan
42 private record struct TextureRef 43 private record struct TextureRef
43 { 44 {
44 public ShaderStage Stage; 45 public ShaderStage Stage;
45 public TextureStorage Storage; 46 public TextureView View;
46 public Auto<DisposableImageView> View; 47 public Auto<DisposableImageView> ImageView;
47 public Auto<DisposableSampler> Sampler; 48 public Auto<DisposableSampler> Sampler;
48 49
49 public TextureRef(ShaderStage stage, TextureStorage storage, Auto<DisposableImageView> view, Auto<DisposableSampler> sampler) 50 public TextureRef(ShaderStage stage, TextureView view, Auto<DisposableImageView> imageView, Auto<DisposableSampler> sampler)
50 { 51 {
51 Stage = stage; 52 Stage = stage;
52 Storage = storage;
53 View = view; 53 View = view;
54 ImageView = imageView;
54 Sampler = sampler; 55 Sampler = sampler;
55 } 56 }
56 } 57 }
@@ -58,14 +59,14 @@ namespace Ryujinx.Graphics.Vulkan
58 private record struct ImageRef 59 private record struct ImageRef
59 { 60 {
60 public ShaderStage Stage; 61 public ShaderStage Stage;
61 public TextureStorage Storage; 62 public TextureView View;
62 public Auto<DisposableImageView> View; 63 public Auto<DisposableImageView> ImageView;
63 64
64 public ImageRef(ShaderStage stage, TextureStorage storage, Auto<DisposableImageView> view) 65 public ImageRef(ShaderStage stage, TextureView view, Auto<DisposableImageView> imageView)
65 { 66 {
66 Stage = stage; 67 Stage = stage;
67 Storage = storage;
68 View = view; 68 View = view;
69 ImageView = imageView;
69 } 70 }
70 } 71 }
71 72
@@ -124,6 +125,8 @@ namespace Ryujinx.Graphics.Vulkan
124 private readonly TextureView _dummyTexture; 125 private readonly TextureView _dummyTexture;
125 private readonly SamplerHolder _dummySampler; 126 private readonly SamplerHolder _dummySampler;
126 127
128 public List<TextureView> FeedbackLoopHazards { get; private set; }
129
127 public DescriptorSetUpdater(VulkanRenderer gd, Device device) 130 public DescriptorSetUpdater(VulkanRenderer gd, Device device)
128 { 131 {
129 _gd = gd; 132 _gd = gd;
@@ -209,10 +212,15 @@ namespace Ryujinx.Graphics.Vulkan
209 _templateUpdater = new(); 212 _templateUpdater = new();
210 } 213 }
211 214
212 public void Initialize() 215 public void Initialize(bool isMainPipeline)
213 { 216 {
214 MemoryOwner<byte> dummyTextureData = MemoryOwner<byte>.RentCleared(4); 217 MemoryOwner<byte> dummyTextureData = MemoryOwner<byte>.RentCleared(4);
215 _dummyTexture.SetData(dummyTextureData); 218 _dummyTexture.SetData(dummyTextureData);
219
220 if (isMainPipeline)
221 {
222 FeedbackLoopHazards = new();
223 }
216 } 224 }
217 225
218 private static bool BindingOverlaps(ref DescriptorBufferInfo info, int bindingOffset, int offset, int size) 226 private static bool BindingOverlaps(ref DescriptorBufferInfo info, int bindingOffset, int offset, int size)
@@ -275,6 +283,18 @@ namespace Ryujinx.Graphics.Vulkan
275 283
276 public void InsertBindingBarriers(CommandBufferScoped cbs) 284 public void InsertBindingBarriers(CommandBufferScoped cbs)
277 { 285 {
286 if ((FeedbackLoopHazards?.Count ?? 0) > 0)
287 {
288 // Clear existing hazards - they will be rebuilt.
289
290 foreach (TextureView hazard in FeedbackLoopHazards)
291 {
292 hazard.DecrementHazardUses();
293 }
294
295 FeedbackLoopHazards.Clear();
296 }
297
278 foreach (ResourceBindingSegment segment in _program.BindingSegments[PipelineBase.TextureSetIndex]) 298 foreach (ResourceBindingSegment segment in _program.BindingSegments[PipelineBase.TextureSetIndex])
279 { 299 {
280 if (segment.Type == ResourceType.TextureAndSampler) 300 if (segment.Type == ResourceType.TextureAndSampler)
@@ -284,7 +304,7 @@ namespace Ryujinx.Graphics.Vulkan
284 for (int i = 0; i < segment.Count; i++) 304 for (int i = 0; i < segment.Count; i++)
285 { 305 {
286 ref var texture = ref _textureRefs[segment.Binding + i]; 306 ref var texture = ref _textureRefs[segment.Binding + i];
287 texture.Storage?.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, texture.Stage.ConvertToPipelineStageFlags()); 307 texture.View?.PrepareForUsage(cbs, texture.Stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
288 } 308 }
289 } 309 }
290 else 310 else
@@ -305,7 +325,7 @@ namespace Ryujinx.Graphics.Vulkan
305 for (int i = 0; i < segment.Count; i++) 325 for (int i = 0; i < segment.Count; i++)
306 { 326 {
307 ref var image = ref _imageRefs[segment.Binding + i]; 327 ref var image = ref _imageRefs[segment.Binding + i];
308 image.Storage?.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, image.Stage.ConvertToPipelineStageFlags()); 328 image.View?.PrepareForUsage(cbs, image.Stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
309 } 329 }
310 } 330 }
311 else 331 else
@@ -385,9 +405,12 @@ namespace Ryujinx.Graphics.Vulkan
385 } 405 }
386 else if (image is TextureView view) 406 else if (image is TextureView view)
387 { 407 {
388 view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags()); 408 ref ImageRef iRef = ref _imageRefs[binding];
389 409
390 _imageRefs[binding] = new(stage, view.Storage, view.GetView(imageFormat).GetIdentityImageView()); 410 iRef.View?.ClearUsage(FeedbackLoopHazards);
411 view?.PrepareForUsage(cbs, stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
412
413 iRef = new(stage, view, view.GetView(imageFormat).GetIdentityImageView());
391 } 414 }
392 else 415 else
393 { 416 {
@@ -486,9 +509,12 @@ namespace Ryujinx.Graphics.Vulkan
486 } 509 }
487 else if (texture is TextureView view) 510 else if (texture is TextureView view)
488 { 511 {
489 view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags()); 512 ref TextureRef iRef = ref _textureRefs[binding];
513
514 iRef.View?.ClearUsage(FeedbackLoopHazards);
515 view?.PrepareForUsage(cbs, stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
490 516
491 _textureRefs[binding] = new(stage, view.Storage, view.GetImageView(), ((SamplerHolder)sampler)?.GetSampler()); 517 iRef = new(stage, view, view.GetImageView(), ((SamplerHolder)sampler)?.GetSampler());
492 } 518 }
493 else 519 else
494 { 520 {
@@ -510,7 +536,7 @@ namespace Ryujinx.Graphics.Vulkan
510 { 536 {
511 view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags()); 537 view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
512 538
513 _textureRefs[binding] = new(stage, view.Storage, view.GetIdentityImageView(), ((SamplerHolder)sampler)?.GetSampler()); 539 _textureRefs[binding] = new(stage, view, view.GetIdentityImageView(), ((SamplerHolder)sampler)?.GetSampler());
514 540
515 SignalDirty(DirtyFlags.Texture); 541 SignalDirty(DirtyFlags.Texture);
516 } 542 }
@@ -836,7 +862,7 @@ namespace Ryujinx.Graphics.Vulkan
836 ref var texture = ref textures[i]; 862 ref var texture = ref textures[i];
837 ref var refs = ref _textureRefs[binding + i]; 863 ref var refs = ref _textureRefs[binding + i];
838 864
839 texture.ImageView = refs.View?.Get(cbs).Value ?? default; 865 texture.ImageView = refs.ImageView?.Get(cbs).Value ?? default;
840 texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default; 866 texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
841 867
842 if (texture.ImageView.Handle == 0) 868 if (texture.ImageView.Handle == 0)
@@ -886,7 +912,7 @@ namespace Ryujinx.Graphics.Vulkan
886 912
887 for (int i = 0; i < count; i++) 913 for (int i = 0; i < count; i++)
888 { 914 {
889 images[i].ImageView = _imageRefs[binding + i].View?.Get(cbs).Value ?? default; 915 images[i].ImageView = _imageRefs[binding + i].ImageView?.Get(cbs).Value ?? default;
890 } 916 }
891 917
892 tu.Push<DescriptorImageInfo>(images[..count]); 918 tu.Push<DescriptorImageInfo>(images[..count]);
@@ -957,7 +983,7 @@ namespace Ryujinx.Graphics.Vulkan
957 ref var texture = ref textures[i]; 983 ref var texture = ref textures[i];
958 ref var refs = ref _textureRefs[binding + i]; 984 ref var refs = ref _textureRefs[binding + i];
959 985
960 texture.ImageView = refs.View?.Get(cbs).Value ?? default; 986 texture.ImageView = refs.ImageView?.Get(cbs).Value ?? default;
961 texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default; 987 texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
962 988
963 if (texture.ImageView.Handle == 0) 989 if (texture.ImageView.Handle == 0)
diff --git a/src/Ryujinx.Graphics.Vulkan/FeedbackLoopAspects.cs b/src/Ryujinx.Graphics.Vulkan/FeedbackLoopAspects.cs
new file mode 100644
index 000000000..22f73679d
--- /dev/null
+++ b/src/Ryujinx.Graphics.Vulkan/FeedbackLoopAspects.cs
@@ -0,0 +1,12 @@
1using System;
2
3namespace Ryujinx.Graphics.Vulkan
4{
5 [Flags]
6 internal enum FeedbackLoopAspects
7 {
8 None = 0,
9 Color = 1 << 0,
10 Depth = 1 << 1,
11 }
12}
diff --git a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs
index 763d26eb5..8d80e9d05 100644
--- a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs
+++ b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs
@@ -302,6 +302,27 @@ namespace Ryujinx.Graphics.Vulkan
302 _depthStencil?.Storage?.AddStoreOpUsage(true); 302 _depthStencil?.Storage?.AddStoreOpUsage(true);
303 } 303 }
304 304
305 public void ClearBindings()
306 {
307 _depthStencil?.Storage.ClearBindings();
308
309 for (int i = 0; i < _colorsCanonical.Length; i++)
310 {
311 _colorsCanonical[i]?.Storage.ClearBindings();
312 }
313 }
314
315 public void AddBindings()
316 {
317 _depthStencil?.Storage.AddBinding(_depthStencil);
318
319 for (int i = 0; i < _colorsCanonical.Length; i++)
320 {
321 TextureView color = _colorsCanonical[i];
322 color?.Storage.AddBinding(color);
323 }
324 }
325
305 public (RenderPassHolder rpHolder, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer( 326 public (RenderPassHolder rpHolder, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
306 VulkanRenderer gd, 327 VulkanRenderer gd,
307 Device device, 328 Device device,
diff --git a/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs b/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs
index b6694bcb3..bd17867b1 100644
--- a/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs
+++ b/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs
@@ -46,6 +46,8 @@ namespace Ryujinx.Graphics.Vulkan
46 public readonly bool SupportsViewportArray2; 46 public readonly bool SupportsViewportArray2;
47 public readonly bool SupportsHostImportedMemory; 47 public readonly bool SupportsHostImportedMemory;
48 public readonly bool SupportsDepthClipControl; 48 public readonly bool SupportsDepthClipControl;
49 public readonly bool SupportsAttachmentFeedbackLoop;
50 public readonly bool SupportsDynamicAttachmentFeedbackLoop;
49 public readonly uint SubgroupSize; 51 public readonly uint SubgroupSize;
50 public readonly SampleCountFlags SupportedSampleCounts; 52 public readonly SampleCountFlags SupportedSampleCounts;
51 public readonly PortabilitySubsetFlags PortabilitySubset; 53 public readonly PortabilitySubsetFlags PortabilitySubset;
@@ -84,6 +86,8 @@ namespace Ryujinx.Graphics.Vulkan
84 bool supportsViewportArray2, 86 bool supportsViewportArray2,
85 bool supportsHostImportedMemory, 87 bool supportsHostImportedMemory,
86 bool supportsDepthClipControl, 88 bool supportsDepthClipControl,
89 bool supportsAttachmentFeedbackLoop,
90 bool supportsDynamicAttachmentFeedbackLoop,
87 uint subgroupSize, 91 uint subgroupSize,
88 SampleCountFlags supportedSampleCounts, 92 SampleCountFlags supportedSampleCounts,
89 PortabilitySubsetFlags portabilitySubset, 93 PortabilitySubsetFlags portabilitySubset,
@@ -121,6 +125,8 @@ namespace Ryujinx.Graphics.Vulkan
121 SupportsViewportArray2 = supportsViewportArray2; 125 SupportsViewportArray2 = supportsViewportArray2;
122 SupportsHostImportedMemory = supportsHostImportedMemory; 126 SupportsHostImportedMemory = supportsHostImportedMemory;
123 SupportsDepthClipControl = supportsDepthClipControl; 127 SupportsDepthClipControl = supportsDepthClipControl;
128 SupportsAttachmentFeedbackLoop = supportsAttachmentFeedbackLoop;
129 SupportsDynamicAttachmentFeedbackLoop = supportsDynamicAttachmentFeedbackLoop;
124 SubgroupSize = subgroupSize; 130 SubgroupSize = subgroupSize;
125 SupportedSampleCounts = supportedSampleCounts; 131 SupportedSampleCounts = supportedSampleCounts;
126 PortabilitySubset = portabilitySubset; 132 PortabilitySubset = portabilitySubset;
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs
index 57fa59264..20c4b2572 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs
@@ -2,6 +2,7 @@ using Ryujinx.Graphics.GAL;
2using Ryujinx.Graphics.Shader; 2using Ryujinx.Graphics.Shader;
3using Silk.NET.Vulkan; 3using Silk.NET.Vulkan;
4using System; 4using System;
5using System.Collections.Generic;
5using System.Linq; 6using System.Linq;
6using System.Numerics; 7using System.Numerics;
7using System.Runtime.CompilerServices; 8using System.Runtime.CompilerServices;
@@ -33,6 +34,7 @@ namespace Ryujinx.Graphics.Vulkan
33 public readonly Action EndRenderPassDelegate; 34 public readonly Action EndRenderPassDelegate;
34 35
35 protected PipelineDynamicState DynamicState; 36 protected PipelineDynamicState DynamicState;
37 protected bool IsMainPipeline;
36 private PipelineState _newState; 38 private PipelineState _newState;
37 private bool _graphicsStateDirty; 39 private bool _graphicsStateDirty;
38 private bool _computeStateDirty; 40 private bool _computeStateDirty;
@@ -85,6 +87,9 @@ namespace Ryujinx.Graphics.Vulkan
85 private bool _tfEnabled; 87 private bool _tfEnabled;
86 private bool _tfActive; 88 private bool _tfActive;
87 89
90 private FeedbackLoopAspects _feedbackLoop;
91 private bool _passWritesDepthStencil;
92
88 private readonly PipelineColorBlendAttachmentState[] _storedBlend; 93 private readonly PipelineColorBlendAttachmentState[] _storedBlend;
89 public ulong DrawCount { get; private set; } 94 public ulong DrawCount { get; private set; }
90 public bool RenderPassActive { get; private set; } 95 public bool RenderPassActive { get; private set; }
@@ -126,7 +131,7 @@ namespace Ryujinx.Graphics.Vulkan
126 131
127 public void Initialize() 132 public void Initialize()
128 { 133 {
129 _descriptorSetUpdater.Initialize(); 134 _descriptorSetUpdater.Initialize(IsMainPipeline);
130 135
131 QuadsToTrisPattern = new IndexBufferPattern(Gd, 4, 6, 0, new[] { 0, 1, 2, 0, 2, 3 }, 4, false); 136 QuadsToTrisPattern = new IndexBufferPattern(Gd, 4, 6, 0, new[] { 0, 1, 2, 0, 2, 3 }, 4, false);
132 TriFanToTrisPattern = new IndexBufferPattern(Gd, 3, 3, 2, new[] { int.MinValue, -1, 0 }, 1, true); 137 TriFanToTrisPattern = new IndexBufferPattern(Gd, 3, 3, 2, new[] { int.MinValue, -1, 0 }, 1, true);
@@ -814,6 +819,8 @@ namespace Ryujinx.Graphics.Vulkan
814 _newState.DepthTestEnable = depthTest.TestEnable; 819 _newState.DepthTestEnable = depthTest.TestEnable;
815 _newState.DepthWriteEnable = depthTest.WriteEnable; 820 _newState.DepthWriteEnable = depthTest.WriteEnable;
816 _newState.DepthCompareOp = depthTest.Func.Convert(); 821 _newState.DepthCompareOp = depthTest.Func.Convert();
822
823 UpdatePassDepthStencil();
817 SignalStateChange(); 824 SignalStateChange();
818 } 825 }
819 826
@@ -1079,6 +1086,8 @@ namespace Ryujinx.Graphics.Vulkan
1079 _newState.StencilFrontPassOp = stencilTest.FrontDpPass.Convert(); 1086 _newState.StencilFrontPassOp = stencilTest.FrontDpPass.Convert();
1080 _newState.StencilFrontDepthFailOp = stencilTest.FrontDpFail.Convert(); 1087 _newState.StencilFrontDepthFailOp = stencilTest.FrontDpFail.Convert();
1081 _newState.StencilFrontCompareOp = stencilTest.FrontFunc.Convert(); 1088 _newState.StencilFrontCompareOp = stencilTest.FrontFunc.Convert();
1089
1090 UpdatePassDepthStencil();
1082 SignalStateChange(); 1091 SignalStateChange();
1083 } 1092 }
1084 1093
@@ -1426,7 +1435,23 @@ namespace Ryujinx.Graphics.Vulkan
1426 } 1435 }
1427 } 1436 }
1428 1437
1438 if (IsMainPipeline)
1439 {
1440 FramebufferParams?.ClearBindings();
1441 }
1442
1429 FramebufferParams = new FramebufferParams(Device, colors, depthStencil); 1443 FramebufferParams = new FramebufferParams(Device, colors, depthStencil);
1444
1445 if (IsMainPipeline)
1446 {
1447 FramebufferParams.AddBindings();
1448
1449 _newState.FeedbackLoopAspects = FeedbackLoopAspects.None;
1450 _bindingBarriersDirty = true;
1451 }
1452
1453 _passWritesDepthStencil = false;
1454 UpdatePassDepthStencil();
1430 UpdatePipelineAttachmentFormats(); 1455 UpdatePipelineAttachmentFormats();
1431 } 1456 }
1432 1457
@@ -1493,11 +1518,82 @@ namespace Ryujinx.Graphics.Vulkan
1493 } 1518 }
1494 } 1519 }
1495 1520
1496 Gd.Barriers.Flush(Cbs, _program, RenderPassActive, _rpHolder, EndRenderPassDelegate); 1521 Gd.Barriers.Flush(Cbs, _program, _feedbackLoop != 0, RenderPassActive, _rpHolder, EndRenderPassDelegate);
1497 1522
1498 _descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Compute); 1523 _descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Compute);
1499 } 1524 }
1500 1525
1526 private bool ChangeFeedbackLoop(FeedbackLoopAspects aspects)
1527 {
1528 if (_feedbackLoop != aspects)
1529 {
1530 if (Gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop)
1531 {
1532 DynamicState.SetFeedbackLoop(aspects);
1533 }
1534 else
1535 {
1536 _newState.FeedbackLoopAspects = aspects;
1537 }
1538
1539 _feedbackLoop = aspects;
1540
1541 return true;
1542 }
1543
1544 return false;
1545 }
1546
1547 [MethodImpl(MethodImplOptions.AggressiveInlining)]
1548 private bool UpdateFeedbackLoop()
1549 {
1550 List<TextureView> hazards = _descriptorSetUpdater.FeedbackLoopHazards;
1551
1552 if ((hazards?.Count ?? 0) > 0)
1553 {
1554 FeedbackLoopAspects aspects = 0;
1555
1556 foreach (TextureView view in hazards)
1557 {
1558 // May need to enforce feedback loop layout here in the future.
1559 // Though technically, it should always work with the general layout.
1560
1561 if (view.Info.Format.IsDepthOrStencil())
1562 {
1563 if (_passWritesDepthStencil)
1564 {
1565 // If depth/stencil isn't written in the pass, it doesn't count as a feedback loop.
1566
1567 aspects |= FeedbackLoopAspects.Depth;
1568 }
1569 }
1570 else
1571 {
1572 aspects |= FeedbackLoopAspects.Color;
1573 }
1574 }
1575
1576 return ChangeFeedbackLoop(aspects);
1577 }
1578 else if (_feedbackLoop != 0)
1579 {
1580 return ChangeFeedbackLoop(FeedbackLoopAspects.None);
1581 }
1582
1583 return false;
1584 }
1585
1586 private void UpdatePassDepthStencil()
1587 {
1588 if (!RenderPassActive)
1589 {
1590 _passWritesDepthStencil = false;
1591 }
1592
1593 // Stencil test being enabled doesn't necessarily mean a write, but it's not critical to check.
1594 _passWritesDepthStencil |= (_newState.DepthTestEnable && _newState.DepthWriteEnable) || _newState.StencilTestEnable;
1595 }
1596
1501 private bool RecreateGraphicsPipelineIfNeeded() 1597 private bool RecreateGraphicsPipelineIfNeeded()
1502 { 1598 {
1503 if (AutoFlush.ShouldFlushDraw(DrawCount)) 1599 if (AutoFlush.ShouldFlushDraw(DrawCount))
@@ -1505,7 +1601,7 @@ namespace Ryujinx.Graphics.Vulkan
1505 Gd.FlushAllCommands(); 1601 Gd.FlushAllCommands();
1506 } 1602 }
1507 1603
1508 DynamicState.ReplayIfDirty(Gd.Api, CommandBuffer); 1604 DynamicState.ReplayIfDirty(Gd, CommandBuffer);
1509 1605
1510 if (_needsIndexBufferRebind && _indexBufferPattern == null) 1606 if (_needsIndexBufferRebind && _indexBufferPattern == null)
1511 { 1607 {
@@ -1539,7 +1635,15 @@ namespace Ryujinx.Graphics.Vulkan
1539 _vertexBufferUpdater.Commit(Cbs); 1635 _vertexBufferUpdater.Commit(Cbs);
1540 } 1636 }
1541 1637
1542 if (_graphicsStateDirty || Pbp != PipelineBindPoint.Graphics) 1638 if (_bindingBarriersDirty)
1639 {
1640 // Stale barriers may have been activated by switching program. Emit any that are relevant.
1641 _descriptorSetUpdater.InsertBindingBarriers(Cbs);
1642
1643 _bindingBarriersDirty = false;
1644 }
1645
1646 if (UpdateFeedbackLoop() || _graphicsStateDirty || Pbp != PipelineBindPoint.Graphics)
1543 { 1647 {
1544 if (!CreatePipeline(PipelineBindPoint.Graphics)) 1648 if (!CreatePipeline(PipelineBindPoint.Graphics))
1545 { 1649 {
@@ -1548,17 +1652,9 @@ namespace Ryujinx.Graphics.Vulkan
1548 1652
1549 _graphicsStateDirty = false; 1653 _graphicsStateDirty = false;
1550 Pbp = PipelineBindPoint.Graphics; 1654 Pbp = PipelineBindPoint.Graphics;
1551
1552 if (_bindingBarriersDirty)
1553 {
1554 // Stale barriers may have been activated by switching program. Emit any that are relevant.
1555 _descriptorSetUpdater.InsertBindingBarriers(Cbs);
1556
1557 _bindingBarriersDirty = false;
1558 }
1559 } 1655 }
1560 1656
1561 Gd.Barriers.Flush(Cbs, _program, RenderPassActive, _rpHolder, EndRenderPassDelegate); 1657 Gd.Barriers.Flush(Cbs, _program, _feedbackLoop != 0, RenderPassActive, _rpHolder, EndRenderPassDelegate);
1562 1658
1563 _descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Graphics); 1659 _descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Graphics);
1564 1660
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs b/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs
index 1cc33f728..ad26ff7b3 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs
@@ -1,5 +1,6 @@
1using Ryujinx.Common.Memory; 1using Ryujinx.Common.Memory;
2using Silk.NET.Vulkan; 2using Silk.NET.Vulkan;
3using Silk.NET.Vulkan.Extensions.EXT;
3 4
4namespace Ryujinx.Graphics.Vulkan 5namespace Ryujinx.Graphics.Vulkan
5{ 6{
@@ -21,6 +22,8 @@ namespace Ryujinx.Graphics.Vulkan
21 22
22 private Array4<float> _blendConstants; 23 private Array4<float> _blendConstants;
23 24
25 private FeedbackLoopAspects _feedbackLoopAspects;
26
24 public uint ViewportsCount; 27 public uint ViewportsCount;
25 public Array16<Viewport> Viewports; 28 public Array16<Viewport> Viewports;
26 29
@@ -32,7 +35,8 @@ namespace Ryujinx.Graphics.Vulkan
32 Scissor = 1 << 2, 35 Scissor = 1 << 2,
33 Stencil = 1 << 3, 36 Stencil = 1 << 3,
34 Viewport = 1 << 4, 37 Viewport = 1 << 4,
35 All = Blend | DepthBias | Scissor | Stencil | Viewport, 38 FeedbackLoop = 1 << 5,
39 All = Blend | DepthBias | Scissor | Stencil | Viewport | FeedbackLoop,
36 } 40 }
37 41
38 private DirtyFlags _dirty; 42 private DirtyFlags _dirty;
@@ -99,13 +103,22 @@ namespace Ryujinx.Graphics.Vulkan
99 } 103 }
100 } 104 }
101 105
106 public void SetFeedbackLoop(FeedbackLoopAspects aspects)
107 {
108 _feedbackLoopAspects = aspects;
109
110 _dirty |= DirtyFlags.FeedbackLoop;
111 }
112
102 public void ForceAllDirty() 113 public void ForceAllDirty()
103 { 114 {
104 _dirty = DirtyFlags.All; 115 _dirty = DirtyFlags.All;
105 } 116 }
106 117
107 public void ReplayIfDirty(Vk api, CommandBuffer commandBuffer) 118 public void ReplayIfDirty(VulkanRenderer gd, CommandBuffer commandBuffer)
108 { 119 {
120 Vk api = gd.Api;
121
109 if (_dirty.HasFlag(DirtyFlags.Blend)) 122 if (_dirty.HasFlag(DirtyFlags.Blend))
110 { 123 {
111 RecordBlend(api, commandBuffer); 124 RecordBlend(api, commandBuffer);
@@ -131,6 +144,11 @@ namespace Ryujinx.Graphics.Vulkan
131 RecordViewport(api, commandBuffer); 144 RecordViewport(api, commandBuffer);
132 } 145 }
133 146
147 if (_dirty.HasFlag(DirtyFlags.FeedbackLoop) && gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop)
148 {
149 RecordFeedbackLoop(gd.DynamicFeedbackLoopApi, commandBuffer);
150 }
151
134 _dirty = DirtyFlags.None; 152 _dirty = DirtyFlags.None;
135 } 153 }
136 154
@@ -169,5 +187,17 @@ namespace Ryujinx.Graphics.Vulkan
169 api.CmdSetViewport(commandBuffer, 0, ViewportsCount, Viewports.AsSpan()); 187 api.CmdSetViewport(commandBuffer, 0, ViewportsCount, Viewports.AsSpan());
170 } 188 }
171 } 189 }
190
191 private readonly void RecordFeedbackLoop(ExtAttachmentFeedbackLoopDynamicState api, CommandBuffer commandBuffer)
192 {
193 ImageAspectFlags aspects = (_feedbackLoopAspects & FeedbackLoopAspects.Color) != 0 ? ImageAspectFlags.ColorBit : 0;
194
195 if ((_feedbackLoopAspects & FeedbackLoopAspects.Depth) != 0)
196 {
197 aspects |= ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit;
198 }
199
200 api.CmdSetAttachmentFeedbackLoopEnable(commandBuffer, aspects);
201 }
172 } 202 }
173} 203}
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs b/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs
index cf65eefb0..54d43bdba 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs
@@ -28,6 +28,8 @@ namespace Ryujinx.Graphics.Vulkan
28 _activeBufferMirrors = new(); 28 _activeBufferMirrors = new();
29 29
30 CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer; 30 CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer;
31
32 IsMainPipeline = true;
31 } 33 }
32 34
33 private void CopyPendingQuery() 35 private void CopyPendingQuery()
@@ -235,7 +237,7 @@ namespace Ryujinx.Graphics.Vulkan
235 237
236 if (Pipeline != null && Pbp == PipelineBindPoint.Graphics) 238 if (Pipeline != null && Pbp == PipelineBindPoint.Graphics)
237 { 239 {
238 DynamicState.ReplayIfDirty(Gd.Api, CommandBuffer); 240 DynamicState.ReplayIfDirty(Gd, CommandBuffer);
239 } 241 }
240 } 242 }
241 243
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs
index 6b6b46a91..a726b9edb 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs
@@ -8,6 +8,7 @@ namespace Ryujinx.Graphics.Vulkan
8 struct PipelineState : IDisposable 8 struct PipelineState : IDisposable
9 { 9 {
10 private const int RequiredSubgroupSize = 32; 10 private const int RequiredSubgroupSize = 32;
11 private const int MaxDynamicStatesCount = 9;
11 12
12 public PipelineUid Internal; 13 public PipelineUid Internal;
13 14
@@ -299,6 +300,12 @@ namespace Ryujinx.Graphics.Vulkan
299 set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFBF) | ((value ? 1UL : 0UL) << 6); 300 set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFBF) | ((value ? 1UL : 0UL) << 6);
300 } 301 }
301 302
303 public FeedbackLoopAspects FeedbackLoopAspects
304 {
305 readonly get => (FeedbackLoopAspects)((Internal.Id8 >> 7) & 0x3);
306 set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFE7F) | (((ulong)value) << 7);
307 }
308
302 public bool HasTessellationControlShader; 309 public bool HasTessellationControlShader;
303 public NativeArray<PipelineShaderStageCreateInfo> Stages; 310 public NativeArray<PipelineShaderStageCreateInfo> Stages;
304 public PipelineLayout PipelineLayout; 311 public PipelineLayout PipelineLayout;
@@ -564,9 +571,11 @@ namespace Ryujinx.Graphics.Vulkan
564 } 571 }
565 572
566 bool supportsExtDynamicState = gd.Capabilities.SupportsExtendedDynamicState; 573 bool supportsExtDynamicState = gd.Capabilities.SupportsExtendedDynamicState;
567 int dynamicStatesCount = supportsExtDynamicState ? 8 : 7; 574 bool supportsFeedbackLoopDynamicState = gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop;
568 575
569 DynamicState* dynamicStates = stackalloc DynamicState[dynamicStatesCount]; 576 DynamicState* dynamicStates = stackalloc DynamicState[MaxDynamicStatesCount];
577
578 int dynamicStatesCount = 7;
570 579
571 dynamicStates[0] = DynamicState.Viewport; 580 dynamicStates[0] = DynamicState.Viewport;
572 dynamicStates[1] = DynamicState.Scissor; 581 dynamicStates[1] = DynamicState.Scissor;
@@ -578,7 +587,12 @@ namespace Ryujinx.Graphics.Vulkan
578 587
579 if (supportsExtDynamicState) 588 if (supportsExtDynamicState)
580 { 589 {
581 dynamicStates[7] = DynamicState.VertexInputBindingStrideExt; 590 dynamicStates[dynamicStatesCount++] = DynamicState.VertexInputBindingStrideExt;
591 }
592
593 if (supportsFeedbackLoopDynamicState)
594 {
595 dynamicStates[dynamicStatesCount++] = DynamicState.AttachmentFeedbackLoopEnableExt;
582 } 596 }
583 597
584 var pipelineDynamicStateCreateInfo = new PipelineDynamicStateCreateInfo 598 var pipelineDynamicStateCreateInfo = new PipelineDynamicStateCreateInfo
@@ -588,9 +602,27 @@ namespace Ryujinx.Graphics.Vulkan
588 PDynamicStates = dynamicStates, 602 PDynamicStates = dynamicStates,
589 }; 603 };
590 604
605 PipelineCreateFlags flags = 0;
606
607 if (gd.Capabilities.SupportsAttachmentFeedbackLoop)
608 {
609 FeedbackLoopAspects aspects = FeedbackLoopAspects;
610
611 if ((aspects & FeedbackLoopAspects.Color) != 0)
612 {
613 flags |= PipelineCreateFlags.CreateColorAttachmentFeedbackLoopBitExt;
614 }
615
616 if ((aspects & FeedbackLoopAspects.Depth) != 0)
617 {
618 flags |= PipelineCreateFlags.CreateDepthStencilAttachmentFeedbackLoopBitExt;
619 }
620 }
621
591 var pipelineCreateInfo = new GraphicsPipelineCreateInfo 622 var pipelineCreateInfo = new GraphicsPipelineCreateInfo
592 { 623 {
593 SType = StructureType.GraphicsPipelineCreateInfo, 624 SType = StructureType.GraphicsPipelineCreateInfo,
625 Flags = flags,
594 StageCount = StagesCount, 626 StageCount = StagesCount,
595 PStages = Stages.Pointer, 627 PStages = Stages.Pointer,
596 PVertexInputState = &vertexInputState, 628 PVertexInputState = &vertexInputState,
diff --git a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs
index f78b9ed47..10b36a3f9 100644
--- a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs
+++ b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs
@@ -4,6 +4,7 @@ using Silk.NET.Vulkan;
4using System; 4using System;
5using System.Collections.Generic; 5using System.Collections.Generic;
6using System.Numerics; 6using System.Numerics;
7using System.Runtime.CompilerServices;
7using Format = Ryujinx.Graphics.GAL.Format; 8using Format = Ryujinx.Graphics.GAL.Format;
8using VkBuffer = Silk.NET.Vulkan.Buffer; 9using VkBuffer = Silk.NET.Vulkan.Buffer;
9using VkFormat = Silk.NET.Vulkan.Format; 10using VkFormat = Silk.NET.Vulkan.Format;
@@ -12,6 +13,11 @@ namespace Ryujinx.Graphics.Vulkan
12{ 13{
13 class TextureStorage : IDisposable 14 class TextureStorage : IDisposable
14 { 15 {
16 private struct TextureSliceInfo
17 {
18 public int BindCount;
19 }
20
15 private const MemoryPropertyFlags DefaultImageMemoryFlags = 21 private const MemoryPropertyFlags DefaultImageMemoryFlags =
16 MemoryPropertyFlags.DeviceLocalBit; 22 MemoryPropertyFlags.DeviceLocalBit;
17 23
@@ -43,6 +49,7 @@ namespace Ryujinx.Graphics.Vulkan
43 private readonly Image _image; 49 private readonly Image _image;
44 private readonly Auto<DisposableImage> _imageAuto; 50 private readonly Auto<DisposableImage> _imageAuto;
45 private readonly Auto<MemoryAllocation> _allocationAuto; 51 private readonly Auto<MemoryAllocation> _allocationAuto;
52 private readonly int _depthOrLayers;
46 private Auto<MemoryAllocation> _foreignAllocationAuto; 53 private Auto<MemoryAllocation> _foreignAllocationAuto;
47 54
48 private Dictionary<Format, TextureStorage> _aliasedStorages; 55 private Dictionary<Format, TextureStorage> _aliasedStorages;
@@ -55,6 +62,9 @@ namespace Ryujinx.Graphics.Vulkan
55 private int _viewsCount; 62 private int _viewsCount;
56 private readonly ulong _size; 63 private readonly ulong _size;
57 64
65 private int _bindCount;
66 private readonly TextureSliceInfo[] _slices;
67
58 public VkFormat VkFormat { get; } 68 public VkFormat VkFormat { get; }
59 69
60 public unsafe TextureStorage( 70 public unsafe TextureStorage(
@@ -73,6 +83,7 @@ namespace Ryujinx.Graphics.Vulkan
73 var depth = (uint)(info.Target == Target.Texture3D ? info.Depth : 1); 83 var depth = (uint)(info.Target == Target.Texture3D ? info.Depth : 1);
74 84
75 VkFormat = format; 85 VkFormat = format;
86 _depthOrLayers = info.GetDepthOrLayers();
76 87
77 var type = info.Target.Convert(); 88 var type = info.Target.Convert();
78 89
@@ -80,7 +91,7 @@ namespace Ryujinx.Graphics.Vulkan
80 91
81 var sampleCountFlags = ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)info.Samples); 92 var sampleCountFlags = ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)info.Samples);
82 93
83 var usage = GetImageUsage(info.Format, info.Target, gd.Capabilities.SupportsShaderStorageImageMultisample); 94 var usage = GetImageUsage(info.Format, info.Target, gd.Capabilities);
84 95
85 var flags = ImageCreateFlags.CreateMutableFormatBit | ImageCreateFlags.CreateExtendedUsageBit; 96 var flags = ImageCreateFlags.CreateMutableFormatBit | ImageCreateFlags.CreateExtendedUsageBit;
86 97
@@ -148,6 +159,8 @@ namespace Ryujinx.Graphics.Vulkan
148 159
149 InitialTransition(ImageLayout.Preinitialized, ImageLayout.General); 160 InitialTransition(ImageLayout.Preinitialized, ImageLayout.General);
150 } 161 }
162
163 _slices = new TextureSliceInfo[levels * _depthOrLayers];
151 } 164 }
152 165
153 public TextureStorage CreateAliasedColorForDepthStorageUnsafe(Format format) 166 public TextureStorage CreateAliasedColorForDepthStorageUnsafe(Format format)
@@ -292,7 +305,7 @@ namespace Ryujinx.Graphics.Vulkan
292 } 305 }
293 } 306 }
294 307
295 public static ImageUsageFlags GetImageUsage(Format format, Target target, bool supportsMsStorage) 308 public static ImageUsageFlags GetImageUsage(Format format, Target target, in HardwareCapabilities capabilities)
296 { 309 {
297 var usage = DefaultUsageFlags; 310 var usage = DefaultUsageFlags;
298 311
@@ -305,11 +318,19 @@ namespace Ryujinx.Graphics.Vulkan
305 usage |= ImageUsageFlags.ColorAttachmentBit; 318 usage |= ImageUsageFlags.ColorAttachmentBit;
306 } 319 }
307 320
321 bool supportsMsStorage = capabilities.SupportsShaderStorageImageMultisample;
322
308 if (format.IsImageCompatible() && (supportsMsStorage || !target.IsMultisample())) 323 if (format.IsImageCompatible() && (supportsMsStorage || !target.IsMultisample()))
309 { 324 {
310 usage |= ImageUsageFlags.StorageBit; 325 usage |= ImageUsageFlags.StorageBit;
311 } 326 }
312 327
328 if (capabilities.SupportsAttachmentFeedbackLoop &&
329 (usage & (ImageUsageFlags.DepthStencilAttachmentBit | ImageUsageFlags.ColorAttachmentBit)) != 0)
330 {
331 usage |= ImageUsageFlags.AttachmentFeedbackLoopBitExt;
332 }
333
313 return usage; 334 return usage;
314 } 335 }
315 336
@@ -510,6 +531,55 @@ namespace Ryujinx.Graphics.Vulkan
510 } 531 }
511 } 532 }
512 533
534 public void AddBinding(TextureView view)
535 {
536 // Assumes a view only has a first level.
537
538 int index = view.FirstLevel * _depthOrLayers + view.FirstLayer;
539 int layers = view.Layers;
540
541 for (int i = 0; i < layers; i++)
542 {
543 ref TextureSliceInfo info = ref _slices[index++];
544
545 info.BindCount++;
546 }
547
548 _bindCount++;
549 }
550
551 public void ClearBindings()
552 {
553 if (_bindCount != 0)
554 {
555 Array.Clear(_slices, 0, _slices.Length);
556
557 _bindCount = 0;
558 }
559 }
560
561 [MethodImpl(MethodImplOptions.AggressiveInlining)]
562 public bool IsBound(TextureView view)
563 {
564 if (_bindCount != 0)
565 {
566 int index = view.FirstLevel * _depthOrLayers + view.FirstLayer;
567 int layers = view.Layers;
568
569 for (int i = 0; i < layers; i++)
570 {
571 ref TextureSliceInfo info = ref _slices[index++];
572
573 if (info.BindCount != 0)
574 {
575 return true;
576 }
577 }
578 }
579
580 return false;
581 }
582
513 public void IncrementViewsCount() 583 public void IncrementViewsCount()
514 { 584 {
515 _viewsCount++; 585 _viewsCount++;
diff --git a/src/Ryujinx.Graphics.Vulkan/TextureView.cs b/src/Ryujinx.Graphics.Vulkan/TextureView.cs
index c5453c0c7..9b3f46662 100644
--- a/src/Ryujinx.Graphics.Vulkan/TextureView.cs
+++ b/src/Ryujinx.Graphics.Vulkan/TextureView.cs
@@ -23,6 +23,8 @@ namespace Ryujinx.Graphics.Vulkan
23 private readonly Auto<DisposableImageView> _imageView2dArray; 23 private readonly Auto<DisposableImageView> _imageView2dArray;
24 private Dictionary<Format, TextureView> _selfManagedViews; 24 private Dictionary<Format, TextureView> _selfManagedViews;
25 25
26 private int _hazardUses;
27
26 private readonly TextureCreateInfo _info; 28 private readonly TextureCreateInfo _info;
27 29
28 private HashTableSlim<RenderPassCacheKey, RenderPassHolder> _renderPasses; 30 private HashTableSlim<RenderPassCacheKey, RenderPassHolder> _renderPasses;
@@ -60,7 +62,7 @@ namespace Ryujinx.Graphics.Vulkan
60 gd.Textures.Add(this); 62 gd.Textures.Add(this);
61 63
62 var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format); 64 var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format);
63 var usage = TextureStorage.GetImageUsage(info.Format, info.Target, gd.Capabilities.SupportsShaderStorageImageMultisample); 65 var usage = TextureStorage.GetImageUsage(info.Format, info.Target, gd.Capabilities);
64 var levels = (uint)info.Levels; 66 var levels = (uint)info.Levels;
65 var layers = (uint)info.GetLayers(); 67 var layers = (uint)info.GetLayers();
66 68
@@ -1034,6 +1036,34 @@ namespace Ryujinx.Graphics.Vulkan
1034 throw new NotImplementedException(); 1036 throw new NotImplementedException();
1035 } 1037 }
1036 1038
1039 public void PrepareForUsage(CommandBufferScoped cbs, PipelineStageFlags flags, List<TextureView> feedbackLoopHazards)
1040 {
1041 Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, flags);
1042
1043 if (feedbackLoopHazards != null && Storage.IsBound(this))
1044 {
1045 feedbackLoopHazards.Add(this);
1046 _hazardUses++;
1047 }
1048 }
1049
1050 public void ClearUsage(List<TextureView> feedbackLoopHazards)
1051 {
1052 if (_hazardUses != 0 && feedbackLoopHazards != null)
1053 {
1054 feedbackLoopHazards.Remove(this);
1055 _hazardUses--;
1056 }
1057 }
1058
1059 public void DecrementHazardUses()
1060 {
1061 if (_hazardUses != 0)
1062 {
1063 _hazardUses--;
1064 }
1065 }
1066
1037 public (RenderPassHolder rpHolder, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer( 1067 public (RenderPassHolder rpHolder, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
1038 VulkanRenderer gd, 1068 VulkanRenderer gd,
1039 Device device, 1069 Device device,
diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs
index 5a9844cb9..2c327fdb7 100644
--- a/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs
+++ b/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs
@@ -44,6 +44,8 @@ namespace Ryujinx.Graphics.Vulkan
44 "VK_EXT_4444_formats", 44 "VK_EXT_4444_formats",
45 "VK_KHR_8bit_storage", 45 "VK_KHR_8bit_storage",
46 "VK_KHR_maintenance2", 46 "VK_KHR_maintenance2",
47 "VK_EXT_attachment_feedback_loop_layout",
48 "VK_EXT_attachment_feedback_loop_dynamic_state",
47 }; 49 };
48 50
49 private static readonly string[] _requiredExtensions = { 51 private static readonly string[] _requiredExtensions = {
@@ -357,6 +359,28 @@ namespace Ryujinx.Graphics.Vulkan
357 features2.PNext = &supportedFeaturesDepthClipControl; 359 features2.PNext = &supportedFeaturesDepthClipControl;
358 } 360 }
359 361
362 PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT supportedFeaturesAttachmentFeedbackLoopLayout = new()
363 {
364 SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesExt,
365 PNext = features2.PNext,
366 };
367
368 if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_layout"))
369 {
370 features2.PNext = &supportedFeaturesAttachmentFeedbackLoopLayout;
371 }
372
373 PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT supportedFeaturesDynamicAttachmentFeedbackLoopLayout = new()
374 {
375 SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesExt,
376 PNext = features2.PNext,
377 };
378
379 if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_dynamic_state"))
380 {
381 features2.PNext = &supportedFeaturesDynamicAttachmentFeedbackLoopLayout;
382 }
383
360 PhysicalDeviceVulkan12Features supportedPhysicalDeviceVulkan12Features = new() 384 PhysicalDeviceVulkan12Features supportedPhysicalDeviceVulkan12Features = new()
361 { 385 {
362 SType = StructureType.PhysicalDeviceVulkan12Features, 386 SType = StructureType.PhysicalDeviceVulkan12Features,
@@ -531,6 +555,36 @@ namespace Ryujinx.Graphics.Vulkan
531 pExtendedFeatures = &featuresDepthClipControl; 555 pExtendedFeatures = &featuresDepthClipControl;
532 } 556 }
533 557
558 PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT featuresAttachmentFeedbackLoopLayout;
559
560 if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_layout") &&
561 supportedFeaturesAttachmentFeedbackLoopLayout.AttachmentFeedbackLoopLayout)
562 {
563 featuresAttachmentFeedbackLoopLayout = new()
564 {
565 SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesExt,
566 PNext = pExtendedFeatures,
567 AttachmentFeedbackLoopLayout = true,
568 };
569
570 pExtendedFeatures = &featuresAttachmentFeedbackLoopLayout;
571 }
572
573 PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT featuresDynamicAttachmentFeedbackLoopLayout;
574
575 if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_dynamic_state") &&
576 supportedFeaturesDynamicAttachmentFeedbackLoopLayout.AttachmentFeedbackLoopDynamicState)
577 {
578 featuresDynamicAttachmentFeedbackLoopLayout = new()
579 {
580 SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesExt,
581 PNext = pExtendedFeatures,
582 AttachmentFeedbackLoopDynamicState = true,
583 };
584
585 pExtendedFeatures = &featuresDynamicAttachmentFeedbackLoopLayout;
586 }
587
534 var enabledExtensions = _requiredExtensions.Union(_desirableExtensions.Intersect(physicalDevice.DeviceExtensions)).ToArray(); 588 var enabledExtensions = _requiredExtensions.Union(_desirableExtensions.Intersect(physicalDevice.DeviceExtensions)).ToArray();
535 589
536 IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length]; 590 IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length];
diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
index c9ce678b7..33e41ab48 100644
--- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
+++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
@@ -38,6 +38,7 @@ namespace Ryujinx.Graphics.Vulkan
38 internal KhrPushDescriptor PushDescriptorApi { get; private set; } 38 internal KhrPushDescriptor PushDescriptorApi { get; private set; }
39 internal ExtTransformFeedback TransformFeedbackApi { get; private set; } 39 internal ExtTransformFeedback TransformFeedbackApi { get; private set; }
40 internal KhrDrawIndirectCount DrawIndirectCountApi { get; private set; } 40 internal KhrDrawIndirectCount DrawIndirectCountApi { get; private set; }
41 internal ExtAttachmentFeedbackLoopDynamicState DynamicFeedbackLoopApi { get; private set; }
41 42
42 internal uint QueueFamilyIndex { get; private set; } 43 internal uint QueueFamilyIndex { get; private set; }
43 internal Queue Queue { get; private set; } 44 internal Queue Queue { get; private set; }
@@ -149,6 +150,11 @@ namespace Ryujinx.Graphics.Vulkan
149 DrawIndirectCountApi = drawIndirectCountApi; 150 DrawIndirectCountApi = drawIndirectCountApi;
150 } 151 }
151 152
153 if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtAttachmentFeedbackLoopDynamicState dynamicFeedbackLoopApi))
154 {
155 DynamicFeedbackLoopApi = dynamicFeedbackLoopApi;
156 }
157
152 if (maxQueueCount >= 2) 158 if (maxQueueCount >= 2)
153 { 159 {
154 Api.GetDeviceQueue(_device, queueFamilyIndex, 1, out var backgroundQueue); 160 Api.GetDeviceQueue(_device, queueFamilyIndex, 1, out var backgroundQueue);
@@ -243,6 +249,16 @@ namespace Ryujinx.Graphics.Vulkan
243 SType = StructureType.PhysicalDeviceDepthClipControlFeaturesExt, 249 SType = StructureType.PhysicalDeviceDepthClipControlFeaturesExt,
244 }; 250 };
245 251
252 PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT featuresAttachmentFeedbackLoop = new()
253 {
254 SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesExt,
255 };
256
257 PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT featuresDynamicAttachmentFeedbackLoop = new()
258 {
259 SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesExt,
260 };
261
246 PhysicalDevicePortabilitySubsetFeaturesKHR featuresPortabilitySubset = new() 262 PhysicalDevicePortabilitySubsetFeaturesKHR featuresPortabilitySubset = new()
247 { 263 {
248 SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr, 264 SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr,
@@ -279,6 +295,22 @@ namespace Ryujinx.Graphics.Vulkan
279 features2.PNext = &featuresDepthClipControl; 295 features2.PNext = &featuresDepthClipControl;
280 } 296 }
281 297
298 bool supportsAttachmentFeedbackLoop = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_layout");
299
300 if (supportsAttachmentFeedbackLoop)
301 {
302 featuresAttachmentFeedbackLoop.PNext = features2.PNext;
303 features2.PNext = &featuresAttachmentFeedbackLoop;
304 }
305
306 bool supportsDynamicAttachmentFeedbackLoop = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_dynamic_state");
307
308 if (supportsDynamicAttachmentFeedbackLoop)
309 {
310 featuresDynamicAttachmentFeedbackLoop.PNext = features2.PNext;
311 features2.PNext = &featuresDynamicAttachmentFeedbackLoop;
312 }
313
282 bool usePortability = _physicalDevice.IsDeviceExtensionPresent("VK_KHR_portability_subset"); 314 bool usePortability = _physicalDevice.IsDeviceExtensionPresent("VK_KHR_portability_subset");
283 315
284 if (usePortability) 316 if (usePortability)
@@ -401,6 +433,8 @@ namespace Ryujinx.Graphics.Vulkan
401 _physicalDevice.IsDeviceExtensionPresent("VK_NV_viewport_array2"), 433 _physicalDevice.IsDeviceExtensionPresent("VK_NV_viewport_array2"),
402 _physicalDevice.IsDeviceExtensionPresent(ExtExternalMemoryHost.ExtensionName), 434 _physicalDevice.IsDeviceExtensionPresent(ExtExternalMemoryHost.ExtensionName),
403 supportsDepthClipControl && featuresDepthClipControl.DepthClipControl, 435 supportsDepthClipControl && featuresDepthClipControl.DepthClipControl,
436 supportsAttachmentFeedbackLoop && featuresAttachmentFeedbackLoop.AttachmentFeedbackLoopLayout,
437 supportsDynamicAttachmentFeedbackLoop && featuresDynamicAttachmentFeedbackLoop.AttachmentFeedbackLoopDynamicState,
404 propertiesSubgroup.SubgroupSize, 438 propertiesSubgroup.SubgroupSize,
405 supportedSampleCounts, 439 supportedSampleCounts,
406 portabilityFlags, 440 portabilityFlags,
diff --git a/src/Ryujinx.Gtk3/Program.cs b/src/Ryujinx.Gtk3/Program.cs
index 8bad1a0c7..2d350374b 100644
--- a/src/Ryujinx.Gtk3/Program.cs
+++ b/src/Ryujinx.Gtk3/Program.cs
@@ -4,6 +4,7 @@ using Ryujinx.Common.Configuration;
4using Ryujinx.Common.GraphicsDriver; 4using Ryujinx.Common.GraphicsDriver;
5using Ryujinx.Common.Logging; 5using Ryujinx.Common.Logging;
6using Ryujinx.Common.SystemInterop; 6using Ryujinx.Common.SystemInterop;
7using Ryujinx.Common.Utilities;
7using Ryujinx.Graphics.Vulkan.MoltenVK; 8using Ryujinx.Graphics.Vulkan.MoltenVK;
8using Ryujinx.Modules; 9using Ryujinx.Modules;
9using Ryujinx.SDL2.Common; 10using Ryujinx.SDL2.Common;
@@ -41,9 +42,6 @@ namespace Ryujinx
41 [LibraryImport("user32.dll", SetLastError = true)] 42 [LibraryImport("user32.dll", SetLastError = true)]
42 public static partial int MessageBoxA(IntPtr hWnd, [MarshalAs(UnmanagedType.LPStr)] string text, [MarshalAs(UnmanagedType.LPStr)] string caption, uint type); 43 public static partial int MessageBoxA(IntPtr hWnd, [MarshalAs(UnmanagedType.LPStr)] string text, [MarshalAs(UnmanagedType.LPStr)] string caption, uint type);
43 44
44 [LibraryImport("libc", SetLastError = true)]
45 private static partial int setenv([MarshalAs(UnmanagedType.LPStr)] string name, [MarshalAs(UnmanagedType.LPStr)] string value, int overwrite);
46
47 private const uint MbIconWarning = 0x30; 45 private const uint MbIconWarning = 0x30;
48 46
49 static Program() 47 static Program()
@@ -105,8 +103,7 @@ namespace Ryujinx
105 throw new NotSupportedException("Failed to initialize multi-threading support."); 103 throw new NotSupportedException("Failed to initialize multi-threading support.");
106 } 104 }
107 105
108 Environment.SetEnvironmentVariable("GDK_BACKEND", "x11"); 106 OsUtils.SetEnvironmentVariableNoCaching("GDK_BACKEND", "x11");
109 setenv("GDK_BACKEND", "x11", 1);
110 } 107 }
111 108
112 if (OperatingSystem.IsMacOS()) 109 if (OperatingSystem.IsMacOS())
@@ -125,19 +122,13 @@ namespace Ryujinx
125 resourcesDataDir = baseDirectory; 122 resourcesDataDir = baseDirectory;
126 } 123 }
127 124
128 static void SetEnvironmentVariableNoCaching(string key, string value)
129 {
130 int res = setenv(key, value, 1);
131 Debug.Assert(res != -1);
132 }
133
134 // On macOS, GTK3 needs XDG_DATA_DIRS to be set, otherwise it will try searching for "gschemas.compiled" in system directories. 125 // On macOS, GTK3 needs XDG_DATA_DIRS to be set, otherwise it will try searching for "gschemas.compiled" in system directories.
135 SetEnvironmentVariableNoCaching("XDG_DATA_DIRS", Path.Combine(resourcesDataDir, "share")); 126 OsUtils.SetEnvironmentVariableNoCaching("XDG_DATA_DIRS", Path.Combine(resourcesDataDir, "share"));
136 127
137 // On macOS, GTK3 needs GDK_PIXBUF_MODULE_FILE to be set, otherwise it will try searching for "loaders.cache" in system directories. 128 // On macOS, GTK3 needs GDK_PIXBUF_MODULE_FILE to be set, otherwise it will try searching for "loaders.cache" in system directories.
138 SetEnvironmentVariableNoCaching("GDK_PIXBUF_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gdk-pixbuf-2.0", "2.10.0", "loaders.cache")); 129 OsUtils.SetEnvironmentVariableNoCaching("GDK_PIXBUF_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gdk-pixbuf-2.0", "2.10.0", "loaders.cache"));
139 130
140 SetEnvironmentVariableNoCaching("GTK_IM_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gtk-3.0", "3.0.0", "immodules.cache")); 131 OsUtils.SetEnvironmentVariableNoCaching("GTK_IM_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gtk-3.0", "3.0.0", "immodules.cache"));
141 } 132 }
142 133
143 string systemPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine); 134 string systemPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine);
@@ -233,9 +224,9 @@ namespace Ryujinx
233 // Logging system information. 224 // Logging system information.
234 PrintSystemInfo(); 225 PrintSystemInfo();
235 226
236 // Enable OGL multithreading on the driver, when available. 227 // Enable OGL multithreading on the driver, and some other flags.
237 BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading; 228 BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading;
238 DriverUtilities.ToggleOGLThreading(threadingMode == BackendThreading.Off); 229 DriverUtilities.InitDriverConfig(threadingMode == BackendThreading.Off);
239 230
240 // Initialize Gtk. 231 // Initialize Gtk.
241 Application.Init(); 232 Application.Init();
diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs
index 5c30cd18f..07995dbdd 100644
--- a/src/Ryujinx.Headless.SDL2/Program.cs
+++ b/src/Ryujinx.Headless.SDL2/Program.cs
@@ -7,6 +7,7 @@ using Ryujinx.Common.Configuration.Hid;
7using Ryujinx.Common.Configuration.Hid.Controller; 7using Ryujinx.Common.Configuration.Hid.Controller;
8using Ryujinx.Common.Configuration.Hid.Controller.Motion; 8using Ryujinx.Common.Configuration.Hid.Controller.Motion;
9using Ryujinx.Common.Configuration.Hid.Keyboard; 9using Ryujinx.Common.Configuration.Hid.Keyboard;
10using Ryujinx.Common.GraphicsDriver;
10using Ryujinx.Common.Logging; 11using Ryujinx.Common.Logging;
11using Ryujinx.Common.Logging.Targets; 12using Ryujinx.Common.Logging.Targets;
12using Ryujinx.Common.SystemInterop; 13using Ryujinx.Common.SystemInterop;
@@ -463,6 +464,8 @@ namespace Ryujinx.Headless.SDL2
463 GraphicsConfig.ShadersDumpPath = option.GraphicsShadersDumpPath; 464 GraphicsConfig.ShadersDumpPath = option.GraphicsShadersDumpPath;
464 GraphicsConfig.EnableMacroHLE = !option.DisableMacroHLE; 465 GraphicsConfig.EnableMacroHLE = !option.DisableMacroHLE;
465 466
467 DriverUtilities.InitDriverConfig(option.BackendThreading == BackendThreading.Off);
468
466 while (true) 469 while (true)
467 { 470 {
468 LoadApplication(option); 471 LoadApplication(option);
diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs
index af9db7d63..6c83cedcf 100644
--- a/src/Ryujinx/Program.cs
+++ b/src/Ryujinx/Program.cs
@@ -117,8 +117,8 @@ namespace Ryujinx.Ava
117 // Logging system information. 117 // Logging system information.
118 PrintSystemInfo(); 118 PrintSystemInfo();
119 119
120 // Enable OGL multithreading on the driver, when available. 120 // Enable OGL multithreading on the driver, and some other flags.
121 DriverUtilities.ToggleOGLThreading(ConfigurationState.Instance.Graphics.BackendThreading == BackendThreading.Off); 121 DriverUtilities.InitDriverConfig(ConfigurationState.Instance.Graphics.BackendThreading == BackendThreading.Off);
122 122
123 // Check if keys exists. 123 // Check if keys exists.
124 if (!File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys"))) 124 if (!File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys")))