ShaderOptExprProp.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. using System;
  2. using System.Collections.Generic;
  3. namespace Ryujinx.Graphics.Gal.Shader
  4. {
  5. static class ShaderOptExprProp
  6. {
  7. private struct UseSite
  8. {
  9. public ShaderIrNode Parent { get; private set; }
  10. public ShaderIrCond Cond { get; private set; }
  11. public int UseIndex { get; private set; }
  12. public int OperIndex { get; private set; }
  13. public UseSite(
  14. ShaderIrNode Parent,
  15. ShaderIrCond Cond,
  16. int UseIndex,
  17. int OperIndex)
  18. {
  19. this.Parent = Parent;
  20. this.Cond = Cond;
  21. this.UseIndex = UseIndex;
  22. this.OperIndex = OperIndex;
  23. }
  24. }
  25. private class RegUse
  26. {
  27. public ShaderIrAsg Asg { get; private set; }
  28. public int AsgIndex { get; private set; }
  29. public int LastSiteIndex { get; private set; }
  30. public ShaderIrCond Cond { get; private set; }
  31. private List<UseSite> Sites;
  32. public RegUse()
  33. {
  34. Sites = new List<UseSite>();
  35. }
  36. public void AddUseSite(UseSite Site)
  37. {
  38. if (LastSiteIndex < Site.UseIndex)
  39. {
  40. LastSiteIndex = Site.UseIndex;
  41. }
  42. Sites.Add(Site);
  43. }
  44. public bool TryPropagate()
  45. {
  46. //This happens when a untiliazied register is used,
  47. //this usually indicates a decoding error, but may also
  48. //be caused by bogus programs (?). In any case, we just
  49. //keep the unitialized access and avoid trying to propagate
  50. //the expression (since we can't propagate what doesn't yet exist).
  51. if (Asg == null)
  52. {
  53. return false;
  54. }
  55. if (Cond != null)
  56. {
  57. //If the assignment is conditional, we can only propagate
  58. //to the use sites that shares the same condition of the assignment.
  59. foreach (UseSite Site in Sites)
  60. {
  61. if (!IsSameCondition(Cond, Site.Cond))
  62. {
  63. return false;
  64. }
  65. }
  66. }
  67. if (Sites.Count > 0)
  68. {
  69. foreach (UseSite Site in Sites)
  70. {
  71. if (Site.Parent is ShaderIrCond Cond)
  72. {
  73. switch (Site.OperIndex)
  74. {
  75. case 0: Cond.Pred = Asg.Src; break;
  76. case 1: Cond.Child = Asg.Src; break;
  77. default: throw new InvalidOperationException();
  78. }
  79. }
  80. else if (Site.Parent is ShaderIrOp Op)
  81. {
  82. switch (Site.OperIndex)
  83. {
  84. case 0: Op.OperandA = Asg.Src; break;
  85. case 1: Op.OperandB = Asg.Src; break;
  86. case 2: Op.OperandC = Asg.Src; break;
  87. default: throw new InvalidOperationException();
  88. }
  89. }
  90. else if (Site.Parent is ShaderIrAsg SiteAsg)
  91. {
  92. SiteAsg.Src = Asg.Src;
  93. }
  94. else
  95. {
  96. throw new InvalidOperationException();
  97. }
  98. }
  99. }
  100. return true;
  101. }
  102. public void SetNewAsg(ShaderIrAsg Asg, int AsgIndex, ShaderIrCond Cond)
  103. {
  104. this.Asg = Asg;
  105. this.AsgIndex = AsgIndex;
  106. this.Cond = Cond;
  107. LastSiteIndex = 0;
  108. Sites.Clear();
  109. }
  110. }
  111. public static void Optimize(List<ShaderIrNode> Nodes, GalShaderType ShaderType)
  112. {
  113. Dictionary<int, RegUse> Uses = new Dictionary<int, RegUse>();
  114. RegUse GetUse(int Key)
  115. {
  116. RegUse Use;
  117. if (!Uses.TryGetValue(Key, out Use))
  118. {
  119. Use = new RegUse();
  120. Uses.Add(Key, Use);
  121. }
  122. return Use;
  123. }
  124. int GetGprKey(int GprIndex)
  125. {
  126. return GprIndex;
  127. }
  128. int GetPredKey(int PredIndex)
  129. {
  130. return PredIndex | 0x10000000;
  131. }
  132. RegUse GetGprUse(int GprIndex)
  133. {
  134. return GetUse(GetGprKey(GprIndex));
  135. }
  136. RegUse GetPredUse(int PredIndex)
  137. {
  138. return GetUse(GetPredKey(PredIndex));
  139. }
  140. void RemoveUse(RegUse Use)
  141. {
  142. if (!Nodes.Remove((ShaderIrNode)Use.Cond ?? Use.Asg))
  143. {
  144. throw new InvalidOperationException();
  145. }
  146. }
  147. void FindRegUses(
  148. List<(int, UseSite)> UseList,
  149. ShaderIrNode Parent,
  150. ShaderIrNode Node,
  151. ShaderIrCond CondNode,
  152. int UseIndex,
  153. int OperIndex = 0)
  154. {
  155. if (Node is ShaderIrAsg Asg)
  156. {
  157. FindRegUses(UseList, Asg, Asg.Src, CondNode, UseIndex);
  158. }
  159. else if (Node is ShaderIrCond Cond)
  160. {
  161. FindRegUses(UseList, Cond, Cond.Pred, CondNode, UseIndex, 0);
  162. FindRegUses(UseList, Cond, Cond.Child, CondNode, UseIndex, 1);
  163. }
  164. else if (Node is ShaderIrOp Op)
  165. {
  166. FindRegUses(UseList, Op, Op.OperandA, CondNode, UseIndex, 0);
  167. FindRegUses(UseList, Op, Op.OperandB, CondNode, UseIndex, 1);
  168. FindRegUses(UseList, Op, Op.OperandC, CondNode, UseIndex, 2);
  169. }
  170. else if (Node is ShaderIrOperGpr Gpr && !Gpr.IsConst)
  171. {
  172. UseList.Add((GetGprKey(Gpr.Index), new UseSite(Parent, CondNode, UseIndex, OperIndex)));
  173. }
  174. else if (Node is ShaderIrOperPred Pred)
  175. {
  176. UseList.Add((GetPredKey(Pred.Index), new UseSite(Parent, CondNode, UseIndex, OperIndex)));
  177. }
  178. }
  179. void TryAddRegUseSite(ShaderIrNode Node, ShaderIrCond CondNode, int UseIndex)
  180. {
  181. List<(int, UseSite)> UseList = new List<(int, UseSite)>();
  182. FindRegUses(UseList, null, Node, CondNode, UseIndex);
  183. foreach ((int Key, UseSite Site) in UseList)
  184. {
  185. GetUse(Key).AddUseSite(Site);
  186. }
  187. }
  188. bool TryPropagate(RegUse Use)
  189. {
  190. //We can only propagate if the registers that the expression depends
  191. //on weren't assigned after the original expression assignment
  192. //to a register took place. We traverse the expression tree to find
  193. //all registers being used, if any of those registers was assigned
  194. //after the assignment to be propagated, then we can't propagate.
  195. if (Use?.Asg == null)
  196. {
  197. return false;
  198. }
  199. List<(int, UseSite)> UseList = new List<(int, UseSite)>();
  200. if (Use.Cond != null)
  201. {
  202. FindRegUses(UseList, null, Use.Cond, null, 0);
  203. }
  204. else
  205. {
  206. FindRegUses(UseList, Use.Asg, Use.Asg.Src, null, 0);
  207. }
  208. foreach ((int Key, UseSite Site) in UseList)
  209. {
  210. //TODO: Build an assignment list inside RegUse,
  211. //and check if there is an assignment inside the
  212. //range of Use.AsgIndex and Use.LastSiteIndex,
  213. //and if that's the case, then we should return false.
  214. //The current method is too conservative.
  215. if (GetUse(Key).AsgIndex >= Use.AsgIndex)
  216. {
  217. return false;
  218. }
  219. }
  220. return Use.TryPropagate();
  221. }
  222. for (int Index = 0, IterCount = 0; Index < Nodes.Count; Index++, IterCount++)
  223. {
  224. ShaderIrNode Node = Nodes[Index];
  225. ShaderIrCond CondNode = null;
  226. if (Node is ShaderIrCond)
  227. {
  228. CondNode = (ShaderIrCond)Node;
  229. }
  230. TryAddRegUseSite(Node, CondNode, IterCount);;
  231. while (Node is ShaderIrCond Cond)
  232. {
  233. Node = Cond.Child;
  234. }
  235. if (!(Node is ShaderIrAsg Asg))
  236. {
  237. continue;
  238. }
  239. RegUse Use = null;
  240. if (Asg.Dst is ShaderIrOperGpr Gpr && !Gpr.IsConst)
  241. {
  242. Use = GetGprUse(Gpr.Index);
  243. }
  244. else if (Asg.Dst is ShaderIrOperPred Pred)
  245. {
  246. Use = GetPredUse(Pred.Index);
  247. }
  248. bool CanRemoveAsg = CondNode == null;
  249. CanRemoveAsg |= IsSameCondition(CondNode, Use?.Cond);
  250. if (CanRemoveAsg && TryPropagate(Use))
  251. {
  252. RemoveUse(Use);
  253. //Note: Only decrement if the removal was successful.
  254. //RemoveUse throws when this is not the case so we should be good.
  255. Index--;
  256. }
  257. //All nodes inside conditional nodes can't be propagated,
  258. //as we don't even know if they will be executed to begin with.
  259. Use?.SetNewAsg(Asg, IterCount, CondNode);
  260. }
  261. foreach (RegUse Use in Uses.Values)
  262. {
  263. //Gprs 0-3 are the color output on fragment shaders,
  264. //so we can't remove the last assignments to those registers.
  265. if (ShaderType == GalShaderType.Fragment)
  266. {
  267. if (Use.Asg?.Dst is ShaderIrOperGpr Gpr && Gpr.Index < 4)
  268. {
  269. continue;
  270. }
  271. }
  272. if (TryPropagate(Use))
  273. {
  274. RemoveUse(Use);
  275. }
  276. }
  277. }
  278. private static bool IsSameCondition(ShaderIrCond CondA, ShaderIrCond CondB)
  279. {
  280. if (CondA == null || CondB == null)
  281. {
  282. return CondA == CondB;
  283. }
  284. if (CondA.Not != CondB.Not)
  285. {
  286. return false;
  287. }
  288. if (CondA.Pred is ShaderIrOperPred PredA)
  289. {
  290. if (!(CondB.Pred is ShaderIrOperPred PredB))
  291. {
  292. return false;
  293. }
  294. if (PredA.Index != PredB.Index)
  295. {
  296. return false;
  297. }
  298. }
  299. else if (CondA.Pred != CondB.Pred)
  300. {
  301. return false;
  302. }
  303. return true;
  304. }
  305. }
  306. }