Initial work
This commit is contained in:
parent
f617fb542a
commit
1876b346fe
518 changed files with 15170 additions and 12486 deletions
46
Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs
Normal file
46
Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs
Normal file
|
@ -0,0 +1,46 @@
|
|||
namespace Ryujinx.Graphics.Shader.Translation
|
||||
{
|
||||
static class AttributeConsts
|
||||
{
|
||||
public const int Layer = 0x064;
|
||||
public const int PointSize = 0x06c;
|
||||
public const int PositionX = 0x070;
|
||||
public const int PositionY = 0x074;
|
||||
public const int PositionZ = 0x078;
|
||||
public const int PositionW = 0x07c;
|
||||
public const int ClipDistance0 = 0x2c0;
|
||||
public const int ClipDistance1 = 0x2c4;
|
||||
public const int ClipDistance2 = 0x2c8;
|
||||
public const int ClipDistance3 = 0x2cc;
|
||||
public const int ClipDistance4 = 0x2d0;
|
||||
public const int ClipDistance5 = 0x2d4;
|
||||
public const int ClipDistance6 = 0x2d8;
|
||||
public const int ClipDistance7 = 0x2dc;
|
||||
public const int PointCoordX = 0x2e0;
|
||||
public const int PointCoordY = 0x2e4;
|
||||
public const int TessCoordX = 0x2f0;
|
||||
public const int TessCoordY = 0x2f4;
|
||||
public const int InstanceId = 0x2f8;
|
||||
public const int VertexId = 0x2fc;
|
||||
public const int FrontFacing = 0x3fc;
|
||||
|
||||
public const int UserAttributesCount = 32;
|
||||
public const int UserAttributeBase = 0x80;
|
||||
public const int UserAttributeEnd = UserAttributeBase + UserAttributesCount * 16;
|
||||
|
||||
|
||||
// Note: Those attributes are used internally by the translator
|
||||
// only, they don't exist on Maxwell.
|
||||
public const int FragmentOutputDepth = 0x1000000;
|
||||
public const int FragmentOutputColorBase = 0x1000010;
|
||||
public const int FragmentOutputColorEnd = FragmentOutputColorBase + 8 * 16;
|
||||
|
||||
public const int ThreadIdX = 0x2000000;
|
||||
public const int ThreadIdY = 0x2000004;
|
||||
public const int ThreadIdZ = 0x2000008;
|
||||
|
||||
public const int CtaIdX = 0x2000010;
|
||||
public const int CtaIdY = 0x2000014;
|
||||
public const int CtaIdZ = 0x2000018;
|
||||
}
|
||||
}
|
108
Ryujinx.Graphics.Shader/Translation/ControlFlowGraph.cs
Normal file
108
Ryujinx.Graphics.Shader/Translation/ControlFlowGraph.cs
Normal file
|
@ -0,0 +1,108 @@
|
|||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Translation
|
||||
{
|
||||
static class ControlFlowGraph
|
||||
{
|
||||
public static BasicBlock[] MakeCfg(Operation[] operations)
|
||||
{
|
||||
Dictionary<Operand, BasicBlock> labels = new Dictionary<Operand, BasicBlock>();
|
||||
|
||||
List<BasicBlock> blocks = new List<BasicBlock>();
|
||||
|
||||
BasicBlock currentBlock = null;
|
||||
|
||||
void NextBlock(BasicBlock nextBlock)
|
||||
{
|
||||
if (currentBlock != null && !EndsWithUnconditionalInst(currentBlock.GetLastOp()))
|
||||
{
|
||||
currentBlock.Next = nextBlock;
|
||||
}
|
||||
|
||||
currentBlock = nextBlock;
|
||||
}
|
||||
|
||||
void NewNextBlock()
|
||||
{
|
||||
BasicBlock block = new BasicBlock(blocks.Count);
|
||||
|
||||
blocks.Add(block);
|
||||
|
||||
NextBlock(block);
|
||||
}
|
||||
|
||||
bool needsNewBlock = true;
|
||||
|
||||
for (int index = 0; index < operations.Length; index++)
|
||||
{
|
||||
Operation operation = operations[index];
|
||||
|
||||
if (operation.Inst == Instruction.MarkLabel)
|
||||
{
|
||||
Operand label = operation.Dest;
|
||||
|
||||
if (labels.TryGetValue(label, out BasicBlock nextBlock))
|
||||
{
|
||||
nextBlock.Index = blocks.Count;
|
||||
|
||||
blocks.Add(nextBlock);
|
||||
|
||||
NextBlock(nextBlock);
|
||||
}
|
||||
else
|
||||
{
|
||||
NewNextBlock();
|
||||
|
||||
labels.Add(label, currentBlock);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (needsNewBlock)
|
||||
{
|
||||
NewNextBlock();
|
||||
}
|
||||
|
||||
currentBlock.Operations.AddLast(operation);
|
||||
}
|
||||
|
||||
needsNewBlock = operation.Inst == Instruction.Branch ||
|
||||
operation.Inst == Instruction.BranchIfTrue ||
|
||||
operation.Inst == Instruction.BranchIfFalse;
|
||||
|
||||
if (needsNewBlock)
|
||||
{
|
||||
Operand label = operation.Dest;
|
||||
|
||||
if (!labels.TryGetValue(label, out BasicBlock branchBlock))
|
||||
{
|
||||
branchBlock = new BasicBlock();
|
||||
|
||||
labels.Add(label, branchBlock);
|
||||
}
|
||||
|
||||
currentBlock.Branch = branchBlock;
|
||||
}
|
||||
}
|
||||
|
||||
return blocks.ToArray();
|
||||
}
|
||||
|
||||
private static bool EndsWithUnconditionalInst(INode node)
|
||||
{
|
||||
if (node is Operation operation)
|
||||
{
|
||||
switch (operation.Inst)
|
||||
{
|
||||
case Instruction.Branch:
|
||||
case Instruction.Discard:
|
||||
case Instruction.Return:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
127
Ryujinx.Graphics.Shader/Translation/Dominance.cs
Normal file
127
Ryujinx.Graphics.Shader/Translation/Dominance.cs
Normal file
|
@ -0,0 +1,127 @@
|
|||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Translation
|
||||
{
|
||||
static class Dominance
|
||||
{
|
||||
// Those methods are an implementation of the algorithms on "A Simple, Fast Dominance Algorithm".
|
||||
// https://www.cs.rice.edu/~keith/EMBED/dom.pdf
|
||||
public static void FindDominators(BasicBlock entry, int blocksCount)
|
||||
{
|
||||
HashSet<BasicBlock> visited = new HashSet<BasicBlock>();
|
||||
|
||||
Stack<BasicBlock> blockStack = new Stack<BasicBlock>();
|
||||
|
||||
List<BasicBlock> postOrderBlocks = new List<BasicBlock>(blocksCount);
|
||||
|
||||
int[] postOrderMap = new int[blocksCount];
|
||||
|
||||
visited.Add(entry);
|
||||
|
||||
blockStack.Push(entry);
|
||||
|
||||
while (blockStack.TryPop(out BasicBlock block))
|
||||
{
|
||||
if (block.Next != null && visited.Add(block.Next))
|
||||
{
|
||||
blockStack.Push(block);
|
||||
blockStack.Push(block.Next);
|
||||
}
|
||||
else if (block.Branch != null && visited.Add(block.Branch))
|
||||
{
|
||||
blockStack.Push(block);
|
||||
blockStack.Push(block.Branch);
|
||||
}
|
||||
else
|
||||
{
|
||||
postOrderMap[block.Index] = postOrderBlocks.Count;
|
||||
|
||||
postOrderBlocks.Add(block);
|
||||
}
|
||||
}
|
||||
|
||||
BasicBlock Intersect(BasicBlock block1, BasicBlock block2)
|
||||
{
|
||||
while (block1 != block2)
|
||||
{
|
||||
while (postOrderMap[block1.Index] < postOrderMap[block2.Index])
|
||||
{
|
||||
block1 = block1.ImmediateDominator;
|
||||
}
|
||||
|
||||
while (postOrderMap[block2.Index] < postOrderMap[block1.Index])
|
||||
{
|
||||
block2 = block2.ImmediateDominator;
|
||||
}
|
||||
}
|
||||
|
||||
return block1;
|
||||
}
|
||||
|
||||
entry.ImmediateDominator = entry;
|
||||
|
||||
bool modified;
|
||||
|
||||
do
|
||||
{
|
||||
modified = false;
|
||||
|
||||
for (int blkIndex = postOrderBlocks.Count - 2; blkIndex >= 0; blkIndex--)
|
||||
{
|
||||
BasicBlock block = postOrderBlocks[blkIndex];
|
||||
|
||||
BasicBlock newIDom = null;
|
||||
|
||||
foreach (BasicBlock predecessor in block.Predecessors)
|
||||
{
|
||||
if (predecessor.ImmediateDominator != null)
|
||||
{
|
||||
if (newIDom != null)
|
||||
{
|
||||
newIDom = Intersect(predecessor, newIDom);
|
||||
}
|
||||
else
|
||||
{
|
||||
newIDom = predecessor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (block.ImmediateDominator != newIDom)
|
||||
{
|
||||
block.ImmediateDominator = newIDom;
|
||||
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (modified);
|
||||
}
|
||||
|
||||
public static void FindDominanceFrontiers(BasicBlock[] blocks)
|
||||
{
|
||||
for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
|
||||
{
|
||||
BasicBlock block = blocks[blkIndex];
|
||||
|
||||
if (block.Predecessors.Count < 2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int pBlkIndex = 0; pBlkIndex < block.Predecessors.Count; pBlkIndex++)
|
||||
{
|
||||
BasicBlock current = block.Predecessors[pBlkIndex];
|
||||
|
||||
while (current != block.ImmediateDominator)
|
||||
{
|
||||
current.DominanceFrontiers.Add(block);
|
||||
|
||||
current = current.ImmediateDominator;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
104
Ryujinx.Graphics.Shader/Translation/EmitterContext.cs
Normal file
104
Ryujinx.Graphics.Shader/Translation/EmitterContext.cs
Normal file
|
@ -0,0 +1,104 @@
|
|||
using Ryujinx.Graphics.Shader.Decoders;
|
||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Translation
|
||||
{
|
||||
class EmitterContext
|
||||
{
|
||||
public Block CurrBlock { get; set; }
|
||||
public OpCode CurrOp { get; set; }
|
||||
|
||||
private ShaderStage _stage;
|
||||
|
||||
private ShaderHeader _header;
|
||||
|
||||
private List<Operation> _operations;
|
||||
|
||||
private Dictionary<ulong, Operand> _labels;
|
||||
|
||||
public EmitterContext(ShaderStage stage, ShaderHeader header)
|
||||
{
|
||||
_stage = stage;
|
||||
_header = header;
|
||||
|
||||
_operations = new List<Operation>();
|
||||
|
||||
_labels = new Dictionary<ulong, Operand>();
|
||||
}
|
||||
|
||||
public Operand Add(Instruction inst, Operand dest = null, params Operand[] sources)
|
||||
{
|
||||
Operation operation = new Operation(inst, dest, sources);
|
||||
|
||||
Add(operation);
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
public void Add(Operation operation)
|
||||
{
|
||||
_operations.Add(operation);
|
||||
}
|
||||
|
||||
public void MarkLabel(Operand label)
|
||||
{
|
||||
Add(Instruction.MarkLabel, label);
|
||||
}
|
||||
|
||||
public Operand GetLabel(ulong address)
|
||||
{
|
||||
if (!_labels.TryGetValue(address, out Operand label))
|
||||
{
|
||||
label = Label();
|
||||
|
||||
_labels.Add(address, label);
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
public void PrepareForReturn()
|
||||
{
|
||||
if (_stage == ShaderStage.Fragment)
|
||||
{
|
||||
if (_header.OmapDepth)
|
||||
{
|
||||
Operand dest = Attribute(AttributeConsts.FragmentOutputDepth);
|
||||
|
||||
Operand src = Register(_header.DepthRegister, RegisterType.Gpr);
|
||||
|
||||
this.Copy(dest, src);
|
||||
}
|
||||
|
||||
int regIndex = 0;
|
||||
|
||||
for (int attachment = 0; attachment < 8; attachment++)
|
||||
{
|
||||
OutputMapTarget target = _header.OmapTargets[attachment];
|
||||
|
||||
for (int component = 0; component < 4; component++)
|
||||
{
|
||||
if (target.ComponentEnabled(component))
|
||||
{
|
||||
Operand dest = Attribute(AttributeConsts.FragmentOutputColorBase + regIndex * 4);
|
||||
|
||||
Operand src = Register(regIndex, RegisterType.Gpr);
|
||||
|
||||
this.Copy(dest, src);
|
||||
|
||||
regIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Operation[] GetOperations()
|
||||
{
|
||||
return _operations.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
445
Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs
Normal file
445
Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs
Normal file
|
@ -0,0 +1,445 @@
|
|||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
|
||||
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Translation
|
||||
{
|
||||
static class EmitterContextInsts
|
||||
{
|
||||
public static Operand BitfieldExtractS32(this EmitterContext context, Operand a, Operand b, Operand c)
|
||||
{
|
||||
return context.Add(Instruction.BitfieldExtractS32, Local(), a, b, c);
|
||||
}
|
||||
|
||||
public static Operand BitfieldExtractU32(this EmitterContext context, Operand a, Operand b, Operand c)
|
||||
{
|
||||
return context.Add(Instruction.BitfieldExtractU32, Local(), a, b, c);
|
||||
}
|
||||
|
||||
public static Operand BitfieldInsert(this EmitterContext context, Operand a, Operand b, Operand c, Operand d)
|
||||
{
|
||||
return context.Add(Instruction.BitfieldInsert, Local(), a, b, c, d);
|
||||
}
|
||||
|
||||
public static Operand BitfieldReverse(this EmitterContext context, Operand a)
|
||||
{
|
||||
return context.Add(Instruction.BitfieldReverse, Local(), a);
|
||||
}
|
||||
|
||||
public static Operand BitwiseAnd(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.BitwiseAnd, Local(), a, b);
|
||||
}
|
||||
|
||||
public static Operand BitwiseExclusiveOr(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.BitwiseExclusiveOr, Local(), a, b);
|
||||
}
|
||||
|
||||
public static Operand BitwiseNot(this EmitterContext context, Operand a, bool invert)
|
||||
{
|
||||
if (invert)
|
||||
{
|
||||
a = context.BitwiseNot(a);
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
public static Operand BitwiseNot(this EmitterContext context, Operand a)
|
||||
{
|
||||
return context.Add(Instruction.BitwiseNot, Local(), a);
|
||||
}
|
||||
|
||||
public static Operand BitwiseOr(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.BitwiseOr, Local(), a, b);
|
||||
}
|
||||
|
||||
public static Operand Branch(this EmitterContext context, Operand d)
|
||||
{
|
||||
return context.Add(Instruction.Branch, d);
|
||||
}
|
||||
|
||||
public static Operand BranchIfFalse(this EmitterContext context, Operand d, Operand a)
|
||||
{
|
||||
return context.Add(Instruction.BranchIfFalse, d, a);
|
||||
}
|
||||
|
||||
public static Operand BranchIfTrue(this EmitterContext context, Operand d, Operand a)
|
||||
{
|
||||
return context.Add(Instruction.BranchIfTrue, d, a);
|
||||
}
|
||||
|
||||
public static Operand ConditionalSelect(this EmitterContext context, Operand a, Operand b, Operand c)
|
||||
{
|
||||
return context.Add(Instruction.ConditionalSelect, Local(), a, b, c);
|
||||
}
|
||||
|
||||
public static Operand Copy(this EmitterContext context, Operand a)
|
||||
{
|
||||
return context.Add(Instruction.Copy, Local(), a);
|
||||
}
|
||||
|
||||
public static void Copy(this EmitterContext context, Operand d, Operand a)
|
||||
{
|
||||
if (d.Type == OperandType.Constant)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
context.Add(Instruction.Copy, d, a);
|
||||
}
|
||||
|
||||
public static Operand Discard(this EmitterContext context)
|
||||
{
|
||||
return context.Add(Instruction.Discard);
|
||||
}
|
||||
|
||||
public static Operand EmitVertex(this EmitterContext context)
|
||||
{
|
||||
return context.Add(Instruction.EmitVertex);
|
||||
}
|
||||
|
||||
public static Operand EndPrimitive(this EmitterContext context)
|
||||
{
|
||||
return context.Add(Instruction.EndPrimitive);
|
||||
}
|
||||
|
||||
public static Operand FPAbsNeg(this EmitterContext context, Operand a, bool abs, bool neg)
|
||||
{
|
||||
return context.FPNegate(context.FPAbsolute(a, abs), neg);
|
||||
}
|
||||
|
||||
public static Operand FPAbsolute(this EmitterContext context, Operand a, bool abs)
|
||||
{
|
||||
if (abs)
|
||||
{
|
||||
a = context.FPAbsolute(a);
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
public static Operand FPAbsolute(this EmitterContext context, Operand a)
|
||||
{
|
||||
return context.Add(Instruction.FP | Instruction.Absolute, Local(), a);
|
||||
}
|
||||
|
||||
public static Operand FPAdd(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.FP | Instruction.Add, Local(), a, b);
|
||||
}
|
||||
|
||||
public static Operand FPCeiling(this EmitterContext context, Operand a)
|
||||
{
|
||||
return context.Add(Instruction.FP | Instruction.Ceiling, Local(), a);
|
||||
}
|
||||
|
||||
public static Operand FPCompareEqual(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.FP | Instruction.CompareEqual, Local(), a, b);
|
||||
}
|
||||
|
||||
public static Operand FPCompareLess(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.FP | Instruction.CompareLess, Local(), a, b);
|
||||
}
|
||||
|
||||
public static Operand FPConvertToS32(this EmitterContext context, Operand a)
|
||||
{
|
||||
return context.Add(Instruction.ConvertFPToS32, Local(), a);
|
||||
}
|
||||
|
||||
public static Operand FPCosine(this EmitterContext context, Operand a)
|
||||
{
|
||||
return context.Add(Instruction.FP | Instruction.Cosine, Local(), a);
|
||||
}
|
||||
|
||||
public static Operand FPDivide(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.FP | Instruction.Divide, Local(), a, b);
|
||||
}
|
||||
|
||||
public static Operand FPExponentB2(this EmitterContext context, Operand a)
|
||||
{
|
||||
return context.Add(Instruction.FP | Instruction.ExponentB2, Local(), a);
|
||||
}
|
||||
|
||||
public static Operand FPFloor(this EmitterContext context, Operand a)
|
||||
{
|
||||
return context.Add(Instruction.FP | Instruction.Floor, Local(), a);
|
||||
}
|
||||
|
||||
public static Operand FPLogarithmB2(this EmitterContext context, Operand a)
|
||||
{
|
||||
return context.Add(Instruction.FP | Instruction.LogarithmB2, Local(), a);
|
||||
}
|
||||
|
||||
public static Operand FPMaximum(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.FP | Instruction.Maximum, Local(), a, b);
|
||||
}
|
||||
|
||||
public static Operand FPMinimum(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.FP | Instruction.Minimum, Local(), a, b);
|
||||
}
|
||||
|
||||
public static Operand FPMultiply(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.FP | Instruction.Multiply, Local(), a, b);
|
||||
}
|
||||
|
||||
public static Operand FPFusedMultiplyAdd(this EmitterContext context, Operand a, Operand b, Operand c)
|
||||
{
|
||||
return context.Add(Instruction.FusedMultiplyAdd, Local(), a, b, c);
|
||||
}
|
||||
|
||||
public static Operand FPNegate(this EmitterContext context, Operand a, bool neg)
|
||||
{
|
||||
if (neg)
|
||||
{
|
||||
a = context.FPNegate(a);
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
public static Operand FPNegate(this EmitterContext context, Operand a)
|
||||
{
|
||||
return context.Add(Instruction.FP | Instruction.Negate, Local(), a);
|
||||
}
|
||||
|
||||
public static Operand FPReciprocal(this EmitterContext context, Operand a)
|
||||
{
|
||||
return context.FPDivide(ConstF(1), a);
|
||||
}
|
||||
|
||||
public static Operand FPReciprocalSquareRoot(this EmitterContext context, Operand a)
|
||||
{
|
||||
return context.Add(Instruction.FP | Instruction.ReciprocalSquareRoot, Local(), a);
|
||||
}
|
||||
|
||||
public static Operand FPSaturate(this EmitterContext context, Operand a, bool sat)
|
||||
{
|
||||
if (sat)
|
||||
{
|
||||
a = context.FPSaturate(a);
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
public static Operand FPSaturate(this EmitterContext context, Operand a)
|
||||
{
|
||||
return context.Add(Instruction.FP | Instruction.Clamp, Local(), a, ConstF(0), ConstF(1));
|
||||
}
|
||||
|
||||
public static Operand FPSine(this EmitterContext context, Operand a)
|
||||
{
|
||||
return context.Add(Instruction.FP | Instruction.Sine, Local(), a);
|
||||
}
|
||||
|
||||
public static Operand FPSquareRoot(this EmitterContext context, Operand a)
|
||||
{
|
||||
return context.Add(Instruction.FP | Instruction.SquareRoot, Local(), a);
|
||||
}
|
||||
|
||||
public static Operand FPTruncate(this EmitterContext context, Operand a)
|
||||
{
|
||||
return context.Add(Instruction.Truncate, Local(), a);
|
||||
}
|
||||
|
||||
public static Operand IAbsNeg(this EmitterContext context, Operand a, bool abs, bool neg)
|
||||
{
|
||||
return context.INegate(context.IAbsolute(a, abs), neg);
|
||||
}
|
||||
|
||||
public static Operand IAbsolute(this EmitterContext context, Operand a, bool abs)
|
||||
{
|
||||
if (abs)
|
||||
{
|
||||
a = context.IAbsolute(a);
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
public static Operand IAbsolute(this EmitterContext context, Operand a)
|
||||
{
|
||||
return context.Add(Instruction.Absolute, Local(), a);
|
||||
}
|
||||
|
||||
public static Operand IAdd(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.Add, Local(), a, b);
|
||||
}
|
||||
|
||||
public static Operand IClampS32(this EmitterContext context, Operand a, Operand b, Operand c)
|
||||
{
|
||||
return context.Add(Instruction.Clamp, Local(), a, b, c);
|
||||
}
|
||||
|
||||
public static Operand IClampU32(this EmitterContext context, Operand a, Operand b, Operand c)
|
||||
{
|
||||
return context.Add(Instruction.ClampU32, Local(), a, b, c);
|
||||
}
|
||||
|
||||
public static Operand ICompareEqual(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.CompareEqual, Local(), a, b);
|
||||
}
|
||||
|
||||
public static Operand ICompareLess(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.CompareLess, Local(), a, b);
|
||||
}
|
||||
|
||||
public static Operand ICompareLessUnsigned(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.CompareLessU32, Local(), a, b);
|
||||
}
|
||||
|
||||
public static Operand ICompareNotEqual(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.CompareNotEqual, Local(), a, b);
|
||||
}
|
||||
|
||||
public static Operand IConvertS32ToFP(this EmitterContext context, Operand a)
|
||||
{
|
||||
return context.Add(Instruction.ConvertS32ToFP, Local(), a);
|
||||
}
|
||||
|
||||
public static Operand IConvertU32ToFP(this EmitterContext context, Operand a)
|
||||
{
|
||||
return context.Add(Instruction.ConvertU32ToFP, Local(), a);
|
||||
}
|
||||
|
||||
public static Operand IMaximumS32(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.Maximum, Local(), a, b);
|
||||
}
|
||||
|
||||
public static Operand IMaximumU32(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.MaximumU32, Local(), a, b);
|
||||
}
|
||||
|
||||
public static Operand IMinimumS32(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.Minimum, Local(), a, b);
|
||||
}
|
||||
|
||||
public static Operand IMinimumU32(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.MinimumU32, Local(), a, b);
|
||||
}
|
||||
|
||||
public static Operand IMultiply(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.Multiply, Local(), a, b);
|
||||
}
|
||||
|
||||
public static Operand INegate(this EmitterContext context, Operand a, bool neg)
|
||||
{
|
||||
if (neg)
|
||||
{
|
||||
a = context.INegate(a);
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
public static Operand INegate(this EmitterContext context, Operand a)
|
||||
{
|
||||
return context.Add(Instruction.Negate, Local(), a);
|
||||
}
|
||||
|
||||
public static Operand ISubtract(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.Subtract, Local(), a, b);
|
||||
}
|
||||
|
||||
public static Operand IsNan(this EmitterContext context, Operand a)
|
||||
{
|
||||
return context.Add(Instruction.IsNan, Local(), a);
|
||||
}
|
||||
|
||||
public static Operand LoadAttribute(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.LoadAttribute, Local(), a, b);
|
||||
}
|
||||
|
||||
public static Operand LoadConstant(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.LoadConstant, Local(), a, b);
|
||||
}
|
||||
|
||||
public static Operand LoadGlobal(this EmitterContext context, Operand a)
|
||||
{
|
||||
return context.Add(Instruction.LoadGlobal, Local(), a);
|
||||
}
|
||||
|
||||
public static Operand LoadLocal(this EmitterContext context, Operand a)
|
||||
{
|
||||
return context.Add(Instruction.LoadLocal, Local(), a);
|
||||
}
|
||||
|
||||
public static Operand PackHalf2x16(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.PackHalf2x16, Local(), a, b);
|
||||
}
|
||||
|
||||
public static Operand Return(this EmitterContext context)
|
||||
{
|
||||
context.PrepareForReturn();
|
||||
|
||||
return context.Add(Instruction.Return);
|
||||
}
|
||||
|
||||
public static Operand ShiftLeft(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.ShiftLeft, Local(), a, b);
|
||||
}
|
||||
|
||||
public static Operand ShiftRightS32(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.ShiftRightS32, Local(), a, b);
|
||||
}
|
||||
|
||||
public static Operand ShiftRightU32(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.ShiftRightU32, Local(), a, b);
|
||||
}
|
||||
|
||||
public static Operand StoreGlobal(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.StoreGlobal, null, a, b);
|
||||
}
|
||||
|
||||
public static Operand StoreLocal(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.StoreLocal, null, a, b);
|
||||
}
|
||||
|
||||
public static Operand UnpackHalf2x16High(this EmitterContext context, Operand a)
|
||||
{
|
||||
return UnpackHalf2x16(context, a, 1);
|
||||
}
|
||||
|
||||
public static Operand UnpackHalf2x16Low(this EmitterContext context, Operand a)
|
||||
{
|
||||
return UnpackHalf2x16(context, a, 0);
|
||||
}
|
||||
|
||||
private static Operand UnpackHalf2x16(this EmitterContext context, Operand a, int index)
|
||||
{
|
||||
Operand dest = Local();
|
||||
|
||||
context.Add(new Operation(Instruction.UnpackHalf2x16, index, dest, a));
|
||||
|
||||
return dest;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||
{
|
||||
static class BranchElimination
|
||||
{
|
||||
public static bool RunPass(BasicBlock block)
|
||||
{
|
||||
if (block.HasBranch && IsRedundantBranch((Operation)block.GetLastOp(), Next(block)))
|
||||
{
|
||||
block.Branch = null;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsRedundantBranch(Operation current, BasicBlock nextBlock)
|
||||
{
|
||||
// Here we check that:
|
||||
// - The current block ends with a branch.
|
||||
// - The next block only contains a branch.
|
||||
// - The branch on the next block is unconditional.
|
||||
// - Both branches are jumping to the same location.
|
||||
// In this case, the branch on the current block can be removed,
|
||||
// as the next block is going to jump to the same place anyway.
|
||||
if (nextBlock == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(nextBlock.Operations.First?.Value is Operation next))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (next.Inst != Instruction.Branch)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return current.Dest == next.Dest;
|
||||
}
|
||||
|
||||
private static BasicBlock Next(BasicBlock block)
|
||||
{
|
||||
block = block.Next;
|
||||
|
||||
while (block != null && block.Operations.Count == 0)
|
||||
{
|
||||
if (block.HasBranch)
|
||||
{
|
||||
throw new InvalidOperationException("Found a bogus empty block that \"ends with a branch\".");
|
||||
}
|
||||
|
||||
block = block.Next;
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,323 @@
|
|||
using Ryujinx.Graphics.Shader.Decoders;
|
||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using System;
|
||||
|
||||
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||
{
|
||||
static class ConstantFolding
|
||||
{
|
||||
public static void RunPass(Operation operation)
|
||||
{
|
||||
if (!AreAllSourcesConstant(operation))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (operation.Inst)
|
||||
{
|
||||
case Instruction.Add:
|
||||
EvaluateBinary(operation, (x, y) => x + y);
|
||||
break;
|
||||
|
||||
case Instruction.BitwiseAnd:
|
||||
EvaluateBinary(operation, (x, y) => x & y);
|
||||
break;
|
||||
|
||||
case Instruction.BitwiseExclusiveOr:
|
||||
EvaluateBinary(operation, (x, y) => x ^ y);
|
||||
break;
|
||||
|
||||
case Instruction.BitwiseNot:
|
||||
EvaluateUnary(operation, (x) => ~x);
|
||||
break;
|
||||
|
||||
case Instruction.BitwiseOr:
|
||||
EvaluateBinary(operation, (x, y) => x | y);
|
||||
break;
|
||||
|
||||
case Instruction.BitfieldExtractS32:
|
||||
BitfieldExtractS32(operation);
|
||||
break;
|
||||
|
||||
case Instruction.BitfieldExtractU32:
|
||||
BitfieldExtractU32(operation);
|
||||
break;
|
||||
|
||||
case Instruction.Clamp:
|
||||
EvaluateTernary(operation, (x, y, z) => Math.Clamp(x, y, z));
|
||||
break;
|
||||
|
||||
case Instruction.ClampU32:
|
||||
EvaluateTernary(operation, (x, y, z) => (int)Math.Clamp((uint)x, (uint)y, (uint)z));
|
||||
break;
|
||||
|
||||
case Instruction.CompareEqual:
|
||||
EvaluateBinary(operation, (x, y) => x == y);
|
||||
break;
|
||||
|
||||
case Instruction.CompareGreater:
|
||||
EvaluateBinary(operation, (x, y) => x > y);
|
||||
break;
|
||||
|
||||
case Instruction.CompareGreaterOrEqual:
|
||||
EvaluateBinary(operation, (x, y) => x >= y);
|
||||
break;
|
||||
|
||||
case Instruction.CompareGreaterOrEqualU32:
|
||||
EvaluateBinary(operation, (x, y) => (uint)x >= (uint)y);
|
||||
break;
|
||||
|
||||
case Instruction.CompareGreaterU32:
|
||||
EvaluateBinary(operation, (x, y) => (uint)x > (uint)y);
|
||||
break;
|
||||
|
||||
case Instruction.CompareLess:
|
||||
EvaluateBinary(operation, (x, y) => x < y);
|
||||
break;
|
||||
|
||||
case Instruction.CompareLessOrEqual:
|
||||
EvaluateBinary(operation, (x, y) => x <= y);
|
||||
break;
|
||||
|
||||
case Instruction.CompareLessOrEqualU32:
|
||||
EvaluateBinary(operation, (x, y) => (uint)x <= (uint)y);
|
||||
break;
|
||||
|
||||
case Instruction.CompareLessU32:
|
||||
EvaluateBinary(operation, (x, y) => (uint)x < (uint)y);
|
||||
break;
|
||||
|
||||
case Instruction.CompareNotEqual:
|
||||
EvaluateBinary(operation, (x, y) => x != y);
|
||||
break;
|
||||
|
||||
case Instruction.Divide:
|
||||
EvaluateBinary(operation, (x, y) => y != 0 ? x / y : 0);
|
||||
break;
|
||||
|
||||
case Instruction.FP | Instruction.Add:
|
||||
EvaluateFPBinary(operation, (x, y) => x + y);
|
||||
break;
|
||||
|
||||
case Instruction.FP | Instruction.Clamp:
|
||||
EvaluateFPTernary(operation, (x, y, z) => Math.Clamp(x, y, z));
|
||||
break;
|
||||
|
||||
case Instruction.FP | Instruction.CompareEqual:
|
||||
EvaluateFPBinary(operation, (x, y) => x == y);
|
||||
break;
|
||||
|
||||
case Instruction.FP | Instruction.CompareGreater:
|
||||
EvaluateFPBinary(operation, (x, y) => x > y);
|
||||
break;
|
||||
|
||||
case Instruction.FP | Instruction.CompareGreaterOrEqual:
|
||||
EvaluateFPBinary(operation, (x, y) => x >= y);
|
||||
break;
|
||||
|
||||
case Instruction.FP | Instruction.CompareLess:
|
||||
EvaluateFPBinary(operation, (x, y) => x < y);
|
||||
break;
|
||||
|
||||
case Instruction.FP | Instruction.CompareLessOrEqual:
|
||||
EvaluateFPBinary(operation, (x, y) => x <= y);
|
||||
break;
|
||||
|
||||
case Instruction.FP | Instruction.CompareNotEqual:
|
||||
EvaluateFPBinary(operation, (x, y) => x != y);
|
||||
break;
|
||||
|
||||
case Instruction.FP | Instruction.Divide:
|
||||
EvaluateFPBinary(operation, (x, y) => x / y);
|
||||
break;
|
||||
|
||||
case Instruction.FP | Instruction.Multiply:
|
||||
EvaluateFPBinary(operation, (x, y) => x * y);
|
||||
break;
|
||||
|
||||
case Instruction.FP | Instruction.Negate:
|
||||
EvaluateFPUnary(operation, (x) => -x);
|
||||
break;
|
||||
|
||||
case Instruction.FP | Instruction.Subtract:
|
||||
EvaluateFPBinary(operation, (x, y) => x - y);
|
||||
break;
|
||||
|
||||
case Instruction.IsNan:
|
||||
EvaluateFPUnary(operation, (x) => float.IsNaN(x));
|
||||
break;
|
||||
|
||||
case Instruction.Maximum:
|
||||
EvaluateBinary(operation, (x, y) => Math.Max(x, y));
|
||||
break;
|
||||
|
||||
case Instruction.MaximumU32:
|
||||
EvaluateBinary(operation, (x, y) => (int)Math.Max((uint)x, (uint)y));
|
||||
break;
|
||||
|
||||
case Instruction.Minimum:
|
||||
EvaluateBinary(operation, (x, y) => Math.Min(x, y));
|
||||
break;
|
||||
|
||||
case Instruction.MinimumU32:
|
||||
EvaluateBinary(operation, (x, y) => (int)Math.Min((uint)x, (uint)y));
|
||||
break;
|
||||
|
||||
case Instruction.Multiply:
|
||||
EvaluateBinary(operation, (x, y) => x * y);
|
||||
break;
|
||||
|
||||
case Instruction.Negate:
|
||||
EvaluateUnary(operation, (x) => -x);
|
||||
break;
|
||||
|
||||
case Instruction.ShiftLeft:
|
||||
EvaluateBinary(operation, (x, y) => x << y);
|
||||
break;
|
||||
|
||||
case Instruction.ShiftRightS32:
|
||||
EvaluateBinary(operation, (x, y) => x >> y);
|
||||
break;
|
||||
|
||||
case Instruction.ShiftRightU32:
|
||||
EvaluateBinary(operation, (x, y) => (int)((uint)x >> y));
|
||||
break;
|
||||
|
||||
case Instruction.Subtract:
|
||||
EvaluateBinary(operation, (x, y) => x - y);
|
||||
break;
|
||||
|
||||
case Instruction.UnpackHalf2x16:
|
||||
UnpackHalf2x16(operation);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool AreAllSourcesConstant(Operation operation)
|
||||
{
|
||||
for (int index = 0; index < operation.SourcesCount; index++)
|
||||
{
|
||||
if (operation.GetSource(index).Type != OperandType.Constant)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void BitfieldExtractS32(Operation operation)
|
||||
{
|
||||
int value = GetBitfieldExtractValue(operation);
|
||||
|
||||
int shift = 32 - operation.GetSource(2).Value;
|
||||
|
||||
value = (value << shift) >> shift;
|
||||
|
||||
operation.TurnIntoCopy(Const(value));
|
||||
}
|
||||
|
||||
private static void BitfieldExtractU32(Operation operation)
|
||||
{
|
||||
operation.TurnIntoCopy(Const(GetBitfieldExtractValue(operation)));
|
||||
}
|
||||
|
||||
private static int GetBitfieldExtractValue(Operation operation)
|
||||
{
|
||||
int value = operation.GetSource(0).Value;
|
||||
int lsb = operation.GetSource(1).Value;
|
||||
int length = operation.GetSource(2).Value;
|
||||
|
||||
return value.Extract(lsb, length);
|
||||
}
|
||||
|
||||
private static void UnpackHalf2x16(Operation operation)
|
||||
{
|
||||
int value = operation.GetSource(0).Value;
|
||||
|
||||
value = (value >> operation.ComponentIndex * 16) & 0xffff;
|
||||
|
||||
operation.TurnIntoCopy(ConstF(HalfConversion.HalfToSingle(value)));
|
||||
}
|
||||
|
||||
private static void FPNegate(Operation operation)
|
||||
{
|
||||
float value = operation.GetSource(0).AsFloat();
|
||||
|
||||
operation.TurnIntoCopy(ConstF(-value));
|
||||
}
|
||||
|
||||
private static void EvaluateUnary(Operation operation, Func<int, int> op)
|
||||
{
|
||||
int x = operation.GetSource(0).Value;
|
||||
|
||||
operation.TurnIntoCopy(Const(op(x)));
|
||||
}
|
||||
|
||||
private static void EvaluateFPUnary(Operation operation, Func<float, float> op)
|
||||
{
|
||||
float x = operation.GetSource(0).AsFloat();
|
||||
|
||||
operation.TurnIntoCopy(ConstF(op(x)));
|
||||
}
|
||||
|
||||
private static void EvaluateFPUnary(Operation operation, Func<float, bool> op)
|
||||
{
|
||||
float x = operation.GetSource(0).AsFloat();
|
||||
|
||||
operation.TurnIntoCopy(Const(op(x) ? IrConsts.True : IrConsts.False));
|
||||
}
|
||||
|
||||
private static void EvaluateBinary(Operation operation, Func<int, int, int> op)
|
||||
{
|
||||
int x = operation.GetSource(0).Value;
|
||||
int y = operation.GetSource(1).Value;
|
||||
|
||||
operation.TurnIntoCopy(Const(op(x, y)));
|
||||
}
|
||||
|
||||
private static void EvaluateBinary(Operation operation, Func<int, int, bool> op)
|
||||
{
|
||||
int x = operation.GetSource(0).Value;
|
||||
int y = operation.GetSource(1).Value;
|
||||
|
||||
operation.TurnIntoCopy(Const(op(x, y) ? IrConsts.True : IrConsts.False));
|
||||
}
|
||||
|
||||
private static void EvaluateFPBinary(Operation operation, Func<float, float, float> op)
|
||||
{
|
||||
float x = operation.GetSource(0).AsFloat();
|
||||
float y = operation.GetSource(1).AsFloat();
|
||||
|
||||
operation.TurnIntoCopy(ConstF(op(x, y)));
|
||||
}
|
||||
|
||||
private static void EvaluateFPBinary(Operation operation, Func<float, float, bool> op)
|
||||
{
|
||||
float x = operation.GetSource(0).AsFloat();
|
||||
float y = operation.GetSource(1).AsFloat();
|
||||
|
||||
operation.TurnIntoCopy(Const(op(x, y) ? IrConsts.True : IrConsts.False));
|
||||
}
|
||||
|
||||
private static void EvaluateTernary(Operation operation, Func<int, int, int, int> op)
|
||||
{
|
||||
int x = operation.GetSource(0).Value;
|
||||
int y = operation.GetSource(1).Value;
|
||||
int z = operation.GetSource(2).Value;
|
||||
|
||||
operation.TurnIntoCopy(Const(op(x, y, z)));
|
||||
}
|
||||
|
||||
private static void EvaluateFPTernary(Operation operation, Func<float, float, float, float> op)
|
||||
{
|
||||
float x = operation.GetSource(0).AsFloat();
|
||||
float y = operation.GetSource(1).AsFloat();
|
||||
float z = operation.GetSource(2).AsFloat();
|
||||
|
||||
operation.TurnIntoCopy(ConstF(op(x, y, z)));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||
{
|
||||
static class GlobalToStorage
|
||||
{
|
||||
private const int StorageDescsBaseOffset = 0x44; // In words.
|
||||
|
||||
private const int UbeStorageDescsBaseOffset = 0x84; // In words.
|
||||
private const int UbeStorageMaxCount = 14;
|
||||
|
||||
private const int StorageDescSize = 4; // In words.
|
||||
private const int StorageMaxCount = 16;
|
||||
|
||||
private const int StorageDescsSize = StorageDescSize * StorageMaxCount;
|
||||
|
||||
public static void RunPass(BasicBlock block, ShaderStage stage)
|
||||
{
|
||||
int sbStart = GetStorageBaseCbOffset(stage);
|
||||
|
||||
int sbEnd = sbStart + StorageDescsSize;
|
||||
|
||||
// This one is only used on compute shaders.
|
||||
// Compute shaders uses two separate sets of storage.
|
||||
int ubeSbStart = UbeStorageDescsBaseOffset;
|
||||
int ubeSbEnd = UbeStorageDescsBaseOffset + StorageDescSize * UbeStorageMaxCount;
|
||||
|
||||
for (LinkedListNode<INode> node = block.Operations.First; node != null; node = node.Next)
|
||||
{
|
||||
if (!(node.Value is Operation operation))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (operation.Inst == Instruction.LoadGlobal ||
|
||||
operation.Inst == Instruction.StoreGlobal)
|
||||
{
|
||||
Operand source = operation.GetSource(0);
|
||||
|
||||
if (source.AsgOp is Operation asgOperation)
|
||||
{
|
||||
int storageIndex = SearchForStorageBase(asgOperation, sbStart, sbEnd);
|
||||
|
||||
/*if (storageIndex < 0 && stage == ShaderStage.Compute)
|
||||
{
|
||||
storageIndex = SearchForStorageBase(asgOperation, ubeSbStart, ubeSbEnd);
|
||||
}*/
|
||||
|
||||
if (storageIndex >= 0)
|
||||
{
|
||||
node = ReplaceGlobalWithStorage(node, storageIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static LinkedListNode<INode> ReplaceGlobalWithStorage(LinkedListNode<INode> node, int storageIndex)
|
||||
{
|
||||
Operation operation = (Operation)node.Value;
|
||||
|
||||
Operation storageOp;
|
||||
|
||||
if (operation.Inst == Instruction.LoadGlobal)
|
||||
{
|
||||
Operand source = operation.GetSource(0);
|
||||
|
||||
storageOp = new Operation(Instruction.LoadStorage, operation.Dest, Const(storageIndex), source);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand src1 = operation.GetSource(0);
|
||||
Operand src2 = operation.GetSource(1);
|
||||
|
||||
storageOp = new Operation(Instruction.StoreStorage, null, Const(storageIndex), src1, src2);
|
||||
}
|
||||
|
||||
for (int index = 0; index < operation.SourcesCount; index++)
|
||||
{
|
||||
operation.SetSource(index, null);
|
||||
}
|
||||
|
||||
LinkedListNode<INode> oldNode = node;
|
||||
|
||||
node = node.List.AddAfter(node, storageOp);
|
||||
|
||||
node.List.Remove(oldNode);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private static int SearchForStorageBase(Operation operation, int sbStart, int sbEnd)
|
||||
{
|
||||
Queue<Operation> assignments = new Queue<Operation>();
|
||||
|
||||
assignments.Enqueue(operation);
|
||||
|
||||
while (assignments.TryDequeue(out operation))
|
||||
{
|
||||
for (int index = 0; index < operation.SourcesCount; index++)
|
||||
{
|
||||
Operand source = operation.GetSource(index);
|
||||
|
||||
if (source.Type == OperandType.ConstantBuffer)
|
||||
{
|
||||
int slot = source.GetCbufSlot();
|
||||
int offset = source.GetCbufOffset();
|
||||
|
||||
if (slot == 0 && offset >= sbStart && offset < sbEnd)
|
||||
{
|
||||
int storageIndex = (offset - sbStart) / StorageDescSize;
|
||||
|
||||
return storageIndex;
|
||||
}
|
||||
}
|
||||
|
||||
if (source.AsgOp is Operation asgOperation)
|
||||
{
|
||||
assignments.Enqueue(asgOperation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static int GetStorageBaseCbOffset(ShaderStage stage)
|
||||
{
|
||||
switch (stage)
|
||||
{
|
||||
case ShaderStage.Compute: return StorageDescsBaseOffset + 2 * StorageDescsSize;
|
||||
case ShaderStage.Vertex: return StorageDescsBaseOffset;
|
||||
case ShaderStage.TessellationControl: return StorageDescsBaseOffset + 1 * StorageDescsSize;
|
||||
case ShaderStage.TessellationEvaluation: return StorageDescsBaseOffset + 2 * StorageDescsSize;
|
||||
case ShaderStage.Geometry: return StorageDescsBaseOffset + 3 * StorageDescsSize;
|
||||
case ShaderStage.Fragment: return StorageDescsBaseOffset + 4 * StorageDescsSize;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||
{
|
||||
static class HalfConversion
|
||||
{
|
||||
public static float HalfToSingle(int value)
|
||||
{
|
||||
int mantissa = (value >> 0) & 0x3ff;
|
||||
int exponent = (value >> 10) & 0x1f;
|
||||
int sign = (value >> 15) & 0x1;
|
||||
|
||||
if (exponent == 0x1f)
|
||||
{
|
||||
// NaN or Infinity.
|
||||
mantissa <<= 13;
|
||||
exponent = 0xff;
|
||||
}
|
||||
else if (exponent != 0 || mantissa != 0 )
|
||||
{
|
||||
if (exponent == 0)
|
||||
{
|
||||
// Denormal.
|
||||
int e = -1;
|
||||
int m = mantissa;
|
||||
|
||||
do
|
||||
{
|
||||
e++;
|
||||
m <<= 1;
|
||||
}
|
||||
while ((m & 0x400) == 0);
|
||||
|
||||
mantissa = m & 0x3ff;
|
||||
exponent = e;
|
||||
}
|
||||
|
||||
mantissa <<= 13;
|
||||
exponent = 127 - 15 + exponent;
|
||||
}
|
||||
|
||||
int output = (sign << 31) | (exponent << 23) | mantissa;
|
||||
|
||||
return BitConverter.Int32BitsToSingle(output);
|
||||
}
|
||||
}
|
||||
}
|
177
Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs
Normal file
177
Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs
Normal file
|
@ -0,0 +1,177 @@
|
|||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||
{
|
||||
static class Optimizer
|
||||
{
|
||||
public static void Optimize(BasicBlock[] blocks, ShaderStage stage)
|
||||
{
|
||||
for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
|
||||
{
|
||||
GlobalToStorage.RunPass(blocks[blkIndex], stage);
|
||||
}
|
||||
|
||||
bool modified;
|
||||
|
||||
do
|
||||
{
|
||||
modified = false;
|
||||
|
||||
for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
|
||||
{
|
||||
BasicBlock block = blocks[blkIndex];
|
||||
|
||||
LinkedListNode<INode> node = block.Operations.First;
|
||||
|
||||
while (node != null)
|
||||
{
|
||||
LinkedListNode<INode> nextNode = node.Next;
|
||||
|
||||
bool isUnused = IsUnused(node.Value);
|
||||
|
||||
if (!(node.Value is Operation operation) || isUnused)
|
||||
{
|
||||
if (isUnused)
|
||||
{
|
||||
RemoveNode(block, node);
|
||||
|
||||
modified = true;
|
||||
}
|
||||
|
||||
node = nextNode;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
ConstantFolding.RunPass(operation);
|
||||
|
||||
Simplification.RunPass(operation);
|
||||
|
||||
if (DestIsLocalVar(operation))
|
||||
{
|
||||
if (operation.Inst == Instruction.Copy)
|
||||
{
|
||||
PropagateCopy(operation);
|
||||
|
||||
RemoveNode(block, node);
|
||||
|
||||
modified = true;
|
||||
}
|
||||
else if (operation.Inst == Instruction.PackHalf2x16 && PropagatePack(operation))
|
||||
{
|
||||
if (operation.Dest.UseOps.Count == 0)
|
||||
{
|
||||
RemoveNode(block, node);
|
||||
}
|
||||
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
node = nextNode;
|
||||
}
|
||||
|
||||
if (BranchElimination.RunPass(block))
|
||||
{
|
||||
RemoveNode(block, block.Operations.Last);
|
||||
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (modified);
|
||||
}
|
||||
|
||||
private static void PropagateCopy(Operation copyOp)
|
||||
{
|
||||
// Propagate copy source operand to all uses of
|
||||
// the destination operand.
|
||||
Operand dest = copyOp.Dest;
|
||||
Operand src = copyOp.GetSource(0);
|
||||
|
||||
INode[] uses = dest.UseOps.ToArray();
|
||||
|
||||
foreach (INode useNode in uses)
|
||||
{
|
||||
for (int index = 0; index < useNode.SourcesCount; index++)
|
||||
{
|
||||
if (useNode.GetSource(index) == dest)
|
||||
{
|
||||
useNode.SetSource(index, src);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool PropagatePack(Operation packOp)
|
||||
{
|
||||
// Propagate pack source operands to uses by unpack
|
||||
// instruction. The source depends on the unpack instruction.
|
||||
bool modified = false;
|
||||
|
||||
Operand dest = packOp.Dest;
|
||||
Operand src0 = packOp.GetSource(0);
|
||||
Operand src1 = packOp.GetSource(1);
|
||||
|
||||
INode[] uses = dest.UseOps.ToArray();
|
||||
|
||||
foreach (INode useNode in uses)
|
||||
{
|
||||
if (!(useNode is Operation operation) || operation.Inst != Instruction.UnpackHalf2x16)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (operation.GetSource(0) == dest)
|
||||
{
|
||||
operation.TurnIntoCopy(operation.ComponentIndex == 1 ? src1 : src0);
|
||||
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
private static void RemoveNode(BasicBlock block, LinkedListNode<INode> llNode)
|
||||
{
|
||||
// Remove a node from the nodes list, and also remove itself
|
||||
// from all the use lists on the operands that this node uses.
|
||||
block.Operations.Remove(llNode);
|
||||
|
||||
Queue<INode> nodes = new Queue<INode>();
|
||||
|
||||
nodes.Enqueue(llNode.Value);
|
||||
|
||||
while (nodes.TryDequeue(out INode node))
|
||||
{
|
||||
for (int index = 0; index < node.SourcesCount; index++)
|
||||
{
|
||||
Operand src = node.GetSource(index);
|
||||
|
||||
if (src.Type != OperandType.LocalVariable)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (src.UseOps.Remove(node) && src.UseOps.Count == 0)
|
||||
{
|
||||
nodes.Enqueue(src.AsgOp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsUnused(INode node)
|
||||
{
|
||||
return DestIsLocalVar(node) && node.Dest.UseOps.Count == 0;
|
||||
}
|
||||
|
||||
private static bool DestIsLocalVar(INode node)
|
||||
{
|
||||
return node.Dest != null && node.Dest.Type == OperandType.LocalVariable;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
|
||||
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||
{
|
||||
static class Simplification
|
||||
{
|
||||
private const int AllOnes = ~0;
|
||||
|
||||
public static void RunPass(Operation operation)
|
||||
{
|
||||
switch (operation.Inst)
|
||||
{
|
||||
case Instruction.Add:
|
||||
case Instruction.BitwiseExclusiveOr:
|
||||
TryEliminateBinaryOpCommutative(operation, 0);
|
||||
break;
|
||||
|
||||
case Instruction.BitwiseAnd:
|
||||
TryEliminateBitwiseAnd(operation);
|
||||
break;
|
||||
|
||||
case Instruction.BitwiseOr:
|
||||
TryEliminateBitwiseOr(operation);
|
||||
break;
|
||||
|
||||
case Instruction.ConditionalSelect:
|
||||
TryEliminateConditionalSelect(operation);
|
||||
break;
|
||||
|
||||
case Instruction.Divide:
|
||||
TryEliminateBinaryOpY(operation, 1);
|
||||
break;
|
||||
|
||||
case Instruction.Multiply:
|
||||
TryEliminateBinaryOpCommutative(operation, 1);
|
||||
break;
|
||||
|
||||
case Instruction.ShiftLeft:
|
||||
case Instruction.ShiftRightS32:
|
||||
case Instruction.ShiftRightU32:
|
||||
case Instruction.Subtract:
|
||||
TryEliminateBinaryOpY(operation, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryEliminateBitwiseAnd(Operation operation)
|
||||
{
|
||||
// Try to recognize and optimize those 3 patterns (in order):
|
||||
// x & 0xFFFFFFFF == x, 0xFFFFFFFF & y == y,
|
||||
// x & 0x00000000 == 0x00000000, 0x00000000 & y == 0x00000000
|
||||
Operand x = operation.GetSource(0);
|
||||
Operand y = operation.GetSource(1);
|
||||
|
||||
if (IsConstEqual(x, AllOnes))
|
||||
{
|
||||
operation.TurnIntoCopy(y);
|
||||
}
|
||||
else if (IsConstEqual(y, AllOnes))
|
||||
{
|
||||
operation.TurnIntoCopy(x);
|
||||
}
|
||||
else if (IsConstEqual(x, 0) || IsConstEqual(y, 0))
|
||||
{
|
||||
operation.TurnIntoCopy(Const(0));
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryEliminateBitwiseOr(Operation operation)
|
||||
{
|
||||
// Try to recognize and optimize those 3 patterns (in order):
|
||||
// x | 0x00000000 == x, 0x00000000 | y == y,
|
||||
// x | 0xFFFFFFFF == 0xFFFFFFFF, 0xFFFFFFFF | y == 0xFFFFFFFF
|
||||
Operand x = operation.GetSource(0);
|
||||
Operand y = operation.GetSource(1);
|
||||
|
||||
if (IsConstEqual(x, 0))
|
||||
{
|
||||
operation.TurnIntoCopy(y);
|
||||
}
|
||||
else if (IsConstEqual(y, 0))
|
||||
{
|
||||
operation.TurnIntoCopy(x);
|
||||
}
|
||||
else if (IsConstEqual(x, AllOnes) || IsConstEqual(y, AllOnes))
|
||||
{
|
||||
operation.TurnIntoCopy(Const(AllOnes));
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryEliminateBinaryOpY(Operation operation, int comparand)
|
||||
{
|
||||
Operand x = operation.GetSource(0);
|
||||
Operand y = operation.GetSource(1);
|
||||
|
||||
if (IsConstEqual(y, comparand))
|
||||
{
|
||||
operation.TurnIntoCopy(x);
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryEliminateBinaryOpCommutative(Operation operation, int comparand)
|
||||
{
|
||||
Operand x = operation.GetSource(0);
|
||||
Operand y = operation.GetSource(1);
|
||||
|
||||
if (IsConstEqual(x, comparand))
|
||||
{
|
||||
operation.TurnIntoCopy(y);
|
||||
}
|
||||
else if (IsConstEqual(y, comparand))
|
||||
{
|
||||
operation.TurnIntoCopy(x);
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryEliminateConditionalSelect(Operation operation)
|
||||
{
|
||||
Operand cond = operation.GetSource(0);
|
||||
|
||||
if (cond.Type != OperandType.Constant)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// The condition is constant, we can turn it into a copy, and select
|
||||
// the source based on the condition value.
|
||||
int srcIndex = cond.Value != 0 ? 1 : 2;
|
||||
|
||||
Operand source = operation.GetSource(srcIndex);
|
||||
|
||||
operation.TurnIntoCopy(source);
|
||||
}
|
||||
|
||||
private static bool IsConstEqual(Operand operand, int comparand)
|
||||
{
|
||||
if (operand.Type != OperandType.Constant)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return operand.Value == comparand;
|
||||
}
|
||||
}
|
||||
}
|
330
Ryujinx.Graphics.Shader/Translation/Ssa.cs
Normal file
330
Ryujinx.Graphics.Shader/Translation/Ssa.cs
Normal file
|
@ -0,0 +1,330 @@
|
|||
using Ryujinx.Graphics.Shader.Decoders;
|
||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Translation
|
||||
{
|
||||
static class Ssa
|
||||
{
|
||||
private const int GprsAndPredsCount = RegisterConsts.GprsCount + RegisterConsts.PredsCount;
|
||||
|
||||
private class DefMap
|
||||
{
|
||||
private Dictionary<Register, Operand> _map;
|
||||
|
||||
private long[] _phiMasks;
|
||||
|
||||
public DefMap()
|
||||
{
|
||||
_map = new Dictionary<Register, Operand>();
|
||||
|
||||
_phiMasks = new long[(RegisterConsts.TotalCount + 63) / 64];
|
||||
}
|
||||
|
||||
public bool TryAddOperand(Register reg, Operand operand)
|
||||
{
|
||||
return _map.TryAdd(reg, operand);
|
||||
}
|
||||
|
||||
public bool TryGetOperand(Register reg, out Operand operand)
|
||||
{
|
||||
return _map.TryGetValue(reg, out operand);
|
||||
}
|
||||
|
||||
public bool AddPhi(Register reg)
|
||||
{
|
||||
int key = GetKeyFromRegister(reg);
|
||||
|
||||
int index = key / 64;
|
||||
int bit = key & 63;
|
||||
|
||||
long mask = 1L << bit;
|
||||
|
||||
if ((_phiMasks[index] & mask) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_phiMasks[index] |= mask;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool HasPhi(Register reg)
|
||||
{
|
||||
int key = GetKeyFromRegister(reg);
|
||||
|
||||
int index = key / 64;
|
||||
int bit = key & 63;
|
||||
|
||||
return (_phiMasks[index] & (1L << bit)) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
private struct Definition
|
||||
{
|
||||
public BasicBlock Block { get; }
|
||||
public Operand Local { get; }
|
||||
|
||||
public Definition(BasicBlock block, Operand local)
|
||||
{
|
||||
Block = block;
|
||||
Local = local;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Rename(BasicBlock[] blocks)
|
||||
{
|
||||
DefMap[] globalDefs = new DefMap[blocks.Length];
|
||||
|
||||
for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
|
||||
{
|
||||
globalDefs[blkIndex] = new DefMap();
|
||||
}
|
||||
|
||||
Queue<BasicBlock> dfPhiBlocks = new Queue<BasicBlock>();
|
||||
|
||||
// First pass, get all defs and locals uses.
|
||||
for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
|
||||
{
|
||||
Operand[] localDefs = new Operand[RegisterConsts.TotalCount];
|
||||
|
||||
Operand RenameLocal(Operand operand)
|
||||
{
|
||||
if (operand != null && operand.Type == OperandType.Register)
|
||||
{
|
||||
Operand local = localDefs[GetKeyFromRegister(operand.GetRegister())];
|
||||
|
||||
operand = local ?? operand;
|
||||
}
|
||||
|
||||
return operand;
|
||||
}
|
||||
|
||||
BasicBlock block = blocks[blkIndex];
|
||||
|
||||
LinkedListNode<INode> node = block.Operations.First;
|
||||
|
||||
while (node != null)
|
||||
{
|
||||
if (node.Value is Operation operation)
|
||||
{
|
||||
for (int index = 0; index < operation.SourcesCount; index++)
|
||||
{
|
||||
operation.SetSource(index, RenameLocal(operation.GetSource(index)));
|
||||
}
|
||||
|
||||
if (operation.Dest != null && operation.Dest.Type == OperandType.Register)
|
||||
{
|
||||
Operand local = Local();
|
||||
|
||||
localDefs[GetKeyFromRegister(operation.Dest.GetRegister())] = local;
|
||||
|
||||
operation.Dest = local;
|
||||
}
|
||||
}
|
||||
|
||||
node = node.Next;
|
||||
}
|
||||
|
||||
for (int index = 0; index < RegisterConsts.TotalCount; index++)
|
||||
{
|
||||
Operand local = localDefs[index];
|
||||
|
||||
if (local == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Register reg = GetRegisterFromKey(index);
|
||||
|
||||
globalDefs[block.Index].TryAddOperand(reg, local);
|
||||
|
||||
dfPhiBlocks.Enqueue(block);
|
||||
|
||||
while (dfPhiBlocks.TryDequeue(out BasicBlock dfPhiBlock))
|
||||
{
|
||||
foreach (BasicBlock domFrontier in dfPhiBlock.DominanceFrontiers)
|
||||
{
|
||||
if (globalDefs[domFrontier.Index].AddPhi(reg))
|
||||
{
|
||||
dfPhiBlocks.Enqueue(domFrontier);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass, rename variables with definitions on different blocks.
|
||||
for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
|
||||
{
|
||||
Operand[] localDefs = new Operand[RegisterConsts.TotalCount];
|
||||
|
||||
BasicBlock block = blocks[blkIndex];
|
||||
|
||||
Operand RenameGlobal(Operand operand)
|
||||
{
|
||||
if (operand != null && operand.Type == OperandType.Register)
|
||||
{
|
||||
int key = GetKeyFromRegister(operand.GetRegister());
|
||||
|
||||
Operand local = localDefs[key];
|
||||
|
||||
if (local != null)
|
||||
{
|
||||
return local;
|
||||
}
|
||||
|
||||
operand = FindDefinitionForCurr(globalDefs, block, operand.GetRegister());
|
||||
|
||||
localDefs[key] = operand;
|
||||
}
|
||||
|
||||
return operand;
|
||||
}
|
||||
|
||||
LinkedListNode<INode> node = block.Operations.First;
|
||||
|
||||
while (node != null)
|
||||
{
|
||||
if (node.Value is Operation operation)
|
||||
{
|
||||
for (int index = 0; index < operation.SourcesCount; index++)
|
||||
{
|
||||
operation.SetSource(index, RenameGlobal(operation.GetSource(index)));
|
||||
}
|
||||
}
|
||||
|
||||
node = node.Next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Operand FindDefinitionForCurr(DefMap[] globalDefs, BasicBlock current, Register reg)
|
||||
{
|
||||
if (globalDefs[current.Index].HasPhi(reg))
|
||||
{
|
||||
return InsertPhi(globalDefs, current, reg);
|
||||
}
|
||||
|
||||
if (current != current.ImmediateDominator)
|
||||
{
|
||||
return FindDefinition(globalDefs, current.ImmediateDominator, reg).Local;
|
||||
}
|
||||
|
||||
return Undef();
|
||||
}
|
||||
|
||||
private static Definition FindDefinition(DefMap[] globalDefs, BasicBlock current, Register reg)
|
||||
{
|
||||
foreach (BasicBlock block in SelfAndImmediateDominators(current))
|
||||
{
|
||||
DefMap defMap = globalDefs[block.Index];
|
||||
|
||||
if (defMap.TryGetOperand(reg, out Operand lastDef))
|
||||
{
|
||||
return new Definition(block, lastDef);
|
||||
}
|
||||
|
||||
if (defMap.HasPhi(reg))
|
||||
{
|
||||
return new Definition(block, InsertPhi(globalDefs, block, reg));
|
||||
}
|
||||
}
|
||||
|
||||
return new Definition(current, Undef());
|
||||
}
|
||||
|
||||
private static IEnumerable<BasicBlock> SelfAndImmediateDominators(BasicBlock block)
|
||||
{
|
||||
while (block != block.ImmediateDominator)
|
||||
{
|
||||
yield return block;
|
||||
|
||||
block = block.ImmediateDominator;
|
||||
}
|
||||
|
||||
yield return block;
|
||||
}
|
||||
|
||||
private static Operand InsertPhi(DefMap[] globalDefs, BasicBlock block, Register reg)
|
||||
{
|
||||
// This block has a Phi that has not been materialized yet, but that
|
||||
// would define a new version of the variable we're looking for. We need
|
||||
// to materialize the Phi, add all the block/operand pairs into the Phi, and
|
||||
// then use the definition from that Phi.
|
||||
Operand local = Local();
|
||||
|
||||
PhiNode phi = new PhiNode(local);
|
||||
|
||||
AddPhi(block, phi);
|
||||
|
||||
globalDefs[block.Index].TryAddOperand(reg, local);
|
||||
|
||||
foreach (BasicBlock predecessor in block.Predecessors)
|
||||
{
|
||||
Definition def = FindDefinition(globalDefs, predecessor, reg);
|
||||
|
||||
phi.AddSource(def.Block, def.Local);
|
||||
}
|
||||
|
||||
return local;
|
||||
}
|
||||
|
||||
private static void AddPhi(BasicBlock block, PhiNode phi)
|
||||
{
|
||||
LinkedListNode<INode> node = block.Operations.First;
|
||||
|
||||
if (node != null)
|
||||
{
|
||||
while (node.Next?.Value is PhiNode)
|
||||
{
|
||||
node = node.Next;
|
||||
}
|
||||
}
|
||||
|
||||
if (node?.Value is PhiNode)
|
||||
{
|
||||
block.Operations.AddAfter(node, phi);
|
||||
}
|
||||
else
|
||||
{
|
||||
block.Operations.AddFirst(phi);
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetKeyFromRegister(Register reg)
|
||||
{
|
||||
if (reg.Type == RegisterType.Gpr)
|
||||
{
|
||||
return reg.Index;
|
||||
}
|
||||
else if (reg.Type == RegisterType.Predicate)
|
||||
{
|
||||
return RegisterConsts.GprsCount + reg.Index;
|
||||
}
|
||||
else /* if (reg.Type == RegisterType.Flag) */
|
||||
{
|
||||
return GprsAndPredsCount + reg.Index;
|
||||
}
|
||||
}
|
||||
|
||||
private static Register GetRegisterFromKey(int key)
|
||||
{
|
||||
if (key < RegisterConsts.GprsCount)
|
||||
{
|
||||
return new Register(key, RegisterType.Gpr);
|
||||
}
|
||||
else if (key < GprsAndPredsCount)
|
||||
{
|
||||
return new Register(key - RegisterConsts.GprsCount, RegisterType.Predicate);
|
||||
}
|
||||
else /* if (key < RegisterConsts.TotalCount) */
|
||||
{
|
||||
return new Register(key - GprsAndPredsCount, RegisterType.Flag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
25
Ryujinx.Graphics.Shader/Translation/TranslationConfig.cs
Normal file
25
Ryujinx.Graphics.Shader/Translation/TranslationConfig.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Translation
|
||||
{
|
||||
public struct TranslationConfig
|
||||
{
|
||||
public int MaxCBufferSize { get; }
|
||||
|
||||
public int Version { get; }
|
||||
|
||||
public TranslationFlags Flags { get; }
|
||||
|
||||
public TranslationConfig(int maxCBufferSize, int version, TranslationFlags flags)
|
||||
{
|
||||
if (maxCBufferSize <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(maxCBufferSize));
|
||||
}
|
||||
|
||||
MaxCBufferSize = maxCBufferSize;
|
||||
Version = version;
|
||||
Flags = flags;
|
||||
}
|
||||
}
|
||||
}
|
11
Ryujinx.Graphics.Shader/Translation/TranslationFlags.cs
Normal file
11
Ryujinx.Graphics.Shader/Translation/TranslationFlags.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace Ryujinx.Graphics.Shader.Translation
|
||||
{
|
||||
public enum TranslationFlags
|
||||
{
|
||||
None = 0,
|
||||
|
||||
Compute = 1 << 0,
|
||||
DebugMode = 1 << 1,
|
||||
Unspecialized = 1 << 2
|
||||
}
|
||||
}
|
300
Ryujinx.Graphics.Shader/Translation/Translator.cs
Normal file
300
Ryujinx.Graphics.Shader/Translation/Translator.cs
Normal file
|
@ -0,0 +1,300 @@
|
|||
using Ryujinx.Graphics.Shader.CodeGen.Glsl;
|
||||
using Ryujinx.Graphics.Shader.Decoders;
|
||||
using Ryujinx.Graphics.Shader.Instructions;
|
||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||
using Ryujinx.Graphics.Shader.Translation.Optimizations;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Translation
|
||||
{
|
||||
public static class Translator
|
||||
{
|
||||
private const int HeaderSize = 0x50;
|
||||
|
||||
public static ShaderProgram Translate(Span<byte> code, TranslationConfig translationConfig)
|
||||
{
|
||||
return Translate(code, Span<byte>.Empty, translationConfig);
|
||||
}
|
||||
|
||||
public static ShaderProgram Translate(Span<byte> code, Span<byte> code2, TranslationConfig translationConfig)
|
||||
{
|
||||
bool compute = (translationConfig.Flags & TranslationFlags.Compute) != 0;
|
||||
bool debugMode = (translationConfig.Flags & TranslationFlags.DebugMode) != 0;
|
||||
|
||||
Operation[] shaderOps = DecodeShader(code, compute, debugMode, out ShaderHeader header);
|
||||
|
||||
if (code2 != Span<byte>.Empty)
|
||||
{
|
||||
// Dual vertex shader.
|
||||
Operation[] shaderOpsB = DecodeShader(code2, compute, debugMode, out header);
|
||||
|
||||
shaderOps = Combine(shaderOps, shaderOpsB);
|
||||
}
|
||||
|
||||
ShaderStage stage;
|
||||
|
||||
if (compute)
|
||||
{
|
||||
stage = ShaderStage.Compute;
|
||||
}
|
||||
else
|
||||
{
|
||||
stage = header.Stage;
|
||||
}
|
||||
|
||||
int maxOutputVertexCount = 0;
|
||||
|
||||
OutputTopology outputTopology = OutputTopology.LineStrip;
|
||||
|
||||
if (!compute)
|
||||
{
|
||||
maxOutputVertexCount = header.MaxOutputVertexCount;
|
||||
outputTopology = header.OutputTopology;
|
||||
}
|
||||
|
||||
ShaderConfig config = new ShaderConfig(
|
||||
stage,
|
||||
translationConfig.Flags,
|
||||
translationConfig.MaxCBufferSize,
|
||||
maxOutputVertexCount,
|
||||
outputTopology);
|
||||
|
||||
BasicBlock[] irBlocks = ControlFlowGraph.MakeCfg(shaderOps);
|
||||
|
||||
Dominance.FindDominators(irBlocks[0], irBlocks.Length);
|
||||
|
||||
Dominance.FindDominanceFrontiers(irBlocks);
|
||||
|
||||
Ssa.Rename(irBlocks);
|
||||
|
||||
Optimizer.Optimize(irBlocks, stage);
|
||||
|
||||
StructuredProgramInfo sInfo = StructuredProgram.MakeStructuredProgram(irBlocks, config);
|
||||
|
||||
GlslProgram program = GlslGenerator.Generate(sInfo, config);
|
||||
|
||||
ShaderProgramInfo spInfo = new ShaderProgramInfo(
|
||||
program.CBufferDescriptors,
|
||||
program.SBufferDescriptors,
|
||||
program.TextureDescriptors,
|
||||
sInfo.InterpolationQualifiers,
|
||||
sInfo.UsesInstanceId);
|
||||
|
||||
string glslCode = program.Code;
|
||||
|
||||
if (translationConfig.Version != 0)
|
||||
{
|
||||
glslCode = "// " + translationConfig.Version + Environment.NewLine + glslCode;
|
||||
}
|
||||
|
||||
return new ShaderProgram(spInfo, stage, glslCode);
|
||||
}
|
||||
|
||||
private static Operation[] DecodeShader(Span<byte> code, bool compute, bool debugMode, out ShaderHeader header)
|
||||
{
|
||||
Block[] cfg;
|
||||
|
||||
EmitterContext context;
|
||||
|
||||
ulong headerSize;
|
||||
|
||||
if (compute)
|
||||
{
|
||||
header = null;
|
||||
|
||||
cfg = Decoder.Decode(code, 0);
|
||||
|
||||
context = new EmitterContext(ShaderStage.Compute, header);
|
||||
|
||||
headerSize = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
header = new ShaderHeader(code);
|
||||
|
||||
cfg = Decoder.Decode(code, HeaderSize);
|
||||
|
||||
context = new EmitterContext(header.Stage, header);
|
||||
|
||||
headerSize = HeaderSize;
|
||||
}
|
||||
|
||||
for (int blkIndex = 0; blkIndex < cfg.Length; blkIndex++)
|
||||
{
|
||||
Block block = cfg[blkIndex];
|
||||
|
||||
context.CurrBlock = block;
|
||||
|
||||
context.MarkLabel(context.GetLabel(block.Address));
|
||||
|
||||
for (int opIndex = 0; opIndex < block.OpCodes.Count; opIndex++)
|
||||
{
|
||||
OpCode op = block.OpCodes[opIndex];
|
||||
|
||||
if (debugMode)
|
||||
{
|
||||
string instName;
|
||||
|
||||
if (op.Emitter != null)
|
||||
{
|
||||
instName = op.Emitter.Method.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
instName = "???";
|
||||
}
|
||||
|
||||
string dbgComment = $"0x{(op.Address - headerSize):X6}: 0x{op.RawOpCode:X16} {instName}";
|
||||
|
||||
context.Add(new CommentNode(dbgComment));
|
||||
}
|
||||
|
||||
if (op.NeverExecute)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Operand predSkipLbl = null;
|
||||
|
||||
bool skipPredicateCheck = op.Emitter == InstEmit.Bra;
|
||||
|
||||
if (op is OpCodeSync opSync)
|
||||
{
|
||||
// If the instruction is a SYNC instruction with only one
|
||||
// possible target address, then the instruction is basically
|
||||
// just a simple branch, we can generate code similar to branch
|
||||
// instructions, with the condition check on the branch itself.
|
||||
skipPredicateCheck |= opSync.Targets.Count < 2;
|
||||
}
|
||||
|
||||
if (!(op.Predicate.IsPT || skipPredicateCheck))
|
||||
{
|
||||
Operand label;
|
||||
|
||||
if (opIndex == block.OpCodes.Count - 1 && block.Next != null)
|
||||
{
|
||||
label = context.GetLabel(block.Next.Address);
|
||||
}
|
||||
else
|
||||
{
|
||||
label = Label();
|
||||
|
||||
predSkipLbl = label;
|
||||
}
|
||||
|
||||
Operand pred = Register(op.Predicate);
|
||||
|
||||
if (op.InvertPredicate)
|
||||
{
|
||||
context.BranchIfTrue(label, pred);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.BranchIfFalse(label, pred);
|
||||
}
|
||||
}
|
||||
|
||||
context.CurrOp = op;
|
||||
|
||||
if (op.Emitter != null)
|
||||
{
|
||||
op.Emitter(context);
|
||||
}
|
||||
|
||||
if (predSkipLbl != null)
|
||||
{
|
||||
context.MarkLabel(predSkipLbl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return context.GetOperations();
|
||||
}
|
||||
|
||||
private static Operation[] Combine(Operation[] a, Operation[] b)
|
||||
{
|
||||
// Here we combine two shaders.
|
||||
// For shader A:
|
||||
// - All user attribute stores on shader A are turned into copies to a
|
||||
// temporary variable. It's assumed that shader B will consume them.
|
||||
// - All return instructions are turned into branch instructions, the
|
||||
// branch target being the start of the shader B code.
|
||||
// For shader B:
|
||||
// - All user attribute loads on shader B are turned into copies from a
|
||||
// temporary variable, as long that attribute is written by shader A.
|
||||
List<Operation> output = new List<Operation>(a.Length + b.Length);
|
||||
|
||||
Operand[] temps = new Operand[AttributeConsts.UserAttributesCount * 4];
|
||||
|
||||
Operand lblB = Label();
|
||||
|
||||
for (int index = 0; index < a.Length; index++)
|
||||
{
|
||||
Operation operation = a[index];
|
||||
|
||||
if (IsUserAttribute(operation.Dest))
|
||||
{
|
||||
int tIndex = (operation.Dest.Value - AttributeConsts.UserAttributeBase) / 4;
|
||||
|
||||
Operand temp = temps[tIndex];
|
||||
|
||||
if (temp == null)
|
||||
{
|
||||
temp = Local();
|
||||
|
||||
temps[tIndex] = temp;
|
||||
}
|
||||
|
||||
operation.Dest = temp;
|
||||
}
|
||||
|
||||
if (operation.Inst == Instruction.Return)
|
||||
{
|
||||
output.Add(new Operation(Instruction.Branch, lblB));
|
||||
}
|
||||
else
|
||||
{
|
||||
output.Add(operation);
|
||||
}
|
||||
}
|
||||
|
||||
output.Add(new Operation(Instruction.MarkLabel, lblB));
|
||||
|
||||
for (int index = 0; index < b.Length; index++)
|
||||
{
|
||||
Operation operation = b[index];
|
||||
|
||||
for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++)
|
||||
{
|
||||
Operand src = operation.GetSource(srcIndex);
|
||||
|
||||
if (IsUserAttribute(src))
|
||||
{
|
||||
Operand temp = temps[(src.Value - AttributeConsts.UserAttributeBase) / 4];
|
||||
|
||||
if (temp != null)
|
||||
{
|
||||
operation.SetSource(srcIndex, temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output.Add(operation);
|
||||
}
|
||||
|
||||
return output.ToArray();
|
||||
}
|
||||
|
||||
private static bool IsUserAttribute(Operand operand)
|
||||
{
|
||||
return operand != null &&
|
||||
operand.Type == OperandType.Attribute &&
|
||||
operand.Value >= AttributeConsts.UserAttributeBase &&
|
||||
operand.Value < AttributeConsts.UserAttributeEnd;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue