New shader translator implementation (#654)
* Start implementing a new shader translator * Fix shift instructions and a typo * Small refactoring on StructuredProgram, move RemovePhis method to a separate class * Initial geometry shader support * Implement TLD4 * Fix -- There's no negation on FMUL32I * Add constant folding and algebraic simplification optimizations, nits * Some leftovers from constant folding * Avoid cast for constant assignments * Add a branch elimination pass, and misc small fixes * Remove redundant branches, add expression propagation and other improvements on the code * Small leftovers -- add missing break and continue, remove unused properties, other improvements * Add null check to handle empty block cases on block visitor * Add HADD2 and HMUL2 half float shader instructions * Optimize pack/unpack sequences, some fixes related to half float instructions * Add TXQ, TLD, TLDS and TLD4S shader texture instructions, and some support for bindless textures, some refactoring on codegen * Fix copy paste mistake that caused RZ to be ignored on the AST instruction * Add workaround for conditional exit, and fix half float instruction with constant buffer * Add missing 0.0 source for TLDS.LZ variants * Simplify the switch for TLDS.LZ * Texture instructions related fixes * Implement the HFMA instruction, and some misc. fixes * Enable constant folding on UnpackHalf2x16 instructions * Refactor HFMA to use OpCode* for opcode decoding rather than on the helper methods * Remove the old shader translator * Remove ShaderDeclInfo and other unused things * Add dual vertex shader support * Add ShaderConfig, used to pass shader type and maximum cbuffer size * Move and rename some instruction enums * Move texture instructions into a separate file * Move operand GetExpression and locals management to OperandManager * Optimize opcode decoding using a simple list and binary search * Add missing condition for do-while on goto elimination * Misc. fixes on texture instructions * Simplify TLDS switch * Address PR feedback, and a nit
This commit is contained in:
parent
b2e88b04a8
commit
6b23a2c125
207 changed files with 11514 additions and 6311 deletions
30
Ryujinx.Graphics/Shader/Translation/AttributeConsts.cs
Normal file
30
Ryujinx.Graphics/Shader/Translation/AttributeConsts.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||
{
|
||||
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 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;
|
||||
}
|
||||
}
|
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
105
Ryujinx.Graphics/Shader/Translation/EmitterContext.cs
Normal file
105
Ryujinx.Graphics/Shader/Translation/EmitterContext.cs
Normal file
|
@ -0,0 +1,105 @@
|
|||
using Ryujinx.Graphics.Gal;
|
||||
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 GalShaderType _shaderType;
|
||||
|
||||
private ShaderHeader _header;
|
||||
|
||||
private List<Operation> _operations;
|
||||
|
||||
private Dictionary<ulong, Operand> _labels;
|
||||
|
||||
public EmitterContext(GalShaderType shaderType, ShaderHeader header)
|
||||
{
|
||||
_shaderType = shaderType;
|
||||
_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 (_shaderType == GalShaderType.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();
|
||||
}
|
||||
}
|
||||
}
|
420
Ryujinx.Graphics/Shader/Translation/EmitterContextInsts.cs
Normal file
420
Ryujinx.Graphics/Shader/Translation/EmitterContextInsts.cs
Normal file
|
@ -0,0 +1,420 @@
|
|||
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 LoadConstant(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.LoadConstant, Local(), a, b);
|
||||
}
|
||||
|
||||
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 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 Eliminate(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 Fold(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,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);
|
||||
}
|
||||
}
|
||||
}
|
172
Ryujinx.Graphics/Shader/Translation/Optimizations/Optimizer.cs
Normal file
172
Ryujinx.Graphics/Shader/Translation/Optimizations/Optimizer.cs
Normal file
|
@ -0,0 +1,172 @@
|
|||
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)
|
||||
{
|
||||
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.Fold(operation);
|
||||
|
||||
Simplification.Simplify(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.Eliminate(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 Simplify(Operation operation)
|
||||
{
|
||||
switch (operation.Inst)
|
||||
{
|
||||
case Instruction.Add:
|
||||
case Instruction.BitwiseExclusiveOr:
|
||||
TryEliminateBinaryOpComutative(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:
|
||||
TryEliminateBinaryOpComutative(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 TryEliminateBinaryOpComutative(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
219
Ryujinx.Graphics/Shader/Translation/Translator.cs
Normal file
219
Ryujinx.Graphics/Shader/Translation/Translator.cs
Normal file
|
@ -0,0 +1,219 @@
|
|||
using Ryujinx.Graphics.Gal;
|
||||
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.Collections.Generic;
|
||||
|
||||
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Translation
|
||||
{
|
||||
public static class Translator
|
||||
{
|
||||
public static ShaderProgram Translate(IGalMemory memory, ulong address, ShaderConfig config)
|
||||
{
|
||||
return Translate(memory, address, 0, config);
|
||||
}
|
||||
|
||||
public static ShaderProgram Translate(
|
||||
IGalMemory memory,
|
||||
ulong address,
|
||||
ulong addressB,
|
||||
ShaderConfig config)
|
||||
{
|
||||
Operation[] shaderOps = DecodeShader(memory, address, config.Type);
|
||||
|
||||
if (addressB != 0)
|
||||
{
|
||||
//Dual vertex shader.
|
||||
Operation[] shaderOpsB = DecodeShader(memory, addressB, config.Type);
|
||||
|
||||
shaderOps = Combine(shaderOps, shaderOpsB);
|
||||
}
|
||||
|
||||
BasicBlock[] irBlocks = ControlFlowGraph.MakeCfg(shaderOps);
|
||||
|
||||
Dominance.FindDominators(irBlocks[0], irBlocks.Length);
|
||||
|
||||
Dominance.FindDominanceFrontiers(irBlocks);
|
||||
|
||||
Ssa.Rename(irBlocks);
|
||||
|
||||
Optimizer.Optimize(irBlocks);
|
||||
|
||||
StructuredProgramInfo sInfo = StructuredProgram.MakeStructuredProgram(irBlocks);
|
||||
|
||||
GlslProgram program = GlslGenerator.Generate(sInfo, config);
|
||||
|
||||
ShaderProgramInfo spInfo = new ShaderProgramInfo(
|
||||
program.CBufferDescriptors,
|
||||
program.TextureDescriptors);
|
||||
|
||||
return new ShaderProgram(spInfo, program.Code);
|
||||
}
|
||||
|
||||
private static Operation[] DecodeShader(IGalMemory memory, ulong address, GalShaderType shaderType)
|
||||
{
|
||||
ShaderHeader header = new ShaderHeader(memory, address);
|
||||
|
||||
Block[] cfg = Decoder.Decode(memory, address);
|
||||
|
||||
EmitterContext context = new EmitterContext(shaderType, header);
|
||||
|
||||
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 (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;
|
||||
|
||||
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