diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index d99a402b..d4ced7c8 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 hostShaders = new List(); 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 ca45aab5..795a26cc 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 funcs = new List(); Queue funcQueue = new Queue(); @@ -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 c7eb27e5..9f0f9010 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 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); + } } } }