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:
parent
b84ba43406
commit
3b90adcd1d
3 changed files with 64 additions and 29 deletions
|
@ -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;
|
||||||
|
|
|
@ -278,7 +278,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
||||||
OpCode op = makeOp(emitter, opAddress, opCode);
|
OpCode op = makeOp(emitter, opAddress, opCode);
|
||||||
|
|
||||||
// We check these patterns to figure out the presence of bindless access
|
// 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 OpCodeTxd txd && txd.IsBindless) ||
|
||||||
(op is OpCodeTld4B) ||
|
(op is OpCodeTld4B) ||
|
||||||
(emitter == InstEmit.TexB) ||
|
(emitter == InstEmit.TexB) ||
|
||||||
|
@ -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]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Reference in a new issue