|
|
@@ -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
|