diff options
author | gdkchan <gab.dark.100@gmail.com> | 2019-04-17 20:57:08 -0300 |
---|---|---|
committer | jduncanator <1518948+jduncanator@users.noreply.github.com> | 2019-04-18 09:57:08 +1000 |
commit | 6b23a2c125b9c48b5ebea92716004ef68698bb0f (patch) | |
tree | 69332df6fbbd8e2bddc522ba682fcc5c7a69e101 /Ryujinx.Graphics/Shader/StructuredIr/StructuredProgramContext.cs | |
parent | b2e88b04a85b41cc60af3485d88c90429e84a218 (diff) |
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
Diffstat (limited to 'Ryujinx.Graphics/Shader/StructuredIr/StructuredProgramContext.cs')
-rw-r--r-- | Ryujinx.Graphics/Shader/StructuredIr/StructuredProgramContext.cs | 292 |
1 files changed, 292 insertions, 0 deletions
diff --git a/Ryujinx.Graphics/Shader/StructuredIr/StructuredProgramContext.cs b/Ryujinx.Graphics/Shader/StructuredIr/StructuredProgramContext.cs new file mode 100644 index 00000000..e1f0503a --- /dev/null +++ b/Ryujinx.Graphics/Shader/StructuredIr/StructuredProgramContext.cs @@ -0,0 +1,292 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System.Collections.Generic; +using System.Linq; + +using static Ryujinx.Graphics.Shader.StructuredIr.AstHelper; + +namespace Ryujinx.Graphics.Shader.StructuredIr +{ + class StructuredProgramContext + { + private HashSet<BasicBlock> _loopTails; + + private Stack<(AstBlock Block, int EndIndex)> _blockStack; + + private Dictionary<Operand, AstOperand> _localsMap; + + private Dictionary<int, AstAssignment> _gotoTempAsgs; + + private List<GotoStatement> _gotos; + + private AstBlock _currBlock; + + private int _currEndIndex; + + public StructuredProgramInfo Info { get; } + + public StructuredProgramContext(int blocksCount) + { + _loopTails = new HashSet<BasicBlock>(); + + _blockStack = new Stack<(AstBlock, int)>(); + + _localsMap = new Dictionary<Operand, AstOperand>(); + + _gotoTempAsgs = new Dictionary<int, AstAssignment>(); + + _gotos = new List<GotoStatement>(); + + _currBlock = new AstBlock(AstBlockType.Main); + + _currEndIndex = blocksCount; + + Info = new StructuredProgramInfo(_currBlock); + } + + public void EnterBlock(BasicBlock block) + { + while (_currEndIndex == block.Index) + { + (_currBlock, _currEndIndex) = _blockStack.Pop(); + } + + if (_gotoTempAsgs.TryGetValue(block.Index, out AstAssignment gotoTempAsg)) + { + AddGotoTempReset(block, gotoTempAsg); + } + + LookForDoWhileStatements(block); + } + + public void LeaveBlock(BasicBlock block, Operation branchOp) + { + LookForIfStatements(block, branchOp); + } + + private void LookForDoWhileStatements(BasicBlock block) + { + //Check if we have any predecessor whose index is greater than the + //current block, this indicates a loop. + bool done = false; + + foreach (BasicBlock predecessor in block.Predecessors.OrderByDescending(x => x.Index)) + { + if (predecessor.Index < block.Index) + { + break; + } + + if (predecessor.Index < _currEndIndex && !done) + { + Operation branchOp = (Operation)predecessor.GetLastOp(); + + NewBlock(AstBlockType.DoWhile, branchOp, predecessor.Index + 1); + + _loopTails.Add(predecessor); + + done = true; + } + else + { + AddGotoTempReset(block, GetGotoTempAsg(block.Index)); + + break; + } + } + } + + private void LookForIfStatements(BasicBlock block, Operation branchOp) + { + if (block.Branch == null) + { + return; + } + + bool isLoop = block.Branch.Index <= block.Index; + + if (block.Branch.Index <= _currEndIndex && !isLoop) + { + NewBlock(AstBlockType.If, branchOp, block.Branch.Index); + } + else if (!_loopTails.Contains(block)) + { + AstAssignment gotoTempAsg = GetGotoTempAsg(block.Branch.Index); + + IAstNode cond = GetBranchCond(AstBlockType.DoWhile, branchOp); + + AddNode(Assign(gotoTempAsg.Destination, cond)); + + AstOperation branch = new AstOperation(branchOp.Inst); + + AddNode(branch); + + GotoStatement gotoStmt = new GotoStatement(branch, gotoTempAsg, isLoop); + + _gotos.Add(gotoStmt); + } + } + + private AstAssignment GetGotoTempAsg(int index) + { + if (_gotoTempAsgs.TryGetValue(index, out AstAssignment gotoTempAsg)) + { + return gotoTempAsg; + } + + AstOperand gotoTemp = NewTemp(VariableType.Bool); + + gotoTempAsg = Assign(gotoTemp, Const(IrConsts.False)); + + _gotoTempAsgs.Add(index, gotoTempAsg); + + return gotoTempAsg; + } + + private void AddGotoTempReset(BasicBlock block, AstAssignment gotoTempAsg) + { + AddNode(gotoTempAsg); + + //For block 0, we don't need to add the extra "reset" at the beggining, + //because it is already the first node to be executed on the shader, + //so it is reset to false by the "local" assignment anyway. + if (block.Index != 0) + { + Info.MainBlock.AddFirst(Assign(gotoTempAsg.Destination, Const(IrConsts.False))); + } + } + + private void NewBlock(AstBlockType type, Operation branchOp, int endIndex) + { + NewBlock(type, GetBranchCond(type, branchOp), endIndex); + } + + private void NewBlock(AstBlockType type, IAstNode cond, int endIndex) + { + AstBlock childBlock = new AstBlock(type, cond); + + AddNode(childBlock); + + _blockStack.Push((_currBlock, _currEndIndex)); + + _currBlock = childBlock; + _currEndIndex = endIndex; + } + + private IAstNode GetBranchCond(AstBlockType type, Operation branchOp) + { + IAstNode cond; + + if (branchOp.Inst == Instruction.Branch) + { + cond = Const(type == AstBlockType.If ? IrConsts.False : IrConsts.True); + } + else + { + cond = GetOperandUse(branchOp.GetSource(0)); + + Instruction invInst = type == AstBlockType.If + ? Instruction.BranchIfTrue + : Instruction.BranchIfFalse; + + if (branchOp.Inst == invInst) + { + cond = new AstOperation(Instruction.LogicalNot, cond); + } + } + + return cond; + } + + public void AddNode(IAstNode node) + { + _currBlock.Add(node); + } + + public GotoStatement[] GetGotos() + { + return _gotos.ToArray(); + } + + private AstOperand NewTemp(VariableType type) + { + AstOperand newTemp = Local(type); + + Info.Locals.Add(newTemp); + + return newTemp; + } + + public AstOperand GetOperandDef(Operand operand) + { + if (TryGetUserAttributeIndex(operand, out int attrIndex)) + { + Info.OAttributes.Add(attrIndex); + } + + return GetOperand(operand); + } + + public AstOperand GetOperandUse(Operand operand) + { + if (TryGetUserAttributeIndex(operand, out int attrIndex)) + { + Info.IAttributes.Add(attrIndex); + } + else if (operand.Type == OperandType.ConstantBuffer) + { + Info.CBuffers.Add(operand.GetCbufSlot()); + } + + return GetOperand(operand); + } + + private AstOperand GetOperand(Operand operand) + { + if (operand == null) + { + return null; + } + + if (operand.Type != OperandType.LocalVariable) + { + return new AstOperand(operand); + } + + if (!_localsMap.TryGetValue(operand, out AstOperand astOperand)) + { + astOperand = new AstOperand(operand); + + _localsMap.Add(operand, astOperand); + + Info.Locals.Add(astOperand); + } + + return astOperand; + } + + private static bool TryGetUserAttributeIndex(Operand operand, out int attrIndex) + { + if (operand.Type == OperandType.Attribute) + { + if (operand.Value >= AttributeConsts.UserAttributeBase && + operand.Value < AttributeConsts.UserAttributeEnd) + { + attrIndex = (operand.Value - AttributeConsts.UserAttributeBase) >> 4; + + return true; + } + else if (operand.Value >= AttributeConsts.FragmentOutputColorBase && + operand.Value < AttributeConsts.FragmentOutputColorEnd) + { + attrIndex = (operand.Value - AttributeConsts.FragmentOutputColorBase) >> 4; + + return true; + } + } + + attrIndex = 0; + + return false; + } + } +}
\ No newline at end of file |