Eliminate redundant multiplications by gl_FragCoord.w on the shader (#4578)

* Eliminate redundant multiplications by gl_FragCoord.w on the shader

* Shader cache version bump
This commit is contained in:
gdkchan 2023-05-19 11:52:31 -03:00 committed by GitHub
parent a40c90e7dd
commit fc26189fe1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 105 additions and 1 deletions

View file

@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 4892;
private const uint CodeGenVersion = 4578;
private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data";

View file

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

View file

@ -4,6 +4,35 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
{
static class Utils
{
public static bool IsInputLoad(INode node)
{
return (node is Operation operation) &&
operation.Inst == Instruction.Load &&
operation.StorageKind == StorageKind.Input;
}
public static bool IsInputLoad(INode node, IoVariable ioVariable, int elemIndex)
{
if (!(node is Operation operation) ||
operation.Inst != Instruction.Load ||
operation.StorageKind != StorageKind.Input ||
operation.SourcesCount != 2)
{
return false;
}
Operand ioVariableSrc = operation.GetSource(0);
if (ioVariableSrc.Type != OperandType.Constant || (IoVariable)ioVariableSrc.Value != ioVariable)
{
return false;
}
Operand elemIndexSrc = operation.GetSource(1);
return elemIndexSrc.Type == OperandType.Constant && elemIndexSrc.Value == elemIndex;
}
private static Operation FindBranchSource(BasicBlock block)
{
foreach (BasicBlock sourceBlock in block.Predecessors)