A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3using System.Collections.ObjectModel;
4using System.Linq;
5using System.IO;
6using UnityEngine;
7using UnityEditor.Build;
8using UnityEditor.Graphing;
9using UnityEditor.ShaderGraph.Internal;
10using UnityEditor.ShaderGraph.Drawing;
11using UnityEngine.Rendering;
12using UnityEngine.Assertions;
13using Pool = UnityEngine.Pool;
14using UnityEngine.Profiling;
15
16namespace UnityEditor.ShaderGraph
17{
18 struct GeneratedShader
19 {
20 public string codeString;
21 public string shaderName;
22 public List<PropertyCollector.TextureInfo> assignedTextures;
23 public string errorMessage;
24
25 public static GeneratedShader Null => new GeneratedShader
26 {
27 codeString = null,
28 shaderName = null,
29 assignedTextures = null,
30 errorMessage = null
31 };
32 }
33
34 class Generator
35 {
36 const string kDebugSymbol = "SHADERGRAPH_DEBUG";
37
38 // readonly data setup in constructor
39 readonly GraphData m_GraphData;
40 readonly AbstractMaterialNode m_OutputNode;
41 readonly GenerationMode m_Mode;
42 readonly string m_PrimaryShaderFullName;
43 readonly AssetCollection m_AssetCollection;
44 readonly bool m_HumanReadable;
45 readonly ReadOnlyCollection<BlockNode> m_ActiveBlocks;
46 readonly ReadOnlyCollection<Target> m_Targets;
47 readonly ReadOnlyCollection<TargetSetupContext> m_TargetContexts;
48 readonly ReadOnlyCollection<string> m_AdditionalShaderIDs;
49
50 readonly GeneratedShader m_PrimaryShader;
51 readonly List<BlockNode> m_PrimaryShaderTemporaryBlocks;
52
53 // direct accessors for primary shader results
54 public string generatedShader => m_PrimaryShader.codeString;
55 public List<PropertyCollector.TextureInfo> configuredTextures => m_PrimaryShader.assignedTextures;
56 public List<BlockNode> temporaryBlocks => m_PrimaryShaderTemporaryBlocks;
57
58 // accessor for all generated shaders
59 public IEnumerable<GeneratedShader> allGeneratedShaders
60 {
61 get
62 {
63 yield return m_PrimaryShader;
64 foreach (var additionalShaderID in m_AdditionalShaderIDs)
65 {
66 yield return BuildShader(additionalShaderID);
67 }
68 }
69 }
70
71 // accessor for all generated compute shaders
72 public IEnumerable<GeneratedShader> allGeneratedComputeShaders
73 {
74 get
75 {
76 // Note: Currently we build one compute shader asset per kernel, can look in the future to see
77 // how to handle multiple kernels per compute shader asset if it's necessary.
78 for (int i = 0; i < m_Targets.Count; i++)
79 {
80 var context = m_TargetContexts[i];
81
82 foreach (KernelCollection.Item kernel in context.kernels)
83 {
84 yield return BuildComputeShader(i, kernel.descriptor);
85 }
86 }
87 }
88 }
89
90 public Generator(GraphData graphData, AbstractMaterialNode outputNode, GenerationMode mode, string primaryShaderName, Target[] targets = null, AssetCollection assetCollection = null, bool humanReadable = false)
91 {
92 m_GraphData = graphData;
93 m_OutputNode = outputNode;
94 m_Mode = mode;
95 if (!string.IsNullOrEmpty(graphData.path))
96 m_PrimaryShaderFullName = graphData.path + "/" + primaryShaderName;
97 else
98 m_PrimaryShaderFullName = primaryShaderName;
99 m_AssetCollection = assetCollection;
100 m_HumanReadable = humanReadable;
101 m_ActiveBlocks = m_GraphData.GetNodes<BlockNode>().ToList().AsReadOnly();
102
103 // get list of targets, and gather data from each
104 if (targets == null)
105 targets = GetTargetImplementations();
106 m_Targets = Array.AsReadOnly(targets);
107
108 var targetContexts = new TargetSetupContext[m_Targets.Count];
109 for (int i = 0; i < m_Targets.Count; i++)
110 {
111 targetContexts[i] = new TargetSetupContext(m_AssetCollection);
112 m_Targets[i].Setup(ref targetContexts[i]);
113 targetContexts[i].SetupFinalize();
114 }
115 m_TargetContexts = Array.AsReadOnly(targetContexts);
116
117 // build list of all the additional shader ids
118 var additionalShaderIDs = new List<string>();
119 for (int i = 0; i < m_TargetContexts.Count; i++)
120 {
121 foreach (var subShader in m_TargetContexts[i].subShaders)
122 {
123 // ignore subshaders without an additionalShaderID -- those are for the primary shader
124 if (string.IsNullOrEmpty(subShader.additionalShaderID))
125 continue;
126
127 if (!additionalShaderIDs.Contains(subShader.additionalShaderID))
128 additionalShaderIDs.Add(subShader.additionalShaderID);
129 }
130 }
131 m_AdditionalShaderIDs = additionalShaderIDs.AsReadOnly();
132
133 m_PrimaryShaderTemporaryBlocks = new List<BlockNode>();
134
135 // build the primary shader immediately (and populate the temporary block list for it)
136 m_PrimaryShader = BuildShader(null, m_PrimaryShaderTemporaryBlocks);
137 }
138
139 Target[] GetTargetImplementations()
140 {
141 if (m_OutputNode == null)
142 {
143 var targets = m_GraphData.activeTargets.ToList();
144 // Sort the built-in target to be last. This is currently a requirement otherwise it'll get picked up for other passes incorrectly
145 targets.Sort(delegate (Target target0, Target target1)
146 {
147 var result = target0.displayName.CompareTo(target1.displayName);
148 // If only one value is built-in, then sort it last
149 if (result != 0)
150 {
151 if (target0.displayName == "Built-In")
152 result = 1;
153 if (target1.displayName == "Built-In")
154 result = -1;
155 }
156
157 return result;
158 });
159 return targets.ToArray();
160 }
161 else
162 {
163 return new Target[] { new PreviewTarget() };
164 }
165 }
166
167 public ActiveFields GatherActiveFieldsFromNode(AbstractMaterialNode outputNode, PassDescriptor pass, List<(BlockFieldDescriptor descriptor, bool isDefaultValue)> activeBlocks, List<BlockFieldDescriptor> connectedBlocks, Target target)
168 {
169 var activeFields = new ActiveFields();
170 if (outputNode == null)
171 {
172 bool hasDotsProperties = false;
173 m_GraphData.ForeachHLSLProperty(h =>
174 {
175 if (h.declaration == HLSLDeclaration.HybridPerInstance)
176 hasDotsProperties = true;
177 });
178
179 var context = new TargetFieldContext(pass, activeBlocks, connectedBlocks, hasDotsProperties);
180 target.GetFields(ref context);
181 var fields = GenerationUtils.GetActiveFieldsFromConditionals(context.conditionalFields.ToArray());
182 foreach (FieldDescriptor field in fields)
183 activeFields.baseInstance.Add(field);
184 }
185 // Preview shader
186 else
187 {
188 activeFields.baseInstance.Add(Fields.GraphPixel);
189 }
190 return activeFields;
191 }
192
193 GeneratedShader ErrorShader(string shaderName, string errorMessage)
194 {
195 Debug.LogError(errorMessage);
196
197 var codeString = ShaderGraphImporter.k_ErrorShader.Replace("Hidden/GraphErrorShader2", shaderName);
198
199 return new GeneratedShader()
200 {
201 codeString = codeString,
202 shaderName = shaderName,
203 assignedTextures = null,
204 errorMessage = errorMessage
205 };
206 }
207
208 // Do we want to return "" in case shaderName is null?
209 private string ProcessShaderName(string shaderName)
210 => shaderName?.Replace("{Name}", m_PrimaryShaderFullName, StringComparison.Ordinal);
211
212 // temporary used by BuildShader()
213 ShaderStringBuilder m_Builder;
214 GeneratedShader BuildShader(string additionalShaderID, List<BlockNode> outTemporaryBlocks = null)
215 {
216 bool isPrimaryShader = string.IsNullOrEmpty(additionalShaderID);
217 string shaderName = isPrimaryShader ? m_PrimaryShaderFullName : ProcessShaderName(additionalShaderID);
218
219 var activeNodeList = Pool.HashSetPool<AbstractMaterialNode>.Get();
220 bool ignoreActiveState = (m_Mode == GenerationMode.Preview); // for previews, we ignore node active state
221 if (m_OutputNode == null)
222 {
223 foreach (var block in m_ActiveBlocks)
224 {
225 // IsActive is equal to if any active implementation has set active blocks
226 // This avoids another call to SetActiveBlocks on each TargetImplementation
227 if (!block.isActive)
228 continue;
229
230 NodeUtils.DepthFirstCollectNodesFromNode(activeNodeList, block, NodeUtils.IncludeSelf.Include, ignoreActiveState: ignoreActiveState);
231 }
232 }
233 else
234 {
235 NodeUtils.DepthFirstCollectNodesFromNode(activeNodeList, m_OutputNode, ignoreActiveState: ignoreActiveState);
236 }
237
238 var shaderProperties = new PropertyCollector();
239 var shaderKeywords = new KeywordCollector();
240 m_GraphData.CollectShaderProperties(shaderProperties, m_Mode);
241 m_GraphData.CollectShaderKeywords(shaderKeywords, m_Mode);
242
243 var graphInputOrderData = new List<GraphInputData>();
244 foreach (var cat in m_GraphData.categories)
245 {
246 foreach (var input in cat.Children)
247 {
248 graphInputOrderData.Add(new GraphInputData()
249 {
250 isKeyword = input is ShaderKeyword,
251 referenceName = input.referenceName
252 });
253 }
254 }
255
256 var variantLimit = this.m_Mode == GenerationMode.Preview
257 ? Mathf.Min(ShaderGraphPreferences.previewVariantLimit, ShaderGraphProjectSettings.instance.shaderVariantLimit)
258 : ShaderGraphProjectSettings.instance.shaderVariantLimit;
259 // Send an action about our current variant usage. This will either add or clear a warning if it exists
260 var action = new ShaderVariantLimitAction(shaderKeywords.permutations.Count, variantLimit);
261 m_GraphData.owner?.graphDataStore?.Dispatch(action);
262
263 if (shaderKeywords.permutations.Count > variantLimit)
264 {
265 // ideally we would not rely on the graph having an asset guid / asset path here (to support compiling asset-less graph datas)
266 string path = AssetDatabase.GUIDToAssetPath(m_GraphData.assetGuid);
267 return ErrorShader(shaderName, $"Error in Shader Graph {path}: {ShaderKeyword.kVariantLimitWarning}");
268 }
269
270 foreach (var activeNode in activeNodeList)
271 {
272 activeNode.SetUsedByGenerator();
273 activeNode.CollectShaderProperties(shaderProperties, m_Mode);
274 }
275
276 // Collect excess shader properties from the TargetImplementation
277 foreach (var target in m_Targets)
278 {
279 target.CollectShaderProperties(shaderProperties, m_Mode);
280 }
281
282 // set the property collector to read only
283 // (to ensure no rogue target or pass starts adding more properties later..)
284 shaderProperties.SetReadOnly();
285
286 // initialize builder
287 m_Builder = new ShaderStringBuilder(humanReadable: m_HumanReadable);
288 m_Builder.AppendLine(@"Shader ""{0}""", shaderName);
289 using (m_Builder.BlockScope())
290 {
291 var shaderDependencies = new List<ShaderDependency>();
292 var shaderCustomEditors = new List<ShaderCustomEditor>();
293 string shaderCustomEditor = typeof(GenericShaderGraphMaterialGUI).FullName;
294 string shaderFallback = "Hidden/Shader Graph/FallbackError";
295
296 GenerationUtils.GeneratePropertiesBlock(m_Builder, shaderProperties, shaderKeywords, m_Mode, graphInputOrderData);
297 for (int i = 0; i < m_Targets.Count; i++)
298 {
299 var context = m_TargetContexts[i];
300
301 // process the subshaders
302 var subShaderProperties = GetSubShaderPropertiesForTarget(m_Targets[i], m_GraphData, m_Mode, m_OutputNode, outTemporaryBlocks);
303 foreach (SubShaderDescriptor subShader in context.subShaders)
304 {
305 // only generate subshaders that belong to the current shader we are building
306 if (subShader.additionalShaderID != additionalShaderID)
307 continue;
308
309 GenerateSubShader(i, subShader, subShaderProperties);
310
311 // pull out shader data from the subshader
312 if (subShader.shaderDependencies != null)
313 shaderDependencies.AddRange(subShader.shaderDependencies);
314
315 if (subShader.shaderCustomEditor != null)
316 shaderCustomEditor = subShader.shaderCustomEditor;
317
318 if (subShader.shaderCustomEditors != null)
319 shaderCustomEditors.AddRange(subShader.shaderCustomEditors);
320
321 if (subShader.shaderFallback != null)
322 shaderFallback = subShader.shaderFallback;
323 }
324 }
325
326 // build shader level data
327 if (!string.IsNullOrEmpty(shaderCustomEditor))
328 m_Builder.AppendLine($"CustomEditor \"{shaderCustomEditor}\"");
329
330 // output custom editors in deterministic order, and only use the first entry for each pipeline asset type
331 shaderCustomEditors.Sort();
332 string lastRenderPipelineAssetType = null;
333 foreach (var customEditor in shaderCustomEditors)
334 {
335 if (customEditor.renderPipelineAssetType != lastRenderPipelineAssetType)
336 m_Builder.AppendLine($"CustomEditorForRenderPipeline \"{customEditor.shaderGUI}\" \"{customEditor.renderPipelineAssetType}\"");
337 lastRenderPipelineAssetType = customEditor.renderPipelineAssetType;
338 }
339
340 // output shader dependencies in deterministic order, and only use the first entry for each dependency name
341 shaderDependencies.Sort();
342 string lastDependencyName = null;
343 foreach (var shaderDependency in shaderDependencies)
344 {
345 if (shaderDependency.dependencyName != lastDependencyName)
346 m_Builder.AppendLine($"Dependency \"{shaderDependency.dependencyName}\" = \"{ProcessShaderName(shaderDependency.shaderName)}\"");
347 lastDependencyName = shaderDependency.dependencyName;
348 }
349
350 if (string.IsNullOrEmpty(shaderFallback))
351 m_Builder.AppendLine("FallBack off");
352 else
353 m_Builder.AppendLine($"FallBack \"{shaderFallback}\"");
354 }
355
356 var generatedShader = new GeneratedShader()
357 {
358 codeString = m_Builder.ToCodeBlock(),
359 shaderName = shaderName,
360 assignedTextures = shaderProperties.GetConfiguredTextures(),
361 errorMessage = null
362 };
363
364 // kill builder to ensure it doesn't get used outside of this function
365 m_Builder = null;
366
367 return generatedShader;
368 }
369
370 void GenerateSubShader(int targetIndex, SubShaderDescriptor descriptor, PropertyCollector subShaderProperties)
371 {
372 if (descriptor.passes == null)
373 return;
374
375 // Early out of preview generation if no passes are used in preview
376 if (m_Mode == GenerationMode.Preview && descriptor.generatesPreview == false)
377 return;
378
379 m_Builder.AppendLine("SubShader");
380 using (m_Builder.BlockScope())
381 {
382 GenerationUtils.GenerateSubShaderTags(m_Targets[targetIndex], descriptor, m_Builder);
383
384 // Get block descriptor list here (from ALL active blocks)
385 List<(BlockFieldDescriptor descriptor, bool isDefaultValue)> activeBlockDescriptors = m_ActiveBlocks.Select(x => (x.descriptor, x.GetInputSlots<MaterialSlot>().FirstOrDefault().IsUsingDefaultValue())).ToList();
386 var connectedBlockDescriptors = m_ActiveBlocks.Where(x => x.IsSlotConnected(0)).Select(x => x.descriptor).ToList();
387
388 foreach (PassCollection.Item pass in descriptor.passes)
389 {
390 var activeFields = GatherActiveFieldsFromNode(m_OutputNode, pass.descriptor, activeBlockDescriptors, connectedBlockDescriptors, m_Targets[targetIndex]);
391
392 // TODO: cleanup this preview check, needed for HD decal preview pass
393 if (m_Mode == GenerationMode.Preview)
394 activeFields.baseInstance.Add(Fields.IsPreview);
395
396 // Check masternode fields for valid passes
397 if (pass.TestActive(activeFields))
398 GenerateShaderPass(targetIndex, pass.descriptor, activeFields, activeBlockDescriptors.Select(x => x.descriptor).ToList(), subShaderProperties);
399 }
400
401 if (descriptor.usePassList != null)
402 {
403 foreach (var usePass in descriptor.usePassList)
404 m_Builder.AppendLine($"UsePass \"{usePass}\"");
405 }
406 }
407 }
408
409 // this builds the list of properties for a Target / Graph combination
410 static PropertyCollector GetSubShaderPropertiesForTarget(Target target, GraphData graph, GenerationMode generationMode, AbstractMaterialNode outputNode, List<BlockNode> outTemporaryBlockNodes)
411 {
412 PropertyCollector subshaderProperties = new PropertyCollector();
413
414 // Collect shader properties declared by active nodes
415 using (var activeNodes = PooledHashSet<AbstractMaterialNode>.Get())
416 {
417 if (outputNode == null)
418 {
419 // shader graph builds active nodes starting from the set of active blocks
420 var currentBlocks = graph.GetNodes<BlockNode>();
421 var activeBlockContext = new TargetActiveBlockContext(currentBlocks.Select(x => x.descriptor).ToList(), null);
422 target.GetActiveBlocks(ref activeBlockContext);
423
424 foreach (var blockFieldDesc in activeBlockContext.activeBlocks)
425 {
426 bool foundBlock = false;
427
428 // attempt to get BlockNode(s) from the stack
429 var vertBlockNode = graph.vertexContext.blocks.FirstOrDefault(x => x.value.descriptor == blockFieldDesc).value;
430 if (vertBlockNode != null)
431 {
432 activeNodes.Add(vertBlockNode);
433 foundBlock = true;
434 }
435
436 var fragBlockNode = graph.fragmentContext.blocks.FirstOrDefault(x => x.value.descriptor == blockFieldDesc).value;
437 if (fragBlockNode != null)
438 {
439 activeNodes.Add(fragBlockNode);
440 foundBlock = true;
441 }
442
443 if (!foundBlock)
444 {
445 // block doesn't exist (user deleted it)
446 // create a temporary block -- don't add to graph, but use it to gather properties
447 var block = new BlockNode();
448 block.Init(blockFieldDesc);
449 block.owner = graph;
450 activeNodes.Add(block);
451
452 // We need to make a list of all of the temporary blocks added
453 // (This is used by the PreviewManager to generate a PreviewProperty)
454 outTemporaryBlockNodes?.Add(block);
455 }
456 }
457 }
458 else
459 {
460 // preview and/or subgraphs build their active node set based on the single output node
461 activeNodes.Add(outputNode);
462 }
463
464 PreviewManager.PropagateNodes(activeNodes, PreviewManager.PropagationDirection.Upstream, activeNodes);
465
466 // NOTE: this is NOT a deterministic ordering
467 foreach (var node in activeNodes)
468 node.CollectShaderProperties(subshaderProperties, generationMode);
469
470 // So we sort the properties after
471 subshaderProperties.Sort();
472 }
473
474 // Collect graph properties
475 {
476 graph.CollectShaderProperties(subshaderProperties, generationMode);
477 }
478
479 // Collect shader properties declared by the Target
480 {
481 target.CollectShaderProperties(subshaderProperties, generationMode);
482 }
483
484 subshaderProperties.SetReadOnly();
485
486 return subshaderProperties;
487 }
488
489 GeneratedShader BuildComputeShader(int targetIndex, KernelDescriptor kernel)
490 {
491 m_Builder = new ShaderStringBuilder(humanReadable: m_HumanReadable);
492
493 // Note: Currently we generate one compute shader asset per kernel.
494 GenerateKernel(targetIndex, kernel);
495
496 var generatedShader = new GeneratedShader
497 {
498 codeString = m_Builder.ToCodeBlock(),
499 shaderName = ProcessShaderName(kernel.name),
500 assignedTextures = null,
501 errorMessage = null
502 };
503
504 // kill builder to ensure it doesn't get used outside of this function
505 m_Builder = null;
506
507 return generatedShader;
508 }
509
510 void GenerateKernel(int targetIndex, KernelDescriptor kernel)
511 {
512 var pass = kernel.passDescriptorReference;
513
514 // Patch the pass descriptor template with the one defined by the kernel.
515 pass.passTemplatePath = kernel.templatePath;
516 pass.sharedTemplateDirectories = kernel.sharedTemplateDirectories;
517
518 // Grab various graph information needed to generate the reference pass descriptor.
519 var outTemporaryBlocks = new List<BlockNode>();
520 var subShaderProperties = GetSubShaderPropertiesForTarget(m_Targets[targetIndex], m_GraphData, m_Mode, m_OutputNode, outTemporaryBlocks);
521
522 List<(BlockFieldDescriptor descriptor, bool isDefaultValue)> activeBlockDescriptors = m_ActiveBlocks.Select(x => (x.descriptor, x.GetInputSlots<MaterialSlot>().FirstOrDefault().IsUsingDefaultValue())).ToList();
523 var connectedBlockDescriptors = m_ActiveBlocks.Where(x => x.IsSlotConnected(0)).Select(x => x.descriptor).ToList();
524
525 var activeFields = GatherActiveFieldsFromNode(m_OutputNode, pass, activeBlockDescriptors, connectedBlockDescriptors, m_Targets[targetIndex]);
526
527 // Invoke the existing shader pass generation routine.
528 GenerateShaderPass(targetIndex, pass, activeFields, activeBlockDescriptors.Select(x => x.descriptor).ToList(), subShaderProperties);
529 }
530
531 void GenerateShaderPass(int targetIndex, PassDescriptor pass, ActiveFields activeFields, List<BlockFieldDescriptor> currentBlockDescriptors, PropertyCollector subShaderProperties)
532 {
533 // Early exit if pass is not used in preview
534 if (m_Mode == GenerationMode.Preview && !pass.useInPreview)
535 return;
536
537 Profiler.BeginSample("GenerateShaderPass");
538
539 // --------------------------------------------------
540 // Debug
541
542 // Get scripting symbols
543 BuildTargetGroup buildTargetGroup = EditorUserBuildSettings.selectedBuildTargetGroup;
544 NamedBuildTarget namedBuildTarget = NamedBuildTarget.FromBuildTargetGroup(buildTargetGroup);
545 if (buildTargetGroup == BuildTargetGroup.Standalone && EditorUserBuildSettings.standaloneBuildSubtarget == StandaloneBuildSubtarget.Server)
546 namedBuildTarget = NamedBuildTarget.Server;
547 string defines = PlayerSettings.GetScriptingDefineSymbols(namedBuildTarget);
548
549 bool isDebug = defines.Contains(kDebugSymbol);
550
551 // --------------------------------------------------
552 // Setup
553
554 // Custom Interpolator Global flags (see definition for details).
555 CustomInterpolatorUtils.generatorNodeOnly = m_OutputNode != null;
556 CustomInterpolatorUtils.generatorSkipFlag = m_Targets[targetIndex].ignoreCustomInterpolators ||
557 !CustomInterpolatorUtils.generatorNodeOnly && (pass.customInterpolators == null || pass.customInterpolators.Count() == 0);
558
559 // Initialize custom interpolator sub generator
560 // NOTE: propertyCollector is not really used anymore -- we use the subshader PropertyCollector instead
561 CustomInterpSubGen customInterpSubGen = new CustomInterpSubGen(m_OutputNode != null);
562
563 // Initiailize Collectors
564 Profiler.BeginSample("CollectShaderKeywords");
565 var propertyCollector = new PropertyCollector();
566 var keywordCollector = new KeywordCollector();
567 m_GraphData.CollectShaderKeywords(keywordCollector, m_Mode);
568 Profiler.EndSample();
569
570 // Get upstream nodes from ShaderPass port mask
571 List<AbstractMaterialNode> vertexNodes;
572 List<AbstractMaterialNode> pixelNodes;
573
574 // Get Port references from ShaderPass
575 var pixelSlots = new List<MaterialSlot>();
576 var vertexSlots = new List<MaterialSlot>();
577
578 if (m_OutputNode == null)
579 {
580 // Update supported block list for current target implementation
581 Profiler.BeginSample("GetCurrentTargetActiveBlocks");
582 var activeBlockContext = new TargetActiveBlockContext(currentBlockDescriptors, pass);
583 m_Targets[targetIndex].GetActiveBlocks(ref activeBlockContext);
584 Profiler.EndSample();
585
586 void ProcessStackForPass(ContextData contextData, BlockFieldDescriptor[] passBlockMask,
587 List<AbstractMaterialNode> nodeList, List<MaterialSlot> slotList)
588 {
589 if (passBlockMask == null)
590 {
591 Profiler.EndSample();
592 return;
593 }
594
595 Profiler.BeginSample("ProcessStackForPass");
596 foreach (var blockFieldDescriptor in passBlockMask)
597 {
598 // Mask blocks on active state
599 // TODO: Can we merge these?
600 if (!activeBlockContext.activeBlocks.Contains(blockFieldDescriptor))
601 continue;
602
603 // Attempt to get BlockNode from the stack
604 var block = contextData.blocks.FirstOrDefault(x => x.value.descriptor == blockFieldDescriptor).value;
605
606 // If the BlockNode doesnt exist in the stack we need to create one
607 // TODO: Can we do the code gen without a node instance?
608 if (block == null)
609 {
610 block = new BlockNode();
611 block.Init(blockFieldDescriptor);
612 block.owner = m_GraphData;
613 }
614 // Dont collect properties from temp nodes
615 else
616 {
617 block.CollectShaderProperties(propertyCollector, m_Mode);
618 }
619
620 // Add nodes and slots from supported vertex blocks
621 NodeUtils.DepthFirstCollectNodesFromNode(nodeList, block, NodeUtils.IncludeSelf.Include);
622 slotList.Add(block.FindSlot<MaterialSlot>(0));
623 activeFields.baseInstance.Add(block.descriptor);
624 }
625 Profiler.EndSample();
626 }
627
628 // Mask blocks per pass
629 vertexNodes = Pool.ListPool<AbstractMaterialNode>.Get();
630 pixelNodes = Pool.ListPool<AbstractMaterialNode>.Get();
631
632 // Process stack for vertex and fragment
633 ProcessStackForPass(m_GraphData.vertexContext, pass.validVertexBlocks, vertexNodes, vertexSlots);
634 ProcessStackForPass(m_GraphData.fragmentContext, pass.validPixelBlocks, pixelNodes, pixelSlots);
635
636 // Collect excess shader properties from the TargetImplementation
637 m_Targets[targetIndex].CollectShaderProperties(propertyCollector, m_Mode);
638 }
639 else if (m_OutputNode is SubGraphOutputNode)
640 {
641 GenerationUtils.GetUpstreamNodesForShaderPass(m_OutputNode, pass, out vertexNodes, out pixelNodes);
642 var slot = m_OutputNode.GetInputSlots<MaterialSlot>().FirstOrDefault();
643 if (slot != null)
644 pixelSlots = new List<MaterialSlot>() { slot };
645 else
646 pixelSlots = new List<MaterialSlot>();
647 vertexSlots = new List<MaterialSlot>();
648 }
649 else
650 {
651 GenerationUtils.GetUpstreamNodesForShaderPass(m_OutputNode, pass, out vertexNodes, out pixelNodes);
652 pixelSlots = new List<MaterialSlot>()
653 {
654 new Vector4MaterialSlot(0, "Out", "Out", SlotType.Output, Vector4.zero) { owner = m_OutputNode },
655 };
656 vertexSlots = new List<MaterialSlot>();
657 }
658
659 // Inject custom interpolator antecedents where appropriate
660 customInterpSubGen.ProcessExistingStackData(vertexNodes, vertexSlots, pixelNodes, activeFields.baseInstance);
661
662 // Track permutation indices for all nodes
663 List<int>[] vertexNodePermutations = new List<int>[vertexNodes.Count];
664 List<int>[] pixelNodePermutations = new List<int>[pixelNodes.Count];
665
666 // Get active fields from upstream Node requirements
667 Profiler.BeginSample("GetActiveFieldsFromUpstreamNodes");
668 ShaderGraphRequirementsPerKeyword graphRequirements;
669 GenerationUtils.GetActiveFieldsAndPermutationsForNodes(pass, keywordCollector, vertexNodes, pixelNodes, new bool[4] { false, false, false, false },
670 vertexNodePermutations, pixelNodePermutations, activeFields, out graphRequirements);
671 Profiler.EndSample();
672
673 // Moved this up so that we can reuse the information to figure out which struct Descriptors
674 // should be populated by custom interpolators.
675 var passStructs = new List<StructDescriptor>();
676 passStructs.AddRange(pass.structs.Select(x => x.descriptor));
677
678 // GET CUSTOM ACTIVE FIELDS HERE!
679
680 // inject custom interpolator fields into the pass structs
681 passStructs = customInterpSubGen.CopyModifyExistingPassStructs(passStructs, activeFields.baseInstance);
682
683 // Get active fields from ShaderPass
684 Profiler.BeginSample("GetActiveFieldsFromPass");
685 GenerationUtils.AddRequiredFields(pass.requiredFields, activeFields.baseInstance);
686 Profiler.EndSample();
687
688 // Function Registry
689 var functionBuilder = new ShaderStringBuilder(humanReadable: m_HumanReadable);
690 var graphIncludes = new IncludeCollection();
691 var functionRegistry = new FunctionRegistry(functionBuilder, graphIncludes, true);
692
693 // Hash table of named $splice(name) commands
694 // Key: splice token
695 // Value: string to splice
696 Dictionary<string, string> spliceCommands = new Dictionary<string, string>();
697
698 // populate splice commands from the pass's customInterpolator descriptors.
699 if (pass.customInterpolators != null)
700 customInterpSubGen.ProcessDescriptors(pass.customInterpolators.Select(item => item.descriptor));
701 customInterpSubGen.AppendToSpliceCommands(spliceCommands);
702
703 // --------------------------------------------------
704 // Dependencies
705
706 // Propagate active field requirements using dependencies
707 // Must be executed before types are built
708 Profiler.BeginSample("PropagateActiveFieldReqs");
709 foreach (var instance in activeFields.all.instances)
710 {
711 GenerationUtils.ApplyFieldDependencies(instance, pass.fieldDependencies);
712 }
713 Profiler.EndSample();
714
715 // --------------------------------------------------
716 // Pass Setup
717
718 // Name
719 if (!string.IsNullOrEmpty(pass.displayName))
720 {
721 spliceCommands.Add("PassName", $"Name \"{pass.displayName}\"");
722 }
723 else
724 {
725 spliceCommands.Add("PassName", "// Name: <None>");
726 }
727
728 // Tags
729 if (!string.IsNullOrEmpty(pass.lightMode))
730 {
731 spliceCommands.Add("LightMode", $"\"LightMode\" = \"{pass.lightMode}\"");
732 }
733 else
734 {
735 spliceCommands.Add("LightMode", "// LightMode: <None>");
736 }
737
738 // --------------------------------------------------
739 // Pass Code
740
741 // Render State
742 Profiler.BeginSample("RenderState");
743 using (var renderStateBuilder = new ShaderStringBuilder(humanReadable: m_HumanReadable))
744 {
745 // Render states need to be separated by RenderState.Type
746 // The first passing ConditionalRenderState of each type is inserted
747 foreach (RenderStateType type in Enum.GetValues(typeof(RenderStateType)))
748 {
749 var renderStates = pass.renderStates?.Where(x => x.descriptor.type == type);
750 if (renderStates != null)
751 {
752 foreach (RenderStateCollection.Item renderState in renderStates)
753 {
754 if (renderState.TestActive(activeFields))
755 {
756 renderStateBuilder.AppendLine(renderState.value);
757
758 // Cull is the only render state type that causes a compilation error
759 // when there are multiple Cull directive with different values in a pass.
760 if (type == RenderStateType.Cull)
761 break;
762 }
763 }
764 }
765 }
766
767 string command = GenerationUtils.GetSpliceCommand(renderStateBuilder.ToCodeBlock(), "RenderState");
768 spliceCommands.Add("RenderState", command);
769 }
770 Profiler.EndSample();
771 // Pragmas
772 Profiler.BeginSample("Pragmas");
773 using (var passPragmaBuilder = new ShaderStringBuilder(humanReadable: m_HumanReadable))
774 {
775 if (pass.pragmas != null)
776 {
777 foreach (PragmaCollection.Item pragma in pass.pragmas)
778 {
779 if (pragma.TestActive(activeFields))
780 passPragmaBuilder.AppendLine(pragma.value);
781 }
782 }
783
784 // Enable this to turn on shader debugging
785 bool debugShader = false;
786 if (debugShader)
787 {
788 passPragmaBuilder.AppendLine("#pragma enable_d3d11_debug_symbols");
789 }
790
791 string command = GenerationUtils.GetSpliceCommand(passPragmaBuilder.ToCodeBlock(), "PassPragmas");
792 spliceCommands.Add("PassPragmas", command);
793 }
794 Profiler.EndSample();
795 // Keywords
796 Profiler.BeginSample("Keywords");
797 using (var passKeywordBuilder = new ShaderStringBuilder(humanReadable: m_HumanReadable))
798 {
799 if (pass.keywords != null)
800 {
801 List<KeywordShaderStage> stages = new List<KeywordShaderStage>();
802 foreach (KeywordCollection.Item keyword in pass.keywords)
803 {
804 if (keyword.TestActive(activeFields))
805 {
806 keyword.descriptor.AppendKeywordDeclarationStrings(passKeywordBuilder);
807 }
808 }
809 }
810
811 string command = GenerationUtils.GetSpliceCommand(passKeywordBuilder.ToCodeBlock(), "PassKeywords");
812 spliceCommands.Add("PassKeywords", command);
813 }
814 Profiler.EndSample();
815
816 List<StructDescriptor> originalPassStructs = new List<StructDescriptor>(passStructs);
817
818 // Note: The code below is copy/pasted into GeneratePassStructsAndInterpolators() in GeneratorDerivativeUtils.cs. If any changes are made to this code,
819 // then a corresponding change needs to be made in that function.
820
821 // -----------------------------
822 // Generated structs and Packing code
823 Profiler.BeginSample("StructsAndPacking");
824 var interpolatorBuilder = new ShaderStringBuilder(humanReadable: m_HumanReadable);
825
826 if (passStructs != null)
827 {
828 var packedStructs = new List<StructDescriptor>();
829 foreach (var shaderStruct in passStructs)
830 {
831 if (shaderStruct.packFields == false)
832 continue; //skip structs that do not need interpolator packs
833
834 List<int> packedCounts = new List<int>();
835 var packStruct = new StructDescriptor();
836
837 //generate packed functions
838 if (activeFields.permutationCount > 0)
839 {
840 var generatedPackedTypes = new Dictionary<string, (ShaderStringBuilder, List<int>)>();
841 foreach (var instance in activeFields.allPermutations.instances)
842 {
843 var instanceGenerator = new ShaderStringBuilder();
844 GenerationUtils.GenerateInterpolatorFunctions(shaderStruct, instance, m_HumanReadable, out instanceGenerator);
845 var key = instanceGenerator.ToCodeBlock();
846 if (generatedPackedTypes.TryGetValue(key, out var value))
847 value.Item2.Add(instance.permutationIndex);
848 else
849 generatedPackedTypes.Add(key, (instanceGenerator, new List<int> { instance.permutationIndex }));
850 }
851
852 var isFirst = true;
853 foreach (var generated in generatedPackedTypes)
854 {
855 if (isFirst)
856 {
857 isFirst = false;
858 interpolatorBuilder.AppendLine(KeywordUtil.GetKeywordPermutationSetConditional(generated.Value.Item2));
859 }
860 else
861 interpolatorBuilder.AppendLine(KeywordUtil.GetKeywordPermutationSetConditional(generated.Value.Item2).Replace("#if", "#elif"));
862
863 //interpolatorBuilder.Concat(generated.Value.Item1);
864 interpolatorBuilder.AppendLines(generated.Value.Item1.ToString());
865 }
866 if (generatedPackedTypes.Count > 0)
867 interpolatorBuilder.AppendLine("#endif");
868 }
869 else
870 {
871 ShaderStringBuilder localInterpolatorBuilder; // GenerateInterpolatorFunctions do the allocation
872 GenerationUtils.GenerateInterpolatorFunctions(shaderStruct, activeFields.baseInstance, m_HumanReadable, out localInterpolatorBuilder);
873 interpolatorBuilder.Concat(localInterpolatorBuilder);
874 }
875 //using interp index from functions, generate packed struct descriptor
876 GenerationUtils.GeneratePackedStruct(shaderStruct, activeFields, out packStruct);
877 packedStructs.Add(packStruct);
878 }
879 passStructs.AddRange(packedStructs);
880 }
881 if (interpolatorBuilder.length != 0) //hard code interpolators to float, TODO: proper handle precision
882 interpolatorBuilder.ReplaceInCurrentMapping(PrecisionUtil.Token, ConcretePrecision.Single.ToShaderString());
883 else
884 interpolatorBuilder.AppendLine("//Interpolator Packs: <None>");
885 spliceCommands.Add("InterpolatorPack", interpolatorBuilder.ToCodeBlock());
886 Profiler.EndSample();
887
888 // Generated String Builders for all struct types
889 Profiler.BeginSample("StructTypes");
890 var passStructBuilder = new ShaderStringBuilder(humanReadable: m_HumanReadable);
891 if (passStructs != null)
892 {
893 var structBuilder = new ShaderStringBuilder(humanReadable: m_HumanReadable);
894 foreach (StructDescriptor shaderStruct in passStructs)
895 {
896 GenerationUtils.GenerateShaderStruct(shaderStruct, activeFields, m_HumanReadable, out structBuilder);
897 structBuilder.ReplaceInCurrentMapping(PrecisionUtil.Token, ConcretePrecision.Single.ToShaderString()); //hard code structs to float, TODO: proper handle precision
898 passStructBuilder.Concat(structBuilder);
899 }
900 }
901 if (passStructBuilder.length == 0)
902 passStructBuilder.AppendLine("//Pass Structs: <None>");
903 spliceCommands.Add("PassStructs", passStructBuilder.ToCodeBlock());
904 Profiler.EndSample();
905 // Note: End of code copy/pasted into GeneratePassStructsAndInterpolators() in GeneratorDerivativeUtils.cs.
906
907
908 // --------------------------------------------------
909 // Graph Vertex
910
911 Profiler.BeginSample("GraphVertex");
912 var vertexBuilder = new ShaderStringBuilder(humanReadable: m_HumanReadable);
913
914 // If vertex modification enabled
915 if (activeFields.baseInstance.Contains(Fields.GraphVertex) && vertexSlots != null)
916 {
917 // Setup
918 string vertexGraphInputName = "VertexDescriptionInputs";
919 string vertexGraphOutputName = "VertexDescription";
920 string vertexGraphFunctionName = "VertexDescriptionFunction";
921 var vertexGraphFunctionBuilder = new ShaderStringBuilder(humanReadable: m_HumanReadable);
922 var vertexGraphOutputBuilder = new ShaderStringBuilder(humanReadable: m_HumanReadable);
923
924 // Build vertex graph outputs
925 // Add struct fields to active fields
926 Profiler.BeginSample("GenerateVertexDescriptionStruct");
927 GenerationUtils.GenerateVertexDescriptionStruct(vertexGraphOutputBuilder, vertexSlots, vertexGraphOutputName, activeFields.baseInstance);
928 Profiler.EndSample();
929
930 // Build vertex graph functions from ShaderPass vertex port mask
931 Profiler.BeginSample("GenerateVertexDescriptionFunction");
932 GenerationUtils.GenerateVertexDescriptionFunction(
933 m_GraphData,
934 vertexGraphFunctionBuilder,
935 functionRegistry,
936 propertyCollector,
937 keywordCollector,
938 m_Mode,
939 m_OutputNode,
940 vertexNodes,
941 vertexNodePermutations,
942 vertexSlots,
943 vertexGraphInputName,
944 vertexGraphFunctionName,
945 vertexGraphOutputName);
946 Profiler.EndSample();
947
948 // Generate final shader strings
949 if (m_HumanReadable)
950 {
951 vertexBuilder.AppendLines(vertexGraphOutputBuilder.ToString());
952 vertexBuilder.AppendNewLine();
953 vertexBuilder.AppendLines(vertexGraphFunctionBuilder.ToString());
954 }
955 else
956 {
957 vertexBuilder.Append(vertexGraphOutputBuilder.ToString());
958 vertexBuilder.AppendNewLine();
959 vertexBuilder.Append(vertexGraphFunctionBuilder.ToString());
960 }
961 }
962
963 // Add to splice commands
964 if (vertexBuilder.length == 0)
965 vertexBuilder.AppendLine("// GraphVertex: <None>");
966 spliceCommands.Add("GraphVertex", vertexBuilder.ToCodeBlock());
967 Profiler.EndSample();
968 // --------------------------------------------------
969 // Graph Pixel
970
971 Profiler.BeginSample("GraphPixel");
972 // Setup
973 string pixelGraphInputName = "SurfaceDescriptionInputs";
974 string pixelGraphOutputName = "SurfaceDescription";
975 string pixelGraphFunctionName = "SurfaceDescriptionFunction";
976 var pixelGraphOutputBuilder = new ShaderStringBuilder(humanReadable: m_HumanReadable);
977 var pixelGraphFunctionBuilder = new ShaderStringBuilder(humanReadable: m_HumanReadable);
978
979 // Build pixel graph outputs
980 // Add struct fields to active fields
981 GenerationUtils.GenerateSurfaceDescriptionStruct(pixelGraphOutputBuilder, pixelSlots, pixelGraphOutputName, activeFields.baseInstance, m_OutputNode is SubGraphOutputNode, pass.virtualTextureFeedback);
982
983 // Build pixel graph functions from ShaderPass pixel port mask
984 GenerationUtils.GenerateSurfaceDescriptionFunction(
985 pixelNodes,
986 pixelNodePermutations,
987 m_OutputNode,
988 m_GraphData,
989 pixelGraphFunctionBuilder,
990 functionRegistry,
991 propertyCollector,
992 keywordCollector,
993 m_Mode,
994 pixelGraphFunctionName,
995 pixelGraphOutputName,
996 null,
997 pixelSlots,
998 pixelGraphInputName,
999 pass.virtualTextureFeedback);
1000
1001 using (var pixelBuilder = new ShaderStringBuilder(humanReadable: m_HumanReadable))
1002 {
1003 // Generate final shader strings
1004 pixelBuilder.AppendLines(pixelGraphOutputBuilder.ToString());
1005 pixelBuilder.AppendNewLine();
1006 pixelBuilder.AppendLines(pixelGraphFunctionBuilder.ToString());
1007
1008 // Add to splice commands
1009 if (pixelBuilder.length == 0)
1010 pixelBuilder.AppendLine("// GraphPixel: <None>");
1011 spliceCommands.Add("GraphPixel", pixelBuilder.ToCodeBlock());
1012 }
1013 Profiler.EndSample();
1014
1015 // --------------------------------------------------
1016 // Graph Functions
1017 Profiler.BeginSample("GraphFunctions");
1018 if (functionBuilder.length == 0)
1019 functionBuilder.AppendLine("// GraphFunctions: <None>");
1020 spliceCommands.Add("GraphFunctions", functionBuilder.ToCodeBlock());
1021 Profiler.EndSample();
1022 // --------------------------------------------------
1023 // Graph Keywords
1024 Profiler.BeginSample("GraphKeywords");
1025 using (var keywordBuilder = new ShaderStringBuilder(humanReadable: m_HumanReadable))
1026 {
1027 keywordCollector.GetKeywordsDeclaration(keywordBuilder, m_Mode);
1028 if (keywordBuilder.length == 0)
1029 keywordBuilder.AppendLine("// GraphKeywords: <None>");
1030 spliceCommands.Add("GraphKeywords", keywordBuilder.ToCodeBlock());
1031 }
1032 Profiler.EndSample();
1033
1034 // --------------------------------------------------
1035 // Graph Properties
1036 Profiler.BeginSample("GraphProperties");
1037 using (var propertyBuilder = new ShaderStringBuilder(humanReadable: m_HumanReadable))
1038 {
1039 subShaderProperties.GetPropertiesDeclaration(propertyBuilder, m_Mode, m_GraphData.graphDefaultConcretePrecision);
1040
1041 if (m_Mode == GenerationMode.VFX)
1042 {
1043 const string k_GraphPropertiesStruct = "GraphProperties";
1044 propertyBuilder.AppendLine($"struct {k_GraphPropertiesStruct}");
1045 using (propertyBuilder.BlockSemicolonScope())
1046 {
1047 m_GraphData.ForeachHLSLProperty(h =>
1048 {
1049 if (!h.IsObjectType() && h.declaration != HLSLDeclaration.Global)
1050 h.AppendTo(propertyBuilder);
1051 });
1052 }
1053 }
1054
1055 if (propertyBuilder.length == 0)
1056 propertyBuilder.AppendLine("// GraphProperties: <None>");
1057 spliceCommands.Add("GraphProperties", propertyBuilder.ToCodeBlock());
1058 }
1059 Profiler.EndSample();
1060
1061 // --------------------------------------------------
1062 // Graph Defines
1063 Profiler.BeginSample("GraphDefines");
1064 using (var graphDefines = new ShaderStringBuilder(humanReadable: m_HumanReadable))
1065 {
1066 graphDefines.AppendLine("#define SHADERPASS {0}", pass.referenceName);
1067
1068 if (pass.defines != null)
1069 {
1070 foreach (DefineCollection.Item define in pass.defines)
1071 {
1072 if (define.TestActive(activeFields))
1073 graphDefines.AppendLine(define.value);
1074 }
1075 }
1076
1077 if (graphRequirements.permutationCount > 0)
1078 {
1079 List<int> activePermutationIndices;
1080
1081 // Depth Texture
1082 activePermutationIndices = graphRequirements.allPermutations.instances
1083 .Where(p => p.requirements.requiresDepthTexture)
1084 .Select(p => p.permutationIndex)
1085 .ToList();
1086 if (activePermutationIndices.Count > 0)
1087 {
1088 graphDefines.AppendLine(KeywordUtil.GetKeywordPermutationSetConditional(activePermutationIndices));
1089 graphDefines.AppendLine("#define REQUIRE_DEPTH_TEXTURE");
1090 graphDefines.AppendLine("#endif");
1091 }
1092
1093 // Opaque Texture
1094 activePermutationIndices = graphRequirements.allPermutations.instances
1095 .Where(p => p.requirements.requiresCameraOpaqueTexture)
1096 .Select(p => p.permutationIndex)
1097 .ToList();
1098 if (activePermutationIndices.Count > 0)
1099 {
1100 graphDefines.AppendLine(KeywordUtil.GetKeywordPermutationSetConditional(activePermutationIndices));
1101 graphDefines.AppendLine("#define REQUIRE_OPAQUE_TEXTURE");
1102 graphDefines.AppendLine("#endif");
1103 }
1104 }
1105 else
1106 {
1107 // Depth Texture
1108 if (graphRequirements.baseInstance.requirements.requiresDepthTexture)
1109 graphDefines.AppendLine("#define REQUIRE_DEPTH_TEXTURE");
1110
1111 // Opaque Texture
1112 if (graphRequirements.baseInstance.requirements.requiresCameraOpaqueTexture)
1113 graphDefines.AppendLine("#define REQUIRE_OPAQUE_TEXTURE");
1114 }
1115
1116 // Add to splice commands
1117 spliceCommands.Add("GraphDefines", graphDefines.ToCodeBlock());
1118 }
1119 Profiler.EndSample();
1120 // --------------------------------------------------
1121 // Includes
1122
1123 var allIncludes = new IncludeCollection();
1124 allIncludes.Add(pass.includes);
1125 allIncludes.Add(graphIncludes);
1126
1127 using (var preGraphIncludeBuilder = new ShaderStringBuilder())
1128 {
1129 foreach (var include in allIncludes.Where(x => x.location == IncludeLocation.Pregraph))
1130 {
1131 if (include.TestActive(activeFields))
1132 preGraphIncludeBuilder.AppendLine(include.value);
1133 }
1134
1135 string command = GenerationUtils.GetSpliceCommand(preGraphIncludeBuilder.ToCodeBlock(), "PreGraphIncludes");
1136 spliceCommands.Add("PreGraphIncludes", command);
1137 }
1138
1139 using (var graphIncludeBuilder = new ShaderStringBuilder())
1140 {
1141 foreach (var include in allIncludes.Where(x => x.location == IncludeLocation.Graph))
1142 {
1143 if (include.TestActive(activeFields))
1144 graphIncludeBuilder.AppendLine(include.value);
1145 }
1146
1147 string command = GenerationUtils.GetSpliceCommand(graphIncludeBuilder.ToCodeBlock(), "GraphIncludes");
1148 spliceCommands.Add("GraphIncludes", command);
1149 }
1150
1151 using (var postGraphIncludeBuilder = new ShaderStringBuilder())
1152 {
1153 foreach (var include in allIncludes.Where(x => x.location == IncludeLocation.Postgraph))
1154 {
1155 if (include.TestActive(activeFields))
1156 postGraphIncludeBuilder.AppendLine(include.value);
1157 }
1158
1159 string command = GenerationUtils.GetSpliceCommand(postGraphIncludeBuilder.ToCodeBlock(), "PostGraphIncludes");
1160 spliceCommands.Add("PostGraphIncludes", command);
1161 }
1162
1163 GeneratorDerivativeUtils.ApplyAnalyticDerivatives(
1164 m_Targets[targetIndex],
1165 spliceCommands,
1166 pass,
1167 activeFields,
1168 subShaderProperties,
1169 propertyCollector,
1170 keywordCollector,
1171 vertexNodes,
1172 pixelNodes,
1173 vertexNodePermutations,
1174 pixelNodePermutations,
1175 originalPassStructs,
1176 pass.analyticDerivativesApplyEmulate,
1177 m_HumanReadable,
1178 m_PrimaryShaderFullName,
1179 m_GraphData.graphDefaultConcretePrecision);
1180
1181 // --------------------------------------------------
1182 // Debug
1183
1184 // Debug output all active fields
1185
1186 using (var debugBuilder = new ShaderStringBuilder(humanReadable: m_HumanReadable))
1187 {
1188 if (isDebug)
1189 {
1190 // Active fields
1191 debugBuilder.AppendLine("// ACTIVE FIELDS:");
1192 foreach (FieldDescriptor field in activeFields.baseInstance.fields)
1193 {
1194 debugBuilder.AppendLine($"//{field.tag}.{field.name}");
1195 }
1196 }
1197 if (debugBuilder.length == 0)
1198 debugBuilder.AppendLine("// <None>");
1199
1200 // Add to splice commands
1201 spliceCommands.Add("Debug", debugBuilder.ToCodeBlock());
1202 }
1203
1204 // --------------------------------------------------
1205 // Additional Commands
1206
1207 if (pass.additionalCommands != null)
1208 {
1209 foreach (AdditionalCommandCollection.Item additionalCommand in pass.additionalCommands)
1210 {
1211 spliceCommands.Add(additionalCommand.field.token, additionalCommand.field.content);
1212 }
1213 }
1214
1215 // --------------------------------------------------
1216 // Finalize
1217
1218 // Pass Template
1219 string passTemplatePath = pass.passTemplatePath;
1220
1221 // Shared Templates
1222 string[] sharedTemplateDirectories = pass.sharedTemplateDirectories;
1223
1224 if (!File.Exists(passTemplatePath))
1225 {
1226 Profiler.EndSample();
1227 return;
1228 }
1229
1230 // Process Template
1231 Profiler.BeginSample("ProcessTemplate");
1232 var templatePreprocessor = new ShaderSpliceUtil.TemplatePreprocessor(activeFields, spliceCommands,
1233 isDebug, sharedTemplateDirectories, m_AssetCollection, m_HumanReadable);
1234 templatePreprocessor.ProcessTemplateFile(passTemplatePath);
1235 m_Builder.Concat(templatePreprocessor.GetShaderCode());
1236
1237 Profiler.EndSample();
1238 // Turn off the skip flag so other passes behave correctly correctly.
1239 CustomInterpolatorUtils.generatorSkipFlag = false;
1240 CustomInterpolatorUtils.generatorNodeOnly = false;
1241 Profiler.EndSample();
1242 }
1243 }
1244}