Traverse PhiNodes for Bindless Elimination (#2089)

This allows bindless handles to be found for image/texture instructions with predicates, when the assignment of the texture handle is within the same predicate.

This seems to cover the remaining bindless handles that compilers seem to be creating due to optimizations.

Will affect newer UE4 games, and games by NdCube (Super Mario Party, Clubhouse Games)
This commit is contained in:
riperiperi 2021-03-09 20:27:44 +00:00 committed by GitHub
parent 1623ab524f
commit ede26556f2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -5,6 +5,66 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
{
class BindlessElimination
{
private static Operation FindBranchSource(BasicBlock block)
{
foreach (BasicBlock sourceBlock in block.Predecessors)
{
if (sourceBlock.Operations.Count > 0)
{
Operation lastOp = sourceBlock.Operations.Last.Value as Operation;
if (lastOp != null &&
((sourceBlock.Next == block && lastOp.Inst == Instruction.BranchIfFalse) ||
(sourceBlock.Branch == block && lastOp.Inst == Instruction.BranchIfTrue)))
{
return lastOp;
}
}
}
return null;
}
private static bool BlockConditionsMatch(BasicBlock currentBlock, BasicBlock queryBlock)
{
// Check if all the conditions for the query block are satisfied by the current block.
// Just checks the top-most conditional for now.
Operation currentBranch = FindBranchSource(currentBlock);
Operation queryBranch = FindBranchSource(queryBlock);
Operand currentCondition = currentBranch?.GetSource(0);
Operand queryCondition = queryBranch?.GetSource(0);
// The condition should be the same operand instance.
return currentBranch != null && queryBranch != null &&
currentBranch.Inst == queryBranch.Inst &&
currentCondition == queryCondition;
}
private static Operand FindLastOperation(Operand source, BasicBlock block)
{
if (source.AsgOp is PhiNode phiNode)
{
// This source can have a different value depending on a previous branch.
// Ensure that conditions met for that branch are also met for the current one.
// Prefer the latest sources for the phi node.
for (int i = phiNode.SourcesCount - 1; i >= 0; i--)
{
BasicBlock phiBlock = phiNode.GetBlock(i);
if (BlockConditionsMatch(block, phiBlock))
{
return phiNode.GetSource(i);
}
}
}
return source;
}
public static void RunPass(BasicBlock block, ShaderConfig config)
{
// We can turn a bindless into regular access by recognizing the pattern
@ -29,7 +89,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
texOp.Inst == Instruction.TextureSample ||
texOp.Inst == Instruction.TextureSize)
{
Operand bindlessHandle = texOp.GetSource(0);
Operand bindlessHandle = FindLastOperation(texOp.GetSource(0), block);
if (bindlessHandle.Type == OperandType.ConstantBuffer)
{
@ -47,8 +107,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
continue;
}
Operand src0 = handleCombineOp.GetSource(0);
Operand src1 = handleCombineOp.GetSource(1);
Operand src0 = FindLastOperation(handleCombineOp.GetSource(0), block);
Operand src1 = FindLastOperation(handleCombineOp.GetSource(1), block);
if (src0.Type != OperandType.ConstantBuffer ||
src1.Type != OperandType.ConstantBuffer || src0.GetCbufSlot() != src1.GetCbufSlot())
@ -60,7 +120,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
}
else if (texOp.Inst == Instruction.ImageLoad || texOp.Inst == Instruction.ImageStore)
{
Operand src0 = texOp.GetSource(0);
Operand src0 = FindLastOperation(texOp.GetSource(0), block);
if (src0.Type == OperandType.ConstantBuffer)
{