From a5d5ca06357e2fe1ee2cf880460109ce9da5fe4e Mon Sep 17 00:00:00 2001
From: mageven <62494521+mageven@users.noreply.github.com>
Date: Sat, 27 Mar 2021 05:20:26 +0530
Subject: [PATCH] Shader Cache: Move bindless checking from translation to
 decode (#2145)

---
 Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs    | 33 ++++++------
 Ryujinx.Graphics.Shader/Decoders/Decoder.cs   | 20 +++++--
 .../Translation/Translator.cs                 | 53 ++++++++-----------
 3 files changed, 57 insertions(+), 49 deletions(-)

diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
index d99a402b11..d4ced7c8be 100644
--- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
@@ -426,6 +426,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
             Hash128 programCodeHash = default;
             GuestShaderCacheEntry[] shaderCacheEntries = null;
 
+            // Current shader cache doesn't support bindless textures
+            if (shaderContexts[0].UsedFeatures.HasFlag(FeatureFlags.Bindless))
+            {
+                isShaderCacheEnabled = false;
+            }
+
             if (isShaderCacheEnabled)
             {
                 isShaderCacheReadOnly = _cacheManager.IsReadOnly;
@@ -448,8 +454,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
                 // The shader isn't currently cached, translate it and compile it.
                 ShaderCodeHolder shader = TranslateShader(shaderContexts[0]);
 
-                bool isDiskShaderCacheIncompatible = shaderContexts[0].UsedFeatures.HasFlag(FeatureFlags.Bindless);
-
                 shader.HostShader = _context.Renderer.CompileShader(ShaderStage.Compute, shader.Program.Code);
 
                 IProgram hostProgram = _context.Renderer.CreateProgram(new IShader[] { shader.HostShader }, null);
@@ -458,7 +462,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
 
                 cpShader = new ShaderBundle(hostProgram, shader);
 
-                if (isShaderCacheEnabled && !isDiskShaderCacheIncompatible)
+                if (isShaderCacheEnabled)
                 {
                     _cpProgramsDiskCache.Add(programCodeHash, cpShader);
 
@@ -536,6 +540,16 @@ namespace Ryujinx.Graphics.Gpu.Shader
             Hash128 programCodeHash = default;
             GuestShaderCacheEntry[] shaderCacheEntries = null;
 
+            // Current shader cache doesn't support bindless textures
+            for (int i = 0; i < shaderContexts.Length; i++)
+            {
+                if (shaderContexts[i] != null && shaderContexts[i].UsedFeatures.HasFlag(FeatureFlags.Bindless))
+                {
+                    isShaderCacheEnabled = false;
+                    break;
+                }
+            }
+
             if (isShaderCacheEnabled)
             {
                 isShaderCacheReadOnly = _cacheManager.IsReadOnly;
@@ -564,17 +578,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
                 shaders[3] = TranslateShader(shaderContexts[4]);
                 shaders[4] = TranslateShader(shaderContexts[5]);
 
-                bool isDiskShaderCacheIncompatible = false;
-
-                for (int i = 0; i < shaderContexts.Length; i++)
-                {
-                    if (shaderContexts[i] != null && shaderContexts[i].UsedFeatures.HasFlag(FeatureFlags.Bindless))
-                    {
-                        isDiskShaderCacheIncompatible = true;
-                        break;
-                    }
-                }
-
                 List<IShader> hostShaders = new List<IShader>();
 
                 for (int stage = 0; stage < Constants.ShaderStages; stage++)
@@ -599,7 +602,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
 
                 gpShaders = new ShaderBundle(hostProgram, shaders);
 
-                if (isShaderCacheEnabled && !isDiskShaderCacheIncompatible)
+                if (isShaderCacheEnabled)
                 {
                     _gpProgramsDiskCache.Add(programCodeHash, gpShaders);
 
diff --git a/Ryujinx.Graphics.Shader/Decoders/Decoder.cs b/Ryujinx.Graphics.Shader/Decoders/Decoder.cs
index ca45aab52b..795a26cc37 100644
--- a/Ryujinx.Graphics.Shader/Decoders/Decoder.cs
+++ b/Ryujinx.Graphics.Shader/Decoders/Decoder.cs
@@ -9,8 +9,10 @@ namespace Ryujinx.Graphics.Shader.Decoders
 {
     static class Decoder
     {
-        public static Block[][] Decode(IGpuAccessor gpuAccessor, ulong startAddress)
+        public static Block[][] Decode(IGpuAccessor gpuAccessor, ulong startAddress, out bool hasBindless)
         {
+            hasBindless = false;
+
             List<Block[]> funcs = new List<Block[]>();
 
             Queue<ulong> funcQueue = new Queue<ulong>();
@@ -84,7 +86,8 @@ namespace Ryujinx.Graphics.Shader.Decoders
                         }
                     }
 
-                    FillBlock(gpuAccessor, currBlock, limitAddress, startAddress);
+                    FillBlock(gpuAccessor, currBlock, limitAddress, startAddress, out bool blockHasBindless);
+                    hasBindless |= blockHasBindless;
 
                     if (currBlock.OpCodes.Count != 0)
                     {
@@ -229,9 +232,11 @@ namespace Ryujinx.Graphics.Shader.Decoders
             IGpuAccessor gpuAccessor,
             Block        block,
             ulong        limitAddress,
-            ulong        startAddress)
+            ulong        startAddress,
+            out bool     hasBindless)
         {
             ulong address = block.Address;
+            hasBindless = false;
 
             do
             {
@@ -272,6 +277,15 @@ namespace Ryujinx.Graphics.Shader.Decoders
 
                 OpCode op = makeOp(emitter, opAddress, opCode);
 
+                // We check these patterns to figure out the presence of bindless access
+                hasBindless |= (op is OpCodeImage image && image.IsBindless) || 
+                    (op is OpCodeTxd txd && txd.IsBindless) ||
+                    (op is OpCodeTld4B) ||
+                    (emitter == InstEmit.TexB) ||
+                    (emitter == InstEmit.TldB) ||
+                    (emitter == InstEmit.TmmlB) ||
+                    (emitter == InstEmit.TxqB);
+
                 block.OpCodes.Add(op);
             }
             while (!IsControlFlowChange(block.GetLastOp()));
diff --git a/Ryujinx.Graphics.Shader/Translation/Translator.cs b/Ryujinx.Graphics.Shader/Translation/Translator.cs
index c7eb27e5d4..9f0f9010d4 100644
--- a/Ryujinx.Graphics.Shader/Translation/Translator.cs
+++ b/Ryujinx.Graphics.Shader/Translation/Translator.cs
@@ -36,22 +36,6 @@ namespace Ryujinx.Graphics.Shader.Translation
             return new TranslatorContext(address, cfg, config);
         }
 
-        private static void ScanForBindless(BasicBlock[] blocks, ShaderConfig config)
-        {
-            for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
-            {
-                // Right now the guest shader cache cannot handle bindless textures correctly.
-                for (LinkedListNode<INode> node = blocks[blkIndex].Operations.First; node != null; node = node.Next)
-                {
-                    if (node.Value is TextureOperation texOp && (texOp.Flags & TextureFlags.Bindless) != 0)
-                    {
-                        config.SetUsedFeature(FeatureFlags.Bindless);
-                        break;
-                    }
-                }
-            }
-        }
-
         internal static ShaderProgram Translate(FunctionCode[] functions, ShaderConfig config, out ShaderProgramInfo shaderProgramInfo)
         {
             var cfgs = new ControlFlowGraph[functions.Length];
@@ -91,8 +75,6 @@ namespace Ryujinx.Graphics.Shader.Translation
                     Dominance.FindDominators(cfg);
                     Dominance.FindDominanceFrontiers(cfg.Blocks);
 
-                    ScanForBindless(cfg.Blocks, config);
-
                     Ssa.Rename(cfg.Blocks);
 
                     Optimizer.RunPass(cfg.Blocks, config);
@@ -129,35 +111,44 @@ namespace Ryujinx.Graphics.Shader.Translation
             Block[][] cfg;
             ulong maxEndAddress = 0;
 
+            bool hasBindless = false;
+
             if ((flags & TranslationFlags.Compute) != 0)
             {
                 config = new ShaderConfig(gpuAccessor, flags, counts);
 
-                cfg = Decoder.Decode(gpuAccessor, address);
+                cfg = Decoder.Decode(gpuAccessor, address, out hasBindless);
             }
             else
             {
                 config = new ShaderConfig(new ShaderHeader(gpuAccessor, address), gpuAccessor, flags, counts);
 
-                cfg = Decoder.Decode(gpuAccessor, address + HeaderSize);
+                cfg = Decoder.Decode(gpuAccessor, address + HeaderSize, out hasBindless);
             }
 
-            for (int funcIndex = 0; funcIndex < cfg.Length; funcIndex++)
+            if (hasBindless)
             {
-                for (int blkIndex = 0; blkIndex < cfg[funcIndex].Length; blkIndex++)
+                config.SetUsedFeature(FeatureFlags.Bindless);
+            }
+            else // Not bindless, fill up texture handles
+            {
+                for (int funcIndex = 0; funcIndex < cfg.Length; funcIndex++)
                 {
-                    Block block = cfg[funcIndex][blkIndex];
-
-                    if (maxEndAddress < block.EndAddress)
+                    for (int blkIndex = 0; blkIndex < cfg[funcIndex].Length; blkIndex++)
                     {
-                        maxEndAddress = block.EndAddress;
-                    }
+                        Block block = cfg[funcIndex][blkIndex];
 
-                    for (int index = 0; index < block.OpCodes.Count; index++)
-                    {
-                        if (block.OpCodes[index] is OpCodeTextureBase texture)
+                        if (maxEndAddress < block.EndAddress)
                         {
-                            config.TextureHandlesForCache.Add(texture.HandleOffset);
+                            maxEndAddress = block.EndAddress;
+                        }
+
+                        for (int index = 0; index < block.OpCodes.Count; index++)
+                        {
+                            if (block.OpCodes[index] is OpCodeTextureBase texture)
+                            {
+                                config.TextureHandlesForCache.Add(texture.HandleOffset);
+                            }
                         }
                     }
                 }