From f1814ca542867f1f10f6f508ba99c3769b64692d Mon Sep 17 00:00:00 2001
From: Gabriel A <gab.dark.100@gmail.com>
Date: Sun, 20 Aug 2023 15:18:52 -0300
Subject: [PATCH] Add spin lock to prevent waiting for fences on multiple
 threads at once on Adreno

---
 .../BackgroundResources.cs                    |  2 +-
 .../CommandBufferPool.cs                      |  6 +-
 src/Ryujinx.Graphics.Vulkan/FenceHolder.cs    | 66 ++++++++++++++++---
 .../MultiFenceHolder.cs                       |  2 +-
 src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs |  6 +-
 5 files changed, 66 insertions(+), 16 deletions(-)

diff --git a/src/Ryujinx.Graphics.Vulkan/BackgroundResources.cs b/src/Ryujinx.Graphics.Vulkan/BackgroundResources.cs
index 24e600a26..cb8a62cf4 100644
--- a/src/Ryujinx.Graphics.Vulkan/BackgroundResources.cs
+++ b/src/Ryujinx.Graphics.Vulkan/BackgroundResources.cs
@@ -29,7 +29,7 @@ namespace Ryujinx.Graphics.Vulkan
 
                 lock (queueLock)
                 {
-                    _pool = new CommandBufferPool(_gd.Api, _device, queue, queueLock, _gd.QueueFamilyIndex, isLight: true);
+                    _pool = new CommandBufferPool(_gd.Api, _device, queue, queueLock, _gd.QueueFamilyIndex, _gd.Vendor == Vendor.Qualcomm, isLight: true);
                 }
             }
 
diff --git a/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs b/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs
index 61cfbb6ec..98da59861 100644
--- a/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs
+++ b/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs
@@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.Vulkan
         private readonly Device _device;
         private readonly Queue _queue;
         private readonly object _queueLock;
+        private readonly bool _fenceNeedsLock;
         private readonly CommandPool _pool;
         private readonly Thread _owner;
 
@@ -61,12 +62,13 @@ namespace Ryujinx.Graphics.Vulkan
         private int _queuedCount;
         private int _inUseCount;
 
-        public unsafe CommandBufferPool(Vk api, Device device, Queue queue, object queueLock, uint queueFamilyIndex, bool isLight = false)
+        public unsafe CommandBufferPool(Vk api, Device device, Queue queue, object queueLock, uint queueFamilyIndex, bool fenceNeedsLock, bool isLight = false)
         {
             _api = api;
             _device = device;
             _queue = queue;
             _queueLock = queueLock;
+            _fenceNeedsLock = fenceNeedsLock;
             _owner = Thread.CurrentThread;
 
             var commandPoolCreateInfo = new CommandPoolCreateInfo
@@ -357,7 +359,7 @@ namespace Ryujinx.Graphics.Vulkan
 
             if (refreshFence)
             {
-                entry.Fence = new FenceHolder(_api, _device);
+                entry.Fence = new FenceHolder(_api, _device, _fenceNeedsLock);
             }
             else
             {
diff --git a/src/Ryujinx.Graphics.Vulkan/FenceHolder.cs b/src/Ryujinx.Graphics.Vulkan/FenceHolder.cs
index 4f0a87160..eb4c3b0ef 100644
--- a/src/Ryujinx.Graphics.Vulkan/FenceHolder.cs
+++ b/src/Ryujinx.Graphics.Vulkan/FenceHolder.cs
@@ -10,12 +10,15 @@ namespace Ryujinx.Graphics.Vulkan
         private readonly Device _device;
         private Fence _fence;
         private int _referenceCount;
+        private int _lock;
+        private readonly bool _needsLock;
         private bool _disposed;
 
-        public unsafe FenceHolder(Vk api, Device device)
+        public unsafe FenceHolder(Vk api, Device device, bool needsLock)
         {
             _api = api;
             _device = device;
+            _needsLock = needsLock;
 
             var fenceCreateInfo = new FenceCreateInfo
             {
@@ -47,6 +50,11 @@ namespace Ryujinx.Graphics.Vulkan
             }
             while (Interlocked.CompareExchange(ref _referenceCount, lastValue + 1, lastValue) != lastValue);
 
+            if (_needsLock)
+            {
+                AcquireLock();
+            }
+
             fence = _fence;
             return true;
         }
@@ -57,6 +65,16 @@ namespace Ryujinx.Graphics.Vulkan
             return _fence;
         }
 
+        public void PutLock()
+        {
+            Put();
+
+            if (_needsLock)
+            {
+                ReleaseLock();
+            }
+        }
+
         public void Put()
         {
             if (Interlocked.Decrement(ref _referenceCount) == 0)
@@ -66,24 +84,54 @@ namespace Ryujinx.Graphics.Vulkan
             }
         }
 
+        private void AcquireLock()
+        {
+            while (!TryAcquireLock())
+            {
+                Thread.SpinWait(32);
+            }
+        }
+
+        private bool TryAcquireLock()
+        {
+            return Interlocked.Exchange(ref _lock, 1) == 0;
+        }
+
+        private void ReleaseLock()
+        {
+            Interlocked.Exchange(ref _lock, 0);
+        }
+
         public void Wait()
         {
-            Span<Fence> fences = stackalloc Fence[]
+            if (_needsLock)
             {
-                _fence,
-            };
+                AcquireLock();
+            }
 
-            FenceHelper.WaitAllIndefinitely(_api, _device, fences);
+            FenceHelper.WaitAllIndefinitely(_api, _device, stackalloc Fence[] { _fence });
+
+            if (_needsLock)
+            {
+                ReleaseLock();
+            }
         }
 
         public bool IsSignaled()
         {
-            Span<Fence> fences = stackalloc Fence[]
+            if (_needsLock && !TryAcquireLock())
             {
-                _fence,
-            };
+                return false;
+            }
 
-            return FenceHelper.AllSignaled(_api, _device, fences);
+            bool result = FenceHelper.AllSignaled(_api, _device, stackalloc Fence[] { _fence });
+
+            if (_needsLock)
+            {
+                ReleaseLock();
+            }
+
+            return result;
         }
 
         public void Dispose()
diff --git a/src/Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs b/src/Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs
index 0bce3b72d..5638a9baa 100644
--- a/src/Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs
+++ b/src/Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs
@@ -205,7 +205,7 @@ namespace Ryujinx.Graphics.Vulkan
 
             for (int i = 0; i < fenceCount; i++)
             {
-                fenceHolders[i].Put();
+                fenceHolders[i].PutLock();
             }
 
             return signaled;
diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
index 3b9b30340..1db104f83 100644
--- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
+++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
@@ -291,6 +291,8 @@ namespace Ryujinx.Graphics.Vulkan
 
             ref var properties = ref properties2.Properties;
 
+            Vendor = VendorUtils.FromId(properties.VendorID);
+
             ulong minResourceAlignment = Math.Max(
                 Math.Max(
                     properties.Limits.MinStorageBufferOffsetAlignment,
@@ -347,7 +349,7 @@ namespace Ryujinx.Graphics.Vulkan
             Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtExternalMemoryHost hostMemoryApi);
             HostMemoryAllocator = new HostMemoryAllocator(MemoryAllocator, Api, hostMemoryApi, _device);
 
-            CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex);
+            CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex, Vendor == Vendor.Qualcomm);
 
             DescriptorSetManager = new DescriptorSetManager(_device, PipelineBase.DescriptorSetLayouts);
 
@@ -697,8 +699,6 @@ namespace Ryujinx.Graphics.Vulkan
 
             string vendorName = VendorUtils.GetNameFromId(properties.VendorID);
 
-            Vendor = VendorUtils.FromId(properties.VendorID);
-
             IsAmdWindows = Vendor == Vendor.Amd && OperatingSystem.IsWindows();
             IsIntelWindows = Vendor == Vendor.Intel && OperatingSystem.IsWindows();
             IsTBDR =