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
This commit is contained in:
gdkchan 2021-06-02 20:41:53 -03:00 committed by GitHub
parent b84ba43406
commit 3b90adcd1d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 64 additions and 29 deletions

View file

@ -36,7 +36,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <summary> /// <summary>
/// Version of the codegen (to be changed when codegen or guest format change). /// Version of the codegen (to be changed when codegen or guest format change).
/// </summary> /// </summary>
private const ulong ShaderCodeGenVersion = 2317; private const ulong ShaderCodeGenVersion = 2329;
// Progress reporting helpers // Progress reporting helpers
private volatile int _shaderCount; private volatile int _shaderCount;

View file

@ -318,6 +318,12 @@ namespace Ryujinx.Graphics.Shader.Decoders
opCode is OpCodeExit; opCode is OpCodeExit;
} }
private enum MergeType
{
Brk = 0,
Sync = 1
}
private struct PathBlockState private struct PathBlockState
{ {
public Block Block { get; } public Block Block { get; }
@ -332,35 +338,39 @@ namespace Ryujinx.Graphics.Shader.Decoders
private RestoreType _restoreType; private RestoreType _restoreType;
private ulong _restoreValue; private ulong _restoreValue;
private MergeType _restoreMergeType;
public bool ReturningFromVisit => _restoreType != RestoreType.None; public bool ReturningFromVisit => _restoreType != RestoreType.None;
public PathBlockState(Block block) public PathBlockState(Block block)
{ {
Block = block; Block = block;
_restoreType = RestoreType.None; _restoreType = RestoreType.None;
_restoreValue = 0; _restoreValue = 0;
_restoreMergeType = default;
} }
public PathBlockState(int oldStackSize) public PathBlockState(int oldStackSize)
{ {
Block = null; Block = null;
_restoreType = RestoreType.PopPushOp; _restoreType = RestoreType.PopPushOp;
_restoreValue = (ulong)oldStackSize; _restoreValue = (ulong)oldStackSize;
_restoreMergeType = default;
} }
public PathBlockState(ulong syncAddress) public PathBlockState(ulong syncAddress, MergeType mergeType)
{ {
Block = null; Block = null;
_restoreType = RestoreType.PushBranchOp; _restoreType = RestoreType.PushBranchOp;
_restoreValue = syncAddress; _restoreValue = syncAddress;
_restoreMergeType = mergeType;
} }
public void RestoreStackState(Stack<ulong> branchStack) public void RestoreStackState(Stack<(ulong, MergeType)> branchStack)
{ {
if (_restoreType == RestoreType.PushBranchOp) if (_restoreType == RestoreType.PushBranchOp)
{ {
branchStack.Push(_restoreValue); branchStack.Push((_restoreValue, _restoreMergeType));
} }
else if (_restoreType == RestoreType.PopPushOp) else if (_restoreType == RestoreType.PopPushOp)
{ {
@ -380,7 +390,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
HashSet<Block> visited = new HashSet<Block>(); HashSet<Block> visited = new HashSet<Block>();
Stack<ulong> branchStack = new Stack<ulong>(); Stack<(ulong, MergeType)> branchStack = new Stack<(ulong, MergeType)>();
void Push(PathBlockState pbs) void Push(PathBlockState pbs)
{ {
@ -426,7 +436,9 @@ namespace Ryujinx.Graphics.Shader.Decoders
for (int index = pushOpIndex; index < pushOpsCount; index++) 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) 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 if (branchStack.Count == 0)
// 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) // If the entire stack was consumed, then the current pop instruction
// for processing. // just consumed the address from out push instruction.
Push(new PathBlockState(targetAddress)); op.Targets.Add(pushOp, op.Targets.Count);
Push(new PathBlockState(blocks[targetAddress]));
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]));
}
} }
} }
} }

View file

@ -1,5 +1,4 @@
using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;