Skip to content

Commit c28c241

Browse files
committed
Feature: New hint - Golfing common expression using #define.
I.e. smoothstep, iResolution, normalize
1 parent 936ff05 commit c28c241

File tree

3 files changed

+78
-6
lines changed

3 files changed

+78
-6
lines changed

ShaderShrinker/Shrinker.Parser/Hinter.cs

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,16 @@ public static class Hinter
2424
// todo - Report when single value passed to any function.
2525
public static IEnumerable<CodeHint> GetHints(this SyntaxNode rootNode)
2626
{
27-
if (!rootNode.HasEntryPointFunction())
28-
yield break;
27+
if (rootNode.HasEntryPointFunction())
28+
{
29+
foreach (var codeHint in DetectFunctionsToInline(rootNode))
30+
yield return codeHint;
2931

30-
foreach (var codeHint in DetectFunctionsToInline(rootNode))
31-
yield return codeHint;
32+
foreach (var codeHint in DetectUnusedFunctionParam(rootNode))
33+
yield return codeHint;
34+
}
3235

33-
foreach (var codeHint in DetectUnusedFunctionParam(rootNode))
36+
foreach (var codeHint in DetectDefinableReferences(rootNode))
3437
yield return codeHint;
3538
}
3639

@@ -71,6 +74,25 @@ private static IEnumerable<CodeHint> DetectUnusedFunctionParam(SyntaxNode rootNo
7174
}
7275
}
7376

77+
private static IEnumerable<CodeHint> DetectDefinableReferences(SyntaxNode rootNode)
78+
{
79+
var candidates = new[] { "smoothstep", "iResolution", "normalize" };
80+
var replacement = new[] { "S smoothstep", "R iResolution", "N normalize" };
81+
var usages = rootNode.TheTree
82+
.Select(o => o.Token?.Content ?? (o as FunctionCallSyntaxNode)?.Name)
83+
.Where(o => candidates.Any(o.StartsWithVarName))
84+
.ToList();
85+
foreach (var candidate in candidates)
86+
{
87+
var usageCount = usages.Count(o => o.StartsWithVarName(candidate));
88+
var oldSize = candidate.Length * usageCount;
89+
var newSize = 8 + candidate.Length + usageCount + usageCount;
90+
91+
if (newSize < oldSize)
92+
yield return new IntroduceDefine(candidate, replacement[candidates.ToList().IndexOf(candidate)]);
93+
}
94+
}
95+
7496
public class UnusedFunctionHint : CodeHint
7597
{
7698
public UnusedFunctionHint(string function) : base(function, "Function is never called.")
@@ -91,5 +113,12 @@ public FunctionHasUnusedParam(string function, string param) : base(function, $"
91113
{
92114
}
93115
}
116+
117+
public class IntroduceDefine : CodeHint
118+
{
119+
public IntroduceDefine(string originalName, string defineNameAndValue) : base(originalName, $"[GOLF] Consider adding '#define {defineNameAndValue}'")
120+
{
121+
}
122+
}
94123
}
95124
}

ShaderShrinker/Shrinker.Parser/Optimizations/SimplifyFunctionDeclarationsExtension.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
// -----------------------------------------------------------------------
1111

1212
using System.Linq;
13-
using Shrinker.Lexer;
1413
using Shrinker.Parser.SyntaxNodes;
1514

1615
namespace Shrinker.Parser.Optimizations

ShaderShrinker/UnitTests/HinterTests.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,49 @@ public void CheckDetectingFunctionWithUnusedParam(
5959
Assert.That(hints, Has.Count.EqualTo(1));
6060
Assert.That(() => hints.Single().Suggestion, Does.Contain("'b'"));
6161
}
62+
63+
[Test]
64+
public void CheckSingleUseResolutionDoesNotTriggerHint()
65+
{
66+
var lexer = new Lexer();
67+
lexer.Load("float main() { return iResolution.x; }");
68+
69+
var rootNode = new Parser(lexer).Parse();
70+
71+
Assert.That(() => rootNode.GetHints(), Is.Empty);
72+
}
73+
74+
[Test]
75+
public void CheckMultiUseResolutionDoesNotTriggerHint()
76+
{
77+
var lexer = new Lexer();
78+
lexer.Load("float main() { return iResolution.x + iResolution.y + iResolution.y; }");
79+
80+
var rootNode = new Parser(lexer).Parse();
81+
82+
Assert.That(() => rootNode.GetHints().OfType<Hinter.IntroduceDefine>().ToList(), Has.Count.EqualTo(1));
83+
}
84+
85+
[Test]
86+
public void CheckSingleUseSmoothstepDoesNotTriggerHint()
87+
{
88+
var lexer = new Lexer();
89+
lexer.Load("float main() { return smoothstep(0.0, 1.0, 0.5); }");
90+
91+
var rootNode = new Parser(lexer).Parse();
92+
93+
Assert.That(() => rootNode.GetHints(), Is.Empty);
94+
}
95+
96+
[Test]
97+
public void CheckMultiUseSmoothstepDoesNotTriggerHint()
98+
{
99+
var lexer = new Lexer();
100+
lexer.Load("float main() { return smoothstep(0.0, 1.0, smoothstep(0.0, 1.0, smoothstep(0.0, 1.0, 0.5))); }");
101+
102+
var rootNode = new Parser(lexer).Parse();
103+
104+
Assert.That(() => rootNode.GetHints().OfType<Hinter.IntroduceDefine>().ToList(), Has.Count.EqualTo(1));
105+
}
62106
}
63107
}

0 commit comments

Comments
 (0)