From ede26556f2b521f223d39d08c7cba2453628462d Mon Sep 17 00:00:00 2001 From: riperiperi Date: Tue, 9 Mar 2021 20:27:44 +0000 Subject: [PATCH] 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) --- .../Optimizations/BindlessElimination.cs | 68 +++++++++++++++++-- 1 file changed, 64 insertions(+), 4 deletions(-) diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs index b326aaa43..7352b18b9 100644 --- a/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs +++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs @@ -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) {