From c41fddd25e8ddd3cf0b3cefeaf6595d2e4ede0fa Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Sun, 26 May 2024 15:20:10 -0300
Subject: [PATCH] Vulkan: Extend full bindless to cover cases with phi nodes
 (#6853)

* Key textures using set and binding (rather than just binding)

* Extend full bindless to cover cases with phi nodes

* Log error on bindless access failure

* Shader cache version bump

* Remove constant buffer match to reduce the chances of full bindless triggering

* Re-enable it for constant buffers, paper mario does actually need it

* Format whitespace
---
 .../Shader/DiskCache/DiskCacheHostStorage.cs  |  2 +-
 .../Optimizations/BindlessElimination.cs      | 55 +++++++++++++++----
 2 files changed, 46 insertions(+), 11 deletions(-)

diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs
index 990c6ba3..fbf48f01 100644
--- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs
+++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs
@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
         private const ushort FileFormatVersionMajor = 1;
         private const ushort FileFormatVersionMinor = 2;
         private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
-        private const uint CodeGenVersion = 6870;
+        private const uint CodeGenVersion = 6852;
 
         private const string SharedTocFileName = "shared.toc";
         private const string SharedDataFileName = "shared.data";
diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs
index 29501b71..02a83fbe 100644
--- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs
+++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs
@@ -38,6 +38,12 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
                     // If we can't do bindless elimination, remove the texture operation.
                     // Set any destination variables to zero.
 
+                    string typeName = texOp.Inst.IsImage()
+                        ? texOp.Type.ToGlslImageType(texOp.Format.GetComponentType())
+                        : texOp.Type.ToGlslTextureType();
+
+                    gpuAccessor.Log($"Failed to find handle source for bindless access of type \"{typeName}\".");
+
                     for (int destIndex = 0; destIndex < texOp.DestsCount; destIndex++)
                     {
                         block.Operations.AddBefore(node, new Operation(Instruction.Copy, texOp.GetDest(destIndex), OperandHelper.Const(0)));
@@ -62,17 +68,22 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
                 return false;
             }
 
-            Operand nvHandle = texOp.GetSource(0);
+            Operand bindlessHandle = texOp.GetSource(0);
 
-            if (nvHandle.AsgOp is not Operation handleOp ||
-                handleOp.Inst != Instruction.Load ||
-                (handleOp.StorageKind != StorageKind.Input && handleOp.StorageKind != StorageKind.StorageBuffer))
+            if (bindlessHandle.AsgOp is PhiNode phi)
             {
-                // Right now, we only allow bindless access when the handle comes from a shader input or storage buffer.
-                // This is an artificial limitation to prevent it from being used in cases where it
-                // would have a large performance impact of loading all textures in the pool.
-                // It might be removed in the future, if we can mitigate the performance impact.
+                for (int srcIndex = 0; srcIndex < phi.SourcesCount; srcIndex++)
+                {
+                    Operand phiSource = phi.GetSource(srcIndex);
 
+                    if (phiSource.AsgOp is not PhiNode && !IsBindlessAccessAllowed(phiSource))
+                    {
+                        return false;
+                    }
+                }
+            }
+            else if (!IsBindlessAccessAllowed(bindlessHandle))
+            {
                 return false;
             }
 
@@ -80,8 +91,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
             Operand samplerHandle = OperandHelper.Local();
             Operand textureIndex = OperandHelper.Local();
 
-            block.Operations.AddBefore(node, new Operation(Instruction.BitwiseAnd, textureHandle, nvHandle, OperandHelper.Const(0xfffff)));
-            block.Operations.AddBefore(node, new Operation(Instruction.ShiftRightU32, samplerHandle, nvHandle, OperandHelper.Const(20)));
+            block.Operations.AddBefore(node, new Operation(Instruction.BitwiseAnd, textureHandle, bindlessHandle, OperandHelper.Const(0xfffff)));
+            block.Operations.AddBefore(node, new Operation(Instruction.ShiftRightU32, samplerHandle, bindlessHandle, OperandHelper.Const(20)));
 
             int texturePoolLength = Math.Max(BindlessToArray.MinimumArrayLength, gpuAccessor.QueryTextureArrayLengthFromPool());
 
@@ -130,6 +141,30 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
             return true;
         }
 
+        private static bool IsBindlessAccessAllowed(Operand nvHandle)
+        {
+            if (nvHandle.Type == OperandType.ConstantBuffer)
+            {
+                // Bindless access with handles from constant buffer is allowed.
+
+                return true;
+            }
+
+            if (nvHandle.AsgOp is not Operation handleOp ||
+                handleOp.Inst != Instruction.Load ||
+                (handleOp.StorageKind != StorageKind.Input && handleOp.StorageKind != StorageKind.StorageBuffer))
+            {
+                // Right now, we only allow bindless access when the handle comes from a shader input or storage buffer.
+                // This is an artificial limitation to prevent it from being used in cases where it
+                // would have a large performance impact of loading all textures in the pool.
+                // It might be removed in the future, if we can mitigate the performance impact.
+
+                return false;
+            }
+
+            return true;
+        }
+
         private static bool TryConvertBindless(BasicBlock block, ResourceManager resourceManager, IGpuAccessor gpuAccessor, TextureOperation texOp)
         {
             if (texOp.Inst == Instruction.TextureSample || texOp.Inst.IsTextureQuery())