diff options
Diffstat (limited to 'src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs')
-rw-r--r-- | src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs | 75 |
1 files changed, 75 insertions, 0 deletions
diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs index 16848bdc..b41e47e4 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs @@ -20,6 +20,12 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations GlobalToStorage.RunPass(blocks[blkIndex], config, ref sbUseMask, ref ubeUseMask); BindlessToIndexed.RunPass(blocks[blkIndex], config); BindlessElimination.RunPass(blocks[blkIndex], config); + + // FragmentCoord only exists on fragment shaders, so we don't need to check other stages. + if (config.Stage == ShaderStage.Fragment) + { + EliminateMultiplyByFragmentCoordW(blocks[blkIndex]); + } } config.SetAccessibleBufferMasks(sbUseMask, ubeUseMask); @@ -281,6 +287,75 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations return modified; } + private static void EliminateMultiplyByFragmentCoordW(BasicBlock block) + { + foreach (INode node in block.Operations) + { + if (node is Operation operation) + { + EliminateMultiplyByFragmentCoordW(operation); + } + } + } + + private static void EliminateMultiplyByFragmentCoordW(Operation operation) + { + // We're looking for the pattern: + // y = x * gl_FragCoord.w + // v = y * (1.0 / gl_FragCoord.w) + // Then we transform it into: + // v = x + // This pattern is common on fragment shaders due to the way how perspective correction is done. + + // We are expecting a multiplication by the reciprocal of gl_FragCoord.w. + if (operation.Inst != (Instruction.FP32 | Instruction.Multiply)) + { + return; + } + + Operand lhs = operation.GetSource(0); + Operand rhs = operation.GetSource(1); + + // Check LHS of the the main multiplication operation. We expect an input being multiplied by gl_FragCoord.w. + if (!(lhs.AsgOp is Operation attrMulOp) || attrMulOp.Inst != (Instruction.FP32 | Instruction.Multiply)) + { + return; + } + + Operand attrMulLhs = attrMulOp.GetSource(0); + Operand attrMulRhs = attrMulOp.GetSource(1); + + // LHS should be any input, RHS should be exactly gl_FragCoord.w. + if (!Utils.IsInputLoad(attrMulLhs.AsgOp) || !Utils.IsInputLoad(attrMulRhs.AsgOp, IoVariable.FragmentCoord, 3)) + { + return; + } + + // RHS of the main multiplication should be a reciprocal operation (1.0 / x). + if (!(rhs.AsgOp is Operation reciprocalOp) || reciprocalOp.Inst != (Instruction.FP32 | Instruction.Divide)) + { + return; + } + + Operand reciprocalLhs = reciprocalOp.GetSource(0); + Operand reciprocalRhs = reciprocalOp.GetSource(1); + + // Check if the divisor is a constant equal to 1.0. + if (reciprocalLhs.Type != OperandType.Constant || reciprocalLhs.AsFloat() != 1.0f) + { + return; + } + + // Check if the dividend is gl_FragCoord.w. + if (!Utils.IsInputLoad(reciprocalRhs.AsgOp, IoVariable.FragmentCoord, 3)) + { + return; + } + + // If everything matches, we can replace the operation with the input load result. + operation.TurnIntoCopy(attrMulLhs); + } + private static void RemoveNode(BasicBlock block, LinkedListNode<INode> llNode) { // Remove a node from the nodes list, and also remove itself |