From 3b90adcd1da57db2fe84062aa1305230d692338e Mon Sep 17 00:00:00 2001 From: gdkchan Date: Wed, 2 Jun 2021 20:41:53 -0300 Subject: [PATCH] Fix shaders with mixed PBK and SSY addresses on the stack (#2329) * Fix shaders with mixed PBK and SSY addresses on the stack * Address PR feedback and nits --- Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs | 2 +- Ryujinx.Graphics.Shader/Decoders/Decoder.cs | 90 +++++++++++++------ .../Translation/Optimizations/Optimizer.cs | 1 - 3 files changed, 64 insertions(+), 29 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 622152458..f72b5e6cc 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -36,7 +36,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// Version of the codegen (to be changed when codegen or guest format change). /// - private const ulong ShaderCodeGenVersion = 2317; + private const ulong ShaderCodeGenVersion = 2329; // Progress reporting helpers private volatile int _shaderCount; diff --git a/Ryujinx.Graphics.Shader/Decoders/Decoder.cs b/Ryujinx.Graphics.Shader/Decoders/Decoder.cs index 795a26cc3..2d00f237b 100644 --- a/Ryujinx.Graphics.Shader/Decoders/Decoder.cs +++ b/Ryujinx.Graphics.Shader/Decoders/Decoder.cs @@ -278,7 +278,7 @@ 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) || + hasBindless |= (op is OpCodeImage image && image.IsBindless) || (op is OpCodeTxd txd && txd.IsBindless) || (op is OpCodeTld4B) || (emitter == InstEmit.TexB) || @@ -318,6 +318,12 @@ namespace Ryujinx.Graphics.Shader.Decoders opCode is OpCodeExit; } + private enum MergeType + { + Brk = 0, + Sync = 1 + } + private struct PathBlockState { public Block Block { get; } @@ -332,35 +338,39 @@ namespace Ryujinx.Graphics.Shader.Decoders private RestoreType _restoreType; private ulong _restoreValue; + private MergeType _restoreMergeType; public bool ReturningFromVisit => _restoreType != RestoreType.None; public PathBlockState(Block block) { - Block = block; - _restoreType = RestoreType.None; - _restoreValue = 0; + Block = block; + _restoreType = RestoreType.None; + _restoreValue = 0; + _restoreMergeType = default; } public PathBlockState(int oldStackSize) { - Block = null; - _restoreType = RestoreType.PopPushOp; - _restoreValue = (ulong)oldStackSize; + Block = null; + _restoreType = RestoreType.PopPushOp; + _restoreValue = (ulong)oldStackSize; + _restoreMergeType = default; } - public PathBlockState(ulong syncAddress) + public PathBlockState(ulong syncAddress, MergeType mergeType) { - Block = null; - _restoreType = RestoreType.PushBranchOp; - _restoreValue = syncAddress; + Block = null; + _restoreType = RestoreType.PushBranchOp; + _restoreValue = syncAddress; + _restoreMergeType = mergeType; } - public void RestoreStackState(Stack branchStack) + public void RestoreStackState(Stack<(ulong, MergeType)> branchStack) { if (_restoreType == RestoreType.PushBranchOp) { - branchStack.Push(_restoreValue); + branchStack.Push((_restoreValue, _restoreMergeType)); } else if (_restoreType == RestoreType.PopPushOp) { @@ -380,7 +390,7 @@ namespace Ryujinx.Graphics.Shader.Decoders HashSet visited = new HashSet(); - Stack branchStack = new Stack(); + Stack<(ulong, MergeType)> branchStack = new Stack<(ulong, MergeType)>(); void Push(PathBlockState pbs) { @@ -426,7 +436,9 @@ namespace Ryujinx.Graphics.Shader.Decoders for (int index = pushOpIndex; index < pushOpsCount; index++) { - branchStack.Push(current.PushOpCodes[index].GetAbsoluteAddress()); + OpCodePush currentPushOp = current.PushOpCodes[index]; + MergeType pushMergeType = currentPushOp.Emitter == InstEmit.Ssy ? MergeType.Sync : MergeType.Brk; + branchStack.Push((currentPushOp.GetAbsoluteAddress(), pushMergeType)); } } @@ -452,24 +464,48 @@ namespace Ryujinx.Graphics.Shader.Decoders } else if (current.GetLastOp() is OpCodeBranchPop op) { - ulong targetAddress = branchStack.Pop(); + MergeType popMergeType = op.Emitter == InstEmit.Sync ? MergeType.Sync : MergeType.Brk; - if (branchStack.Count == 0) + bool found = true; + ulong targetAddress = 0UL; + MergeType mergeType; + + do { - branchStack.Push(targetAddress); + if (branchStack.Count == 0) + { + found = false; + break; + } - op.Targets.Add(pushOp, op.Targets.Count); + (targetAddress, mergeType) = branchStack.Pop(); - pushOp.PopOps.TryAdd(op, Local()); + // Push the target address (this will be used to push the address + // back into the SSY/PBK stack when we return from that block), + Push(new PathBlockState(targetAddress, mergeType)); } - else + while (mergeType != popMergeType); + + // Make sure we found the correct address, + // the push and pop instruction types must match, so: + // - BRK can only consume addresses pushed by PBK. + // - SYNC can only consume addresses pushed by SSY. + if (found) { - // First we push the target address (this will be used to push the - // address back into the SSY/PBK stack when we return from that block), - // then we push the block itself into the work "queue" (well, it's a stack) - // for processing. - Push(new PathBlockState(targetAddress)); - Push(new PathBlockState(blocks[targetAddress])); + if (branchStack.Count == 0) + { + // If the entire stack was consumed, then the current pop instruction + // just consumed the address from out push instruction. + op.Targets.Add(pushOp, op.Targets.Count); + + pushOp.PopOps.TryAdd(op, Local()); + } + else + { + // Push the block itself into the work "queue" (well, it's a stack) + // for processing. + Push(new PathBlockState(blocks[targetAddress])); + } } } } diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs index d184e6b4b..51fe825f4 100644 --- a/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs +++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs @@ -1,5 +1,4 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; -using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq;