Skip to content

Commit 6bc79e8

Browse files
committed
Feature: Precalculate clamp, length, normalize, floor, ceil, trunc, and fract.
1 parent 882da8f commit 6bc79e8

File tree

4 files changed

+221
-4
lines changed

4 files changed

+221
-4
lines changed

ShaderShrinker/Shrinker.Parser/Optimizations/PerformArithmeticExtension.cs

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,10 +138,66 @@ public static bool PerformArithmetic(this SyntaxNode rootNode)
138138
}
139139
}
140140

141+
// clamp(n, min, max) => <the result>
142+
foreach (var clampNode in functionCalls
143+
.Where(o => o.Name == "clamp" && o.Params.IsNumericCsv())
144+
.ToList())
145+
{
146+
var xyz = clampNode.Params.Children.Where(o => o.Token is FloatToken).Select(o => ((FloatToken)o.Token).Number).ToList();
147+
if (xyz.Count == 3)
148+
{
149+
clampNode.Params.Remove();
150+
clampNode.ReplaceWith(new GenericSyntaxNode(FloatToken.From(Math.Min(Math.Max(xyz[0], xyz[1]), xyz[2]), MaxDp)));
151+
didChange = true;
152+
}
153+
}
154+
155+
// length(vecN) => <the result>
156+
foreach (var lengthNode in functionCalls
157+
.Where(o => o.Name == "length" && o.Params.Children.Count == 2)
158+
.ToList())
159+
{
160+
var nums = GetVectorNumericCsv(lengthNode.Params.Children.First());
161+
if (nums == null)
162+
continue;
163+
164+
var len = Math.Sqrt(nums.Sum(o => o * o));
165+
lengthNode.ReplaceWith(new GenericSyntaxNode(FloatToken.From(len, MaxDp)));
166+
didChange = true;
167+
}
168+
169+
// normalize(vecN) => <the result>
170+
foreach (var normalizeNode in functionCalls
171+
.Where(o => o.Name == "normalize" && o.Params.Children.Count == 2)
172+
.ToList())
173+
{
174+
var nums = GetVectorNumericCsv(normalizeNode.Params.Children.First());
175+
if (nums == null)
176+
continue;
177+
178+
var mag = Math.Sqrt(nums.Sum(o => o * o));
179+
180+
// Squash vecN(n, n, n, ...) down to vecN(n)
181+
if (nums.Count > 1 && nums.Distinct().Count() == 1)
182+
nums = new List<double> { nums.First() };
183+
184+
var newVectorNumNodes = nums.SelectMany(o => new[]
185+
{
186+
new GenericSyntaxNode(new CommaToken()),
187+
new GenericSyntaxNode(FloatToken.From(o / mag, MaxDp).AsIntIfPossible())
188+
}).Skip(1);
189+
190+
var newVectorBrackets = new RoundBracketSyntaxNode(newVectorNumNodes);
191+
var newVectorNode = new GenericSyntaxNode(normalizeNode.Params.Children.First().Token.Content);
192+
normalizeNode.ReplaceWith(newVectorNode).InsertNextSibling(newVectorBrackets);
193+
didChange = true;
194+
}
195+
141196
// Constant math/trig functions => <the result>
142197
var mathOp = new List<Tuple<string, Func<double, double>>>
143198
{
144199
new Tuple<string, Func<double, double>>("abs", Math.Abs),
200+
new Tuple<string, Func<double, double>>("length", Math.Abs),
145201
new Tuple<string, Func<double, double>>("sqrt", d => Math.Sqrt(Math.Abs(d))),
146202
new Tuple<string, Func<double, double>>("sin", Math.Sin),
147203
new Tuple<string, Func<double, double>>("cos", Math.Cos),
@@ -151,7 +207,11 @@ public static bool PerformArithmetic(this SyntaxNode rootNode)
151207
new Tuple<string, Func<double, double>>("atan", Math.Atan),
152208
new Tuple<string, Func<double, double>>("radians", x => x / 180.0 * Math.PI),
153209
new Tuple<string, Func<double, double>>("degrees", x => x / Math.PI * 180.0),
154-
new Tuple<string, Func<double, double>>("sign", x => Math.Sign(x))
210+
new Tuple<string, Func<double, double>>("sign", x => Math.Sign(x)),
211+
new Tuple<string, Func<double, double>>("floor", Math.Floor),
212+
new Tuple<string, Func<double, double>>("ceil", Math.Ceiling),
213+
new Tuple<string, Func<double, double>>("trunc", Math.Truncate),
214+
new Tuple<string, Func<double, double>>("fract", x => x - Math.Truncate(x))
155215
};
156216

157217
foreach (var mathNode in functionCalls
@@ -335,6 +395,35 @@ public static bool PerformArithmetic(this SyntaxNode rootNode)
335395
return repeatSimplifications;
336396
}
337397

398+
private static List<double> GetVectorNumericCsv(SyntaxNode vectorNode)
399+
{
400+
if (vectorNode.Next is not RoundBracketSyntaxNode brackets || !brackets.IsNumericCsv())
401+
return null;
402+
403+
// Get vector type.
404+
int c;
405+
switch (vectorNode.Token?.Content)
406+
{
407+
case "vec2":
408+
c = 2;
409+
break;
410+
case "vec3":
411+
c = 3;
412+
break;
413+
case "vec4":
414+
c = 4;
415+
break;
416+
default:
417+
return null;
418+
}
419+
420+
// Support vectors with single component (E.g. vec3(x)).
421+
var nums = brackets.GetCsv().Select(o => double.Parse(o.Single().Token.Content)).ToList();
422+
while (nums.Count < c)
423+
nums.Add(nums.Last());
424+
return nums;
425+
}
426+
338427
private static bool IsSafeToPerformMath(SyntaxNode lhsNumNode, SymbolOperatorToken symbol, SyntaxNode rhsNumNode)
339428
{
340429
if (lhsNumNode == null || symbol == null || rhsNumNode == null)

ShaderShrinker/Shrinker.sln.DotSettings

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,7 @@ namespace $NAMESPACE$&#xD;
635635
<s:Boolean x:Key="/Default/UserDictionary/Words/=directsharedmemory/@EntryIndexedValue">True</s:Boolean>
636636
<s:Boolean x:Key="/Default/UserDictionary/Words/=Enabledness/@EntryIndexedValue">True</s:Boolean>
637637
<s:Boolean x:Key="/Default/UserDictionary/Words/=fastmath/@EntryIndexedValue">True</s:Boolean>
638+
<s:Boolean x:Key="/Default/UserDictionary/Words/=Fract/@EntryIndexedValue">True</s:Boolean>
638639
<s:Boolean x:Key="/Default/UserDictionary/Words/=glsl/@EntryIndexedValue">True</s:Boolean>
639640
<s:Boolean x:Key="/Default/UserDictionary/Words/=hintable/@EntryIndexedValue">True</s:Boolean>
640641
<s:Boolean x:Key="/Default/UserDictionary/Words/=IDOM/@EntryIndexedValue">True</s:Boolean>
@@ -647,6 +648,7 @@ namespace $NAMESPACE$&#xD;
647648
<s:Boolean x:Key="/Default/UserDictionary/Words/=Mako/@EntryIndexedValue">True</s:Boolean>
648649
<s:Boolean x:Key="/Default/UserDictionary/Words/=MYIRO/@EntryIndexedValue">True</s:Boolean>
649650
<s:Boolean x:Key="/Default/UserDictionary/Words/=Nielson/@EntryIndexedValue">True</s:Boolean>
651+
<s:Boolean x:Key="/Default/UserDictionary/Words/=nums/@EntryIndexedValue">True</s:Boolean>
650652
<s:Boolean x:Key="/Default/UserDictionary/Words/=Plex/@EntryIndexedValue">True</s:Boolean>
651653
<s:Boolean x:Key="/Default/UserDictionary/Words/=printrun/@EntryIndexedValue">True</s:Boolean>
652654
<s:Boolean x:Key="/Default/UserDictionary/Words/=screenpro/@EntryIndexedValue">True</s:Boolean>
@@ -661,6 +663,7 @@ namespace $NAMESPACE$&#xD;
661663
<s:Boolean x:Key="/Default/UserDictionary/Words/=thumbnailer/@EntryIndexedValue">True</s:Boolean>
662664
<s:Boolean x:Key="/Default/UserDictionary/Words/=Thumbnailing/@EntryIndexedValue">True</s:Boolean>
663665
<s:Boolean x:Key="/Default/UserDictionary/Words/=Ticketer/@EntryIndexedValue">True</s:Boolean>
666+
<s:Boolean x:Key="/Default/UserDictionary/Words/=Trunc/@EntryIndexedValue">True</s:Boolean>
664667
<s:Boolean x:Key="/Default/UserDictionary/Words/=t_002Eprof/@EntryIndexedValue">True</s:Boolean>
665668
<s:Boolean x:Key="/Default/UserDictionary/Words/=uvec/@EntryIndexedValue">True</s:Boolean>
666669
<s:Boolean x:Key="/Default/UserDictionary/Words/=yxxy/@EntryIndexedValue">True</s:Boolean>

ShaderShrinker/UnitTests/TestFiles/SimplifiedReference/Temple.glsl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,10 @@ void mainImage(out vec4 fragColor, vec2 fragCoord) {
145145
vec2 hit = vec2(0),
146146
uv = (fragCoord - .5 * iResolution.xy) / iResolution.y;
147147
vec3 p,
148-
forward = normalize(vec3(-20, -10.5, 25)),
148+
forward = vec3(-.59359, -.31163, .74198),
149149
right = normalize(cross(vec3(0, 1, 0), forward)),
150150
rayDir = normalize(forward + uv.x * right + uv.y * cross(forward, right)),
151-
sunPosUnit = normalize(vec3(.5, .8, .7)),
151+
sunPosUnit = vec3(.42563, .68101, .59588),
152152
sunPos = sunPosUnit * 1e2,
153153
col = vec3(0);
154154
float d = 0.;

ShaderShrinker/UnitTests/VectorArithmeticTests.cs

Lines changed: 126 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ public void CheckArithmeticWithAbsFunction(
122122
}
123123

124124
[Test, Sequential]
125-
public void CheckArithmeticWitSqrtFunction(
125+
public void CheckArithmeticWithSqrtFunction(
126126
[Values("float f = sqrt(4.0);",
127127
"float f = sqrt(-8.0);",
128128
"float f; f = sqrt(sqrt(4.));")] string code,
@@ -142,6 +142,129 @@ public void CheckArithmeticWitSqrtFunction(
142142
Assert.That(rootNode.ToCode().ToSimple(), Is.EqualTo(expected));
143143
}
144144

145+
[Test, Sequential]
146+
public void CheckArithmeticWithLengthFunction(
147+
[Values("float f = length(12.34);",
148+
"float f = length(-12.34);",
149+
"float f = length(vec2(3, 4));",
150+
"float f = length(vec3(9));")] string code,
151+
[Values("float f = 12.34;",
152+
"float f = 12.34;",
153+
"float f = 5.;",
154+
"float f = 15.58846;")] string expected)
155+
{
156+
var lexer = new Lexer();
157+
lexer.Load(code);
158+
159+
var options = CustomOptions.None();
160+
options.PerformArithmetic = true;
161+
var rootNode = new Parser(lexer)
162+
.Parse()
163+
.Simplify(options);
164+
165+
Assert.That(rootNode.ToCode().ToSimple(), Is.EqualTo(expected));
166+
}
167+
168+
[Test, Sequential]
169+
public void CheckArithmeticWithNormalizeFunction(
170+
[Values("float f = normalize(vec2(3, 4));",
171+
"float f = normalize(vec3(9));")] string code,
172+
[Values("float f = vec2(.6, .8);",
173+
"float f = vec3(.57735);")] string expected)
174+
{
175+
var lexer = new Lexer();
176+
lexer.Load(code);
177+
178+
var options = CustomOptions.None();
179+
options.PerformArithmetic = true;
180+
var rootNode = new Parser(lexer)
181+
.Parse()
182+
.Simplify(options);
183+
184+
Assert.That(rootNode.ToCode().ToSimple(), Is.EqualTo(expected));
185+
}
186+
187+
[Test, Sequential]
188+
public void CheckArithmeticWithClampFunction(
189+
[Values("float f = clamp(12.34, 0.0, 20.0);",
190+
"float f = clamp(12.34, 15.0, 20.0);",
191+
"float f = clamp(12.34, 0.0, 10.0);")] string code,
192+
[Values("float f = 12.34;",
193+
"float f = 15.;",
194+
"float f = 10.;")] string expected)
195+
{
196+
var lexer = new Lexer();
197+
lexer.Load(code);
198+
199+
var options = CustomOptions.None();
200+
options.PerformArithmetic = true;
201+
var rootNode = new Parser(lexer)
202+
.Parse()
203+
.Simplify(options);
204+
205+
Assert.That(rootNode.ToCode().ToSimple(), Is.EqualTo(expected));
206+
}
207+
208+
[Test]
209+
public void CheckArithmeticWithFloorFunction()
210+
{
211+
var lexer = new Lexer();
212+
lexer.Load("float f = floor(12.3);");
213+
214+
var options = CustomOptions.None();
215+
options.PerformArithmetic = true;
216+
var rootNode = new Parser(lexer)
217+
.Parse()
218+
.Simplify(options);
219+
220+
Assert.That(rootNode.ToCode().ToSimple(), Is.EqualTo("float f = 12.;"));
221+
}
222+
223+
[Test]
224+
public void CheckArithmeticWithCeilFunction()
225+
{
226+
var lexer = new Lexer();
227+
lexer.Load("float f = ceil(12.3);");
228+
229+
var options = CustomOptions.None();
230+
options.PerformArithmetic = true;
231+
var rootNode = new Parser(lexer)
232+
.Parse()
233+
.Simplify(options);
234+
235+
Assert.That(rootNode.ToCode().ToSimple(), Is.EqualTo("float f = 13.;"));
236+
}
237+
238+
[Test]
239+
public void CheckArithmeticWithTruncFunction()
240+
{
241+
var lexer = new Lexer();
242+
lexer.Load("float f = trunc(12.9);");
243+
244+
var options = CustomOptions.None();
245+
options.PerformArithmetic = true;
246+
var rootNode = new Parser(lexer)
247+
.Parse()
248+
.Simplify(options);
249+
250+
Assert.That(rootNode.ToCode().ToSimple(), Is.EqualTo("float f = 12.;"));
251+
}
252+
253+
[Test]
254+
public void CheckArithmeticWithFractFunction()
255+
{
256+
var lexer = new Lexer();
257+
lexer.Load("float f = fract(12.9);");
258+
259+
var options = CustomOptions.None();
260+
options.PerformArithmetic = true;
261+
var rootNode = new Parser(lexer)
262+
.Parse()
263+
.Simplify(options);
264+
265+
Assert.That(rootNode.ToCode().ToSimple(), Is.EqualTo("float f = .9;"));
266+
}
267+
145268
[Test, Sequential]
146269
public void CheckArithmeticWithTrigFunctions(
147270
[Values("float f = sin(3.141);",
@@ -326,5 +449,7 @@ public void CheckArithmeticWithVectorAndVector(
326449

327450
Assert.That(rootNode.ToCode().ToSimple(), Is.EqualTo(expected));
328451
}
452+
453+
// todo - normalize(vec3(-20, -10.5, 25))
329454
}
330455
}

0 commit comments

Comments
 (0)