99// </summary>
1010// -----------------------------------------------------------------------
1111
12+ using System ;
1213using System . Collections . Generic ;
14+ using System . IO ;
1315using System . Linq ;
16+ using System . Net ;
1417using Shrinker . Parser . SyntaxNodes ;
1518
1619namespace Shrinker . Parser . Optimizations
@@ -31,6 +34,125 @@ public static void GolfNames(this SyntaxNode rootNode)
3134 }
3235 }
3336
37+ /// <summary>
38+ /// Add #defines for commonly-used terms (E.g. smoothstep, et).
39+ /// </summary>
40+ public static void GolfDefineCommonTerms ( this SyntaxNode rootNode )
41+ {
42+ var userDefinedNames = rootNode . FindUserDefinedNames ( ) ;
43+ var defineMap = new Dictionary < string , string [ ] >
44+ {
45+ { "abs" , new [ ] { "A" } } ,
46+ { "acos" , new [ ] { "AC" } } ,
47+ { "acosh" , new [ ] { "ACH" } } ,
48+ { "asin" , new [ ] { "AS" } } ,
49+ { "asinh" , new [ ] { "ASH" } } ,
50+ { "atan" , new [ ] { "AT" } } ,
51+ { "atanh" , new [ ] { "ATH" } } ,
52+ { "BitsToInt" , new [ ] { "B2I" } } ,
53+ { "BitsToUint" , new [ ] { "B2U" } } ,
54+ { "ceil" , new [ ] { "CL" } } ,
55+ { "clamp" , new [ ] { "C" } } ,
56+ { "cos" , new [ ] { "CS" } } ,
57+ { "cosh" , new [ ] { "CH" } } ,
58+ { "cross" , new [ ] { "X" } } ,
59+ { "degrees" , new [ ] { "DG" } } ,
60+ { "determinant" , new [ ] { "DT" } } ,
61+ { "dFdx" , new [ ] { "DX" } } ,
62+ { "dFdy" , new [ ] { "DY" } } ,
63+ { "distance" , new [ ] { "DST" } } ,
64+ { "dot" , new [ ] { "D" } } ,
65+ { "faceforward" , new [ ] { "FF" } } ,
66+ { "floor" , new [ ] { "F" } } ,
67+ { "fract" , new [ ] { "FC" } } ,
68+ { "fwidth" , new [ ] { "FW" } } ,
69+ { "greaterThan" , new [ ] { "GT" } } ,
70+ { "greaterThanEqual" , new [ ] { "GTE" } } ,
71+ { "intBitsTo" , new [ ] { "IB2" } } ,
72+ { "inverse" , new [ ] { "I" } } ,
73+ { "inversesqrt" , new [ ] { "IS" } } ,
74+ { "length" , new [ ] { "L" } } ,
75+ { "lessThan" , new [ ] { "LT" } } ,
76+ { "lessThanEqual" , new [ ] { "LTE" } } ,
77+ { "matrixCompMult" , new [ ] { "MCM" } } ,
78+ { "max" , new [ ] { "MX" } } ,
79+ { "min" , new [ ] { "MN" } } ,
80+ { "modf" , new [ ] { "MF" } } ,
81+ { "normalize" , new [ ] { "N" , "U" , "NM" } } ,
82+ { "notEqual" , new [ ] { "NE" } } ,
83+ { "radians" , new [ ] { "R" } } ,
84+ { "reflect" , new [ ] { "RL" } } ,
85+ { "refract" , new [ ] { "RR" } } ,
86+ { "round" , new [ ] { "RND" } } ,
87+ { "sin" , new [ ] { "SN" } } ,
88+ { "sinh" , new [ ] { "SNH" } } ,
89+ { "smoothstep" , new [ ] { "S" , "SS" } } ,
90+ { "sqrt" , new [ ] { "SQ" } } ,
91+ { "tan" , new [ ] { "TN" } } ,
92+ { "tanh" , new [ ] { "TH" } } ,
93+ { "texelFetch" , new [ ] { "TF" } } ,
94+ { "texelFetchOffset" , new [ ] { "TFO" } } ,
95+ { "texture" , new [ ] { "T" } } ,
96+ { "textureGrad" , new [ ] { "TG" } } ,
97+ { "textureGradOffset" , new [ ] { "TGO" } } ,
98+ { "textureLod" , new [ ] { "TL" } } ,
99+ { "textureLodOffset" , new [ ] { "TLO" } } ,
100+ { "textureProj" , new [ ] { "TP" } } ,
101+ { "textureProjGrad" , new [ ] { "TPG" } } ,
102+ { "textureProjLod" , new [ ] { "TPL" } } ,
103+ { "textureProjLodOffset" , new [ ] { "TPLO" } } ,
104+ { "textureSize" , new [ ] { "TS" } } ,
105+ { "transpose" , new [ ] { "TNS" } } ,
106+ { "trunc" , new [ ] { "TC" } } ,
107+ { "uintBitsTo" , new [ ] { "UB2" } }
108+ } ;
109+
110+ foreach ( var n in new [ ] { 2 , 3 , 4 } )
111+ {
112+ defineMap . Add ( $ "ivec{ n } ", new [ ] { $ "iv{ n } ", $ "IV{ n } " } ) ;
113+ defineMap . Add ( $ "vec{ n } ", new [ ] { $ "v{ n } ", $ "V{ n } ", $ "_v{ n } " } ) ;
114+ defineMap . Add ( $ "mat{ n } ", new [ ] { $ "m{ n } ", $ "M{ n } ", $ "_m{ n } " } ) ;
115+ }
116+
117+ // Check for duplicates.
118+ var allKeys = defineMap . SelectMany ( o => o . Value ) . ToList ( ) ;
119+ var duplicates = allKeys . Where ( o => allKeys . Count ( s => s == o ) > 1 ) . ToList ( ) ;
120+ if ( duplicates . Any ( ) )
121+ throw new InvalidOperationException ( "Duplicate substitutes detected: " + duplicates . Aggregate ( ( a , b ) => $ "{ a } , { b } ") ) ;
122+
123+ var keywordNodes =
124+ rootNode . TheTree
125+ . OfType < GlslFunctionCallSyntaxNode > ( )
126+ . Where ( o => defineMap . ContainsKey ( o . Name ) )
127+ . ToList ( ) ;
128+ foreach ( var keyword in defineMap . Keys )
129+ {
130+ // How many occurrences are in the code?
131+ var nodes = keywordNodes . Where ( o => o . Name == keyword ) . ToList ( ) ;
132+ if ( nodes . Count <= 1 )
133+ continue ; // Not worth it.
134+
135+ // What would the substitute be?
136+ var replacement = defineMap [ keyword ] . FirstOrDefault ( o => ! userDefinedNames . Contains ( o ) ) ;
137+ if ( replacement == null )
138+ continue ; // Replacement term is already in use in the code. Oh well - we tried...
139+
140+ // What would the saving be, should we #define it?
141+ var toAdd = $ "#define { replacement } { keyword } ". Length + nodes . Count * replacement . Length ;
142+ var toRemove = nodes . Count * keyword . Length ;
143+ if ( toAdd >= toRemove )
144+ continue ; // Could replace with #define, but not worth it.
145+
146+ // We'll get a space saving - Replace the nodes...
147+ nodes . ForEach ( o => o . Rename ( replacement ) ) ;
148+
149+ // ...and add a #define.
150+ var existingDefine = rootNode . Children . OfType < PragmaDefineSyntaxNode > ( ) . FirstOrDefault ( ) ;
151+ var insertBefore = existingDefine ?? rootNode . Children . First ( ) ;
152+ insertBefore . Parent . InsertChild ( insertBefore . NodeIndex , new PragmaDefineSyntaxNode ( replacement , null , new SyntaxNode [ ] { new GenericSyntaxNode ( keyword ) } ) ) ;
153+ }
154+ }
155+
34156 private static string NameWithoutDotSuffix ( string name ) => name . Split ( '.' ) . First ( ) ;
35157
36158 private static Dictionary < string , string > BuildGolfRenameMap ( SyntaxNode rootNode )
0 commit comments