void BuildShader() { var activeNodeList = Graphing.ListPool <AbstractMaterialNode> .Get(); NodeUtils.DepthFirstCollectNodesFromNode(activeNodeList, m_OutputNode); var shaderProperties = new PropertyCollector(); var shaderKeywords = new KeywordCollector(); m_GraphData.CollectShaderProperties(shaderProperties, m_Mode); m_GraphData.CollectShaderKeywords(shaderKeywords, m_Mode); if (m_GraphData.GetKeywordPermutationCount() > ShaderGraphPreferences.variantLimit) { m_GraphData.AddValidationError(m_OutputNode.guid, ShaderKeyword.kVariantLimitWarning, Rendering.ShaderCompilerMessageSeverity.Error); m_ConfiguredTextures = shaderProperties.GetConfiguredTexutres(); m_Builder.AppendLines(ShaderGraphImporter.k_ErrorShader); } GetTargetImplementations(); foreach (var activeNode in activeNodeList.OfType <AbstractMaterialNode>()) { activeNode.CollectShaderProperties(shaderProperties, m_Mode); } m_Builder.AppendLine(@"Shader ""{0}""", m_Name); using (m_Builder.BlockScope()) { GenerationUtils.GeneratePropertiesBlock(m_Builder, shaderProperties, shaderKeywords, m_Mode); for (int i = 0; i < m_TargetImplementations.Length; i++) { TargetSetupContext context = new TargetSetupContext(); context.SetMasterNode(m_OutputNode as IMasterNode); // Instead of setup target, we can also just do get context m_TargetImplementations[i].SetupTarget(ref context); GetAssetDependencyPaths(context); GenerateSubShader(i, context.descriptor); } // Either grab the pipeline default for the active node or the user override if (m_OutputNode is ICanChangeShaderGUI canChangeShaderGui) { string customEditor = GenerationUtils.FinalCustomEditorString(canChangeShaderGui); if (customEditor != null) { m_Builder.AppendLine("CustomEditor \"" + customEditor + "\""); } } m_Builder.AppendLine(@"FallBack ""Hidden/Shader Graph/FallbackError"""); } m_ConfiguredTextures = shaderProperties.GetConfiguredTexutres(); }
public void CollectShaderKeywords(KeywordCollector keywords, GenerationMode generationMode) { if (asset == null) { return; } foreach (var keyword in asset.keywords) { keywords.AddShaderKeyword(keyword as ShaderKeyword); } }
public static void GenerateVertexDescriptionFunction( GraphData graph, ShaderStringBuilder builder, FunctionRegistry functionRegistry, PropertyCollector shaderProperties, KeywordCollector shaderKeywords, GenerationMode mode, AbstractMaterialNode rootNode, List <AbstractMaterialNode> nodes, List <int>[] keywordPermutationsPerNode, List <MaterialSlot> slots, string graphInputStructName = "VertexDescriptionInputs", string functionName = "PopulateVertexData", string graphOutputStructName = k_VertexDescriptionStructName) { if (graph == null) { return; } graph.CollectShaderProperties(shaderProperties, mode); builder.AppendLine("{0} {1}({2} IN)", graphOutputStructName, functionName, graphInputStructName); using (builder.BlockScope()) { builder.AppendLine("{0} description = ({0})0;", graphOutputStructName); for (int i = 0; i < nodes.Count; i++) { GenerateDescriptionForNode(nodes[i], keywordPermutationsPerNode[i], functionRegistry, builder, shaderProperties, shaderKeywords, graph, mode); } functionRegistry.builder.currentNode = null; builder.currentNode = null; if (slots.Count != 0) { foreach (var slot in slots) { var isSlotConnected = slot.owner.owner.GetEdges(slot.slotReference).Any(); var slotName = NodeUtils.GetHLSLSafeName(slot.shaderOutputName); var slotValue = isSlotConnected ? ((AbstractMaterialNode)slot.owner).GetSlotValue(slot.id, mode, slot.owner.concretePrecision) : slot.GetDefaultValue(mode, slot.owner.concretePrecision); builder.AppendLine("description.{0} = {1};", slotName, slotValue); } } builder.AppendLine("return description;"); } }
public string GetShader(GenerationMode mode, string outputName, out List <PropertyCollector.TextureInfo> configuredTextures, List <string> sourceAssetDependencyPaths = null) { var activeNodeList = ListPool <AbstractMaterialNode> .Get(); NodeUtils.DepthFirstCollectNodesFromNode(activeNodeList, this); var shaderProperties = new PropertyCollector(); var shaderKeywords = new KeywordCollector(); if (owner != null) { owner.CollectShaderProperties(shaderProperties, mode); owner.CollectShaderKeywords(shaderKeywords, mode); } if (owner.GetKeywordPermutationCount() > ShaderGraphPreferences.variantLimit) { owner.AddValidationError(tempId, ShaderKeyword.kVariantLimitWarning, Rendering.ShaderCompilerMessageSeverity.Error); configuredTextures = shaderProperties.GetConfiguredTexutres(); return(ShaderGraphImporter.k_ErrorShader); } foreach (var activeNode in activeNodeList.OfType <AbstractMaterialNode>()) { activeNode.CollectShaderProperties(shaderProperties, mode); } var finalShader = new ShaderStringBuilder(); finalShader.AppendLine(@"Shader ""{0}""", outputName); using (finalShader.BlockScope()) { GraphUtil.GeneratePropertiesBlock(finalShader, shaderProperties, shaderKeywords, mode); foreach (var subShader in m_SubShaders) { if (mode != GenerationMode.Preview || subShader.IsPipelineCompatible(GraphicsSettings.renderPipelineAsset)) { finalShader.AppendLines(subShader.GetSubshader(this, mode, sourceAssetDependencyPaths)); } } finalShader.AppendLine(@"FallBack ""Hidden/InternalErrorShader"""); } configuredTextures = shaderProperties.GetConfiguredTexutres(); return(finalShader.ToString()); }
public static void GenerateSurfaceDescriptionFunction( List <AbstractMaterialNode> nodes, List <int>[] keywordPermutationsPerNode, AbstractMaterialNode rootNode, GraphData graph, ShaderStringBuilder surfaceDescriptionFunction, FunctionRegistry functionRegistry, PropertyCollector shaderProperties, KeywordCollector shaderKeywords, GenerationMode mode, string functionName = "PopulateSurfaceData", string surfaceDescriptionName = "SurfaceDescription", Vector1ShaderProperty outputIdProperty = null, IEnumerable <MaterialSlot> slots = null, string graphInputStructName = "SurfaceDescriptionInputs") { if (graph == null) { return; } graph.CollectShaderProperties(shaderProperties, mode); surfaceDescriptionFunction.AppendLine(String.Format("{0} {1}(SurfaceDescriptionInputs IN)", surfaceDescriptionName, functionName), false); using (surfaceDescriptionFunction.BlockScope()) { surfaceDescriptionFunction.AppendLine("{0} surface = ({0})0;", surfaceDescriptionName); for (int i = 0; i < nodes.Count; i++) { GenerateDescriptionForNode(nodes[i], keywordPermutationsPerNode[i], functionRegistry, surfaceDescriptionFunction, shaderProperties, shaderKeywords, graph, mode); } functionRegistry.builder.currentNode = null; surfaceDescriptionFunction.currentNode = null; GenerateSurfaceDescriptionRemap(graph, rootNode, slots, surfaceDescriptionFunction, mode); surfaceDescriptionFunction.AppendLine("return surface;"); } }
static void GenerateDescriptionForNode( AbstractMaterialNode activeNode, List <int> keywordPermutations, FunctionRegistry functionRegistry, ShaderStringBuilder descriptionFunction, PropertyCollector shaderProperties, KeywordCollector shaderKeywords, GraphData graph, GenerationMode mode) { if (activeNode is IGeneratesFunction functionNode) { functionRegistry.builder.currentNode = activeNode; functionNode.GenerateNodeFunction(functionRegistry, mode); functionRegistry.builder.ReplaceInCurrentMapping(PrecisionUtil.Token, activeNode.concretePrecision.ToShaderString()); } if (activeNode is IGeneratesBodyCode bodyNode) { if (keywordPermutations != null) { descriptionFunction.AppendLine(KeywordUtil.GetKeywordPermutationSetConditional(keywordPermutations)); } descriptionFunction.currentNode = activeNode; bodyNode.GenerateNodeCode(descriptionFunction, mode); descriptionFunction.ReplaceInCurrentMapping(PrecisionUtil.Token, activeNode.concretePrecision.ToShaderString()); if (keywordPermutations != null) { descriptionFunction.AppendLine("#endif"); } } activeNode.CollectShaderProperties(shaderProperties, mode); if (activeNode is SubGraphNode subGraphNode) { subGraphNode.CollectShaderKeywords(shaderKeywords, mode); } }
void GenerateShaderPass(int targetIndex, PassDescriptor pass, ActiveFields activeFields) { // Early exit if pass is not used in preview if (m_Mode == GenerationMode.Preview && !pass.useInPreview) { return; } // -------------------------------------------------- // Debug // Get scripting symbols BuildTargetGroup buildTargetGroup = EditorUserBuildSettings.selectedBuildTargetGroup; string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(buildTargetGroup); bool isDebug = defines.Contains(kDebugSymbol); // -------------------------------------------------- // Setup // Initiailize Collectors var propertyCollector = new PropertyCollector(); var keywordCollector = new KeywordCollector(); m_OutputNode.owner.CollectShaderKeywords(keywordCollector, m_Mode); // Get upstream nodes from ShaderPass port mask List <AbstractMaterialNode> vertexNodes; List <AbstractMaterialNode> pixelNodes; GenerationUtils.GetUpstreamNodesForShaderPass(m_OutputNode, pass, out vertexNodes, out pixelNodes); // Track permutation indices for all nodes List <int>[] vertexNodePermutations = new List <int> [vertexNodes.Count]; List <int>[] pixelNodePermutations = new List <int> [pixelNodes.Count]; // Get active fields from upstream Node requirements ShaderGraphRequirementsPerKeyword graphRequirements; GenerationUtils.GetActiveFieldsAndPermutationsForNodes(m_OutputNode, pass, keywordCollector, vertexNodes, pixelNodes, vertexNodePermutations, pixelNodePermutations, activeFields, out graphRequirements); // GET CUSTOM ACTIVE FIELDS HERE! // Get active fields from ShaderPass GenerationUtils.AddRequiredFields(pass.requiredFields, activeFields.baseInstance); // Get Port references from ShaderPass List <MaterialSlot> pixelSlots; List <MaterialSlot> vertexSlots; if (m_OutputNode is IMasterNode) { pixelSlots = GenerationUtils.FindMaterialSlotsOnNode(pass.pixelPorts, m_OutputNode); vertexSlots = GenerationUtils.FindMaterialSlotsOnNode(pass.vertexPorts, m_OutputNode); } else if (m_OutputNode is SubGraphOutputNode) { pixelSlots = new List <MaterialSlot>() { m_OutputNode.GetInputSlots <MaterialSlot>().FirstOrDefault(), }; vertexSlots = new List <MaterialSlot>(); } else { pixelSlots = new List <MaterialSlot>() { new Vector4MaterialSlot(0, "Out", "Out", SlotType.Output, Vector4.zero) { owner = m_OutputNode }, }; vertexSlots = new List <MaterialSlot>(); } // Function Registry var functionBuilder = new ShaderStringBuilder(); var functionRegistry = new FunctionRegistry(functionBuilder); // Hash table of named $splice(name) commands // Key: splice token // Value: string to splice Dictionary <string, string> spliceCommands = new Dictionary <string, string>(); // -------------------------------------------------- // Dependencies // Propagate active field requirements using dependencies // Must be executed before types are built foreach (var instance in activeFields.all.instances) { GenerationUtils.ApplyFieldDependencies(instance, pass.fieldDependencies); } // -------------------------------------------------- // Pass Setup // Name if (!string.IsNullOrEmpty(pass.displayName)) { spliceCommands.Add("PassName", $"Name \"{pass.displayName}\""); } else { spliceCommands.Add("PassName", "// Name: <None>"); } // Tags if (!string.IsNullOrEmpty(pass.lightMode)) { spliceCommands.Add("LightMode", $"\"LightMode\" = \"{pass.lightMode}\""); } else { spliceCommands.Add("LightMode", "// LightMode: <None>"); } // -------------------------------------------------- // Pass Code // Render State using (var renderStateBuilder = new ShaderStringBuilder()) { // Render states need to be separated by RenderState.Type // The first passing ConditionalRenderState of each type is inserted foreach (RenderStateType type in Enum.GetValues(typeof(RenderStateType))) { var renderStates = pass.renderStates?.Where(x => x.descriptor.type == type); if (renderStates != null) { foreach (RenderStateCollection.Item renderState in renderStates) { if (renderState.TestActive(activeFields)) { renderStateBuilder.AppendLine(renderState.value); break; } } } } string command = GenerationUtils.GetSpliceCommand(renderStateBuilder.ToCodeBlock(), "RenderState"); spliceCommands.Add("RenderState", command); } // Pragmas using (var passPragmaBuilder = new ShaderStringBuilder()) { if (pass.pragmas != null) { foreach (PragmaCollection.Item pragma in pass.pragmas) { if (pragma.TestActive(activeFields)) { passPragmaBuilder.AppendLine(pragma.value); } } } string command = GenerationUtils.GetSpliceCommand(passPragmaBuilder.ToCodeBlock(), "PassPragmas"); spliceCommands.Add("PassPragmas", command); } // Includes using (var preGraphIncludeBuilder = new ShaderStringBuilder()) { if (pass.includes != null) { foreach (IncludeCollection.Item include in pass.includes.Where(x => x.descriptor.location == IncludeLocation.Pregraph)) { if (include.TestActive(activeFields)) { preGraphIncludeBuilder.AppendLine(include.value); } } } string command = GenerationUtils.GetSpliceCommand(preGraphIncludeBuilder.ToCodeBlock(), "PreGraphIncludes"); spliceCommands.Add("PreGraphIncludes", command); } using (var postGraphIncludeBuilder = new ShaderStringBuilder()) { if (pass.includes != null) { foreach (IncludeCollection.Item include in pass.includes.Where(x => x.descriptor.location == IncludeLocation.Postgraph)) { if (include.TestActive(activeFields)) { postGraphIncludeBuilder.AppendLine(include.value); } } } string command = GenerationUtils.GetSpliceCommand(postGraphIncludeBuilder.ToCodeBlock(), "PostGraphIncludes"); spliceCommands.Add("PostGraphIncludes", command); } // Keywords using (var passKeywordBuilder = new ShaderStringBuilder()) { if (pass.keywords != null) { foreach (KeywordCollection.Item keyword in pass.keywords) { if (keyword.TestActive(activeFields)) { passKeywordBuilder.AppendLine(keyword.value); } } } string command = GenerationUtils.GetSpliceCommand(passKeywordBuilder.ToCodeBlock(), "PassKeywords"); spliceCommands.Add("PassKeywords", command); } // ----------------------------- // Generated structs and Packing code var interpolatorBuilder = new ShaderStringBuilder(); var passStructs = new List <StructDescriptor>(); if (pass.structs != null) { passStructs.AddRange(pass.structs.Select(x => x.descriptor)); foreach (StructCollection.Item shaderStruct in pass.structs) { if (shaderStruct.descriptor.packFields == false) { continue; //skip structs that do not need interpolator packs } List <int> packedCounts = new List <int>(); var packStruct = new StructDescriptor(); //generate packed functions if (activeFields.permutationCount > 0) { var generatedPackedTypes = new Dictionary <string, (ShaderStringBuilder, List <int>)>(); foreach (var instance in activeFields.allPermutations.instances) { var instanceGenerator = new ShaderStringBuilder(); GenerationUtils.GenerateInterpolatorFunctions(shaderStruct.descriptor, instance, out instanceGenerator); var key = instanceGenerator.ToCodeBlock(); if (generatedPackedTypes.TryGetValue(key, out var value)) { value.Item2.Add(instance.permutationIndex); } else { generatedPackedTypes.Add(key, (instanceGenerator, new List <int> { instance.permutationIndex }));
static void GetActiveFieldsAndPermutationsForNodes(AbstractMaterialNode masterNode, ShaderPass pass, KeywordCollector keywordCollector, List <AbstractMaterialNode> vertexNodes, List <AbstractMaterialNode> pixelNodes, List <int>[] vertexNodePermutations, List <int>[] pixelNodePermutations, ActiveFields activeFields, out ShaderGraphRequirementsPerKeyword graphRequirements) { // Initialize requirements ShaderGraphRequirementsPerKeyword pixelRequirements = new ShaderGraphRequirementsPerKeyword(); ShaderGraphRequirementsPerKeyword vertexRequirements = new ShaderGraphRequirementsPerKeyword(); graphRequirements = new ShaderGraphRequirementsPerKeyword(); // Evaluate all Keyword permutations if (keywordCollector.permutations.Count > 0) { for (int i = 0; i < keywordCollector.permutations.Count; i++) { // Get active nodes for this permutation var localVertexNodes = Graphing.ListPool <AbstractMaterialNode> .Get(); var localPixelNodes = Graphing.ListPool <AbstractMaterialNode> .Get(); NodeUtils.DepthFirstCollectNodesFromNode(localVertexNodes, masterNode, NodeUtils.IncludeSelf.Include, pass.vertexPorts, keywordCollector.permutations[i]); NodeUtils.DepthFirstCollectNodesFromNode(localPixelNodes, masterNode, NodeUtils.IncludeSelf.Include, pass.pixelPorts, keywordCollector.permutations[i]); // Track each vertex node in this permutation foreach (AbstractMaterialNode vertexNode in localVertexNodes) { int nodeIndex = vertexNodes.IndexOf(vertexNode); if (vertexNodePermutations[nodeIndex] == null) { vertexNodePermutations[nodeIndex] = new List <int>(); } vertexNodePermutations[nodeIndex].Add(i); } // Track each pixel node in this permutation foreach (AbstractMaterialNode pixelNode in localPixelNodes) { int nodeIndex = pixelNodes.IndexOf(pixelNode); if (pixelNodePermutations[nodeIndex] == null) { pixelNodePermutations[nodeIndex] = new List <int>(); } pixelNodePermutations[nodeIndex].Add(i); } // Get requirements for this permutation vertexRequirements[i].SetRequirements(ShaderGraphRequirements.FromNodes(localVertexNodes, ShaderStageCapability.Vertex, false)); pixelRequirements[i].SetRequirements(ShaderGraphRequirements.FromNodes(localPixelNodes, ShaderStageCapability.Fragment, false)); // Add active fields AddActiveFieldsFromGraphRequirements(activeFields[i], vertexRequirements[i].requirements, "VertexDescriptionInputs"); AddActiveFieldsFromGraphRequirements(activeFields[i], pixelRequirements[i].requirements, "SurfaceDescriptionInputs"); } } // No Keywords else { // Get requirements vertexRequirements.baseInstance.SetRequirements(ShaderGraphRequirements.FromNodes(vertexNodes, ShaderStageCapability.Vertex, false)); pixelRequirements.baseInstance.SetRequirements(ShaderGraphRequirements.FromNodes(pixelNodes, ShaderStageCapability.Fragment, false)); // Add active fields AddActiveFieldsFromGraphRequirements(activeFields.baseInstance, vertexRequirements.baseInstance.requirements, "VertexDescriptionInputs"); AddActiveFieldsFromGraphRequirements(activeFields.baseInstance, pixelRequirements.baseInstance.requirements, "SurfaceDescriptionInputs"); } // Build graph requirements graphRequirements.UnionWith(pixelRequirements); graphRequirements.UnionWith(vertexRequirements); }
public static bool GenerateShaderPass(AbstractMaterialNode masterNode, ShaderPass pass, GenerationMode mode, ActiveFields activeFields, ShaderGenerator result, List <string> sourceAssetDependencyPaths, List <Dependency[]> dependencies, string resourceClassName, string assemblyName) { // -------------------------------------------------- // Debug // Get scripting symbols BuildTargetGroup buildTargetGroup = EditorUserBuildSettings.selectedBuildTargetGroup; string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(buildTargetGroup); bool isDebug = defines.Contains(kDebugSymbol); // -------------------------------------------------- // Setup // Initiailize Collectors var propertyCollector = new PropertyCollector(); var keywordCollector = new KeywordCollector(); masterNode.owner.CollectShaderKeywords(keywordCollector, mode); // Get upstream nodes from ShaderPass port mask List <AbstractMaterialNode> vertexNodes; List <AbstractMaterialNode> pixelNodes; GetUpstreamNodesForShaderPass(masterNode, pass, out vertexNodes, out pixelNodes); // Track permutation indices for all nodes List <int>[] vertexNodePermutations = new List <int> [vertexNodes.Count]; List <int>[] pixelNodePermutations = new List <int> [pixelNodes.Count]; // Get active fields from upstream Node requirements ShaderGraphRequirementsPerKeyword graphRequirements; GetActiveFieldsAndPermutationsForNodes(masterNode, pass, keywordCollector, vertexNodes, pixelNodes, vertexNodePermutations, pixelNodePermutations, activeFields, out graphRequirements); // GET CUSTOM ACTIVE FIELDS HERE! // Get active fields from ShaderPass AddRequiredFields(pass.requiredAttributes, activeFields.baseInstance); AddRequiredFields(pass.requiredVaryings, activeFields.baseInstance); // Get Port references from ShaderPass var pixelSlots = FindMaterialSlotsOnNode(pass.pixelPorts, masterNode); var vertexSlots = FindMaterialSlotsOnNode(pass.vertexPorts, masterNode); // Function Registry var functionBuilder = new ShaderStringBuilder(); var functionRegistry = new FunctionRegistry(functionBuilder); // Hash table of named $splice(name) commands // Key: splice token // Value: string to splice Dictionary <string, string> spliceCommands = new Dictionary <string, string>(); // -------------------------------------------------- // Dependencies // Propagate active field requirements using dependencies // Must be executed before types are built foreach (var instance in activeFields.all.instances) { ShaderSpliceUtil.ApplyDependencies(instance, dependencies); } // -------------------------------------------------- // Pass Setup // Name if (!string.IsNullOrEmpty(pass.displayName)) { spliceCommands.Add("PassName", $"Name \"{pass.displayName}\""); } else { spliceCommands.Add("PassName", "// Name: <None>"); } // Tags if (!string.IsNullOrEmpty(pass.lightMode)) { spliceCommands.Add("LightMode", $"\"LightMode\" = \"{pass.lightMode}\""); } else { spliceCommands.Add("LightMode", "// LightMode: <None>"); } // Render state BuildRenderStatesFromPass(pass, ref spliceCommands); // -------------------------------------------------- // Pass Code // Pragmas using (var passPragmaBuilder = new ShaderStringBuilder()) { if (pass.pragmas != null) { foreach (string pragma in pass.pragmas) { passPragmaBuilder.AppendLine($"#pragma {pragma}"); } } if (passPragmaBuilder.length == 0) { passPragmaBuilder.AppendLine("// PassPragmas: <None>"); } spliceCommands.Add("PassPragmas", passPragmaBuilder.ToCodeBlack()); } // Includes using (var passIncludeBuilder = new ShaderStringBuilder()) { if (pass.includes != null) { foreach (string include in pass.includes) { passIncludeBuilder.AppendLine($"#include \"{include}\""); } } if (passIncludeBuilder.length == 0) { passIncludeBuilder.AppendLine("// PassIncludes: <None>"); } spliceCommands.Add("PassIncludes", passIncludeBuilder.ToCodeBlack()); } // Keywords using (var passKeywordBuilder = new ShaderStringBuilder()) { if (pass.keywords != null) { foreach (KeywordDescriptor keyword in pass.keywords) { passKeywordBuilder.AppendLine(keyword.ToDeclarationString()); } } if (passKeywordBuilder.length == 0) { passKeywordBuilder.AppendLine("// PassKeywords: <None>"); } spliceCommands.Add("PassKeywords", passKeywordBuilder.ToCodeBlack()); } // -------------------------------------------------- // Graph Vertex var vertexBuilder = new ShaderStringBuilder(); // If vertex modification enabled if (activeFields.baseInstance.Contains("features.graphVertex")) { // Setup string vertexGraphInputName = "VertexDescriptionInputs"; string vertexGraphOutputName = "VertexDescription"; string vertexGraphFunctionName = "VertexDescriptionFunction"; var vertexGraphInputGenerator = new ShaderGenerator(); var vertexGraphFunctionBuilder = new ShaderStringBuilder(); var vertexGraphOutputBuilder = new ShaderStringBuilder(); // Build vertex graph inputs ShaderSpliceUtil.BuildType(GetTypeForStruct("VertexDescriptionInputs", resourceClassName, assemblyName), activeFields, vertexGraphInputGenerator, isDebug); // Build vertex graph outputs // Add struct fields to active fields SubShaderGenerator.GenerateVertexDescriptionStruct(vertexGraphOutputBuilder, vertexSlots, vertexGraphOutputName, activeFields.baseInstance); // Build vertex graph functions from ShaderPass vertex port mask SubShaderGenerator.GenerateVertexDescriptionFunction( masterNode.owner as GraphData, vertexGraphFunctionBuilder, functionRegistry, propertyCollector, keywordCollector, mode, masterNode, vertexNodes, vertexNodePermutations, vertexSlots, vertexGraphInputName, vertexGraphFunctionName, vertexGraphOutputName); // Generate final shader strings vertexBuilder.AppendLines(vertexGraphInputGenerator.GetShaderString(0, false)); vertexBuilder.AppendNewLine(); vertexBuilder.AppendLines(vertexGraphOutputBuilder.ToString()); vertexBuilder.AppendNewLine(); vertexBuilder.AppendLines(vertexGraphFunctionBuilder.ToString()); } // Add to splice commands if (vertexBuilder.length == 0) { vertexBuilder.AppendLine("// GraphVertex: <None>"); } spliceCommands.Add("GraphVertex", vertexBuilder.ToCodeBlack()); // -------------------------------------------------- // Graph Pixel // Setup string pixelGraphInputName = "SurfaceDescriptionInputs"; string pixelGraphOutputName = "SurfaceDescription"; string pixelGraphFunctionName = "SurfaceDescriptionFunction"; var pixelGraphInputGenerator = new ShaderGenerator(); var pixelGraphOutputBuilder = new ShaderStringBuilder(); var pixelGraphFunctionBuilder = new ShaderStringBuilder(); // Build pixel graph inputs ShaderSpliceUtil.BuildType(GetTypeForStruct("SurfaceDescriptionInputs", resourceClassName, assemblyName), activeFields, pixelGraphInputGenerator, isDebug); // Build pixel graph outputs // Add struct fields to active fields SubShaderGenerator.GenerateSurfaceDescriptionStruct(pixelGraphOutputBuilder, pixelSlots, pixelGraphOutputName, activeFields.baseInstance); // Build pixel graph functions from ShaderPass pixel port mask SubShaderGenerator.GenerateSurfaceDescriptionFunction( pixelNodes, pixelNodePermutations, masterNode, masterNode.owner as GraphData, pixelGraphFunctionBuilder, functionRegistry, propertyCollector, keywordCollector, mode, pixelGraphFunctionName, pixelGraphOutputName, null, pixelSlots, pixelGraphInputName); using (var pixelBuilder = new ShaderStringBuilder()) { // Generate final shader strings pixelBuilder.AppendLines(pixelGraphInputGenerator.GetShaderString(0, false)); pixelBuilder.AppendNewLine(); pixelBuilder.AppendLines(pixelGraphOutputBuilder.ToString()); pixelBuilder.AppendNewLine(); pixelBuilder.AppendLines(pixelGraphFunctionBuilder.ToString()); // Add to splice commands if (pixelBuilder.length == 0) { pixelBuilder.AppendLine("// GraphPixel: <None>"); } spliceCommands.Add("GraphPixel", pixelBuilder.ToCodeBlack()); } // -------------------------------------------------- // Graph Functions if (functionBuilder.length == 0) { functionBuilder.AppendLine("// GraphFunctions: <None>"); } spliceCommands.Add("GraphFunctions", functionBuilder.ToCodeBlack()); // -------------------------------------------------- // Graph Keywords using (var keywordBuilder = new ShaderStringBuilder()) { keywordCollector.GetKeywordsDeclaration(keywordBuilder, mode); if (keywordBuilder.length == 0) { keywordBuilder.AppendLine("// GraphKeywords: <None>"); } spliceCommands.Add("GraphKeywords", keywordBuilder.ToCodeBlack()); } // -------------------------------------------------- // Graph Properties using (var propertyBuilder = new ShaderStringBuilder()) { propertyCollector.GetPropertiesDeclaration(propertyBuilder, mode, masterNode.owner.concretePrecision); if (propertyBuilder.length == 0) { propertyBuilder.AppendLine("// GraphProperties: <None>"); } spliceCommands.Add("GraphProperties", propertyBuilder.ToCodeBlack()); } // -------------------------------------------------- // Graph Defines using (var graphDefines = new ShaderStringBuilder()) { graphDefines.AppendLine("#define {0}", pass.referenceName); if (graphRequirements.permutationCount > 0) { List <int> activePermutationIndices; // Depth Texture activePermutationIndices = graphRequirements.allPermutations.instances .Where(p => p.requirements.requiresDepthTexture) .Select(p => p.permutationIndex) .ToList(); if (activePermutationIndices.Count > 0) { graphDefines.AppendLine(KeywordUtil.GetKeywordPermutationSetConditional(activePermutationIndices)); graphDefines.AppendLine("#define REQUIRE_DEPTH_TEXTURE"); graphDefines.AppendLine("#endif"); } // Opaque Texture activePermutationIndices = graphRequirements.allPermutations.instances .Where(p => p.requirements.requiresCameraOpaqueTexture) .Select(p => p.permutationIndex) .ToList(); if (activePermutationIndices.Count > 0) { graphDefines.AppendLine(KeywordUtil.GetKeywordPermutationSetConditional(activePermutationIndices)); graphDefines.AppendLine("#define REQUIRE_OPAQUE_TEXTURE"); graphDefines.AppendLine("#endif"); } } else { // Depth Texture if (graphRequirements.baseInstance.requirements.requiresDepthTexture) { graphDefines.AppendLine("#define REQUIRE_DEPTH_TEXTURE"); } // Opaque Texture if (graphRequirements.baseInstance.requirements.requiresCameraOpaqueTexture) { graphDefines.AppendLine("#define REQUIRE_OPAQUE_TEXTURE"); } } // Add to splice commands spliceCommands.Add("GraphDefines", graphDefines.ToCodeBlack()); } // -------------------------------------------------- // Main // Main include is expected to contain vert/frag definitions for the pass // This must be defined after all graph code using (var mainBuilder = new ShaderStringBuilder()) { mainBuilder.AppendLine($"#include \"{pass.varyingsInclude}\""); mainBuilder.AppendLine($"#include \"{pass.passInclude}\""); // Add to splice commands spliceCommands.Add("MainInclude", mainBuilder.ToCodeBlack()); } // -------------------------------------------------- // Debug // Debug output all active fields using (var debugBuilder = new ShaderStringBuilder()) { if (isDebug) { // Active fields debugBuilder.AppendLine("// ACTIVE FIELDS:"); foreach (string field in activeFields.baseInstance.fields) { debugBuilder.AppendLine("// " + field); } } if (debugBuilder.length == 0) { debugBuilder.AppendLine("// <None>"); } // Add to splice commands spliceCommands.Add("Debug", debugBuilder.ToCodeBlack()); } // -------------------------------------------------- // Finalize // Get Template string templateLocation = GetTemplatePath("PassMesh.template"); if (!File.Exists(templateLocation)) { return(false); } // Get Template preprocessor string templatePath = "Assets/com.unity.render-pipelines.universal/Editor/ShaderGraph/Templates"; var templatePreprocessor = new ShaderSpliceUtil.TemplatePreprocessor(activeFields, spliceCommands, isDebug, templatePath, sourceAssetDependencyPaths, assemblyName, resourceClassName); // Process Template templatePreprocessor.ProcessTemplateFile(templateLocation); result.AddShaderChunk(templatePreprocessor.GetShaderCode().ToString(), false); return(true); }
public static GenerationResults GetShader(this GraphData graph, AbstractMaterialNode node, GenerationMode mode, string name) { // ----------------------------------------------------- // // SETUP // // ----------------------------------------------------- // // ------------------------------------- // String builders var finalShader = new ShaderStringBuilder(); var results = new GenerationResults(); var shaderProperties = new PropertyCollector(); var shaderKeywords = new KeywordCollector(); var shaderPropertyUniforms = new ShaderStringBuilder(); var shaderKeywordDeclarations = new ShaderStringBuilder(); var shaderKeywordPermutations = new ShaderStringBuilder(1); var functionBuilder = new ShaderStringBuilder(); var functionRegistry = new FunctionRegistry(functionBuilder); var vertexDescriptionFunction = new ShaderStringBuilder(0); var surfaceDescriptionInputStruct = new ShaderStringBuilder(0); var surfaceDescriptionStruct = new ShaderStringBuilder(0); var surfaceDescriptionFunction = new ShaderStringBuilder(0); var vertexInputs = new ShaderStringBuilder(0); graph.CollectShaderKeywords(shaderKeywords, mode); if (graph.GetKeywordPermutationCount() > ShaderGraphPreferences.variantLimit) { graph.AddValidationError(node.tempId, ShaderKeyword.kVariantLimitWarning, Rendering.ShaderCompilerMessageSeverity.Error); results.configuredTextures = shaderProperties.GetConfiguredTexutres(); results.shader = string.Empty; return(results); } // ------------------------------------- // Get Slot and Node lists var activeNodeList = ListPool <AbstractMaterialNode> .Get(); NodeUtils.DepthFirstCollectNodesFromNode(activeNodeList, node); var slots = new List <MaterialSlot>(); if (node is IMasterNode || node is SubGraphOutputNode) { slots.AddRange(node.GetInputSlots <MaterialSlot>()); } else { var outputSlots = node.GetOutputSlots <MaterialSlot>().ToList(); if (outputSlots.Count > 0) { slots.Add(outputSlots[0]); } } // ------------------------------------- // Get Requirements var requirements = ShaderGraphRequirements.FromNodes(activeNodeList, ShaderStageCapability.Fragment); // ----------------------------------------------------- // // KEYWORDS // // ----------------------------------------------------- // // ------------------------------------- // Get keyword permutations graph.CollectShaderKeywords(shaderKeywords, mode); // Track permutation indicies for all nodes and requirements List <int>[] keywordPermutationsPerNode = new List <int> [activeNodeList.Count]; // ------------------------------------- // Evaluate all permutations for (int i = 0; i < shaderKeywords.permutations.Count; i++) { // Get active nodes for this permutation var localNodes = ListPool <AbstractMaterialNode> .Get(); NodeUtils.DepthFirstCollectNodesFromNode(localNodes, node, keywordPermutation: shaderKeywords.permutations[i]); // Track each pixel node in this permutation foreach (AbstractMaterialNode pixelNode in localNodes) { int nodeIndex = activeNodeList.IndexOf(pixelNode); if (keywordPermutationsPerNode[nodeIndex] == null) { keywordPermutationsPerNode[nodeIndex] = new List <int>(); } keywordPermutationsPerNode[nodeIndex].Add(i); } // Get active requirements for this permutation var localSurfaceRequirements = ShaderGraphRequirements.FromNodes(localNodes, ShaderStageCapability.Fragment, false); var localPixelRequirements = ShaderGraphRequirements.FromNodes(localNodes, ShaderStageCapability.Fragment); } // ----------------------------------------------------- // // START VERTEX DESCRIPTION // // ----------------------------------------------------- // // ------------------------------------- // Generate Vertex Description function vertexDescriptionFunction.AppendLine("GraphVertexInput PopulateVertexData(GraphVertexInput v)"); using (vertexDescriptionFunction.BlockScope()) { vertexDescriptionFunction.AppendLine("return v;"); } // ----------------------------------------------------- // // START SURFACE DESCRIPTION // // ----------------------------------------------------- // // ------------------------------------- // Generate Input structure for Surface Description function // Surface Description Input requirements are needed to exclude intermediate translation spaces GenerateSurfaceInputStruct(surfaceDescriptionInputStruct, requirements, "SurfaceDescriptionInputs"); results.previewMode = PreviewMode.Preview2D; foreach (var pNode in activeNodeList) { if (pNode.previewMode == PreviewMode.Preview3D) { results.previewMode = PreviewMode.Preview3D; break; } } // ------------------------------------- // Generate Output structure for Surface Description function GenerateSurfaceDescriptionStruct(surfaceDescriptionStruct, slots, useIdsInNames: !(node is IMasterNode)); // ------------------------------------- // Generate Surface Description function GenerateSurfaceDescriptionFunction( activeNodeList, keywordPermutationsPerNode, node, graph, surfaceDescriptionFunction, functionRegistry, shaderProperties, shaderKeywords, mode, outputIdProperty: results.outputIdProperty); // ----------------------------------------------------- // // GENERATE VERTEX > PIXEL PIPELINE // // ----------------------------------------------------- // // ------------------------------------- // Keyword declarations shaderKeywords.GetKeywordsDeclaration(shaderKeywordDeclarations, mode); // ------------------------------------- // Property uniforms shaderProperties.GetPropertiesDeclaration(shaderPropertyUniforms, mode, graph.concretePrecision); // ------------------------------------- // Generate Input structure for Vertex shader GenerateApplicationVertexInputs(requirements, vertexInputs); // ----------------------------------------------------- // // FINALIZE // // ----------------------------------------------------- // // ------------------------------------- // Build final shader finalShader.AppendLine(@"Shader ""{0}""", name); using (finalShader.BlockScope()) { SubShaderGenerator.GeneratePropertiesBlock(finalShader, shaderProperties, shaderKeywords, mode); finalShader.AppendNewLine(); finalShader.AppendLine(@"HLSLINCLUDE"); finalShader.AppendLine(@"#include ""Assets/Scripts/URP/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"""); finalShader.AppendLine(@"#include ""Assets/Scripts/URP/com.unity.render-pipelines.core/ShaderLibrary/Packing.hlsl"""); finalShader.AppendLine(@"#include ""Assets/Scripts/URP/com.unity.render-pipelines.core/ShaderLibrary/NormalSurfaceGradient.hlsl"""); finalShader.AppendLine(@"#include ""Assets/Scripts/URP/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"""); finalShader.AppendLine(@"#include ""Assets/Scripts/URP/com.unity.render-pipelines.core/ShaderLibrary/UnityInstancing.hlsl"""); finalShader.AppendLine(@"#include ""Assets/Scripts/URP/com.unity.render-pipelines.core/ShaderLibrary/EntityLighting.hlsl"""); finalShader.AppendLine(@"#include ""Assets/Scripts/URP/com.unity.shadergraph/ShaderGraphLibrary/ShaderVariables.hlsl"""); finalShader.AppendLine(@"#include ""Assets/Scripts/URP/com.unity.shadergraph/ShaderGraphLibrary/ShaderVariablesFunctions.hlsl"""); finalShader.AppendLine(@"#include ""Assets/Scripts/URP/com.unity.shadergraph/ShaderGraphLibrary/Functions.hlsl"""); finalShader.AppendLines(shaderKeywordDeclarations.ToString()); finalShader.AppendLine(@"#define SHADERGRAPH_PREVIEW 1"); finalShader.AppendNewLine(); finalShader.AppendLines(shaderKeywordPermutations.ToString()); finalShader.AppendLines(shaderPropertyUniforms.ToString()); finalShader.AppendNewLine(); finalShader.AppendLines(surfaceDescriptionInputStruct.ToString()); finalShader.AppendNewLine(); finalShader.Concat(functionBuilder); finalShader.AppendNewLine(); finalShader.AppendLines(surfaceDescriptionStruct.ToString()); finalShader.AppendNewLine(); finalShader.AppendLines(surfaceDescriptionFunction.ToString()); finalShader.AppendNewLine(); finalShader.AppendLines(vertexInputs.ToString()); finalShader.AppendNewLine(); finalShader.AppendLines(vertexDescriptionFunction.ToString()); finalShader.AppendNewLine(); finalShader.AppendLine(@"ENDHLSL"); finalShader.AppendLines(ShaderGenerator.GetPreviewSubShader(node, requirements)); ListPool <AbstractMaterialNode> .Release(activeNodeList); } // ------------------------------------- // Finalize results.configuredTextures = shaderProperties.GetConfiguredTexutres(); ShaderSourceMap sourceMap; results.shader = finalShader.ToString(out sourceMap); results.sourceMap = sourceMap; return(results); }
public static void GeneratePropertiesBlock(ShaderStringBuilder sb, PropertyCollector propertyCollector, KeywordCollector keywordCollector, GenerationMode mode) { sb.AppendLine("Properties"); using (sb.BlockScope()) { foreach (var prop in propertyCollector.properties.Where(x => x.generatePropertyBlock)) { sb.AppendLine(prop.GetPropertyBlockString()); } // Keywords use hardcoded state in preview // Do not add them to the Property Block if (mode == GenerationMode.Preview) { return; } foreach (var key in keywordCollector.keywords.Where(x => x.generatePropertyBlock)) { sb.AppendLine(key.GetPropertyBlockString()); } } }
// TODO: could get rid of this if we could run a codegen prepass (with proper keyword #ifdef) public static void GenerateVirtualTextureFeedback( List <AbstractMaterialNode> downstreamNodesIncludingRoot, List <int>[] keywordPermutationsPerNode, ShaderStringBuilder surfaceDescriptionFunction, KeywordCollector shaderKeywords) { // A note on how we handle vt feedback in combination with keywords: // We essentially generate a fully separate feedback path for each permutation of keywords // so per permutation we gather variables contribution to feedback and we generate // feedback gathering for each permutation individually. var feedbackVariablesPerPermutation = PooledList <PooledList <string> > .Get(); try { if (shaderKeywords.permutations.Count >= 1) { for (int i = 0; i < shaderKeywords.permutations.Count; i++) { feedbackVariablesPerPermutation.Add(PooledList <string> .Get()); } } else { // Create a dummy single permutation feedbackVariablesPerPermutation.Add(PooledList <string> .Get()); } int index = 0; //for keywordPermutationsPerNode foreach (var node in downstreamNodesIncludingRoot) { if (node is SampleVirtualTextureNode vtNode) { if (vtNode.noFeedback) { continue; } if (keywordPermutationsPerNode[index] == null) { Debug.Assert(shaderKeywords.permutations.Count == 0, $"Shader has {shaderKeywords.permutations.Count} permutations but keywordPermutationsPerNode of some nodes are null."); feedbackVariablesPerPermutation[0].Add(vtNode.GetFeedbackVariableName()); } else { foreach (int perm in keywordPermutationsPerNode[index]) { feedbackVariablesPerPermutation[perm].Add(vtNode.GetFeedbackVariableName()); } } } if (node is SubGraphNode sgNode) { if (sgNode.asset == null) { continue; } if (keywordPermutationsPerNode[index] == null) { Debug.Assert(shaderKeywords.permutations.Count == 0, $"Shader has {shaderKeywords.permutations.Count} permutations but keywordPermutationsPerNode of some nodes are null."); foreach (var feedbackSlot in sgNode.asset.vtFeedbackVariables) { feedbackVariablesPerPermutation[0].Add(node.GetVariableNameForNode() + "_" + feedbackSlot); } } else { foreach (var feedbackSlot in sgNode.asset.vtFeedbackVariables) { foreach (int perm in keywordPermutationsPerNode[index]) { feedbackVariablesPerPermutation[perm].Add(node.GetVariableNameForNode() + "_" + feedbackSlot); } } } } index++; } index = 0; foreach (var feedbackVariables in feedbackVariablesPerPermutation) { // If it's a dummy single always-on permutation don't put an ifdef around the code if (shaderKeywords.permutations.Count >= 1) { surfaceDescriptionFunction.AppendLine(KeywordUtil.GetKeywordPermutationConditional(index)); } using (surfaceDescriptionFunction.BlockScope()) { if (feedbackVariables.Count == 0) { string feedBackCode = "surface.VTPackedFeedback = float4(1.0f,1.0f,1.0f,1.0f);"; surfaceDescriptionFunction.AppendLine(feedBackCode); } else if (feedbackVariables.Count == 1) { string feedBackCode = "surface.VTPackedFeedback = GetPackedVTFeedback(" + feedbackVariables[0] + ");"; surfaceDescriptionFunction.AppendLine(feedBackCode); } else if (feedbackVariables.Count > 1) { surfaceDescriptionFunction.AppendLine("float4 VTFeedback_array[" + feedbackVariables.Count + "];"); int arrayIndex = 0; foreach (var variable in feedbackVariables) { surfaceDescriptionFunction.AppendLine("VTFeedback_array[" + arrayIndex + "] = " + variable + ";"); arrayIndex++; } // TODO: should read from NDCPosition instead... surfaceDescriptionFunction.AppendLine("uint pixelColumn = (IN.ScreenPosition.x / IN.ScreenPosition.w) * _ScreenParams.x;"); surfaceDescriptionFunction.AppendLine( "surface.VTPackedFeedback = GetPackedVTFeedback(VTFeedback_array[(pixelColumn + _FrameCount) % (uint)" + feedbackVariables.Count + "]);"); } } if (shaderKeywords.permutations.Count >= 1) { surfaceDescriptionFunction.AppendLine("#endif"); } index++; } } finally { foreach (var list in feedbackVariablesPerPermutation) { list.Dispose(); } feedbackVariablesPerPermutation.Dispose(); } }