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}