diff options
| author | gdkchan <gab.dark.100@gmail.com> | 2018-04-08 16:17:35 -0300 |
|---|---|---|
| committer | gdkchan <gab.dark.100@gmail.com> | 2018-04-08 16:41:38 -0300 |
| commit | b9aa3966c00b4bb3ff0292dc28ed53ad26cf284b (patch) | |
| tree | cd2ab3d65c61ac6c6ceb312116e5d138868a3e18 /Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs | |
| parent | 7acd0e01226d64d05b2675f6ae07507039a31835 (diff) | |
Merge shader branch, adding support for GLSL decompilation, a macro
interpreter, and a rewrite of the GPU code.
Diffstat (limited to 'Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs')
| -rw-r--r-- | Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs new file mode 100644 index 00000000..69457aeb --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderOptExprProp.cs @@ -0,0 +1,266 @@ +using System; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gal.Shader +{ + static class ShaderOptExprProp + { + private struct UseSite + { + public object Parent; + + public int OperIndex; + + public UseSite(object Parent, int OperIndex) + { + this.Parent = Parent; + this.OperIndex = OperIndex; + } + } + + private class RegUse + { + public ShaderIrAsg Asg { get; private set; } + + public int AsgIndex { get; private set; } + + private bool Propagate; + + private List<UseSite> Sites; + + public RegUse() + { + Sites = new List<UseSite>(); + } + + public void AddUseSite(UseSite Site) + { + Sites.Add(Site); + } + + public bool TryPropagate() + { + //This happens when a untiliazied register is used, + //this usually indicates a decoding error, but may also + //be cased by bogus programs (?). In any case, we just + //keep the unitialized access and avoid trying to propagate + //the expression (since we can't propagate what doesn't yet exist). + if (Asg == null || !Propagate) + { + return false; + } + + if (Sites.Count > 0) + { + foreach (UseSite Site in Sites) + { + if (Site.Parent is ShaderIrCond Cond) + { + switch (Site.OperIndex) + { + case 0: Cond.Pred = Asg.Src; break; + case 1: Cond.Child = Asg.Src; break; + + default: throw new InvalidOperationException(); + } + } + else if (Site.Parent is ShaderIrOp Op) + { + switch (Site.OperIndex) + { + case 0: Op.OperandA = Asg.Src; break; + case 1: Op.OperandB = Asg.Src; break; + case 2: Op.OperandC = Asg.Src; break; + + default: throw new InvalidOperationException(); + } + } + else if (Site.Parent is ShaderIrAsg SiteAsg) + { + SiteAsg.Src = Asg.Src; + } + else + { + throw new InvalidOperationException(); + } + } + } + + return true; + } + + public void SetNewAsg(ShaderIrAsg Asg, int AsgIndex, bool Propagate) + { + this.Asg = Asg; + this.AsgIndex = AsgIndex; + this.Propagate = Propagate; + + Sites.Clear(); + } + } + + public static void Optimize(List<ShaderIrNode> Nodes, GalShaderType ShaderType) + { + Dictionary<int, RegUse> Uses = new Dictionary<int, RegUse>(); + + RegUse GetUse(int Key) + { + RegUse Use; + + if (!Uses.TryGetValue(Key, out Use)) + { + Use = new RegUse(); + + Uses.Add(Key, Use); + } + + return Use; + } + + int GetGprKey(int GprIndex) + { + return GprIndex; + } + + int GetPredKey(int PredIndex) + { + return PredIndex | 0x10000000; + } + + RegUse GetGprUse(int GprIndex) + { + return GetUse(GetGprKey(GprIndex)); + } + + RegUse GetPredUse(int PredIndex) + { + return GetUse(GetPredKey(PredIndex)); + } + + void FindRegUses(List<(int, UseSite)> UseList, object Parent, ShaderIrNode Node, int OperIndex = 0) + { + if (Node is ShaderIrAsg Asg) + { + FindRegUses(UseList, Asg, Asg.Src); + } + else if (Node is ShaderIrCond Cond) + { + FindRegUses(UseList, Cond, Cond.Pred, 0); + FindRegUses(UseList, Cond, Cond.Child, 1); + } + else if (Node is ShaderIrOp Op) + { + FindRegUses(UseList, Op, Op.OperandA, 0); + FindRegUses(UseList, Op, Op.OperandB, 1); + FindRegUses(UseList, Op, Op.OperandC, 2); + } + else if (Node is ShaderIrOperGpr Gpr && Gpr.Index != ShaderIrOperGpr.ZRIndex) + { + UseList.Add((GetGprKey(Gpr.Index), new UseSite(Parent, OperIndex))); + } + else if (Node is ShaderIrOperPred Pred) + { + UseList.Add((GetPredKey(Pred.Index), new UseSite(Parent, OperIndex))); + } + } + + void TryAddRegUseSite(ShaderIrNode Node) + { + List<(int, UseSite)> UseList = new List<(int, UseSite)>(); + + FindRegUses(UseList, null, Node); + + foreach ((int Key, UseSite Site) in UseList) + { + GetUse(Key).AddUseSite(Site); + } + } + + bool TryPropagate(RegUse Use) + { + //We can only propagate if the registers that the expression depends + //on weren't assigned after the original expression assignment + //to a register took place. We traverse the expression tree to find + //all registers being used, if any of those registers was assigned + //after the assignment to be propagated, then we can't propagate. + if (Use?.Asg == null) + { + return false; + } + + List<(int, UseSite)> UseList = new List<(int, UseSite)>(); + + FindRegUses(UseList, Use.Asg, Use.Asg.Src); + + foreach ((int Key, UseSite Site) in UseList) + { + if (GetUse(Key).AsgIndex >= Use.AsgIndex) + { + return false; + } + } + + return Use.TryPropagate(); + } + + for (int Index = 0, AsgIndex = 0; Index < Nodes.Count; Index++, AsgIndex++) + { + ShaderIrNode Node = Nodes[Index]; + + bool IsConditional = Node is ShaderIrCond; + + TryAddRegUseSite(Node); + + while (Node is ShaderIrCond Cond) + { + Node = Cond.Child; + } + + if (!(Node is ShaderIrAsg Asg)) + { + continue; + } + + RegUse Use = null; + + if (Asg.Dst is ShaderIrOperGpr Gpr && Gpr.Index != ShaderIrOperGpr.ZRIndex) + { + Use = GetGprUse(Gpr.Index); + } + else if (Asg.Dst is ShaderIrOperPred Pred) + { + Use = GetPredUse(Pred.Index); + } + + if (!IsConditional && TryPropagate(Use)) + { + Nodes.Remove(Use.Asg); + + Index--; + } + + //All nodes inside conditional nodes can't be propagated, + //as we don't even know if they will be executed to begin with. + Use?.SetNewAsg(Asg, AsgIndex, !IsConditional); + } + + foreach (RegUse Use in Uses.Values) + { + //Gprs 0-3 are the color output on fragment shaders, + //so we can't remove the last assignments to those registers. + if (ShaderType == GalShaderType.Fragment) + { + if (Use.Asg?.Dst is ShaderIrOperGpr Gpr && Gpr.Index < 4) + { + continue; + } + } + + if (TryPropagate(Use)) + { + Nodes.Remove(Use.Asg); + } + } + } + } +}
\ No newline at end of file |
