mirror of
				https://github.com/ryujinx-mirror/ryujinx.git
				synced 2025-11-04 03:18:58 -06:00 
			
		
		
		
	Vulkan: Feedback loop detection and barriers (#7226)
* 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
This commit is contained in:
		@@ -1,13 +1,33 @@
 | 
			
		||||
using Ryujinx.Common.Utilities;
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Ryujinx.Common.GraphicsDriver
 | 
			
		||||
{
 | 
			
		||||
    public static class DriverUtilities
 | 
			
		||||
    {
 | 
			
		||||
        private static void AddMesaFlags(string envVar, string newFlags)
 | 
			
		||||
        {
 | 
			
		||||
            string existingFlags = Environment.GetEnvironmentVariable(envVar);
 | 
			
		||||
 | 
			
		||||
            string flags = existingFlags == null ? newFlags : $"{existingFlags},{newFlags}";
 | 
			
		||||
 | 
			
		||||
            OsUtils.SetEnvironmentVariableNoCaching(envVar, flags);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static void InitDriverConfig(bool oglThreading)
 | 
			
		||||
        {
 | 
			
		||||
            if (OperatingSystem.IsLinux())
 | 
			
		||||
            {
 | 
			
		||||
                AddMesaFlags("RADV_DEBUG", "nodcc");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            ToggleOGLThreading(oglThreading);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static void ToggleOGLThreading(bool enabled)
 | 
			
		||||
        {
 | 
			
		||||
            Environment.SetEnvironmentVariable("mesa_glthread", enabled.ToString().ToLower());
 | 
			
		||||
            Environment.SetEnvironmentVariable("__GL_THREADED_OPTIMIZATIONS", enabled ? "1" : "0");
 | 
			
		||||
            OsUtils.SetEnvironmentVariableNoCaching("mesa_glthread", enabled.ToString().ToLower());
 | 
			
		||||
            OsUtils.SetEnvironmentVariableNoCaching("__GL_THREADED_OPTIMIZATIONS", enabled ? "1" : "0");
 | 
			
		||||
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										24
									
								
								src/Ryujinx.Common/Utilities/OsUtils.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/Ryujinx.Common/Utilities/OsUtils.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
using System.Runtime.InteropServices;
 | 
			
		||||
 | 
			
		||||
namespace Ryujinx.Common.Utilities
 | 
			
		||||
{
 | 
			
		||||
    public partial class OsUtils
 | 
			
		||||
    {
 | 
			
		||||
        [LibraryImport("libc", SetLastError = true)]
 | 
			
		||||
        private static partial int setenv([MarshalAs(UnmanagedType.LPStr)] string name, [MarshalAs(UnmanagedType.LPStr)] string value, int overwrite);
 | 
			
		||||
 | 
			
		||||
        public static void SetEnvironmentVariableNoCaching(string key, string value)
 | 
			
		||||
        {
 | 
			
		||||
            // Set the value in the cached environment variables, too.
 | 
			
		||||
            Environment.SetEnvironmentVariable(key, value);
 | 
			
		||||
 | 
			
		||||
            if (!OperatingSystem.IsWindows())
 | 
			
		||||
            {
 | 
			
		||||
                int res = setenv(key, value, 1);
 | 
			
		||||
                Debug.Assert(res != -1);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -32,10 +32,12 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
            CommandBuffer
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private bool _feedbackLoopActive;
 | 
			
		||||
        private PipelineStageFlags _incoherentBufferWriteStages;
 | 
			
		||||
        private PipelineStageFlags _incoherentTextureWriteStages;
 | 
			
		||||
        private PipelineStageFlags _extraStages;
 | 
			
		||||
        private IncoherentBarrierType _queuedIncoherentBarrier;
 | 
			
		||||
        private bool _queuedFeedbackLoopBarrier;
 | 
			
		||||
 | 
			
		||||
        public BarrierBatch(VulkanRenderer gd)
 | 
			
		||||
        {
 | 
			
		||||
@@ -53,17 +55,6 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
                stages |= PipelineStageFlags.TransformFeedbackBitExt;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!gd.IsTBDR)
 | 
			
		||||
            {
 | 
			
		||||
                // Desktop GPUs can transform image barriers into memory barriers.
 | 
			
		||||
 | 
			
		||||
                access |= AccessFlags.DepthStencilAttachmentWriteBit | AccessFlags.ColorAttachmentWriteBit;
 | 
			
		||||
                access |= AccessFlags.DepthStencilAttachmentReadBit | AccessFlags.ColorAttachmentReadBit;
 | 
			
		||||
 | 
			
		||||
                stages |= PipelineStageFlags.EarlyFragmentTestsBit | PipelineStageFlags.LateFragmentTestsBit;
 | 
			
		||||
                stages |= PipelineStageFlags.ColorAttachmentOutputBit;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return (access, stages);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -178,16 +169,34 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    _queuedIncoherentBarrier = IncoherentBarrierType.None;
 | 
			
		||||
                    _queuedFeedbackLoopBarrier = false;
 | 
			
		||||
                }
 | 
			
		||||
                else if (_feedbackLoopActive && _queuedFeedbackLoopBarrier)
 | 
			
		||||
                {
 | 
			
		||||
                    // Feedback loop barrier.
 | 
			
		||||
 | 
			
		||||
                    MemoryBarrier barrier = new MemoryBarrier()
 | 
			
		||||
                    {
 | 
			
		||||
                        SType = StructureType.MemoryBarrier,
 | 
			
		||||
                        SrcAccessMask = AccessFlags.ShaderWriteBit,
 | 
			
		||||
                        DstAccessMask = AccessFlags.ShaderReadBit
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                    QueueBarrier(barrier, PipelineStageFlags.FragmentShaderBit, PipelineStageFlags.AllGraphicsBit);
 | 
			
		||||
 | 
			
		||||
                    _queuedFeedbackLoopBarrier = false;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                _feedbackLoopActive = false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public unsafe void Flush(CommandBufferScoped cbs, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass)
 | 
			
		||||
        {
 | 
			
		||||
            Flush(cbs, null, inRenderPass, rpHolder, endRenderPass);
 | 
			
		||||
            Flush(cbs, null, false, inRenderPass, rpHolder, endRenderPass);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public unsafe void Flush(CommandBufferScoped cbs, ShaderCollection program, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass)
 | 
			
		||||
        public unsafe void Flush(CommandBufferScoped cbs, ShaderCollection program, bool feedbackLoopActive, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass)
 | 
			
		||||
        {
 | 
			
		||||
            if (program != null)
 | 
			
		||||
            {
 | 
			
		||||
@@ -195,6 +204,8 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
                _incoherentTextureWriteStages |= program.IncoherentTextureWriteStages;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            _feedbackLoopActive |= feedbackLoopActive;
 | 
			
		||||
 | 
			
		||||
            FlushMemoryBarrier(program, inRenderPass);
 | 
			
		||||
 | 
			
		||||
            if (!inRenderPass && rpHolder != null)
 | 
			
		||||
@@ -406,6 +417,8 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
            {
 | 
			
		||||
                _queuedIncoherentBarrier = type;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            _queuedFeedbackLoopBarrier = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void QueueTextureBarrier()
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ using Ryujinx.Graphics.Shader;
 | 
			
		||||
using Silk.NET.Vulkan;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Buffers;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Runtime.CompilerServices;
 | 
			
		||||
using System.Runtime.InteropServices;
 | 
			
		||||
using CompareOp = Ryujinx.Graphics.GAL.CompareOp;
 | 
			
		||||
@@ -42,15 +43,15 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
        private record struct TextureRef
 | 
			
		||||
        {
 | 
			
		||||
            public ShaderStage Stage;
 | 
			
		||||
            public TextureStorage Storage;
 | 
			
		||||
            public Auto<DisposableImageView> View;
 | 
			
		||||
            public TextureView View;
 | 
			
		||||
            public Auto<DisposableImageView> ImageView;
 | 
			
		||||
            public Auto<DisposableSampler> Sampler;
 | 
			
		||||
 | 
			
		||||
            public TextureRef(ShaderStage stage, TextureStorage storage, Auto<DisposableImageView> view, Auto<DisposableSampler> sampler)
 | 
			
		||||
            public TextureRef(ShaderStage stage, TextureView view, Auto<DisposableImageView> imageView, Auto<DisposableSampler> sampler)
 | 
			
		||||
            {
 | 
			
		||||
                Stage = stage;
 | 
			
		||||
                Storage = storage;
 | 
			
		||||
                View = view;
 | 
			
		||||
                ImageView = imageView;
 | 
			
		||||
                Sampler = sampler;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -58,14 +59,14 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
        private record struct ImageRef
 | 
			
		||||
        {
 | 
			
		||||
            public ShaderStage Stage;
 | 
			
		||||
            public TextureStorage Storage;
 | 
			
		||||
            public Auto<DisposableImageView> View;
 | 
			
		||||
            public TextureView View;
 | 
			
		||||
            public Auto<DisposableImageView> ImageView;
 | 
			
		||||
 | 
			
		||||
            public ImageRef(ShaderStage stage, TextureStorage storage, Auto<DisposableImageView> view)
 | 
			
		||||
            public ImageRef(ShaderStage stage, TextureView view, Auto<DisposableImageView> imageView)
 | 
			
		||||
            {
 | 
			
		||||
                Stage = stage;
 | 
			
		||||
                Storage = storage;
 | 
			
		||||
                View = view;
 | 
			
		||||
                ImageView = imageView;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -124,6 +125,8 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
        private readonly TextureView _dummyTexture;
 | 
			
		||||
        private readonly SamplerHolder _dummySampler;
 | 
			
		||||
 | 
			
		||||
        public List<TextureView> FeedbackLoopHazards { get; private set; }
 | 
			
		||||
 | 
			
		||||
        public DescriptorSetUpdater(VulkanRenderer gd, Device device)
 | 
			
		||||
        {
 | 
			
		||||
            _gd = gd;
 | 
			
		||||
@@ -209,10 +212,15 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
            _templateUpdater = new();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Initialize()
 | 
			
		||||
        public void Initialize(bool isMainPipeline)
 | 
			
		||||
        {
 | 
			
		||||
            MemoryOwner<byte> dummyTextureData = MemoryOwner<byte>.RentCleared(4);
 | 
			
		||||
            _dummyTexture.SetData(dummyTextureData);
 | 
			
		||||
 | 
			
		||||
            if (isMainPipeline)
 | 
			
		||||
            {
 | 
			
		||||
                FeedbackLoopHazards = new();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static bool BindingOverlaps(ref DescriptorBufferInfo info, int bindingOffset, int offset, int size)
 | 
			
		||||
@@ -275,6 +283,18 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
 | 
			
		||||
        public void InsertBindingBarriers(CommandBufferScoped cbs)
 | 
			
		||||
        {
 | 
			
		||||
            if ((FeedbackLoopHazards?.Count ?? 0) > 0)
 | 
			
		||||
            {
 | 
			
		||||
                // Clear existing hazards - they will be rebuilt.
 | 
			
		||||
 | 
			
		||||
                foreach (TextureView hazard in FeedbackLoopHazards)
 | 
			
		||||
                {
 | 
			
		||||
                    hazard.DecrementHazardUses();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                FeedbackLoopHazards.Clear();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            foreach (ResourceBindingSegment segment in _program.BindingSegments[PipelineBase.TextureSetIndex])
 | 
			
		||||
            {
 | 
			
		||||
                if (segment.Type == ResourceType.TextureAndSampler)
 | 
			
		||||
@@ -284,7 +304,7 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
                        for (int i = 0; i < segment.Count; i++)
 | 
			
		||||
                        {
 | 
			
		||||
                            ref var texture = ref _textureRefs[segment.Binding + i];
 | 
			
		||||
                            texture.Storage?.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, texture.Stage.ConvertToPipelineStageFlags());
 | 
			
		||||
                            texture.View?.PrepareForUsage(cbs, texture.Stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
@@ -305,7 +325,7 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
                        for (int i = 0; i < segment.Count; i++)
 | 
			
		||||
                        {
 | 
			
		||||
                            ref var image = ref _imageRefs[segment.Binding + i];
 | 
			
		||||
                            image.Storage?.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, image.Stage.ConvertToPipelineStageFlags());
 | 
			
		||||
                            image.View?.PrepareForUsage(cbs, image.Stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
@@ -385,9 +405,12 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
            }
 | 
			
		||||
            else if (image is TextureView view)
 | 
			
		||||
            {
 | 
			
		||||
                view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
 | 
			
		||||
                ref ImageRef iRef = ref _imageRefs[binding];
 | 
			
		||||
 | 
			
		||||
                _imageRefs[binding] = new(stage, view.Storage, view.GetView(imageFormat).GetIdentityImageView());
 | 
			
		||||
                iRef.View?.ClearUsage(FeedbackLoopHazards);
 | 
			
		||||
                view?.PrepareForUsage(cbs, stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
 | 
			
		||||
 | 
			
		||||
                iRef = new(stage, view, view.GetView(imageFormat).GetIdentityImageView());
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
@@ -486,9 +509,12 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
            }
 | 
			
		||||
            else if (texture is TextureView view)
 | 
			
		||||
            {
 | 
			
		||||
                view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
 | 
			
		||||
                ref TextureRef iRef = ref _textureRefs[binding];
 | 
			
		||||
 | 
			
		||||
                _textureRefs[binding] = new(stage, view.Storage, view.GetImageView(), ((SamplerHolder)sampler)?.GetSampler());
 | 
			
		||||
                iRef.View?.ClearUsage(FeedbackLoopHazards);
 | 
			
		||||
                view?.PrepareForUsage(cbs, stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
 | 
			
		||||
 | 
			
		||||
                iRef = new(stage, view, view.GetImageView(), ((SamplerHolder)sampler)?.GetSampler());
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
@@ -510,7 +536,7 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
            {
 | 
			
		||||
                view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
 | 
			
		||||
 | 
			
		||||
                _textureRefs[binding] = new(stage, view.Storage, view.GetIdentityImageView(), ((SamplerHolder)sampler)?.GetSampler());
 | 
			
		||||
                _textureRefs[binding] = new(stage, view, view.GetIdentityImageView(), ((SamplerHolder)sampler)?.GetSampler());
 | 
			
		||||
 | 
			
		||||
                SignalDirty(DirtyFlags.Texture);
 | 
			
		||||
            }
 | 
			
		||||
@@ -836,7 +862,7 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
                                ref var texture = ref textures[i];
 | 
			
		||||
                                ref var refs = ref _textureRefs[binding + i];
 | 
			
		||||
 | 
			
		||||
                                texture.ImageView = refs.View?.Get(cbs).Value ?? default;
 | 
			
		||||
                                texture.ImageView = refs.ImageView?.Get(cbs).Value ?? default;
 | 
			
		||||
                                texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
 | 
			
		||||
 | 
			
		||||
                                if (texture.ImageView.Handle == 0)
 | 
			
		||||
@@ -886,7 +912,7 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
 | 
			
		||||
                            for (int i = 0; i < count; i++)
 | 
			
		||||
                            {
 | 
			
		||||
                                images[i].ImageView = _imageRefs[binding + i].View?.Get(cbs).Value ?? default;
 | 
			
		||||
                                images[i].ImageView = _imageRefs[binding + i].ImageView?.Get(cbs).Value ?? default;
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            tu.Push<DescriptorImageInfo>(images[..count]);
 | 
			
		||||
@@ -957,7 +983,7 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
                            ref var texture = ref textures[i];
 | 
			
		||||
                            ref var refs = ref _textureRefs[binding + i];
 | 
			
		||||
 | 
			
		||||
                            texture.ImageView = refs.View?.Get(cbs).Value ?? default;
 | 
			
		||||
                            texture.ImageView = refs.ImageView?.Get(cbs).Value ?? default;
 | 
			
		||||
                            texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
 | 
			
		||||
 | 
			
		||||
                            if (texture.ImageView.Handle == 0)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								src/Ryujinx.Graphics.Vulkan/FeedbackLoopAspects.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/Ryujinx.Graphics.Vulkan/FeedbackLoopAspects.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
{
 | 
			
		||||
    [Flags]
 | 
			
		||||
    internal enum FeedbackLoopAspects
 | 
			
		||||
    {
 | 
			
		||||
        None = 0,
 | 
			
		||||
        Color = 1 << 0,
 | 
			
		||||
        Depth = 1 << 1,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -302,6 +302,27 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
            _depthStencil?.Storage?.AddStoreOpUsage(true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void ClearBindings()
 | 
			
		||||
        {
 | 
			
		||||
            _depthStencil?.Storage.ClearBindings();
 | 
			
		||||
 | 
			
		||||
            for (int i = 0; i < _colorsCanonical.Length; i++)
 | 
			
		||||
            {
 | 
			
		||||
                _colorsCanonical[i]?.Storage.ClearBindings();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void AddBindings()
 | 
			
		||||
        {
 | 
			
		||||
            _depthStencil?.Storage.AddBinding(_depthStencil);
 | 
			
		||||
 | 
			
		||||
            for (int i = 0; i < _colorsCanonical.Length; i++)
 | 
			
		||||
            {
 | 
			
		||||
                TextureView color = _colorsCanonical[i];
 | 
			
		||||
                color?.Storage.AddBinding(color);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public (RenderPassHolder rpHolder, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
 | 
			
		||||
            VulkanRenderer gd,
 | 
			
		||||
            Device device,
 | 
			
		||||
 
 | 
			
		||||
@@ -46,6 +46,8 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
        public readonly bool SupportsViewportArray2;
 | 
			
		||||
        public readonly bool SupportsHostImportedMemory;
 | 
			
		||||
        public readonly bool SupportsDepthClipControl;
 | 
			
		||||
        public readonly bool SupportsAttachmentFeedbackLoop;
 | 
			
		||||
        public readonly bool SupportsDynamicAttachmentFeedbackLoop;
 | 
			
		||||
        public readonly uint SubgroupSize;
 | 
			
		||||
        public readonly SampleCountFlags SupportedSampleCounts;
 | 
			
		||||
        public readonly PortabilitySubsetFlags PortabilitySubset;
 | 
			
		||||
@@ -84,6 +86,8 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
            bool supportsViewportArray2,
 | 
			
		||||
            bool supportsHostImportedMemory,
 | 
			
		||||
            bool supportsDepthClipControl,
 | 
			
		||||
            bool supportsAttachmentFeedbackLoop,
 | 
			
		||||
            bool supportsDynamicAttachmentFeedbackLoop,
 | 
			
		||||
            uint subgroupSize,
 | 
			
		||||
            SampleCountFlags supportedSampleCounts,
 | 
			
		||||
            PortabilitySubsetFlags portabilitySubset,
 | 
			
		||||
@@ -121,6 +125,8 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
            SupportsViewportArray2 = supportsViewportArray2;
 | 
			
		||||
            SupportsHostImportedMemory = supportsHostImportedMemory;
 | 
			
		||||
            SupportsDepthClipControl = supportsDepthClipControl;
 | 
			
		||||
            SupportsAttachmentFeedbackLoop = supportsAttachmentFeedbackLoop;
 | 
			
		||||
            SupportsDynamicAttachmentFeedbackLoop = supportsDynamicAttachmentFeedbackLoop;
 | 
			
		||||
            SubgroupSize = subgroupSize;
 | 
			
		||||
            SupportedSampleCounts = supportedSampleCounts;
 | 
			
		||||
            PortabilitySubset = portabilitySubset;
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ using Ryujinx.Graphics.GAL;
 | 
			
		||||
using Ryujinx.Graphics.Shader;
 | 
			
		||||
using Silk.NET.Vulkan;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Numerics;
 | 
			
		||||
using System.Runtime.CompilerServices;
 | 
			
		||||
@@ -33,6 +34,7 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
        public readonly Action EndRenderPassDelegate;
 | 
			
		||||
 | 
			
		||||
        protected PipelineDynamicState DynamicState;
 | 
			
		||||
        protected bool IsMainPipeline;
 | 
			
		||||
        private PipelineState _newState;
 | 
			
		||||
        private bool _graphicsStateDirty;
 | 
			
		||||
        private bool _computeStateDirty;
 | 
			
		||||
@@ -85,6 +87,9 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
        private bool _tfEnabled;
 | 
			
		||||
        private bool _tfActive;
 | 
			
		||||
 | 
			
		||||
        private FeedbackLoopAspects _feedbackLoop;
 | 
			
		||||
        private bool _passWritesDepthStencil;
 | 
			
		||||
 | 
			
		||||
        private readonly PipelineColorBlendAttachmentState[] _storedBlend;
 | 
			
		||||
        public ulong DrawCount { get; private set; }
 | 
			
		||||
        public bool RenderPassActive { get; private set; }
 | 
			
		||||
@@ -126,7 +131,7 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
 | 
			
		||||
        public void Initialize()
 | 
			
		||||
        {
 | 
			
		||||
            _descriptorSetUpdater.Initialize();
 | 
			
		||||
            _descriptorSetUpdater.Initialize(IsMainPipeline);
 | 
			
		||||
 | 
			
		||||
            QuadsToTrisPattern = new IndexBufferPattern(Gd, 4, 6, 0, new[] { 0, 1, 2, 0, 2, 3 }, 4, false);
 | 
			
		||||
            TriFanToTrisPattern = new IndexBufferPattern(Gd, 3, 3, 2, new[] { int.MinValue, -1, 0 }, 1, true);
 | 
			
		||||
@@ -814,6 +819,8 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
            _newState.DepthTestEnable = depthTest.TestEnable;
 | 
			
		||||
            _newState.DepthWriteEnable = depthTest.WriteEnable;
 | 
			
		||||
            _newState.DepthCompareOp = depthTest.Func.Convert();
 | 
			
		||||
 | 
			
		||||
            UpdatePassDepthStencil();
 | 
			
		||||
            SignalStateChange();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -1079,6 +1086,8 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
            _newState.StencilFrontPassOp = stencilTest.FrontDpPass.Convert();
 | 
			
		||||
            _newState.StencilFrontDepthFailOp = stencilTest.FrontDpFail.Convert();
 | 
			
		||||
            _newState.StencilFrontCompareOp = stencilTest.FrontFunc.Convert();
 | 
			
		||||
 | 
			
		||||
            UpdatePassDepthStencil();
 | 
			
		||||
            SignalStateChange();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -1426,7 +1435,23 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (IsMainPipeline)
 | 
			
		||||
            {
 | 
			
		||||
                FramebufferParams?.ClearBindings();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            FramebufferParams = new FramebufferParams(Device, colors, depthStencil);
 | 
			
		||||
 | 
			
		||||
            if (IsMainPipeline)
 | 
			
		||||
            {
 | 
			
		||||
                FramebufferParams.AddBindings();
 | 
			
		||||
 | 
			
		||||
                _newState.FeedbackLoopAspects = FeedbackLoopAspects.None;
 | 
			
		||||
                _bindingBarriersDirty = true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            _passWritesDepthStencil = false;
 | 
			
		||||
            UpdatePassDepthStencil();
 | 
			
		||||
            UpdatePipelineAttachmentFormats();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -1493,11 +1518,82 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Gd.Barriers.Flush(Cbs, _program, RenderPassActive, _rpHolder, EndRenderPassDelegate);
 | 
			
		||||
            Gd.Barriers.Flush(Cbs, _program, _feedbackLoop != 0, RenderPassActive, _rpHolder, EndRenderPassDelegate);
 | 
			
		||||
 | 
			
		||||
            _descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Compute);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private bool ChangeFeedbackLoop(FeedbackLoopAspects aspects)
 | 
			
		||||
        {
 | 
			
		||||
            if (_feedbackLoop != aspects)
 | 
			
		||||
            {
 | 
			
		||||
                if (Gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop)
 | 
			
		||||
                {
 | 
			
		||||
                    DynamicState.SetFeedbackLoop(aspects);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    _newState.FeedbackLoopAspects = aspects;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                _feedbackLoop = aspects;
 | 
			
		||||
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | 
			
		||||
        private bool UpdateFeedbackLoop()
 | 
			
		||||
        {
 | 
			
		||||
            List<TextureView> hazards = _descriptorSetUpdater.FeedbackLoopHazards;
 | 
			
		||||
 | 
			
		||||
            if ((hazards?.Count ?? 0) > 0)
 | 
			
		||||
            {
 | 
			
		||||
                FeedbackLoopAspects aspects = 0;
 | 
			
		||||
 | 
			
		||||
                foreach (TextureView view in hazards)
 | 
			
		||||
                {
 | 
			
		||||
                    // May need to enforce feedback loop layout here in the future.
 | 
			
		||||
                    // Though technically, it should always work with the general layout.
 | 
			
		||||
 | 
			
		||||
                    if (view.Info.Format.IsDepthOrStencil())
 | 
			
		||||
                    {
 | 
			
		||||
                        if (_passWritesDepthStencil)
 | 
			
		||||
                        {
 | 
			
		||||
                            // If depth/stencil isn't written in the pass, it doesn't count as a feedback loop.
 | 
			
		||||
 | 
			
		||||
                            aspects |= FeedbackLoopAspects.Depth;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        aspects |= FeedbackLoopAspects.Color;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return ChangeFeedbackLoop(aspects);
 | 
			
		||||
            }
 | 
			
		||||
            else if (_feedbackLoop != 0)
 | 
			
		||||
            {
 | 
			
		||||
                return ChangeFeedbackLoop(FeedbackLoopAspects.None);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void UpdatePassDepthStencil()
 | 
			
		||||
        {
 | 
			
		||||
            if (!RenderPassActive)
 | 
			
		||||
            {
 | 
			
		||||
                _passWritesDepthStencil = false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Stencil test being enabled doesn't necessarily mean a write, but it's not critical to check.
 | 
			
		||||
            _passWritesDepthStencil |= (_newState.DepthTestEnable && _newState.DepthWriteEnable) || _newState.StencilTestEnable;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private bool RecreateGraphicsPipelineIfNeeded()
 | 
			
		||||
        {
 | 
			
		||||
            if (AutoFlush.ShouldFlushDraw(DrawCount))
 | 
			
		||||
@@ -1505,7 +1601,7 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
                Gd.FlushAllCommands();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            DynamicState.ReplayIfDirty(Gd.Api, CommandBuffer);
 | 
			
		||||
            DynamicState.ReplayIfDirty(Gd, CommandBuffer);
 | 
			
		||||
 | 
			
		||||
            if (_needsIndexBufferRebind && _indexBufferPattern == null)
 | 
			
		||||
            {
 | 
			
		||||
@@ -1539,7 +1635,15 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
                _vertexBufferUpdater.Commit(Cbs);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (_graphicsStateDirty || Pbp != PipelineBindPoint.Graphics)
 | 
			
		||||
            if (_bindingBarriersDirty)
 | 
			
		||||
            {
 | 
			
		||||
                // Stale barriers may have been activated by switching program. Emit any that are relevant.
 | 
			
		||||
                _descriptorSetUpdater.InsertBindingBarriers(Cbs);
 | 
			
		||||
 | 
			
		||||
                _bindingBarriersDirty = false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (UpdateFeedbackLoop() || _graphicsStateDirty || Pbp != PipelineBindPoint.Graphics)
 | 
			
		||||
            {
 | 
			
		||||
                if (!CreatePipeline(PipelineBindPoint.Graphics))
 | 
			
		||||
                {
 | 
			
		||||
@@ -1548,17 +1652,9 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
 | 
			
		||||
                _graphicsStateDirty = false;
 | 
			
		||||
                Pbp = PipelineBindPoint.Graphics;
 | 
			
		||||
 | 
			
		||||
                if (_bindingBarriersDirty)
 | 
			
		||||
                {
 | 
			
		||||
                    // Stale barriers may have been activated by switching program. Emit any that are relevant.
 | 
			
		||||
                    _descriptorSetUpdater.InsertBindingBarriers(Cbs);
 | 
			
		||||
 | 
			
		||||
                    _bindingBarriersDirty = false;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Gd.Barriers.Flush(Cbs, _program, RenderPassActive, _rpHolder, EndRenderPassDelegate);
 | 
			
		||||
            Gd.Barriers.Flush(Cbs, _program, _feedbackLoop != 0, RenderPassActive, _rpHolder, EndRenderPassDelegate);
 | 
			
		||||
 | 
			
		||||
            _descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Graphics);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
using Ryujinx.Common.Memory;
 | 
			
		||||
using Silk.NET.Vulkan;
 | 
			
		||||
using Silk.NET.Vulkan.Extensions.EXT;
 | 
			
		||||
 | 
			
		||||
namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
{
 | 
			
		||||
@@ -21,6 +22,8 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
 | 
			
		||||
        private Array4<float> _blendConstants;
 | 
			
		||||
 | 
			
		||||
        private FeedbackLoopAspects _feedbackLoopAspects;
 | 
			
		||||
 | 
			
		||||
        public uint ViewportsCount;
 | 
			
		||||
        public Array16<Viewport> Viewports;
 | 
			
		||||
 | 
			
		||||
@@ -32,7 +35,8 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
            Scissor = 1 << 2,
 | 
			
		||||
            Stencil = 1 << 3,
 | 
			
		||||
            Viewport = 1 << 4,
 | 
			
		||||
            All = Blend | DepthBias | Scissor | Stencil | Viewport,
 | 
			
		||||
            FeedbackLoop = 1 << 5,
 | 
			
		||||
            All = Blend | DepthBias | Scissor | Stencil | Viewport | FeedbackLoop,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private DirtyFlags _dirty;
 | 
			
		||||
@@ -99,13 +103,22 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void SetFeedbackLoop(FeedbackLoopAspects aspects)
 | 
			
		||||
        {
 | 
			
		||||
            _feedbackLoopAspects = aspects;
 | 
			
		||||
 | 
			
		||||
            _dirty |= DirtyFlags.FeedbackLoop;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void ForceAllDirty()
 | 
			
		||||
        {
 | 
			
		||||
            _dirty = DirtyFlags.All;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void ReplayIfDirty(Vk api, CommandBuffer commandBuffer)
 | 
			
		||||
        public void ReplayIfDirty(VulkanRenderer gd, CommandBuffer commandBuffer)
 | 
			
		||||
        {
 | 
			
		||||
            Vk api = gd.Api;
 | 
			
		||||
 | 
			
		||||
            if (_dirty.HasFlag(DirtyFlags.Blend))
 | 
			
		||||
            {
 | 
			
		||||
                RecordBlend(api, commandBuffer);
 | 
			
		||||
@@ -131,6 +144,11 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
                RecordViewport(api, commandBuffer);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (_dirty.HasFlag(DirtyFlags.FeedbackLoop) && gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop)
 | 
			
		||||
            {
 | 
			
		||||
                RecordFeedbackLoop(gd.DynamicFeedbackLoopApi, commandBuffer);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            _dirty = DirtyFlags.None;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -169,5 +187,17 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
                api.CmdSetViewport(commandBuffer, 0, ViewportsCount, Viewports.AsSpan());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private readonly void RecordFeedbackLoop(ExtAttachmentFeedbackLoopDynamicState api, CommandBuffer commandBuffer)
 | 
			
		||||
        {
 | 
			
		||||
            ImageAspectFlags aspects = (_feedbackLoopAspects & FeedbackLoopAspects.Color) != 0 ? ImageAspectFlags.ColorBit : 0;
 | 
			
		||||
 | 
			
		||||
            if ((_feedbackLoopAspects & FeedbackLoopAspects.Depth) != 0)
 | 
			
		||||
            {
 | 
			
		||||
                aspects |= ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            api.CmdSetAttachmentFeedbackLoopEnable(commandBuffer, aspects);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,8 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
            _activeBufferMirrors = new();
 | 
			
		||||
 | 
			
		||||
            CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer;
 | 
			
		||||
 | 
			
		||||
            IsMainPipeline = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void CopyPendingQuery()
 | 
			
		||||
@@ -235,7 +237,7 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
 | 
			
		||||
            if (Pipeline != null && Pbp == PipelineBindPoint.Graphics)
 | 
			
		||||
            {
 | 
			
		||||
                DynamicState.ReplayIfDirty(Gd.Api, CommandBuffer);
 | 
			
		||||
                DynamicState.ReplayIfDirty(Gd, CommandBuffer);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
    struct PipelineState : IDisposable
 | 
			
		||||
    {
 | 
			
		||||
        private const int RequiredSubgroupSize = 32;
 | 
			
		||||
        private const int MaxDynamicStatesCount = 9;
 | 
			
		||||
 | 
			
		||||
        public PipelineUid Internal;
 | 
			
		||||
 | 
			
		||||
@@ -299,6 +300,12 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
            set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFBF) | ((value ? 1UL : 0UL) << 6);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public FeedbackLoopAspects FeedbackLoopAspects
 | 
			
		||||
        {
 | 
			
		||||
            readonly get => (FeedbackLoopAspects)((Internal.Id8 >> 7) & 0x3);
 | 
			
		||||
            set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFE7F) | (((ulong)value) << 7);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool HasTessellationControlShader;
 | 
			
		||||
        public NativeArray<PipelineShaderStageCreateInfo> Stages;
 | 
			
		||||
        public PipelineLayout PipelineLayout;
 | 
			
		||||
@@ -564,9 +571,11 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                bool supportsExtDynamicState = gd.Capabilities.SupportsExtendedDynamicState;
 | 
			
		||||
                int dynamicStatesCount = supportsExtDynamicState ? 8 : 7;
 | 
			
		||||
                bool supportsFeedbackLoopDynamicState = gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop;
 | 
			
		||||
 | 
			
		||||
                DynamicState* dynamicStates = stackalloc DynamicState[dynamicStatesCount];
 | 
			
		||||
                DynamicState* dynamicStates = stackalloc DynamicState[MaxDynamicStatesCount];
 | 
			
		||||
 | 
			
		||||
                int dynamicStatesCount = 7;
 | 
			
		||||
 | 
			
		||||
                dynamicStates[0] = DynamicState.Viewport;
 | 
			
		||||
                dynamicStates[1] = DynamicState.Scissor;
 | 
			
		||||
@@ -578,7 +587,12 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
 | 
			
		||||
                if (supportsExtDynamicState)
 | 
			
		||||
                {
 | 
			
		||||
                    dynamicStates[7] = DynamicState.VertexInputBindingStrideExt;
 | 
			
		||||
                    dynamicStates[dynamicStatesCount++] = DynamicState.VertexInputBindingStrideExt;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (supportsFeedbackLoopDynamicState)
 | 
			
		||||
                {
 | 
			
		||||
                    dynamicStates[dynamicStatesCount++] = DynamicState.AttachmentFeedbackLoopEnableExt;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var pipelineDynamicStateCreateInfo = new PipelineDynamicStateCreateInfo
 | 
			
		||||
@@ -588,9 +602,27 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
                    PDynamicStates = dynamicStates,
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                PipelineCreateFlags flags = 0;
 | 
			
		||||
 | 
			
		||||
                if (gd.Capabilities.SupportsAttachmentFeedbackLoop)
 | 
			
		||||
                {
 | 
			
		||||
                    FeedbackLoopAspects aspects = FeedbackLoopAspects;
 | 
			
		||||
 | 
			
		||||
                    if ((aspects & FeedbackLoopAspects.Color) != 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        flags |= PipelineCreateFlags.CreateColorAttachmentFeedbackLoopBitExt;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if ((aspects & FeedbackLoopAspects.Depth) != 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        flags |= PipelineCreateFlags.CreateDepthStencilAttachmentFeedbackLoopBitExt;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var pipelineCreateInfo = new GraphicsPipelineCreateInfo
 | 
			
		||||
                {
 | 
			
		||||
                    SType = StructureType.GraphicsPipelineCreateInfo,
 | 
			
		||||
                    Flags = flags,
 | 
			
		||||
                    StageCount = StagesCount,
 | 
			
		||||
                    PStages = Stages.Pointer,
 | 
			
		||||
                    PVertexInputState = &vertexInputState,
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ using Silk.NET.Vulkan;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Numerics;
 | 
			
		||||
using System.Runtime.CompilerServices;
 | 
			
		||||
using Format = Ryujinx.Graphics.GAL.Format;
 | 
			
		||||
using VkBuffer = Silk.NET.Vulkan.Buffer;
 | 
			
		||||
using VkFormat = Silk.NET.Vulkan.Format;
 | 
			
		||||
@@ -12,6 +13,11 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
{
 | 
			
		||||
    class TextureStorage : IDisposable
 | 
			
		||||
    {
 | 
			
		||||
        private struct TextureSliceInfo
 | 
			
		||||
        {
 | 
			
		||||
            public int BindCount;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private const MemoryPropertyFlags DefaultImageMemoryFlags =
 | 
			
		||||
            MemoryPropertyFlags.DeviceLocalBit;
 | 
			
		||||
 | 
			
		||||
@@ -43,6 +49,7 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
        private readonly Image _image;
 | 
			
		||||
        private readonly Auto<DisposableImage> _imageAuto;
 | 
			
		||||
        private readonly Auto<MemoryAllocation> _allocationAuto;
 | 
			
		||||
        private readonly int _depthOrLayers;
 | 
			
		||||
        private Auto<MemoryAllocation> _foreignAllocationAuto;
 | 
			
		||||
 | 
			
		||||
        private Dictionary<Format, TextureStorage> _aliasedStorages;
 | 
			
		||||
@@ -55,6 +62,9 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
        private int _viewsCount;
 | 
			
		||||
        private readonly ulong _size;
 | 
			
		||||
 | 
			
		||||
        private int _bindCount;
 | 
			
		||||
        private readonly TextureSliceInfo[] _slices;
 | 
			
		||||
 | 
			
		||||
        public VkFormat VkFormat { get; }
 | 
			
		||||
 | 
			
		||||
        public unsafe TextureStorage(
 | 
			
		||||
@@ -73,6 +83,7 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
            var depth = (uint)(info.Target == Target.Texture3D ? info.Depth : 1);
 | 
			
		||||
 | 
			
		||||
            VkFormat = format;
 | 
			
		||||
            _depthOrLayers = info.GetDepthOrLayers();
 | 
			
		||||
 | 
			
		||||
            var type = info.Target.Convert();
 | 
			
		||||
 | 
			
		||||
@@ -80,7 +91,7 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
 | 
			
		||||
            var sampleCountFlags = ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)info.Samples);
 | 
			
		||||
 | 
			
		||||
            var usage = GetImageUsage(info.Format, info.Target, gd.Capabilities.SupportsShaderStorageImageMultisample);
 | 
			
		||||
            var usage = GetImageUsage(info.Format, info.Target, gd.Capabilities);
 | 
			
		||||
 | 
			
		||||
            var flags = ImageCreateFlags.CreateMutableFormatBit | ImageCreateFlags.CreateExtendedUsageBit;
 | 
			
		||||
 | 
			
		||||
@@ -148,6 +159,8 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
 | 
			
		||||
                InitialTransition(ImageLayout.Preinitialized, ImageLayout.General);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            _slices = new TextureSliceInfo[levels * _depthOrLayers];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public TextureStorage CreateAliasedColorForDepthStorageUnsafe(Format format)
 | 
			
		||||
@@ -292,7 +305,7 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static ImageUsageFlags GetImageUsage(Format format, Target target, bool supportsMsStorage)
 | 
			
		||||
        public static ImageUsageFlags GetImageUsage(Format format, Target target, in HardwareCapabilities capabilities)
 | 
			
		||||
        {
 | 
			
		||||
            var usage = DefaultUsageFlags;
 | 
			
		||||
 | 
			
		||||
@@ -305,11 +318,19 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
                usage |= ImageUsageFlags.ColorAttachmentBit;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            bool supportsMsStorage = capabilities.SupportsShaderStorageImageMultisample;
 | 
			
		||||
 | 
			
		||||
            if (format.IsImageCompatible() && (supportsMsStorage || !target.IsMultisample()))
 | 
			
		||||
            {
 | 
			
		||||
                usage |= ImageUsageFlags.StorageBit;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (capabilities.SupportsAttachmentFeedbackLoop &&
 | 
			
		||||
                (usage & (ImageUsageFlags.DepthStencilAttachmentBit | ImageUsageFlags.ColorAttachmentBit)) != 0)
 | 
			
		||||
            {
 | 
			
		||||
                usage |= ImageUsageFlags.AttachmentFeedbackLoopBitExt;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return usage;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -510,6 +531,55 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void AddBinding(TextureView view)
 | 
			
		||||
        {
 | 
			
		||||
            // Assumes a view only has a first level.
 | 
			
		||||
 | 
			
		||||
            int index = view.FirstLevel * _depthOrLayers + view.FirstLayer;
 | 
			
		||||
            int layers = view.Layers;
 | 
			
		||||
 | 
			
		||||
            for (int i = 0; i < layers; i++)
 | 
			
		||||
            {
 | 
			
		||||
                ref TextureSliceInfo info = ref _slices[index++];
 | 
			
		||||
 | 
			
		||||
                info.BindCount++;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            _bindCount++;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void ClearBindings()
 | 
			
		||||
        {
 | 
			
		||||
            if (_bindCount != 0)
 | 
			
		||||
            {
 | 
			
		||||
                Array.Clear(_slices, 0, _slices.Length);
 | 
			
		||||
 | 
			
		||||
                _bindCount = 0;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | 
			
		||||
        public bool IsBound(TextureView view)
 | 
			
		||||
        {
 | 
			
		||||
            if (_bindCount != 0)
 | 
			
		||||
            {
 | 
			
		||||
                int index = view.FirstLevel * _depthOrLayers + view.FirstLayer;
 | 
			
		||||
                int layers = view.Layers;
 | 
			
		||||
 | 
			
		||||
                for (int i = 0; i < layers; i++)
 | 
			
		||||
                {
 | 
			
		||||
                    ref TextureSliceInfo info = ref _slices[index++];
 | 
			
		||||
 | 
			
		||||
                    if (info.BindCount != 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        return true;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void IncrementViewsCount()
 | 
			
		||||
        {
 | 
			
		||||
            _viewsCount++;
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,8 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
        private readonly Auto<DisposableImageView> _imageView2dArray;
 | 
			
		||||
        private Dictionary<Format, TextureView> _selfManagedViews;
 | 
			
		||||
 | 
			
		||||
        private int _hazardUses;
 | 
			
		||||
 | 
			
		||||
        private readonly TextureCreateInfo _info;
 | 
			
		||||
 | 
			
		||||
        private HashTableSlim<RenderPassCacheKey, RenderPassHolder> _renderPasses;
 | 
			
		||||
@@ -60,7 +62,7 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
            gd.Textures.Add(this);
 | 
			
		||||
 | 
			
		||||
            var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format);
 | 
			
		||||
            var usage = TextureStorage.GetImageUsage(info.Format, info.Target, gd.Capabilities.SupportsShaderStorageImageMultisample);
 | 
			
		||||
            var usage = TextureStorage.GetImageUsage(info.Format, info.Target, gd.Capabilities);
 | 
			
		||||
            var levels = (uint)info.Levels;
 | 
			
		||||
            var layers = (uint)info.GetLayers();
 | 
			
		||||
 | 
			
		||||
@@ -1034,6 +1036,34 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
            throw new NotImplementedException();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void PrepareForUsage(CommandBufferScoped cbs, PipelineStageFlags flags, List<TextureView> feedbackLoopHazards)
 | 
			
		||||
        {
 | 
			
		||||
            Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, flags);
 | 
			
		||||
 | 
			
		||||
            if (feedbackLoopHazards != null && Storage.IsBound(this))
 | 
			
		||||
            {
 | 
			
		||||
                feedbackLoopHazards.Add(this);
 | 
			
		||||
                _hazardUses++;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void ClearUsage(List<TextureView> feedbackLoopHazards)
 | 
			
		||||
        {
 | 
			
		||||
            if (_hazardUses != 0 && feedbackLoopHazards != null)
 | 
			
		||||
            {
 | 
			
		||||
                feedbackLoopHazards.Remove(this);
 | 
			
		||||
                _hazardUses--;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void DecrementHazardUses()
 | 
			
		||||
        {
 | 
			
		||||
            if (_hazardUses != 0)
 | 
			
		||||
            {
 | 
			
		||||
                _hazardUses--;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public (RenderPassHolder rpHolder, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
 | 
			
		||||
            VulkanRenderer gd,
 | 
			
		||||
            Device device,
 | 
			
		||||
 
 | 
			
		||||
@@ -44,6 +44,8 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
            "VK_EXT_4444_formats",
 | 
			
		||||
            "VK_KHR_8bit_storage",
 | 
			
		||||
            "VK_KHR_maintenance2",
 | 
			
		||||
            "VK_EXT_attachment_feedback_loop_layout",
 | 
			
		||||
            "VK_EXT_attachment_feedback_loop_dynamic_state",
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        private static readonly string[] _requiredExtensions = {
 | 
			
		||||
@@ -357,6 +359,28 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
                features2.PNext = &supportedFeaturesDepthClipControl;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT supportedFeaturesAttachmentFeedbackLoopLayout = new()
 | 
			
		||||
            {
 | 
			
		||||
                SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesExt,
 | 
			
		||||
                PNext = features2.PNext,
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_layout"))
 | 
			
		||||
            {
 | 
			
		||||
                features2.PNext = &supportedFeaturesAttachmentFeedbackLoopLayout;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT supportedFeaturesDynamicAttachmentFeedbackLoopLayout = new()
 | 
			
		||||
            {
 | 
			
		||||
                SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesExt,
 | 
			
		||||
                PNext = features2.PNext,
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_dynamic_state"))
 | 
			
		||||
            {
 | 
			
		||||
                features2.PNext = &supportedFeaturesDynamicAttachmentFeedbackLoopLayout;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            PhysicalDeviceVulkan12Features supportedPhysicalDeviceVulkan12Features = new()
 | 
			
		||||
            {
 | 
			
		||||
                SType = StructureType.PhysicalDeviceVulkan12Features,
 | 
			
		||||
@@ -531,6 +555,36 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
                pExtendedFeatures = &featuresDepthClipControl;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT featuresAttachmentFeedbackLoopLayout;
 | 
			
		||||
 | 
			
		||||
            if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_layout") &&
 | 
			
		||||
                supportedFeaturesAttachmentFeedbackLoopLayout.AttachmentFeedbackLoopLayout)
 | 
			
		||||
            {
 | 
			
		||||
                featuresAttachmentFeedbackLoopLayout = new()
 | 
			
		||||
                {
 | 
			
		||||
                    SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesExt,
 | 
			
		||||
                    PNext = pExtendedFeatures,
 | 
			
		||||
                    AttachmentFeedbackLoopLayout = true,
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                pExtendedFeatures = &featuresAttachmentFeedbackLoopLayout;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT featuresDynamicAttachmentFeedbackLoopLayout;
 | 
			
		||||
 | 
			
		||||
            if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_dynamic_state") &&
 | 
			
		||||
                supportedFeaturesDynamicAttachmentFeedbackLoopLayout.AttachmentFeedbackLoopDynamicState)
 | 
			
		||||
            {
 | 
			
		||||
                featuresDynamicAttachmentFeedbackLoopLayout = new()
 | 
			
		||||
                {
 | 
			
		||||
                    SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesExt,
 | 
			
		||||
                    PNext = pExtendedFeatures,
 | 
			
		||||
                    AttachmentFeedbackLoopDynamicState = true,
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                pExtendedFeatures = &featuresDynamicAttachmentFeedbackLoopLayout;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var enabledExtensions = _requiredExtensions.Union(_desirableExtensions.Intersect(physicalDevice.DeviceExtensions)).ToArray();
 | 
			
		||||
 | 
			
		||||
            IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length];
 | 
			
		||||
 
 | 
			
		||||
@@ -38,6 +38,7 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
        internal KhrPushDescriptor PushDescriptorApi { get; private set; }
 | 
			
		||||
        internal ExtTransformFeedback TransformFeedbackApi { get; private set; }
 | 
			
		||||
        internal KhrDrawIndirectCount DrawIndirectCountApi { get; private set; }
 | 
			
		||||
        internal ExtAttachmentFeedbackLoopDynamicState DynamicFeedbackLoopApi { get; private set; }
 | 
			
		||||
 | 
			
		||||
        internal uint QueueFamilyIndex { get; private set; }
 | 
			
		||||
        internal Queue Queue { get; private set; }
 | 
			
		||||
@@ -149,6 +150,11 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
                DrawIndirectCountApi = drawIndirectCountApi;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtAttachmentFeedbackLoopDynamicState dynamicFeedbackLoopApi))
 | 
			
		||||
            {
 | 
			
		||||
                DynamicFeedbackLoopApi = dynamicFeedbackLoopApi;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (maxQueueCount >= 2)
 | 
			
		||||
            {
 | 
			
		||||
                Api.GetDeviceQueue(_device, queueFamilyIndex, 1, out var backgroundQueue);
 | 
			
		||||
@@ -243,6 +249,16 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
                SType = StructureType.PhysicalDeviceDepthClipControlFeaturesExt,
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT featuresAttachmentFeedbackLoop = new()
 | 
			
		||||
            {
 | 
			
		||||
                SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesExt,
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT featuresDynamicAttachmentFeedbackLoop = new()
 | 
			
		||||
            {
 | 
			
		||||
                SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesExt,
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            PhysicalDevicePortabilitySubsetFeaturesKHR featuresPortabilitySubset = new()
 | 
			
		||||
            {
 | 
			
		||||
                SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr,
 | 
			
		||||
@@ -279,6 +295,22 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
                features2.PNext = &featuresDepthClipControl;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            bool supportsAttachmentFeedbackLoop = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_layout");
 | 
			
		||||
 | 
			
		||||
            if (supportsAttachmentFeedbackLoop)
 | 
			
		||||
            {
 | 
			
		||||
                featuresAttachmentFeedbackLoop.PNext = features2.PNext;
 | 
			
		||||
                features2.PNext = &featuresAttachmentFeedbackLoop;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            bool supportsDynamicAttachmentFeedbackLoop = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_dynamic_state");
 | 
			
		||||
 | 
			
		||||
            if (supportsDynamicAttachmentFeedbackLoop)
 | 
			
		||||
            {
 | 
			
		||||
                featuresDynamicAttachmentFeedbackLoop.PNext = features2.PNext;
 | 
			
		||||
                features2.PNext = &featuresDynamicAttachmentFeedbackLoop;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            bool usePortability = _physicalDevice.IsDeviceExtensionPresent("VK_KHR_portability_subset");
 | 
			
		||||
 | 
			
		||||
            if (usePortability)
 | 
			
		||||
@@ -401,6 +433,8 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
                _physicalDevice.IsDeviceExtensionPresent("VK_NV_viewport_array2"),
 | 
			
		||||
                _physicalDevice.IsDeviceExtensionPresent(ExtExternalMemoryHost.ExtensionName),
 | 
			
		||||
                supportsDepthClipControl && featuresDepthClipControl.DepthClipControl,
 | 
			
		||||
                supportsAttachmentFeedbackLoop && featuresAttachmentFeedbackLoop.AttachmentFeedbackLoopLayout,
 | 
			
		||||
                supportsDynamicAttachmentFeedbackLoop && featuresDynamicAttachmentFeedbackLoop.AttachmentFeedbackLoopDynamicState,
 | 
			
		||||
                propertiesSubgroup.SubgroupSize,
 | 
			
		||||
                supportedSampleCounts,
 | 
			
		||||
                portabilityFlags,
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ using Ryujinx.Common.Configuration;
 | 
			
		||||
using Ryujinx.Common.GraphicsDriver;
 | 
			
		||||
using Ryujinx.Common.Logging;
 | 
			
		||||
using Ryujinx.Common.SystemInterop;
 | 
			
		||||
using Ryujinx.Common.Utilities;
 | 
			
		||||
using Ryujinx.Graphics.Vulkan.MoltenVK;
 | 
			
		||||
using Ryujinx.Modules;
 | 
			
		||||
using Ryujinx.SDL2.Common;
 | 
			
		||||
@@ -41,9 +42,6 @@ namespace Ryujinx
 | 
			
		||||
        [LibraryImport("user32.dll", SetLastError = true)]
 | 
			
		||||
        public static partial int MessageBoxA(IntPtr hWnd, [MarshalAs(UnmanagedType.LPStr)] string text, [MarshalAs(UnmanagedType.LPStr)] string caption, uint type);
 | 
			
		||||
 | 
			
		||||
        [LibraryImport("libc", SetLastError = true)]
 | 
			
		||||
        private static partial int setenv([MarshalAs(UnmanagedType.LPStr)] string name, [MarshalAs(UnmanagedType.LPStr)] string value, int overwrite);
 | 
			
		||||
 | 
			
		||||
        private const uint MbIconWarning = 0x30;
 | 
			
		||||
 | 
			
		||||
        static Program()
 | 
			
		||||
@@ -105,8 +103,7 @@ namespace Ryujinx
 | 
			
		||||
                    throw new NotSupportedException("Failed to initialize multi-threading support.");
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                Environment.SetEnvironmentVariable("GDK_BACKEND", "x11");
 | 
			
		||||
                setenv("GDK_BACKEND", "x11", 1);
 | 
			
		||||
                OsUtils.SetEnvironmentVariableNoCaching("GDK_BACKEND", "x11");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (OperatingSystem.IsMacOS())
 | 
			
		||||
@@ -125,19 +122,13 @@ namespace Ryujinx
 | 
			
		||||
                    resourcesDataDir = baseDirectory;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                static void SetEnvironmentVariableNoCaching(string key, string value)
 | 
			
		||||
                {
 | 
			
		||||
                    int res = setenv(key, value, 1);
 | 
			
		||||
                    Debug.Assert(res != -1);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // On macOS, GTK3 needs XDG_DATA_DIRS to be set, otherwise it will try searching for "gschemas.compiled" in system directories.
 | 
			
		||||
                SetEnvironmentVariableNoCaching("XDG_DATA_DIRS", Path.Combine(resourcesDataDir, "share"));
 | 
			
		||||
                OsUtils.SetEnvironmentVariableNoCaching("XDG_DATA_DIRS", Path.Combine(resourcesDataDir, "share"));
 | 
			
		||||
 | 
			
		||||
                // On macOS, GTK3 needs GDK_PIXBUF_MODULE_FILE to be set, otherwise it will try searching for "loaders.cache" in system directories.
 | 
			
		||||
                SetEnvironmentVariableNoCaching("GDK_PIXBUF_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gdk-pixbuf-2.0", "2.10.0", "loaders.cache"));
 | 
			
		||||
                OsUtils.SetEnvironmentVariableNoCaching("GDK_PIXBUF_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gdk-pixbuf-2.0", "2.10.0", "loaders.cache"));
 | 
			
		||||
 | 
			
		||||
                SetEnvironmentVariableNoCaching("GTK_IM_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gtk-3.0", "3.0.0", "immodules.cache"));
 | 
			
		||||
                OsUtils.SetEnvironmentVariableNoCaching("GTK_IM_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gtk-3.0", "3.0.0", "immodules.cache"));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            string systemPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine);
 | 
			
		||||
@@ -233,9 +224,9 @@ namespace Ryujinx
 | 
			
		||||
            // Logging system information.
 | 
			
		||||
            PrintSystemInfo();
 | 
			
		||||
 | 
			
		||||
            // Enable OGL multithreading on the driver, when available.
 | 
			
		||||
            // Enable OGL multithreading on the driver, and some other flags.
 | 
			
		||||
            BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading;
 | 
			
		||||
            DriverUtilities.ToggleOGLThreading(threadingMode == BackendThreading.Off);
 | 
			
		||||
            DriverUtilities.InitDriverConfig(threadingMode == BackendThreading.Off);
 | 
			
		||||
 | 
			
		||||
            // Initialize Gtk.
 | 
			
		||||
            Application.Init();
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ using Ryujinx.Common.Configuration.Hid;
 | 
			
		||||
using Ryujinx.Common.Configuration.Hid.Controller;
 | 
			
		||||
using Ryujinx.Common.Configuration.Hid.Controller.Motion;
 | 
			
		||||
using Ryujinx.Common.Configuration.Hid.Keyboard;
 | 
			
		||||
using Ryujinx.Common.GraphicsDriver;
 | 
			
		||||
using Ryujinx.Common.Logging;
 | 
			
		||||
using Ryujinx.Common.Logging.Targets;
 | 
			
		||||
using Ryujinx.Common.SystemInterop;
 | 
			
		||||
@@ -463,6 +464,8 @@ namespace Ryujinx.Headless.SDL2
 | 
			
		||||
            GraphicsConfig.ShadersDumpPath = option.GraphicsShadersDumpPath;
 | 
			
		||||
            GraphicsConfig.EnableMacroHLE = !option.DisableMacroHLE;
 | 
			
		||||
 | 
			
		||||
            DriverUtilities.InitDriverConfig(option.BackendThreading == BackendThreading.Off);
 | 
			
		||||
 | 
			
		||||
            while (true)
 | 
			
		||||
            {
 | 
			
		||||
                LoadApplication(option);
 | 
			
		||||
 
 | 
			
		||||
@@ -117,8 +117,8 @@ namespace Ryujinx.Ava
 | 
			
		||||
            // Logging system information.
 | 
			
		||||
            PrintSystemInfo();
 | 
			
		||||
 | 
			
		||||
            // Enable OGL multithreading on the driver, when available.
 | 
			
		||||
            DriverUtilities.ToggleOGLThreading(ConfigurationState.Instance.Graphics.BackendThreading == BackendThreading.Off);
 | 
			
		||||
            // Enable OGL multithreading on the driver, and some other flags.
 | 
			
		||||
            DriverUtilities.InitDriverConfig(ConfigurationState.Instance.Graphics.BackendThreading == BackendThreading.Off);
 | 
			
		||||
 | 
			
		||||
            // Check if keys exists.
 | 
			
		||||
            if (!File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys")))
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user