| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 |
- 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);
- }
- }
- }
- }
- }
|