diff options
author | riperiperi <rhy3756547@hotmail.com> | 2024-09-02 01:28:16 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-01 21:28:16 -0300 |
commit | ca59c3f4998e2d1beb3b0d0214611e3332238557 (patch) | |
tree | 38d1a2e2c0b4a906b258ee2e988256299e3e866d | |
parent | fdd7ee791cd37546390856f38eab16ea78451742 (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
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 @@ | |||
1 | using Ryujinx.Common.Utilities; | ||
1 | using System; | 2 | using System; |
2 | 3 | ||
3 | namespace Ryujinx.Common.GraphicsDriver | 4 | namespace 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 @@ | |||
1 | using System; | ||
2 | using System.Diagnostics; | ||
3 | using System.Runtime.InteropServices; | ||
4 | |||
5 | namespace 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; | |||
4 | using Silk.NET.Vulkan; | 4 | using Silk.NET.Vulkan; |
5 | using System; | 5 | using System; |
6 | using System.Buffers; | 6 | using System.Buffers; |
7 | using System.Collections.Generic; | ||
7 | using System.Runtime.CompilerServices; | 8 | using System.Runtime.CompilerServices; |
8 | using System.Runtime.InteropServices; | 9 | using System.Runtime.InteropServices; |
9 | using CompareOp = Ryujinx.Graphics.GAL.CompareOp; | 10 | using 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 @@ | |||
1 | using System; | ||
2 | |||
3 | namespace 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; | |||
2 | using Ryujinx.Graphics.Shader; | 2 | using Ryujinx.Graphics.Shader; |
3 | using Silk.NET.Vulkan; | 3 | using Silk.NET.Vulkan; |
4 | using System; | 4 | using System; |
5 | using System.Collections.Generic; | ||
5 | using System.Linq; | 6 | using System.Linq; |
6 | using System.Numerics; | 7 | using System.Numerics; |
7 | using System.Runtime.CompilerServices; | 8 | using 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 @@ | |||
1 | using Ryujinx.Common.Memory; | 1 | using Ryujinx.Common.Memory; |
2 | using Silk.NET.Vulkan; | 2 | using Silk.NET.Vulkan; |
3 | using Silk.NET.Vulkan.Extensions.EXT; | ||
3 | 4 | ||
4 | namespace Ryujinx.Graphics.Vulkan | 5 | namespace 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; | |||
4 | using System; | 4 | using System; |
5 | using System.Collections.Generic; | 5 | using System.Collections.Generic; |
6 | using System.Numerics; | 6 | using System.Numerics; |
7 | using System.Runtime.CompilerServices; | ||
7 | using Format = Ryujinx.Graphics.GAL.Format; | 8 | using Format = Ryujinx.Graphics.GAL.Format; |
8 | using VkBuffer = Silk.NET.Vulkan.Buffer; | 9 | using VkBuffer = Silk.NET.Vulkan.Buffer; |
9 | using VkFormat = Silk.NET.Vulkan.Format; | 10 | using 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; | |||
4 | using Ryujinx.Common.GraphicsDriver; | 4 | using Ryujinx.Common.GraphicsDriver; |
5 | using Ryujinx.Common.Logging; | 5 | using Ryujinx.Common.Logging; |
6 | using Ryujinx.Common.SystemInterop; | 6 | using Ryujinx.Common.SystemInterop; |
7 | using Ryujinx.Common.Utilities; | ||
7 | using Ryujinx.Graphics.Vulkan.MoltenVK; | 8 | using Ryujinx.Graphics.Vulkan.MoltenVK; |
8 | using Ryujinx.Modules; | 9 | using Ryujinx.Modules; |
9 | using Ryujinx.SDL2.Common; | 10 | using 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; | |||
7 | using Ryujinx.Common.Configuration.Hid.Controller; | 7 | using Ryujinx.Common.Configuration.Hid.Controller; |
8 | using Ryujinx.Common.Configuration.Hid.Controller.Motion; | 8 | using Ryujinx.Common.Configuration.Hid.Controller.Motion; |
9 | using Ryujinx.Common.Configuration.Hid.Keyboard; | 9 | using Ryujinx.Common.Configuration.Hid.Keyboard; |
10 | using Ryujinx.Common.GraphicsDriver; | ||
10 | using Ryujinx.Common.Logging; | 11 | using Ryujinx.Common.Logging; |
11 | using Ryujinx.Common.Logging.Targets; | 12 | using Ryujinx.Common.Logging.Targets; |
12 | using Ryujinx.Common.SystemInterop; | 13 | using 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"))) |