A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3using System.IO;
4using System.Linq;
5using UnityEditor.Graphing;
6using UnityEditor.ShaderGraph.Internal;
7using UnityEngine.Profiling;
8using Pool = UnityEngine.Pool;
9
10namespace UnityEditor.ShaderGraph
11{
12 internal static class GenerationUtils
13 {
14 const string kErrorString = @"ERROR!";
15
16 internal static List<FieldDescriptor> GetActiveFieldsFromConditionals(ConditionalField[] conditionalFields)
17 {
18 var fields = new List<FieldDescriptor>();
19 if (conditionalFields != null)
20 {
21 foreach (ConditionalField conditionalField in conditionalFields)
22 {
23 if (conditionalField.condition == true)
24 {
25 fields.Add(conditionalField.field);
26 }
27 }
28 }
29
30 return fields;
31 }
32
33 internal static void GenerateSubShaderTags(Target target, SubShaderDescriptor descriptor, ShaderStringBuilder builder)
34 {
35 builder.AppendLine("Tags");
36 using (builder.BlockScope())
37 {
38 // Pipeline tag
39 if (!string.IsNullOrEmpty(descriptor.pipelineTag))
40 builder.AppendLine($"\"RenderPipeline\"=\"{descriptor.pipelineTag}\"");
41 else
42 builder.AppendLine("// RenderPipeline: <None>");
43
44 // Render Type
45 if (!string.IsNullOrEmpty(descriptor.renderType))
46 builder.AppendLine($"\"RenderType\"=\"{descriptor.renderType}\"");
47 else
48 builder.AppendLine("// RenderType: <None>");
49
50 // Custom shader tags.
51 if (!string.IsNullOrEmpty(descriptor.customTags))
52 builder.AppendLine(descriptor.customTags);
53
54 // Render Queue
55 if (!string.IsNullOrEmpty(descriptor.renderQueue))
56 builder.AppendLine($"\"Queue\"=\"{descriptor.renderQueue}\"");
57 else
58 builder.AppendLine("// Queue: <None>");
59
60 // DisableBatching tag
61 if (!string.IsNullOrEmpty(descriptor.disableBatchingTag))
62 builder.AppendLine($"\"DisableBatching\"=\"{descriptor.disableBatchingTag}\"");
63 else
64 builder.AppendLine("// DisableBatching: <None>");
65
66 // ShaderGraphShader tag (so we can tell what shadergraph built)
67 builder.AppendLine("\"ShaderGraphShader\"=\"true\"");
68
69 if (target is IHasMetadata metadata)
70 builder.AppendLine($"\"ShaderGraphTargetId\"=\"{metadata.identifier}\"");
71
72 // IgnoreProjector
73 if(!string.IsNullOrEmpty(descriptor.IgnoreProjector))
74 builder.AppendLine($"\"IgnoreProjector\"=\"{descriptor.IgnoreProjector}\"");
75
76 // PreviewType
77 if(!string.IsNullOrEmpty(descriptor.PreviewType))
78 builder.AppendLine($"\"PreviewType\"=\"{descriptor.PreviewType}\"");
79
80 // CanUseSpriteAtlas
81 if(!string.IsNullOrEmpty(descriptor.CanUseSpriteAtlas))
82 builder.AppendLine($"\"CanUseSpriteAtlas\"=\"{descriptor.CanUseSpriteAtlas}\"");
83 }
84 }
85
86 static bool IsFieldActive(FieldDescriptor field, IActiveFields activeFields, bool isOptional)
87 {
88 bool fieldActive = true;
89 if (!activeFields.Contains(field) && isOptional)
90 fieldActive = false; //if the field is optional and not inside of active fields
91 return fieldActive;
92 }
93
94 internal static void GenerateShaderStruct(StructDescriptor shaderStruct, ActiveFields activeFields, bool humanReadable, out ShaderStringBuilder structBuilder)
95 {
96 structBuilder = new ShaderStringBuilder(humanReadable: humanReadable);
97 structBuilder.AppendLine($"struct {shaderStruct.name}");
98 using (structBuilder.BlockSemicolonScope())
99 {
100 foreach (var activeField in GetActiveFieldsAndKeyword(shaderStruct, activeFields))
101 {
102 var subscript = activeField.field;
103 var keywordIfDefs = activeField.keywordIfDefs;
104
105 //if field is active:
106 if (subscript.HasPreprocessor())
107 structBuilder.AppendLine($"#if {subscript.preprocessor}");
108
109 //if in permutation, add permutation ifdef
110 if (!string.IsNullOrEmpty(keywordIfDefs))
111 structBuilder.AppendLine(keywordIfDefs);
112
113 //check for a semantic, build string if valid
114 string semantic = subscript.HasSemantic() ? $" : {subscript.semantic}" : string.Empty;
115 structBuilder.AppendLine($"{subscript.interpolation} {subscript.type} {subscript.name}{semantic};");
116
117 //if in permutation, add permutation endif
118 if (!string.IsNullOrEmpty(keywordIfDefs))
119 structBuilder.AppendLine("#endif"); //TODO: add debug collector
120
121 if (subscript.HasPreprocessor())
122 structBuilder.AppendLine("#endif");
123 }
124 }
125 }
126
127 struct PackedEntry
128 {
129 public struct Input
130 {
131 public FieldDescriptor field;
132 public int startChannel;
133 public int channelCount;
134 }
135
136 public Input[] inputFields;
137 public FieldDescriptor packedField;
138 }
139
140 static IEnumerable<(FieldDescriptor field, string keywordIfDefs)> GetActiveFieldsAndKeyword(StructDescriptor shaderStruct, ActiveFields activeFields)
141 {
142 var activeFieldList = shaderStruct.fields
143 .Select(currentField =>
144 {
145 bool fieldIsActive;
146 var currentKeywordIfDefs = string.Empty;
147
148 if (activeFields.permutationCount > 0)
149 {
150 //find all active fields per permutation
151 var instances = activeFields.allPermutations.instances
152 .Where(i => IsFieldActive(currentField, i, currentField.subscriptOptions.HasFlag(StructFieldOptions.Optional))).ToList();
153 fieldIsActive = instances.Count > 0;
154 if (fieldIsActive)
155 currentKeywordIfDefs = KeywordUtil.GetKeywordPermutationSetConditional(instances.Select(i => i.permutationIndex).ToList());
156 }
157 else
158 fieldIsActive = IsFieldActive(currentField, activeFields.baseInstance, currentField.subscriptOptions.HasFlag(StructFieldOptions.Optional));
159 //else just find active fields
160
161 if (fieldIsActive)
162 {
163 return
164 (
165 field: currentField,
166 keywordIfDefs: currentKeywordIfDefs
167 );
168 }
169
170 return
171 (
172 field: null,
173 keywordIfDefs: null
174 );
175 }).Where(o => o.field != null);
176 return activeFieldList;
177 }
178
179 static IEnumerable<FieldDescriptor> GetActiveFields(StructDescriptor shaderStruct, ActiveFields activeFields)
180 {
181 return GetActiveFieldsAndKeyword(shaderStruct, activeFields).Select(o => o.field);
182 }
183
184 static IEnumerable<FieldDescriptor> GetActiveFields(StructDescriptor shaderStruct, IActiveFields activeFields)
185 {
186 var activeFieldList = shaderStruct.fields
187 .Where(field => IsFieldActive(field, activeFields, field.subscriptOptions.HasFlag(StructFieldOptions.Optional)));
188 return activeFieldList;
189 }
190
191 static PackedEntry[] GeneratePackingLayout(StructDescriptor shaderStruct, ActiveFields activeFields)
192 {
193 var activeFieldList = GetActiveFields(shaderStruct, activeFields);
194 return GeneratePackingLayout(shaderStruct.name, activeFieldList);
195 }
196
197 static PackedEntry[] GeneratePackingLayout(StructDescriptor shaderStruct, IActiveFields activeFields)
198 {
199 var activeFieldList = GetActiveFields(shaderStruct, activeFields);
200 return GeneratePackingLayout(shaderStruct.name, activeFieldList);
201 }
202
203 static PackedEntry[] GeneratePackingLayout(string baseStructName, IEnumerable<FieldDescriptor> activeFields)
204 {
205 const int kPreUnpackable = 0;
206 const int kPackable = 1;
207 const int kPostUnpackable = 2;
208
209 var fieldCategorized = activeFields
210 .GroupBy(subscript =>
211 {
212 if (subscript.HasPreprocessor())
213 {
214 //special case, "UNITY_STEREO_INSTANCING_ENABLED" fields must be packed at the end of the struct because they are system generated semantics
215 if (subscript.preprocessor.Contains("INSTANCING"))
216 return kPostUnpackable;
217
218 //special case, "SHADER_STAGE_FRAGMENT" fields must be packed at the end of the struct,
219 //otherwise the vertex output struct will have different semantic ordering than the fragment input struct.
220 if (subscript.preprocessor.Contains("SHADER_STAGE_FRAGMENT"))
221 return kPostUnpackable;
222
223 return kPreUnpackable;
224 }
225
226 if (subscript.HasSemantic() || subscript.vectorCount == 0)
227 return kPreUnpackable;
228
229 return kPackable;
230 }).OrderBy(o => o.Key);
231
232 var packStructName = "Packed" + baseStructName;
233 int currentInterpolatorIndex = 0;
234 var packedEntries = new List<PackedEntry>();
235 foreach (var collection in fieldCategorized)
236 {
237 var packingEnabled = collection.Key == kPackable;
238 if (packingEnabled)
239 {
240 var groupByInterpolator = collection.GroupBy(field => string.IsNullOrEmpty(field.interpolation) ? string.Empty : field.interpolation);
241 foreach (var collectionInterpolator in groupByInterpolator)
242 {
243 //OrderByDescending is stable sort
244 var elementToPack = collectionInterpolator.OrderByDescending(o => o.vectorCount).ToList();
245 var totalVectorCount = elementToPack.Sum(o => o.vectorCount);
246
247 const bool allowSplitting = true;
248#pragma warning disable 162
249 int maxInterpolatorCount;
250 if (allowSplitting)
251 maxInterpolatorCount = ((totalVectorCount + 3) & ~0x03) >> 2;
252 else
253 maxInterpolatorCount = elementToPack.Count;
254#pragma warning restore 162
255
256 var intermediateInterpolator = Enumerable.Range(0, maxInterpolatorCount).Select(_ =>
257 (
258 fields : new List<PackedEntry.Input>(),
259 vectorCount : 0
260 )
261 ).ToList();
262
263 const int kMaxVectorCount = 4;
264 //First Pass *without* channel splitting
265 int itElement = 0;
266 while (itElement < elementToPack.Count)
267 {
268 var currentElement = elementToPack[itElement];
269
270 var availableSlotIndex = intermediateInterpolator.FindIndex(o =>
271 o.vectorCount + currentElement.vectorCount <= kMaxVectorCount);
272
273 if (availableSlotIndex != -1)
274 {
275 elementToPack.RemoveAt(itElement);
276 var slot = intermediateInterpolator[availableSlotIndex];
277 slot.vectorCount += currentElement.vectorCount;
278 slot.fields.Add(new PackedEntry.Input()
279 {
280 field = currentElement,
281 startChannel = 0,
282 channelCount = currentElement.vectorCount,
283 });
284 intermediateInterpolator[availableSlotIndex] = slot;
285 }
286 else
287 {
288 itElement++;
289 }
290 }
291
292 if (!allowSplitting && elementToPack.Count > 0)
293 throw new InvalidOperationException("Unexpected failure in interpolator packing algorithm.");
294
295 //Second Pass *with* channel splitting
296 foreach (var remainingElement in elementToPack)
297 {
298 int currentStartChannel = 0;
299 while (currentStartChannel < remainingElement.vectorCount)
300 {
301 var availableSlotIndex = intermediateInterpolator.FindIndex(o => o.vectorCount < kMaxVectorCount);
302 if (availableSlotIndex == -1)
303 throw new InvalidOperationException("Unexpected failure in interpolator packing algorithm.");
304
305 var slot = intermediateInterpolator[availableSlotIndex];
306 var currentChannelCount = Math.Min(kMaxVectorCount - slot.vectorCount, kMaxVectorCount - (remainingElement.vectorCount - currentStartChannel));
307 slot.vectorCount += currentChannelCount;
308 slot.fields.Add(new PackedEntry.Input()
309 {
310 field = remainingElement,
311 startChannel = currentStartChannel,
312 channelCount = currentChannelCount,
313 });
314 intermediateInterpolator[availableSlotIndex] = slot;
315 currentStartChannel += currentChannelCount;
316 }
317 }
318
319 packedEntries.AddRange(intermediateInterpolator
320 .Where(o => o.vectorCount > 0)
321 .Select(o =>
322 {
323 var allName = o.fields.Select(f =>
324 f.channelCount == f.field.vectorCount
325 ? f.field.name
326 : f.field.name + ShaderSpliceUtil.GetChannelSwizzle(f.startChannel, f.channelCount));
327 var name = o.fields.Count == 1
328 ? allName.First()
329 : "packed_" + allName.Aggregate((a, b) => $"{a}_{b}");
330 return new PackedEntry()
331 {
332 inputFields = o.fields.ToArray(),
333 packedField = new FieldDescriptor
334 (
335 tag: packStructName,
336 name: name,
337 define: string.Empty,
338 type: $"float{o.vectorCount}",
339 semantic: $"INTERP{currentInterpolatorIndex++}",
340 preprocessor: string.Empty,
341 subscriptOptions: StructFieldOptions.Static,
342 interpolation: collectionInterpolator.Key
343 )
344 };
345 }));
346 }
347 }
348 else
349 {
350 foreach (var field in collection)
351 {
352 var inputFields = new[]
353 {
354 new PackedEntry.Input()
355 {
356 field = field,
357 channelCount = 0,
358 startChannel = 0
359 }
360 };
361
362 //Auto add semantic if needed
363 if (!field.HasSemantic())
364 {
365 var newField = new FieldDescriptor
366 (
367 tag: packStructName,
368 name: field.name,
369 define: field.define,
370 type: field.type,
371 semantic: $"INTERP{currentInterpolatorIndex++}",
372 preprocessor: field.preprocessor,
373 subscriptOptions: StructFieldOptions.Static,
374 interpolation: field.interpolation
375 );
376 packedEntries.Add(new()
377 {
378 inputFields = inputFields,
379 packedField = newField
380 });
381 }
382 else
383 {
384 packedEntries.Add(new PackedEntry()
385 {
386 inputFields = inputFields,
387 packedField = field
388 });
389 }
390 }
391 }
392 }
393
394 return packedEntries.ToArray();
395 }
396
397 internal static void GeneratePackedStruct(StructDescriptor shaderStruct, ActiveFields activeFields, out StructDescriptor packStruct)
398 {
399 var packingLayout = GeneratePackingLayout(shaderStruct, activeFields);
400 var packStructName = "Packed" + shaderStruct.name;
401 packStruct = new StructDescriptor()
402 {
403 name = packStructName,
404 packFields = true,
405 fields = packingLayout.Select(o => o.packedField).ToArray()
406 };
407 }
408
409 internal static void GenerateInterpolatorFunctions(StructDescriptor shaderStruct, IActiveFields activeFields, bool humanReadable, out ShaderStringBuilder interpolatorBuilder)
410 {
411 //set up function string builders and struct builder
412 var packBuilder = new ShaderStringBuilder(humanReadable: humanReadable);
413 var unpackBuilder = new ShaderStringBuilder(humanReadable: humanReadable);
414 interpolatorBuilder = new ShaderStringBuilder(humanReadable: humanReadable);
415 string packedStruct = "Packed" + shaderStruct.name;
416
417 //declare function headers
418 packBuilder.AppendLine($"{packedStruct} Pack{shaderStruct.name} ({shaderStruct.name} input)");
419 packBuilder.AppendLine("{");
420 packBuilder.IncreaseIndent();
421 packBuilder.AppendLine($"{packedStruct} output;");
422 packBuilder.AppendLine($"ZERO_INITIALIZE({packedStruct}, output);");
423
424 unpackBuilder.AppendLine($"{shaderStruct.name} Unpack{shaderStruct.name} ({packedStruct} input)");
425 unpackBuilder.AppendLine("{");
426 unpackBuilder.IncreaseIndent();
427 unpackBuilder.AppendLine($"{shaderStruct.name} output;");
428
429 var packingLayout = GeneratePackingLayout(shaderStruct, activeFields);
430 foreach (var packEntry in packingLayout)
431 {
432 int firstPackedChannel = 0;
433 foreach (var input in packEntry.inputFields)
434 {
435 if (input.field.HasPreprocessor())
436 {
437 packBuilder.AppendLine($"#if {input.field.preprocessor}");
438 unpackBuilder.AppendLine($"#if {input.field.preprocessor}");
439 }
440
441 var packedChannels = string.Empty;
442 var unpackedChannel = string.Empty;
443 if (input.channelCount != 0)
444 {
445 if (firstPackedChannel != 0 || input.channelCount != packEntry.packedField.vectorCount)
446 packedChannels = $".{ShaderSpliceUtil.GetChannelSwizzle(firstPackedChannel, input.channelCount)}";
447 if (input.startChannel != 0 || input.channelCount != input.field.vectorCount)
448 unpackedChannel = $".{ShaderSpliceUtil.GetChannelSwizzle(input.startChannel, input.channelCount)}";
449 }
450
451 packBuilder.AppendLine($"output.{packEntry.packedField.name}{packedChannels} = input.{input.field.name}{unpackedChannel};");
452 unpackBuilder.AppendLine($"output.{input.field.name}{unpackedChannel} = input.{packEntry.packedField.name}{packedChannels};");
453 firstPackedChannel += input.field.vectorCount;
454
455 if (input.field.HasPreprocessor())
456 {
457 packBuilder.AppendLine("#endif");
458 unpackBuilder.AppendLine("#endif");
459 }
460 }
461 }
462
463 //close function declarations
464 packBuilder.AppendLine("return output;");
465 packBuilder.DecreaseIndent();
466 packBuilder.AppendLine("}");
467 packBuilder.AppendNewLine();
468
469 unpackBuilder.AppendLine("return output;");
470 unpackBuilder.DecreaseIndent();
471 unpackBuilder.AppendLine("}");
472 unpackBuilder.AppendNewLine();
473
474 interpolatorBuilder.Concat(packBuilder);
475 interpolatorBuilder.Concat(unpackBuilder);
476 }
477
478 internal static void GetUpstreamNodesForShaderPass(AbstractMaterialNode outputNode, PassDescriptor pass, out List<AbstractMaterialNode> vertexNodes, out List<AbstractMaterialNode> pixelNodes)
479 {
480 // Traverse Graph Data
481 vertexNodes = Pool.ListPool<AbstractMaterialNode>.Get();
482 NodeUtils.DepthFirstCollectNodesFromNode(vertexNodes, outputNode, NodeUtils.IncludeSelf.Include);
483
484 pixelNodes = Pool.ListPool<AbstractMaterialNode>.Get();
485 NodeUtils.DepthFirstCollectNodesFromNode(pixelNodes, outputNode, NodeUtils.IncludeSelf.Include);
486 }
487
488 internal static void GetActiveFieldsAndPermutationsForNodes(PassDescriptor pass,
489 KeywordCollector keywordCollector, List<AbstractMaterialNode> vertexNodes, List<AbstractMaterialNode> pixelNodes,
490 bool[] texCoordNeedsDerivs,
491 List<int>[] vertexNodePermutations, List<int>[] pixelNodePermutations,
492 ActiveFields activeFields, out ShaderGraphRequirementsPerKeyword graphRequirements)
493 {
494 // Initialize requirements
495 ShaderGraphRequirementsPerKeyword pixelRequirements = new ShaderGraphRequirementsPerKeyword();
496 ShaderGraphRequirementsPerKeyword vertexRequirements = new ShaderGraphRequirementsPerKeyword();
497 graphRequirements = new ShaderGraphRequirementsPerKeyword();
498
499 // Evaluate all Keyword permutations
500 if (keywordCollector.permutations.Count > 0)
501 {
502 for (int i = 0; i < keywordCollector.permutations.Count; i++)
503 {
504 // Get active nodes for this permutation
505 var localVertexNodes = Pool.HashSetPool<AbstractMaterialNode>.Get();
506 var localPixelNodes = Pool.HashSetPool<AbstractMaterialNode>.Get();
507
508 localVertexNodes.EnsureCapacity(vertexNodes.Count);
509 localPixelNodes.EnsureCapacity(pixelNodes.Count);
510
511 foreach (var vertexNode in vertexNodes)
512 {
513 NodeUtils.DepthFirstCollectNodesFromNode(localVertexNodes, vertexNode, NodeUtils.IncludeSelf.Include, keywordCollector.permutations[i]);
514 }
515
516 foreach (var pixelNode in pixelNodes)
517 {
518 NodeUtils.DepthFirstCollectNodesFromNode(localPixelNodes, pixelNode, NodeUtils.IncludeSelf.Include, keywordCollector.permutations[i]);
519 }
520
521 // Track each vertex node in this permutation
522 foreach (AbstractMaterialNode vertexNode in localVertexNodes)
523 {
524 int nodeIndex = vertexNodes.IndexOf(vertexNode);
525
526 if (vertexNodePermutations[nodeIndex] == null)
527 vertexNodePermutations[nodeIndex] = new List<int>();
528 vertexNodePermutations[nodeIndex].Add(i);
529 }
530
531 // Track each pixel node in this permutation
532 foreach (AbstractMaterialNode pixelNode in localPixelNodes)
533 {
534 int nodeIndex = pixelNodes.IndexOf(pixelNode);
535
536 if (pixelNodePermutations[nodeIndex] == null)
537 pixelNodePermutations[nodeIndex] = new List<int>();
538 pixelNodePermutations[nodeIndex].Add(i);
539 }
540
541 // Get requirements for this permutation
542 vertexRequirements[i].SetRequirements(ShaderGraphRequirements.FromNodes(localVertexNodes, ShaderStageCapability.Vertex, false, texCoordNeedsDerivs));
543 pixelRequirements[i].SetRequirements(ShaderGraphRequirements.FromNodes(localPixelNodes, ShaderStageCapability.Fragment, false, texCoordNeedsDerivs));
544
545 // Add active fields
546 var conditionalFields = GetActiveFieldsFromConditionals(GetConditionalFieldsFromPixelRequirements(pixelRequirements[i].requirements));
547 if (activeFields[i].Contains(Fields.GraphVertex))
548 {
549 conditionalFields.AddRange(GetActiveFieldsFromConditionals(GetConditionalFieldsFromVertexRequirements(vertexRequirements[i].requirements)));
550 }
551 foreach (var field in conditionalFields)
552 {
553 activeFields[i].Add(field);
554 }
555 }
556 }
557 // No Keywords
558 else
559 {
560 // Get requirements
561 vertexRequirements.baseInstance.SetRequirements(ShaderGraphRequirements.FromNodes(vertexNodes, ShaderStageCapability.Vertex, false, texCoordNeedsDerivs));
562 pixelRequirements.baseInstance.SetRequirements(ShaderGraphRequirements.FromNodes(pixelNodes, ShaderStageCapability.Fragment, false, texCoordNeedsDerivs));
563
564 // Add active fields
565 var conditionalFields = GetActiveFieldsFromConditionals(GetConditionalFieldsFromPixelRequirements(pixelRequirements.baseInstance.requirements));
566 if (activeFields.baseInstance.Contains(Fields.GraphVertex))
567 {
568 conditionalFields.AddRange(GetActiveFieldsFromConditionals(GetConditionalFieldsFromVertexRequirements(vertexRequirements.baseInstance.requirements)));
569 }
570 foreach (var field in conditionalFields)
571 {
572 activeFields.baseInstance.Add(field);
573 }
574 }
575
576 // Build graph requirements
577 graphRequirements.UnionWith(pixelRequirements);
578 graphRequirements.UnionWith(vertexRequirements);
579 }
580
581 static ConditionalField[] GetConditionalFieldsFromVertexRequirements(ShaderGraphRequirements requirements)
582 {
583 return new ConditionalField[]
584 {
585 new ConditionalField(StructFields.VertexDescriptionInputs.ScreenPosition, requirements.requiresScreenPosition),
586 new ConditionalField(StructFields.VertexDescriptionInputs.NDCPosition, requirements.requiresNDCPosition),
587 new ConditionalField(StructFields.VertexDescriptionInputs.PixelPosition, requirements.requiresPixelPosition),
588
589 new ConditionalField(StructFields.VertexDescriptionInputs.VertexColor, requirements.requiresVertexColor),
590
591 new ConditionalField(StructFields.VertexDescriptionInputs.ObjectSpaceNormal, (requirements.requiresNormal & NeededCoordinateSpace.Object) > 0),
592 new ConditionalField(StructFields.VertexDescriptionInputs.ViewSpaceNormal, (requirements.requiresNormal & NeededCoordinateSpace.View) > 0),
593 new ConditionalField(StructFields.VertexDescriptionInputs.WorldSpaceNormal, (requirements.requiresNormal & NeededCoordinateSpace.World) > 0),
594 new ConditionalField(StructFields.VertexDescriptionInputs.TangentSpaceNormal, (requirements.requiresNormal & NeededCoordinateSpace.Tangent) > 0),
595
596 new ConditionalField(StructFields.VertexDescriptionInputs.ObjectSpaceViewDirection, (requirements.requiresViewDir & NeededCoordinateSpace.Object) > 0),
597 new ConditionalField(StructFields.VertexDescriptionInputs.ViewSpaceViewDirection, (requirements.requiresViewDir & NeededCoordinateSpace.View) > 0),
598 new ConditionalField(StructFields.VertexDescriptionInputs.WorldSpaceViewDirection, (requirements.requiresViewDir & NeededCoordinateSpace.World) > 0),
599 new ConditionalField(StructFields.VertexDescriptionInputs.TangentSpaceViewDirection, (requirements.requiresViewDir & NeededCoordinateSpace.Tangent) > 0),
600
601 new ConditionalField(StructFields.VertexDescriptionInputs.ObjectSpaceTangent, (requirements.requiresTangent & NeededCoordinateSpace.Object) > 0),
602 new ConditionalField(StructFields.VertexDescriptionInputs.ViewSpaceTangent, (requirements.requiresTangent & NeededCoordinateSpace.View) > 0),
603 new ConditionalField(StructFields.VertexDescriptionInputs.WorldSpaceTangent, (requirements.requiresTangent & NeededCoordinateSpace.World) > 0),
604 new ConditionalField(StructFields.VertexDescriptionInputs.TangentSpaceTangent, (requirements.requiresTangent & NeededCoordinateSpace.Tangent) > 0),
605
606 new ConditionalField(StructFields.VertexDescriptionInputs.ObjectSpaceBiTangent, (requirements.requiresBitangent & NeededCoordinateSpace.Object) > 0),
607 new ConditionalField(StructFields.VertexDescriptionInputs.ViewSpaceBiTangent, (requirements.requiresBitangent & NeededCoordinateSpace.View) > 0),
608 new ConditionalField(StructFields.VertexDescriptionInputs.WorldSpaceBiTangent, (requirements.requiresBitangent & NeededCoordinateSpace.World) > 0),
609 new ConditionalField(StructFields.VertexDescriptionInputs.TangentSpaceBiTangent, (requirements.requiresBitangent & NeededCoordinateSpace.Tangent) > 0),
610
611 new ConditionalField(StructFields.VertexDescriptionInputs.ObjectSpacePosition, (requirements.requiresPosition & NeededCoordinateSpace.Object) > 0),
612 new ConditionalField(StructFields.VertexDescriptionInputs.ViewSpacePosition, (requirements.requiresPosition & NeededCoordinateSpace.View) > 0),
613 new ConditionalField(StructFields.VertexDescriptionInputs.WorldSpacePosition, (requirements.requiresPosition & NeededCoordinateSpace.World) > 0),
614 new ConditionalField(StructFields.VertexDescriptionInputs.TangentSpacePosition, (requirements.requiresPosition & NeededCoordinateSpace.Tangent) > 0),
615 new ConditionalField(StructFields.VertexDescriptionInputs.AbsoluteWorldSpacePosition, (requirements.requiresPosition & NeededCoordinateSpace.AbsoluteWorld) > 0),
616
617 new ConditionalField(StructFields.VertexDescriptionInputs.ObjectSpacePositionPredisplacement, (requirements.requiresPositionPredisplacement & NeededCoordinateSpace.Object) > 0),
618 new ConditionalField(StructFields.VertexDescriptionInputs.ViewSpacePositionPredisplacement, (requirements.requiresPositionPredisplacement & NeededCoordinateSpace.View) > 0),
619 new ConditionalField(StructFields.VertexDescriptionInputs.WorldSpacePositionPredisplacement, (requirements.requiresPositionPredisplacement & NeededCoordinateSpace.World) > 0),
620 new ConditionalField(StructFields.VertexDescriptionInputs.TangentSpacePositionPredisplacement, (requirements.requiresPositionPredisplacement & NeededCoordinateSpace.Tangent) > 0),
621 new ConditionalField(StructFields.VertexDescriptionInputs.AbsoluteWorldSpacePositionPredisplacement, (requirements.requiresPositionPredisplacement & NeededCoordinateSpace.AbsoluteWorld) > 0),
622
623 new ConditionalField(StructFields.VertexDescriptionInputs.uv0, requirements.requiresMeshUVs.Contains(UVChannel.UV0)),
624 new ConditionalField(StructFields.VertexDescriptionInputs.uv1, requirements.requiresMeshUVs.Contains(UVChannel.UV1)),
625 new ConditionalField(StructFields.VertexDescriptionInputs.uv2, requirements.requiresMeshUVs.Contains(UVChannel.UV2)),
626 new ConditionalField(StructFields.VertexDescriptionInputs.uv3, requirements.requiresMeshUVs.Contains(UVChannel.UV3)),
627
628 new ConditionalField(GeneratorDerivativeUtils.uv0Ddx, requirements.requiresMeshUVDerivatives.Contains(UVChannel.UV0)),
629 new ConditionalField(GeneratorDerivativeUtils.uv0Ddy, requirements.requiresMeshUVDerivatives.Contains(UVChannel.UV0)),
630 new ConditionalField(GeneratorDerivativeUtils.uv1Ddx, requirements.requiresMeshUVDerivatives.Contains(UVChannel.UV1)),
631 new ConditionalField(GeneratorDerivativeUtils.uv1Ddy, requirements.requiresMeshUVDerivatives.Contains(UVChannel.UV1)),
632 new ConditionalField(GeneratorDerivativeUtils.uv2Ddx, requirements.requiresMeshUVDerivatives.Contains(UVChannel.UV2)),
633 new ConditionalField(GeneratorDerivativeUtils.uv2Ddy, requirements.requiresMeshUVDerivatives.Contains(UVChannel.UV2)),
634 new ConditionalField(GeneratorDerivativeUtils.uv3Ddx, requirements.requiresMeshUVDerivatives.Contains(UVChannel.UV3)),
635 new ConditionalField(GeneratorDerivativeUtils.uv3Ddy, requirements.requiresMeshUVDerivatives.Contains(UVChannel.UV3)),
636
637 new ConditionalField(StructFields.VertexDescriptionInputs.TimeParameters, requirements.requiresTime),
638
639 new ConditionalField(StructFields.VertexDescriptionInputs.BoneWeights, requirements.requiresVertexSkinning),
640 new ConditionalField(StructFields.VertexDescriptionInputs.BoneIndices, requirements.requiresVertexSkinning),
641 new ConditionalField(StructFields.VertexDescriptionInputs.VertexID, requirements.requiresVertexID),
642 new ConditionalField(StructFields.VertexDescriptionInputs.InstanceID, requirements.requiresInstanceID),
643
644 new ConditionalField(Fields.ObjectToWorld, requirements.requiresTransforms.Contains(NeededTransform.ObjectToWorld)),
645 new ConditionalField(Fields.WorldToObject, requirements.requiresTransforms.Contains(NeededTransform.WorldToObject)),
646 };
647 }
648
649 static ConditionalField[] GetConditionalFieldsFromPixelRequirements(ShaderGraphRequirements requirements)
650 {
651 return new ConditionalField[]
652 {
653 new ConditionalField(StructFields.SurfaceDescriptionInputs.ScreenPosition, requirements.requiresScreenPosition),
654 new ConditionalField(StructFields.SurfaceDescriptionInputs.NDCPosition, requirements.requiresNDCPosition),
655 new ConditionalField(StructFields.SurfaceDescriptionInputs.PixelPosition, requirements.requiresPixelPosition),
656
657 new ConditionalField(StructFields.SurfaceDescriptionInputs.VertexColor, requirements.requiresVertexColor),
658 new ConditionalField(StructFields.SurfaceDescriptionInputs.FaceSign, requirements.requiresFaceSign),
659
660 new ConditionalField(StructFields.SurfaceDescriptionInputs.ObjectSpaceNormal, (requirements.requiresNormal & NeededCoordinateSpace.Object) > 0),
661 new ConditionalField(StructFields.SurfaceDescriptionInputs.ViewSpaceNormal, (requirements.requiresNormal & NeededCoordinateSpace.View) > 0),
662 new ConditionalField(StructFields.SurfaceDescriptionInputs.WorldSpaceNormal, (requirements.requiresNormal & NeededCoordinateSpace.World) > 0),
663 new ConditionalField(StructFields.SurfaceDescriptionInputs.TangentSpaceNormal, (requirements.requiresNormal & NeededCoordinateSpace.Tangent) > 0),
664
665 new ConditionalField(StructFields.SurfaceDescriptionInputs.ObjectSpaceViewDirection, (requirements.requiresViewDir & NeededCoordinateSpace.Object) > 0),
666 new ConditionalField(StructFields.SurfaceDescriptionInputs.ViewSpaceViewDirection, (requirements.requiresViewDir & NeededCoordinateSpace.View) > 0),
667 new ConditionalField(StructFields.SurfaceDescriptionInputs.WorldSpaceViewDirection, (requirements.requiresViewDir & NeededCoordinateSpace.World) > 0),
668 new ConditionalField(StructFields.SurfaceDescriptionInputs.TangentSpaceViewDirection, (requirements.requiresViewDir & NeededCoordinateSpace.Tangent) > 0),
669
670 new ConditionalField(StructFields.SurfaceDescriptionInputs.ObjectSpaceTangent, (requirements.requiresTangent & NeededCoordinateSpace.Object) > 0),
671 new ConditionalField(StructFields.SurfaceDescriptionInputs.ViewSpaceTangent, (requirements.requiresTangent & NeededCoordinateSpace.View) > 0),
672 new ConditionalField(StructFields.SurfaceDescriptionInputs.WorldSpaceTangent, (requirements.requiresTangent & NeededCoordinateSpace.World) > 0),
673 new ConditionalField(StructFields.SurfaceDescriptionInputs.TangentSpaceTangent, (requirements.requiresTangent & NeededCoordinateSpace.Tangent) > 0),
674
675 new ConditionalField(StructFields.SurfaceDescriptionInputs.ObjectSpaceBiTangent, (requirements.requiresBitangent & NeededCoordinateSpace.Object) > 0),
676 new ConditionalField(StructFields.SurfaceDescriptionInputs.ViewSpaceBiTangent, (requirements.requiresBitangent & NeededCoordinateSpace.View) > 0),
677 new ConditionalField(StructFields.SurfaceDescriptionInputs.WorldSpaceBiTangent, (requirements.requiresBitangent & NeededCoordinateSpace.World) > 0),
678 new ConditionalField(StructFields.SurfaceDescriptionInputs.TangentSpaceBiTangent, (requirements.requiresBitangent & NeededCoordinateSpace.Tangent) > 0),
679
680 new ConditionalField(StructFields.SurfaceDescriptionInputs.ObjectSpacePosition, (requirements.requiresPosition & NeededCoordinateSpace.Object) > 0),
681 new ConditionalField(StructFields.SurfaceDescriptionInputs.ViewSpacePosition, (requirements.requiresPosition & NeededCoordinateSpace.View) > 0),
682 new ConditionalField(StructFields.SurfaceDescriptionInputs.WorldSpacePosition, (requirements.requiresPosition & NeededCoordinateSpace.World) > 0),
683 new ConditionalField(StructFields.SurfaceDescriptionInputs.TangentSpacePosition, (requirements.requiresPosition & NeededCoordinateSpace.Tangent) > 0),
684 new ConditionalField(StructFields.SurfaceDescriptionInputs.AbsoluteWorldSpacePosition, (requirements.requiresPosition & NeededCoordinateSpace.AbsoluteWorld) > 0),
685
686 new ConditionalField(StructFields.SurfaceDescriptionInputs.ObjectSpacePositionPredisplacement, (requirements.requiresPositionPredisplacement & NeededCoordinateSpace.Object) > 0),
687 new ConditionalField(StructFields.SurfaceDescriptionInputs.ViewSpacePositionPredisplacement, (requirements.requiresPositionPredisplacement & NeededCoordinateSpace.View) > 0),
688 new ConditionalField(StructFields.SurfaceDescriptionInputs.WorldSpacePositionPredisplacement, (requirements.requiresPositionPredisplacement & NeededCoordinateSpace.World) > 0),
689 new ConditionalField(StructFields.SurfaceDescriptionInputs.TangentSpacePositionPredisplacement, (requirements.requiresPositionPredisplacement & NeededCoordinateSpace.Tangent) > 0),
690 new ConditionalField(StructFields.SurfaceDescriptionInputs.AbsoluteWorldSpacePositionPredisplacement, (requirements.requiresPositionPredisplacement & NeededCoordinateSpace.AbsoluteWorld) > 0),
691
692 new ConditionalField(StructFields.SurfaceDescriptionInputs.uv0, requirements.requiresMeshUVs.Contains(UVChannel.UV0)),
693 new ConditionalField(StructFields.SurfaceDescriptionInputs.uv1, requirements.requiresMeshUVs.Contains(UVChannel.UV1)),
694 new ConditionalField(StructFields.SurfaceDescriptionInputs.uv2, requirements.requiresMeshUVs.Contains(UVChannel.UV2)),
695 new ConditionalField(StructFields.SurfaceDescriptionInputs.uv3, requirements.requiresMeshUVs.Contains(UVChannel.UV3)),
696
697 new ConditionalField(StructFields.SurfaceDescriptionInputs.TimeParameters, requirements.requiresTime),
698
699 new ConditionalField(StructFields.SurfaceDescriptionInputs.BoneWeights, requirements.requiresVertexSkinning),
700 new ConditionalField(StructFields.SurfaceDescriptionInputs.BoneIndices, requirements.requiresVertexSkinning),
701 new ConditionalField(StructFields.SurfaceDescriptionInputs.VertexID, requirements.requiresVertexID),
702 new ConditionalField(StructFields.SurfaceDescriptionInputs.InstanceID, requirements.requiresInstanceID),
703
704 new ConditionalField(Fields.ObjectToWorld, requirements.requiresTransforms.Contains(NeededTransform.ObjectToWorld)),
705 new ConditionalField(Fields.WorldToObject, requirements.requiresTransforms.Contains(NeededTransform.WorldToObject)),
706 };
707 }
708
709 internal static void AddRequiredFields(FieldCollection passRequiredFields, IActiveFieldsSet activeFields)
710 {
711 if (passRequiredFields != null)
712 {
713 foreach (FieldCollection.Item requiredField in passRequiredFields)
714 {
715 activeFields.AddAll(requiredField.field);
716 }
717 }
718 }
719
720 internal static void ApplyFieldDependencies(IActiveFields activeFields, DependencyCollection dependencies)
721 {
722 // add active fields to queue
723 Queue<FieldDescriptor> fieldsToPropagate = new Queue<FieldDescriptor>();
724 foreach (var f in activeFields.fields)
725 {
726 fieldsToPropagate.Enqueue(f);
727 }
728
729 // foreach field in queue:
730 while (fieldsToPropagate.Count > 0)
731 {
732 FieldDescriptor field = fieldsToPropagate.Dequeue();
733 if (activeFields.Contains(field)) // this should always be true
734 {
735 if (dependencies == null)
736 return;
737
738 // find all dependencies of field that are not already active
739 foreach (DependencyCollection.Item d in dependencies.Where(d => (d.dependency.field == field) && !activeFields.Contains(d.dependency.dependsOn)))
740 {
741 // activate them and add them to the queue
742 activeFields.Add(d.dependency.dependsOn);
743 fieldsToPropagate.Enqueue(d.dependency.dependsOn);
744 }
745 }
746 }
747 }
748
749 internal static List<MaterialSlot> FindMaterialSlotsOnNode(IEnumerable<int> slots, AbstractMaterialNode node)
750 {
751 if (slots == null)
752 return null;
753
754 var activeSlots = new List<MaterialSlot>();
755 foreach (var id in slots)
756 {
757 MaterialSlot slot = node.FindSlot<MaterialSlot>(id);
758 if (slot != null)
759 {
760 activeSlots.Add(slot);
761 }
762 }
763 return activeSlots;
764 }
765
766 internal static string AdaptNodeOutput(AbstractMaterialNode node, int outputSlotId, ConcreteSlotValueType convertToType)
767 {
768 var outputSlot = node.FindOutputSlot<MaterialSlot>(outputSlotId);
769
770 if (outputSlot == null)
771 return kErrorString;
772
773 var convertFromType = outputSlot.concreteValueType;
774 var rawOutput = node.GetVariableNameForSlot(outputSlotId);
775 if (convertFromType == convertToType)
776 return rawOutput;
777
778 switch (convertToType)
779 {
780 case ConcreteSlotValueType.Boolean:
781 switch (convertFromType)
782 {
783 case ConcreteSlotValueType.Vector1:
784 return string.Format("((bool) {0})", rawOutput);
785 case ConcreteSlotValueType.Vector2:
786 case ConcreteSlotValueType.Vector3:
787 case ConcreteSlotValueType.Vector4:
788 return string.Format("((bool) {0}.x)", rawOutput);
789 default:
790 return kErrorString;
791 }
792 case ConcreteSlotValueType.Vector1:
793 if (convertFromType == ConcreteSlotValueType.Boolean)
794 return string.Format("(($precision) {0})", rawOutput);
795 else
796 return string.Format("({0}).x", rawOutput);
797 case ConcreteSlotValueType.Vector2:
798 switch (convertFromType)
799 {
800 case ConcreteSlotValueType.Boolean:
801 return string.Format("((($precision) {0}).xx)", rawOutput);
802 case ConcreteSlotValueType.Vector1:
803 return string.Format("({0}.xx)", rawOutput);
804 case ConcreteSlotValueType.Vector3:
805 case ConcreteSlotValueType.Vector4:
806 return string.Format("({0}.xy)", rawOutput);
807 default:
808 return kErrorString;
809 }
810 case ConcreteSlotValueType.Vector3:
811 switch (convertFromType)
812 {
813 case ConcreteSlotValueType.Boolean:
814 return string.Format("((($precision) {0}).xxx)", rawOutput);
815 case ConcreteSlotValueType.Vector1:
816 return string.Format("({0}.xxx)", rawOutput);
817 case ConcreteSlotValueType.Vector2:
818 return string.Format("($precision3({0}, 0.0))", rawOutput);
819 case ConcreteSlotValueType.Vector4:
820 return string.Format("({0}.xyz)", rawOutput);
821 default:
822 return kErrorString;
823 }
824 case ConcreteSlotValueType.Vector4:
825 switch (convertFromType)
826 {
827 case ConcreteSlotValueType.Boolean:
828 return string.Format("((($precision) {0}).xxxx)", rawOutput);
829 case ConcreteSlotValueType.Vector1:
830 return string.Format("({0}.xxxx)", rawOutput);
831 case ConcreteSlotValueType.Vector2:
832 return string.Format("($precision4({0}, 0.0, 1.0))", rawOutput);
833 case ConcreteSlotValueType.Vector3:
834 return string.Format("($precision4({0}, 1.0))", rawOutput);
835 default:
836 return kErrorString;
837 }
838 case ConcreteSlotValueType.Matrix3:
839 return rawOutput;
840 case ConcreteSlotValueType.Matrix2:
841 return rawOutput;
842 case ConcreteSlotValueType.PropertyConnectionState:
843 return node.GetConnnectionStateVariableNameForSlot(outputSlotId);
844 default:
845 return kErrorString;
846 }
847 }
848
849 internal static string AdaptNodeOutputForPreview(AbstractMaterialNode node, int outputSlotId)
850 {
851 string rawOutput = node.GetVariableNameForSlot(outputSlotId);
852 return AdaptNodeOutputForPreview(node, outputSlotId, rawOutput);
853 }
854
855 internal static string AdaptNodeOutputForPreview(AbstractMaterialNode node, int slotId, string variableName)
856 {
857 var slot = node.FindSlot<MaterialSlot>(slotId);
858
859 if (slot == null)
860 return kErrorString;
861
862 var convertFromType = slot.concreteValueType;
863
864 // preview is always dimension 4
865 switch (convertFromType)
866 {
867 case ConcreteSlotValueType.Vector1:
868 return string.Format("half4({0}, {0}, {0}, 1.0)", variableName);
869 case ConcreteSlotValueType.Vector2:
870 return string.Format("half4({0}.x, {0}.y, 0.0, 1.0)", variableName);
871 case ConcreteSlotValueType.Vector3:
872 return string.Format("half4({0}.x, {0}.y, {0}.z, 1.0)", variableName);
873 case ConcreteSlotValueType.Vector4:
874 return string.Format("half4({0}.x, {0}.y, {0}.z, 1.0)", variableName);
875 case ConcreteSlotValueType.Boolean:
876 return string.Format("half4({0}, {0}, {0}, 1.0)", variableName);
877 default:
878 return "half4(0, 0, 0, 0)";
879 }
880 }
881
882 static void GenerateSpaceTranslationSurfaceInputs(
883 NeededCoordinateSpace neededSpaces,
884 InterpolatorType interpolatorType,
885 ShaderStringBuilder builder,
886 string format = "float3 {0};")
887 {
888 if ((neededSpaces & NeededCoordinateSpace.Object) > 0)
889 builder.AppendLine(format, CoordinateSpace.Object.ToVariableName(interpolatorType));
890
891 if ((neededSpaces & NeededCoordinateSpace.World) > 0)
892 builder.AppendLine(format, CoordinateSpace.World.ToVariableName(interpolatorType));
893
894 if ((neededSpaces & NeededCoordinateSpace.View) > 0)
895 builder.AppendLine(format, CoordinateSpace.View.ToVariableName(interpolatorType));
896
897 if ((neededSpaces & NeededCoordinateSpace.Tangent) > 0)
898 builder.AppendLine(format, CoordinateSpace.Tangent.ToVariableName(interpolatorType));
899
900 if ((neededSpaces & NeededCoordinateSpace.AbsoluteWorld) > 0)
901 builder.AppendLine(format, CoordinateSpace.AbsoluteWorld.ToVariableName(interpolatorType));
902 }
903
904 internal static void GeneratePropertiesBlock(ShaderStringBuilder sb, PropertyCollector propertyCollector, KeywordCollector keywordCollector, GenerationMode mode, List<GraphInputData> graphInputs)
905 {
906 sb.AppendLine("Properties");
907 using (sb.BlockScope())
908 {
909 if (graphInputs == null || graphInputs.Count == 0)
910 {
911 foreach (var prop in propertyCollector.properties.Where(x => x.shouldGeneratePropertyBlock))
912 {
913 prop.AppendPropertyBlockStrings(sb);
914 }
915
916 // Keywords use hardcoded state in preview
917 // Do not add them to the Property Block
918 if (mode == GenerationMode.Preview)
919 return;
920
921 foreach (var key in keywordCollector.keywords.Where(x => x.generatePropertyBlock))
922 {
923 key.AppendPropertyBlockStrings(sb);
924 }
925 }
926 else
927 {
928 var propertyInputs = propertyCollector.properties.Where(x => x.shouldGeneratePropertyBlock).ToList();
929 var keywordInputs = keywordCollector.keywords.Where(x => x.generatePropertyBlock).ToList();
930 foreach (var input in graphInputs)
931 {
932 if (input.isKeyword && mode != GenerationMode.Preview)
933 {
934 var keyword = keywordInputs.FirstOrDefault(x => x.referenceName.CompareTo(input.referenceName) == 0);
935 if (keyword != null)
936 {
937 keyword.AppendPropertyBlockStrings(sb);
938 keywordInputs.Remove(keyword);
939 }
940 }
941 else if (!input.isKeyword)
942 {
943 var property = propertyInputs.FirstOrDefault(x => x.referenceName.CompareTo(input.referenceName) == 0);
944 if (property != null)
945 {
946 property.AppendPropertyBlockStrings(sb);
947 propertyInputs.Remove(property);
948 }
949 }
950 }
951
952 foreach (var property in propertyInputs)
953 {
954 property.AppendPropertyBlockStrings(sb);
955 }
956
957 if (mode != GenerationMode.Preview)
958 {
959 foreach (var keyword in keywordInputs)
960 {
961 keyword.AppendPropertyBlockStrings(sb);
962 }
963 }
964 }
965 }
966 }
967
968 internal static void GenerateSurfaceInputStruct(ShaderStringBuilder sb, ShaderGraphRequirements requirements, string structName)
969 {
970 sb.AppendLine($"struct {structName}");
971 using (sb.BlockSemicolonScope())
972 {
973 GenerateSpaceTranslationSurfaceInputs(requirements.requiresNormal, InterpolatorType.Normal, sb);
974 GenerateSpaceTranslationSurfaceInputs(requirements.requiresTangent, InterpolatorType.Tangent, sb);
975 GenerateSpaceTranslationSurfaceInputs(requirements.requiresBitangent, InterpolatorType.BiTangent, sb);
976 GenerateSpaceTranslationSurfaceInputs(requirements.requiresViewDir, InterpolatorType.ViewDirection, sb);
977 GenerateSpaceTranslationSurfaceInputs(requirements.requiresPosition, InterpolatorType.Position, sb);
978 GenerateSpaceTranslationSurfaceInputs(requirements.requiresPositionPredisplacement, InterpolatorType.PositionPredisplacement, sb);
979
980 if (requirements.requiresVertexColor)
981 sb.AppendLine("float4 {0};", ShaderGeneratorNames.VertexColor);
982
983 if (requirements.requiresScreenPosition)
984 sb.AppendLine("float4 {0};", ShaderGeneratorNames.ScreenPosition);
985
986 if (requirements.requiresNDCPosition)
987 sb.AppendLine("float2 {0};", ShaderGeneratorNames.NDCPosition);
988
989 if (requirements.requiresPixelPosition)
990 sb.AppendLine("float2 {0};", ShaderGeneratorNames.PixelPosition);
991
992 if (requirements.requiresFaceSign)
993 sb.AppendLine("float {0};", ShaderGeneratorNames.FaceSign);
994
995 foreach (var channel in requirements.requiresMeshUVs.Distinct())
996 sb.AppendLine("half4 {0};", channel.GetUVName());
997
998 if (requirements.requiresTime)
999 {
1000 sb.AppendLine("float3 {0};", ShaderGeneratorNames.TimeParameters);
1001 }
1002
1003 if (requirements.requiresVertexSkinning)
1004 {
1005 sb.AppendLine("uint4 {0};", ShaderGeneratorNames.BoneIndices);
1006 sb.AppendLine("float4 {0};", ShaderGeneratorNames.BoneWeights);
1007 }
1008
1009 if (requirements.requiresVertexID)
1010 {
1011 sb.AppendLine("uint {0};", ShaderGeneratorNames.VertexID);
1012 }
1013
1014 if (requirements.requiresInstanceID)
1015 {
1016 sb.AppendLine("uint {0};", ShaderGeneratorNames.InstanceID);
1017 }
1018 }
1019 }
1020
1021 internal static void GenerateSurfaceInputTransferCode(ShaderStringBuilder sb, ShaderGraphRequirements requirements, string structName, string variableName)
1022 {
1023 sb.AppendLine($"{structName} {variableName};");
1024
1025 GenerateSpaceTranslationSurfaceInputs(requirements.requiresNormal, InterpolatorType.Normal, sb, $"{variableName}.{{0}} = IN.{{0}};");
1026 GenerateSpaceTranslationSurfaceInputs(requirements.requiresTangent, InterpolatorType.Tangent, sb, $"{variableName}.{{0}} = IN.{{0}};");
1027 GenerateSpaceTranslationSurfaceInputs(requirements.requiresBitangent, InterpolatorType.BiTangent, sb, $"{variableName}.{{0}} = IN.{{0}};");
1028 GenerateSpaceTranslationSurfaceInputs(requirements.requiresViewDir, InterpolatorType.ViewDirection, sb, $"{variableName}.{{0}} = IN.{{0}};");
1029 GenerateSpaceTranslationSurfaceInputs(requirements.requiresPosition, InterpolatorType.Position, sb, $"{variableName}.{{0}} = IN.{{0}};");
1030 GenerateSpaceTranslationSurfaceInputs(requirements.requiresPositionPredisplacement, InterpolatorType.PositionPredisplacement, sb, $"{variableName}.{{0}} = IN.{{0}};");
1031
1032 if (requirements.requiresVertexColor)
1033 sb.AppendLine($"{variableName}.{ShaderGeneratorNames.VertexColor} = IN.{ShaderGeneratorNames.VertexColor};");
1034
1035 if (requirements.requiresScreenPosition)
1036 sb.AppendLine($"{variableName}.{ShaderGeneratorNames.ScreenPosition} = IN.{ShaderGeneratorNames.ScreenPosition};");
1037
1038 if (requirements.requiresNDCPosition)
1039 sb.AppendLine($"{variableName}.{ShaderGeneratorNames.NDCPosition} = IN.{ShaderGeneratorNames.NDCPosition};");
1040
1041 if (requirements.requiresPixelPosition)
1042 sb.AppendLine($"{variableName}.{ShaderGeneratorNames.PixelPosition} = IN.{ShaderGeneratorNames.PixelPosition};");
1043
1044 if (requirements.requiresFaceSign)
1045 sb.AppendLine($"{variableName}.{ShaderGeneratorNames.FaceSign} = IN.{ShaderGeneratorNames.FaceSign};");
1046
1047 foreach (var channel in requirements.requiresMeshUVs.Distinct())
1048 sb.AppendLine($"{variableName}.{channel.GetUVName()} = IN.{channel.GetUVName()};");
1049
1050 if (requirements.requiresTime)
1051 {
1052 sb.AppendLine($"{variableName}.{ShaderGeneratorNames.TimeParameters} = IN.{ShaderGeneratorNames.TimeParameters};");
1053 }
1054
1055 if (requirements.requiresVertexSkinning)
1056 {
1057 sb.AppendLine($"{variableName}.{ShaderGeneratorNames.BoneIndices} = IN.{ShaderGeneratorNames.BoneIndices};");
1058 sb.AppendLine($"{variableName}.{ShaderGeneratorNames.BoneWeights} = IN.{ShaderGeneratorNames.BoneWeights};");
1059 }
1060
1061 if (requirements.requiresVertexID)
1062 {
1063 sb.AppendLine($"{variableName}.{ShaderGeneratorNames.VertexID} = IN.{ShaderGeneratorNames.VertexID};");
1064 }
1065
1066 if (requirements.requiresInstanceID)
1067 {
1068 sb.AppendLine($"{variableName}.{ShaderGeneratorNames.InstanceID} = IN.{ShaderGeneratorNames.InstanceID};");
1069 }
1070 }
1071
1072 internal static void GenerateSurfaceDescriptionStruct(ShaderStringBuilder surfaceDescriptionStruct, List<MaterialSlot> slots, string structName = "SurfaceDescription", IActiveFieldsSet activeFields = null, bool isSubgraphOutput = false, bool virtualTextureFeedback = false)
1073 {
1074 surfaceDescriptionStruct.AppendLine("struct {0}", structName);
1075 using (surfaceDescriptionStruct.BlockSemicolonScope())
1076 {
1077 if (slots != null)
1078 {
1079 if (isSubgraphOutput)
1080 {
1081 var firstSlot = slots.FirstOrDefault();
1082 if (firstSlot != null)
1083 {
1084 var hlslName = $"{NodeUtils.GetHLSLSafeName(firstSlot.shaderOutputName)}_{firstSlot.id}";
1085 surfaceDescriptionStruct.AppendLine("{0} {1};", firstSlot.concreteValueType.ToShaderString(firstSlot.owner.concretePrecision), hlslName);
1086 surfaceDescriptionStruct.AppendLine("{0} {1};", ConcreteSlotValueType.Vector4.ToShaderString(firstSlot.owner.concretePrecision), "Out");
1087 }
1088 else
1089 surfaceDescriptionStruct.AppendLine("{0} {1};", ConcreteSlotValueType.Vector4.ToShaderString(ConcretePrecision.Single), "Out");
1090 }
1091 else
1092 {
1093 foreach (var slot in slots)
1094 {
1095 string hlslName = NodeUtils.GetHLSLSafeName(slot.shaderOutputName);
1096
1097 surfaceDescriptionStruct.AppendLine("{0} {1};", slot.concreteValueType.ToShaderString(slot.owner.concretePrecision), hlslName);
1098
1099 if (activeFields != null)
1100 {
1101 var structField = new FieldDescriptor(structName, hlslName, "");
1102 activeFields.AddAll(structField);
1103 }
1104 }
1105 }
1106 }
1107
1108 // TODO: move this into the regular FieldDescriptor system with a conditional, doesn't belong as a special case here
1109 if (virtualTextureFeedback)
1110 {
1111 surfaceDescriptionStruct.AppendLine("{0} {1};", ConcreteSlotValueType.Vector4.ToShaderString(ConcretePrecision.Single), "VTPackedFeedback");
1112
1113 if (!isSubgraphOutput && activeFields != null)
1114 {
1115 var structField = new FieldDescriptor(structName, "VTPackedFeedback", "");
1116 activeFields.AddAll(structField);
1117 }
1118 }
1119 }
1120 }
1121
1122 internal static void GenerateSurfaceDescriptionFunction(
1123 List<AbstractMaterialNode> nodes,
1124 List<int>[] keywordPermutationsPerNode,
1125 AbstractMaterialNode rootNode,
1126 GraphData graph,
1127 ShaderStringBuilder surfaceDescriptionFunction,
1128 FunctionRegistry functionRegistry,
1129 PropertyCollector shaderProperties,
1130 KeywordCollector shaderKeywords,
1131 GenerationMode mode,
1132 string functionName = "PopulateSurfaceData",
1133 string surfaceDescriptionName = "SurfaceDescription",
1134 Vector1ShaderProperty outputIdProperty = null,
1135 IEnumerable<MaterialSlot> slots = null,
1136 string graphInputStructName = "SurfaceDescriptionInputs",
1137 bool virtualTextureFeedback = false)
1138 {
1139 if (graph == null)
1140 return;
1141
1142 graph.CollectShaderProperties(shaderProperties, mode);
1143
1144 if (mode == GenerationMode.VFX)
1145 {
1146 const string k_GraphProperties = "GraphProperties";
1147 surfaceDescriptionFunction.AppendLine(String.Format("{0} {1}(SurfaceDescriptionInputs IN, {2} PROP)", surfaceDescriptionName, functionName, k_GraphProperties), false);
1148 }
1149 else
1150 surfaceDescriptionFunction.AppendLine(String.Format("{0} {1}(SurfaceDescriptionInputs IN)", surfaceDescriptionName, functionName), false);
1151
1152 using (surfaceDescriptionFunction.BlockScope())
1153 {
1154 surfaceDescriptionFunction.AppendLine("{0} surface = ({0})0;", surfaceDescriptionName);
1155 for (int i = 0; i < nodes.Count; i++)
1156 {
1157 GenerateDescriptionForNode(nodes[i], keywordPermutationsPerNode[i], functionRegistry, surfaceDescriptionFunction,
1158 shaderProperties, shaderKeywords,
1159 graph, mode);
1160 }
1161
1162 functionRegistry.builder.currentNode = null;
1163 surfaceDescriptionFunction.currentNode = null;
1164
1165 GenerateSurfaceDescriptionRemap(graph, rootNode, slots,
1166 surfaceDescriptionFunction, mode);
1167
1168 if (virtualTextureFeedback)
1169 {
1170 VirtualTexturingFeedbackUtils.GenerateVirtualTextureFeedback(
1171 nodes,
1172 keywordPermutationsPerNode,
1173 surfaceDescriptionFunction,
1174 shaderKeywords);
1175 }
1176
1177 surfaceDescriptionFunction.AppendLine("return surface;");
1178 }
1179 }
1180
1181 static void GenerateDescriptionForNode(
1182 AbstractMaterialNode activeNode,
1183 List<int> keywordPermutations,
1184 FunctionRegistry functionRegistry,
1185 ShaderStringBuilder descriptionFunction,
1186 PropertyCollector shaderProperties,
1187 KeywordCollector shaderKeywords,
1188 GraphData graph,
1189 GenerationMode mode)
1190 {
1191 if (activeNode is IGeneratesFunction functionNode)
1192 {
1193 functionRegistry.builder.currentNode = activeNode;
1194 Profiler.BeginSample("GenerateNodeFunction");
1195 functionNode.GenerateNodeFunction(functionRegistry, mode);
1196 Profiler.EndSample();
1197 }
1198
1199 if (activeNode is IGeneratesBodyCode bodyNode)
1200 {
1201 if (keywordPermutations != null)
1202 descriptionFunction.AppendLine(KeywordUtil.GetKeywordPermutationSetConditional(keywordPermutations));
1203
1204 descriptionFunction.currentNode = activeNode;
1205 Profiler.BeginSample("GenerateNodeCode");
1206 bodyNode.GenerateNodeCode(descriptionFunction, mode);
1207 Profiler.EndSample();
1208 descriptionFunction.ReplaceInCurrentMapping(PrecisionUtil.Token, activeNode.concretePrecision.ToShaderString());
1209
1210 if (keywordPermutations != null)
1211 descriptionFunction.AppendLine("#endif");
1212 }
1213
1214 activeNode.CollectShaderProperties(shaderProperties, mode);
1215
1216 if (activeNode is SubGraphNode subGraphNode)
1217 {
1218 subGraphNode.CollectShaderKeywords(shaderKeywords, mode);
1219 }
1220 }
1221
1222 static void GenerateSurfaceDescriptionRemap(
1223 GraphData graph,
1224 AbstractMaterialNode rootNode,
1225 IEnumerable<MaterialSlot> slots,
1226 ShaderStringBuilder surfaceDescriptionFunction,
1227 GenerationMode mode)
1228 {
1229 if (rootNode == null)
1230 {
1231 foreach (var input in slots)
1232 {
1233 if (input != null)
1234 {
1235 var node = input.owner;
1236 var foundEdges = graph.GetEdges(input.slotReference).ToArray();
1237 var hlslName = NodeUtils.GetHLSLSafeName(input.shaderOutputName);
1238 if (foundEdges.Any())
1239 surfaceDescriptionFunction.AppendLine($"surface.{hlslName} = {node.GetSlotValue(input.id, mode, node.concretePrecision)};");
1240 else
1241 surfaceDescriptionFunction.AppendLine($"surface.{hlslName} = {input.GetDefaultValue(mode, node.concretePrecision)};");
1242 }
1243 }
1244 }
1245 else if (rootNode is SubGraphOutputNode)
1246 {
1247 var slot = slots.FirstOrDefault();
1248 if (slot != null)
1249 {
1250 var foundEdges = graph.GetEdges(slot.slotReference).ToArray();
1251 var hlslName = $"{NodeUtils.GetHLSLSafeName(slot.shaderOutputName)}_{slot.id}";
1252 if (foundEdges.Any())
1253 surfaceDescriptionFunction.AppendLine($"surface.{hlslName} = {rootNode.GetSlotValue(slot.id, mode, rootNode.concretePrecision)};");
1254 else
1255 surfaceDescriptionFunction.AppendLine($"surface.{hlslName} = {slot.GetDefaultValue(mode, rootNode.concretePrecision)};");
1256 surfaceDescriptionFunction.AppendLine($"surface.Out = all(isfinite(surface.{hlslName})) ? {GenerationUtils.AdaptNodeOutputForPreview(rootNode, slot.id, "surface." + hlslName)} : float4(1.0f, 0.0f, 1.0f, 1.0f);");
1257 }
1258 }
1259 else
1260 {
1261 var slot = rootNode.GetOutputSlots<MaterialSlot>().FirstOrDefault();
1262 if (slot != null)
1263 {
1264 string slotValue;
1265 string previewOutput;
1266 if (rootNode.isActive)
1267 {
1268 slotValue = rootNode.GetSlotValue(slot.id, mode, rootNode.concretePrecision);
1269 previewOutput = GenerationUtils.AdaptNodeOutputForPreview(rootNode, slot.id);
1270 }
1271 else
1272 {
1273 slotValue = rootNode.GetSlotValue(slot.id, mode, rootNode.concretePrecision);
1274 previewOutput = "float4(0.0f, 0.0f, 0.0f, 0.0f)";
1275 }
1276 surfaceDescriptionFunction.AppendLine($"surface.Out = all(isfinite({slotValue})) ? {previewOutput} : float4(1.0f, 0.0f, 1.0f, 1.0f);");
1277 }
1278 }
1279 }
1280
1281 const string k_VertexDescriptionStructName = "VertexDescription";
1282 internal static void GenerateVertexDescriptionStruct(ShaderStringBuilder builder, List<MaterialSlot> slots, string structName = k_VertexDescriptionStructName, IActiveFieldsSet activeFields = null)
1283 {
1284 builder.AppendLine("struct {0}", structName);
1285 using (builder.BlockSemicolonScope())
1286 {
1287 foreach (var slot in slots)
1288 {
1289 string hlslName = NodeUtils.ConvertToValidHLSLIdentifier(slot.shaderOutputName);
1290 builder.AppendLine("{0} {1};", slot.concreteValueType.ToShaderString(slot.owner.concretePrecision), hlslName);
1291
1292 if (activeFields != null)
1293 {
1294 var structField = new FieldDescriptor(structName, hlslName, "");
1295 activeFields.AddAll(structField);
1296 }
1297 }
1298 }
1299 }
1300
1301 internal static void GenerateVertexDescriptionFunction(
1302 GraphData graph,
1303 ShaderStringBuilder builder,
1304 FunctionRegistry functionRegistry,
1305 PropertyCollector shaderProperties,
1306 KeywordCollector shaderKeywords,
1307 GenerationMode mode,
1308 AbstractMaterialNode rootNode,
1309 List<AbstractMaterialNode> nodes,
1310 List<int>[] keywordPermutationsPerNode,
1311 List<MaterialSlot> slots,
1312 string graphInputStructName = "VertexDescriptionInputs",
1313 string functionName = "PopulateVertexData",
1314 string graphOutputStructName = k_VertexDescriptionStructName)
1315 {
1316 if (graph == null)
1317 return;
1318
1319 graph.CollectShaderProperties(shaderProperties, mode);
1320
1321 if (mode == GenerationMode.VFX)
1322 {
1323 const string k_GraphProperties = "GraphProperties";
1324 builder.AppendLine("{0} {1}({2} IN, {3} PROP)", graphOutputStructName, functionName, graphInputStructName, k_GraphProperties);
1325 }
1326 else
1327 builder.AppendLine("{0} {1}({2} IN)", graphOutputStructName, functionName, graphInputStructName);
1328
1329 using (builder.BlockScope())
1330 {
1331 builder.AppendLine("{0} description = ({0})0;", graphOutputStructName);
1332 Profiler.BeginSample("GenerateNodeDescriptions");
1333 for (int i = 0; i < nodes.Count; i++)
1334 {
1335 GenerateDescriptionForNode(nodes[i], keywordPermutationsPerNode[i], functionRegistry, builder,
1336 shaderProperties, shaderKeywords,
1337 graph, mode);
1338 }
1339 Profiler.EndSample();
1340
1341 functionRegistry.builder.currentNode = null;
1342 builder.currentNode = null;
1343
1344 if (slots.Count != 0)
1345 {
1346 foreach (var slot in slots)
1347 {
1348 var isSlotConnected = graph.GetEdges(slot.slotReference).Any();
1349 var slotName = NodeUtils.ConvertToValidHLSLIdentifier(slot.shaderOutputName);
1350 var slotValue = isSlotConnected ?
1351 ((AbstractMaterialNode)slot.owner).GetSlotValue(slot.id, mode, slot.owner.concretePrecision) : slot.GetDefaultValue(mode, slot.owner.concretePrecision);
1352 builder.AppendLine("description.{0} = {1};", slotName, slotValue);
1353 }
1354 }
1355
1356 builder.AppendLine("return description;");
1357 }
1358 }
1359
1360 internal static string GetSpliceCommand(string command, string token)
1361 {
1362 return !string.IsNullOrEmpty(command) ? command : $"// {token}: <None>";
1363 }
1364
1365 internal static string GetDefaultTemplatePath(string templateName)
1366 {
1367 var basePath = "Packages/com.unity.shadergraph/Editor/Generation/Templates/";
1368 string templatePath = Path.Combine(basePath, templateName);
1369
1370 if (File.Exists(templatePath))
1371 return templatePath;
1372
1373 throw new FileNotFoundException(string.Format(@"Cannot find a template with name ""{0}"".", templateName));
1374 }
1375
1376 internal static string[] defaultDefaultSharedTemplateDirectories = new string[]
1377 {
1378 "Packages/com.unity.shadergraph/Editor/Generation/Templates"
1379 };
1380
1381 internal static string[] GetDefaultSharedTemplateDirectories()
1382 {
1383 return defaultDefaultSharedTemplateDirectories;
1384 }
1385
1386 // Returns null if no 'CustomEditor "___"' line should be added, otherwise the name of the ShaderGUI class.
1387 // Note that it's okay to add an "invalid" ShaderGUI (no class found) as Unity will simply take no action if that's the case, unless if its BaseShaderGUI.
1388 public static string FinalCustomEditorString(ICanChangeShaderGUI canChangeShaderGUI)
1389 {
1390 string finalOverrideName = canChangeShaderGUI.ShaderGUIOverride;
1391 if (string.IsNullOrEmpty(finalOverrideName))
1392 return null;
1393
1394 // Do not add to the final shader if the base ShaderGUI is wanted, as errors will occur.
1395 if (finalOverrideName.Equals("BaseShaderGUI") || finalOverrideName.Equals("UnityEditor.BaseShaderGUI"))
1396 return null;
1397
1398 return finalOverrideName;
1399 }
1400 }
1401}