mirror of
https://github.com/Ryujinx/Ryujinx.git
synced 2024-10-01 12:30:00 +02:00
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:
parent
a40c90e7dd
commit
fc26189fe1
3 changed files with 105 additions and 1 deletions
|
@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||||
private const ushort FileFormatVersionMajor = 1;
|
private const ushort FileFormatVersionMajor = 1;
|
||||||
private const ushort FileFormatVersionMinor = 2;
|
private const ushort FileFormatVersionMinor = 2;
|
||||||
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
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 SharedTocFileName = "shared.toc";
|
||||||
private const string SharedDataFileName = "shared.data";
|
private const string SharedDataFileName = "shared.data";
|
||||||
|
|
|
@ -20,6 +20,12 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||||
GlobalToStorage.RunPass(blocks[blkIndex], config, ref sbUseMask, ref ubeUseMask);
|
GlobalToStorage.RunPass(blocks[blkIndex], config, ref sbUseMask, ref ubeUseMask);
|
||||||
BindlessToIndexed.RunPass(blocks[blkIndex], config);
|
BindlessToIndexed.RunPass(blocks[blkIndex], config);
|
||||||
BindlessElimination.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);
|
config.SetAccessibleBufferMasks(sbUseMask, ubeUseMask);
|
||||||
|
@ -281,6 +287,75 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||||
return modified;
|
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)
|
private static void RemoveNode(BasicBlock block, LinkedListNode<INode> llNode)
|
||||||
{
|
{
|
||||||
// Remove a node from the nodes list, and also remove itself
|
// Remove a node from the nodes list, and also remove itself
|
||||||
|
|
|
@ -4,6 +4,35 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||||
{
|
{
|
||||||
static class Utils
|
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)
|
private static Operation FindBranchSource(BasicBlock block)
|
||||||
{
|
{
|
||||||
foreach (BasicBlock sourceBlock in block.Predecessors)
|
foreach (BasicBlock sourceBlock in block.Predecessors)
|
||||||
|
|
Loading…
Reference in a new issue