Skip to content

Commit 2990ff1

Browse files
april-roszkowskiEvergreen
authored andcommitted
[Port][6000.4][UUM-135644] Allow shader-build-settings-compatible enum keywords in Shader Graph
1 parent fbdf210 commit 2990ff1

19 files changed

Lines changed: 2755 additions & 134 deletions

File tree

Packages/com.unity.shadergraph/Editor/Data/Graphs/ShaderKeyword.cs

Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ public KeywordType keywordType
6868

6969
internal bool IsDynamic => m_KeywordDefinition == KeywordDefinition.DynamicBranch;
7070

71+
// If true, indicates that ShaderGraph may pre-compute permutations containing this keyword
72+
internal bool IsPermutable => !IsDynamic && !IsShaderBuildSettingsCompatible;
73+
7174
[SerializeField]
7275
private KeywordDefinition m_KeywordDefinition = KeywordDefinition.ShaderFeature;
7376

@@ -104,6 +107,8 @@ public List<KeywordEntry> entries
104107
set => m_Entries = value;
105108
}
106109

110+
public bool HasNoneEntry => entries.Count > 0 && entries[0].IsNoneKeyword;
111+
107112
[SerializeField]
108113
private int m_Value;
109114

@@ -116,6 +121,17 @@ public int value
116121
[SerializeField]
117122
private bool m_IsEditable = true; // this serializes !isBuiltIn
118123

124+
[SerializeField]
125+
private bool m_IsShaderBuildSettingsCompatible = true;
126+
127+
// Note that if this field is true, we must effectively treat this keyword as a dynamic branch for code gen
128+
// purposes as its definition may be overridden by project settings.
129+
public bool IsShaderBuildSettingsCompatible
130+
{
131+
get => m_IsShaderBuildSettingsCompatible;
132+
set => m_IsShaderBuildSettingsCompatible = value;
133+
}
134+
119135
public bool isBuiltIn
120136
{
121137
get => !m_IsEditable;
@@ -130,6 +146,21 @@ public bool isBuiltIn
130146

131147
internal override ConcreteSlotValueType concreteShaderValueType => keywordType.ToConcreteSlotValueType();
132148

149+
public IEnumerable<(KeywordEntry entry, int value)> GetEntriesExcludingNone()
150+
{
151+
for (int i = HasNoneEntry ? 1 : 0; i < entries.Count; ++i)
152+
yield return (entries[i], i);
153+
}
154+
155+
// Enumerates the enum's entries in the order one would emit 'if', 'else if', and 'else' branches
156+
public IEnumerable<(KeywordEntry entry, int value)> GetEntriesInBranchOrder()
157+
{
158+
foreach (var entry in GetEntriesExcludingNone())
159+
yield return entry;
160+
if (HasNoneEntry)
161+
yield return (entries[0], 0);
162+
}
163+
133164
public override string GetOldDefaultReferenceName()
134165
{
135166
// _ON suffix is required for exposing Boolean type to Material
@@ -184,10 +215,16 @@ public string GetKeywordPreviewDeclarationString()
184215
case KeywordType.Boolean:
185216
return $"#define {referenceName} {(value == 0 ? "false" : " true")}";
186217
case KeywordType.Enum:
187-
string result = $"#define {referenceName}_{entries[value].referenceName} true";
188-
for (int i = 0; i < entries.Count; ++i)
189-
if (i != value)
190-
result += $"\n#define {referenceName}_{entries[i].referenceName} false";
218+
string result = entries[value].IsNoneKeyword ?
219+
string.Empty : $"#define {referenceName}_{entries[value].referenceName} true";
220+
foreach ((KeywordEntry entry, int entryValue) in GetEntriesExcludingNone())
221+
{
222+
if (entryValue != value)
223+
{
224+
string newline = result.Length == 0 ? string.Empty : "\n";
225+
result += $"{newline}#define {referenceName}_{entry.referenceName} false";
226+
}
227+
}
191228
return result;
192229
default:
193230
throw new ArgumentOutOfRangeException();
@@ -196,9 +233,19 @@ public string GetKeywordPreviewDeclarationString()
196233
else switch (keywordType)
197234
{
198235
case KeywordType.Boolean:
199-
return value == 1 ? $"#define {referenceName}" : string.Empty;
236+
return value == 1 ? $"#define {referenceName} true" : $"static const bool {referenceName} = false;";
200237
case KeywordType.Enum:
201-
return $"#define {referenceName}_{entries[value].referenceName}";
238+
string enumResult = entries[value].IsNoneKeyword ?
239+
string.Empty : $"#define {referenceName}_{entries[value].referenceName} true";
240+
foreach ((KeywordEntry entry, int entryValue) in GetEntriesExcludingNone())
241+
{
242+
if (entryValue != value)
243+
{
244+
string newline = enumResult.Length == 0 ? string.Empty : "\n";
245+
enumResult += $"{newline}static const bool {referenceName}_{entry.referenceName} = false;";
246+
}
247+
}
248+
return enumResult;
202249
default:
203250
throw new ArgumentOutOfRangeException();
204251
}
@@ -219,16 +266,17 @@ internal override ShaderInput Copy()
219266
keywordScope = keywordScope,
220267
entries = entries,
221268
keywordStages = keywordStages,
222-
overrideReferenceName = overrideReferenceName
269+
overrideReferenceName = overrideReferenceName,
270+
IsShaderBuildSettingsCompatible = IsShaderBuildSettingsCompatible,
223271
};
224272
}
225273

226-
public override int latestVersion => 1;
274+
public override int latestVersion => 2;
227275
public override void OnAfterDeserialize(string json)
228276
{
229277
if (sgVersion == 0)
230278
{
231-
// we now allow keywords to control whether they are exposed (for Material control) or not.
279+
// Version 1 allows keywords to control whether they are exposed (for Material control) or not.
232280
// old exposable keywords set their exposed state to maintain previous behavior
233281
// (where bool keywords only showed up in the material when ending in "_ON")
234282
if (isExposable)
@@ -240,6 +288,14 @@ public override void OnAfterDeserialize(string json)
240288
}
241289
ChangeVersion(1);
242290
}
291+
if (sgVersion < 2)
292+
{
293+
// Version 2 marks the point at which we changed KeywordNode code gen to be compatible with
294+
// Shader Build Settings. Legacy shader graphs should default to incompatible KeywordNode
295+
// code gen.
296+
IsShaderBuildSettingsCompatible = false;
297+
ChangeVersion(2);
298+
}
243299
}
244300
}
245301
}

Packages/com.unity.shadergraph/Editor/Data/Implementation/NodeUtils.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ public static void DepthFirstCollectNodesFromNode(ICollection<AbstractMaterialNo
118118

119119
// If this node is a keyword node and we have an active keyword permutation
120120
// The only valid port id is the port that corresponds to that keywords value in the active permutation
121-
if (node is KeywordNode keywordNode && keywordPermutation != null && !keywordNode.keyword.IsDynamic)
121+
if (node is KeywordNode keywordNode && keywordPermutation != null && keywordNode.keyword.IsPermutable)
122122
{
123123
var valueInPermutation = keywordPermutation.Where(x => x.Key == keywordNode.keyword).FirstOrDefault();
124124
ids = new int[] { keywordNode.GetSlotIdForPermutation(valueInPermutation) };

Packages/com.unity.shadergraph/Editor/Data/Nodes/Utility/KeywordNode.cs

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,8 @@ void UpdatePorts()
141141
public void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode generationMode)
142142
{
143143
var outputSlot = FindOutputSlot<MaterialSlot>(OutputSlotId);
144-
if (keyword.keywordDefinition == KeywordDefinition.DynamicBranch)
144+
if (keyword.keywordDefinition == KeywordDefinition.DynamicBranch
145+
|| keyword.IsShaderBuildSettingsCompatible)
145146
{
146147
switch (keyword.keywordType)
147148
{
@@ -152,16 +153,23 @@ public void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode generationMo
152153
break;
153154
case KeywordType.Enum:
154155
sb.AppendLine(string.Format($"{outputSlot.concreteValueType.ToShaderString()} {GetVariableNameForSlot(OutputSlotId)};"));
155-
for(int i = 0; i < keyword.entries.Count; ++i)
156+
int i = 0;
157+
foreach ((KeywordEntry entry, int entryValue) in keyword.GetEntriesInBranchOrder())
156158
{
157-
var keywordEntry = keyword.entries[i];
158-
string keywordName = $"{keyword.referenceName}_{keywordEntry.referenceName}";
159-
var value = GetSlotValue(keywordEntry.id, generationMode);
160-
sb.AppendLine(string.Format($"{(i != 0 ? "else" : "")} if({keywordName}) {GetVariableNameForSlot(OutputSlotId)} = {value};"));
159+
string keywordName = $"{keyword.referenceName}_{entry.referenceName}";
160+
var value = GetSlotValue(entry.id, generationMode);
161+
string assignment = $"{GetVariableNameForSlot(OutputSlotId)} = {value};";
162+
if (i == 0)
163+
sb.AppendLine($"if ({keywordName}) {assignment}");
164+
else if (i == keyword.entries.Count - 1)
165+
sb.AppendLine($"else {assignment}");
166+
else
167+
sb.AppendLine($"else if ({keywordName}) {assignment}");
168+
++i;
161169
}
162170
break;
163171
}
164-
}
172+
}
165173
else switch (keyword.keywordType)
166174
{
167175
case KeywordType.Boolean:
@@ -181,25 +189,28 @@ public void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode generationMo
181189
case KeywordType.Enum:
182190
{
183191
// Iterate all entries in the keyword
184-
for (int i = 0; i < keyword.entries.Count; i++)
192+
int i = 0;
193+
foreach ((KeywordEntry entry, int entryValue) in keyword.GetEntriesInBranchOrder())
185194
{
186195
// Insert conditional
187196
if (i == 0)
188197
{
189-
sb.AppendLine($"#if defined({keyword.referenceName}_{keyword.entries[i].referenceName})");
198+
sb.AppendLine($"#if defined({keyword.referenceName}_{entry.referenceName})");
190199
}
191200
else if (i == keyword.entries.Count - 1)
192201
{
193202
sb.AppendLine("#else");
194203
}
195204
else
196205
{
197-
sb.AppendLine($"#elif defined({keyword.referenceName}_{keyword.entries[i].referenceName})");
206+
sb.AppendLine($"#elif defined({keyword.referenceName}_{entry.referenceName})");
198207
}
199208

200209
// Append per-slot code
201-
var value = GetSlotValue(GetSlotIdForPermutation(new KeyValuePair<ShaderKeyword, int>(keyword, i)), generationMode);
210+
var value = GetSlotValue(GetSlotIdForPermutation(new KeyValuePair<ShaderKeyword, int>(keyword, entryValue)), generationMode);
202211
sb.AppendLine(string.Format($"{outputSlot.concreteValueType.ToShaderString()} {GetVariableNameForSlot(OutputSlotId)} = {value};"));
212+
213+
++i;
203214
}
204215

205216
// End condition

Packages/com.unity.shadergraph/Editor/Data/Util/KeywordCollector.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public void AddShaderKeyword(ShaderKeyword chunk)
2525

2626
keywords.Add(chunk);
2727

28-
if (!chunk.IsDynamic)
28+
if (chunk.IsPermutable)
2929
permutableKeywords.Add(chunk);
3030
}
3131

@@ -67,21 +67,29 @@ public void CalculateKeywordPermutations()
6767

6868
for (int i = 0; i < permutableKeywords.Count; i++)
6969
{
70-
currentPermutation.Add(new KeyValuePair<ShaderKeyword, int>(permutableKeywords[i], 0));
70+
var firstEntryValue = GetEntryValues(permutableKeywords[i]).First();
71+
currentPermutation.Add(new KeyValuePair<ShaderKeyword, int>(permutableKeywords[i], firstEntryValue));
7172
}
7273

7374
// Recursively permute keywords
7475
PermuteKeywords(permutableKeywords, currentPermutation, 0);
7576
}
7677

78+
IEnumerable<int> GetEntryValues(ShaderKeyword keyword)
79+
{
80+
if (keyword.keywordType == KeywordType.Enum)
81+
return keyword.GetEntriesInBranchOrder().Select(x => x.value);
82+
else
83+
return new int[] { 0, 1 };
84+
}
85+
7786
void PermuteKeywords(List<ShaderKeyword> keywords, List<KeyValuePair<ShaderKeyword, int>> currentPermutation, int currentIndex)
7887
{
7988
if (currentIndex == keywords.Count)
8089
return;
8190

8291
// Iterate each possible keyword at the current index
83-
int entryCount = keywords[currentIndex].keywordType == KeywordType.Enum ? keywords[currentIndex].entries.Count : 2;
84-
for (int i = 0; i < entryCount; i++)
92+
foreach (int i in GetEntryValues(keywords[currentIndex]))
8593
{
8694
// Set the index in the current permutation to the correct value
8795
currentPermutation[currentIndex] = new KeyValuePair<ShaderKeyword, int>(keywords[currentIndex], i);

Packages/com.unity.shadergraph/Editor/Data/Util/KeywordUtil.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -233,15 +233,20 @@ public static void GetKeywordPermutationDeclarations(ShaderStringBuilder sb, Lis
233233
// Iterate all keywords that are part of the permutation
234234
for (int i = 0; i < permutations[p].Count; i++)
235235
{
236+
(ShaderKeyword keyword, int entryValue) = permutations[p][i];
237+
236238
// When previous keyword was inserted subsequent requires &&
237239
string and = appendAnd ? " && " : string.Empty;
238240

239-
switch (permutations[p][i].Key.keywordType)
241+
switch (keyword.keywordType)
240242
{
241243
case KeywordType.Enum:
242244
{
243-
sb.Append($"{and}defined({permutations[p][i].Key.referenceName}_{permutations[p][i].Key.entries[permutations[p][i].Value].referenceName})");
244-
appendAnd = true;
245+
if (!keyword.entries[entryValue].IsNoneKeyword)
246+
{
247+
sb.Append($"{and}defined({keyword.referenceName}_{keyword.entries[entryValue].referenceName})");
248+
appendAnd = true;
249+
}
245250
break;
246251
}
247252
case KeywordType.Boolean:

0 commit comments

Comments
 (0)