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 abstractMaterialGraph = owner as GraphData; if (abstractMaterialGraph != null) abstractMaterialGraph.CollectShaderProperties(shaderProperties, mode); foreach (var activeNode in activeNodeList.OfType<AbstractMaterialNode>()) activeNode.CollectShaderProperties(shaderProperties, mode); var finalShader = new ShaderStringBuilder(); finalShader.AppendLine(@"Shader ""{0}""", outputName); using (finalShader.BlockScope()) { finalShader.AppendLine("Properties"); using (finalShader.BlockScope()) { finalShader.AppendLine(shaderProperties.GetPropertiesBlock(0)); } 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(); }
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(); }
private static List <AbstractMaterialNode> GetAntecedents(BlockNode blockNode) { if (blockNode != null && blockNode.isCustomBlock && blockNode.isActive && blockNode.GetInputNodeFromSlot(0) != null) { List <AbstractMaterialNode> results = new List <AbstractMaterialNode>(); NodeUtils.DepthFirstCollectNodesFromNode(results, blockNode, NodeUtils.IncludeSelf.Exclude); return(results != null && results.Count() == 0 ? null : results); } return(null); }
public virtual void GenerateNodeFunction(FunctionRegistry registry, GraphContext graphContext, GenerationMode generationMode) { if (subGraphAsset == null || referencedGraph == null) return; List<AbstractMaterialNode> nodes = new List<AbstractMaterialNode>(); NodeUtils.DepthFirstCollectNodesFromNode(nodes, referencedGraph.outputNode); foreach (var node in nodes.OfType<AbstractMaterialNode>()) { node.ValidateNode(); if (node is IGeneratesFunction) (node as IGeneratesFunction).GenerateNodeFunction(registry, graphContext, generationMode); } string functionName = SubGraphFunctionName(graphContext); ShaderGraphRequirements reqs = ShaderGraphRequirements.FromNodes(new List<AbstractMaterialNode> {this}); registry.ProvideFunction(functionName, s => { s.AppendLine("// Subgraph function"); // Generate arguments... first INPUTS var arguments = new List<string>(); foreach (var prop in referencedGraph.properties) arguments.Add(string.Format("{0}", prop.GetPropertyAsArgumentString())); // now pass surface inputs arguments.Add(string.Format("{0} IN", graphContext.graphInputStructName)); // Now generate outputs foreach (var slot in outputNode.graphOutputs) arguments.Add(string.Format("out {0} {1}", slot.concreteValueType.ToString(referencedGraph.outputNode.precision), slot.shaderOutputName)); // Create the function protoype from the arguments s.AppendLine("void {0}({1})" , functionName , arguments.Aggregate((current, next) => string.Format("{0}, {1}", current, next))); // now generate the function using (s.BlockScope()) { // Just grab the body from the active nodes var bodyGenerator = new ShaderGenerator(); foreach (var node in nodes.OfType<AbstractMaterialNode>()) { if (node is IGeneratesBodyCode) (node as IGeneratesBodyCode).GenerateNodeCode(bodyGenerator, graphContext, generationMode); } outputNode.RemapOutputs(bodyGenerator, generationMode); s.Append(bodyGenerator.GetShaderString(1)); } }); }
static void GetUpstreamNodesForShaderPass(AbstractMaterialNode masterNode, ShaderPass pass, out List <AbstractMaterialNode> vertexNodes, out List <AbstractMaterialNode> pixelNodes) { // Traverse Graph Data vertexNodes = Graphing.ListPool <AbstractMaterialNode> .Get(); NodeUtils.DepthFirstCollectNodesFromNode(vertexNodes, masterNode, NodeUtils.IncludeSelf.Include, pass.vertexPorts); pixelNodes = Graphing.ListPool <AbstractMaterialNode> .Get(); NodeUtils.DepthFirstCollectNodesFromNode(pixelNodes, masterNode, NodeUtils.IncludeSelf.Include, pass.pixelPorts); }
public override void CollectPreviewMaterialProperties(List<PreviewProperty> properties) { base.CollectPreviewMaterialProperties(properties); if (referencedGraph == null) return; List<PreviewProperty> props = new List<PreviewProperty>(); List<AbstractMaterialNode> nodes = new List<AbstractMaterialNode>(); NodeUtils.DepthFirstCollectNodesFromNode(nodes, referencedGraph.outputNode); foreach (var node in nodes) node.CollectPreviewMaterialProperties(props); properties.AddRange(props); }
public override string GetShader(GenerationMode mode, string outputName, out List <PropertyCollector.TextureInfo> configuredTextures) { var activeNodeList = ListPool <INode> .Get(); NodeUtils.DepthFirstCollectNodesFromNode(activeNodeList, this); var shaderProperties = new PropertyCollector(); var abstractMaterialGraph = owner as AbstractMaterialGraph; if (abstractMaterialGraph != null) { abstractMaterialGraph.CollectShaderProperties(shaderProperties, mode); } foreach (var activeNode in activeNodeList.OfType <AbstractMaterialNode>()) { activeNode.CollectShaderProperties(shaderProperties, mode); } var finalShader = new ShaderGenerator(); finalShader.AddShaderChunk(string.Format(@"Shader ""{0}""", outputName), false); finalShader.AddShaderChunk("{", false); finalShader.Indent(); finalShader.AddShaderChunk("Properties", false); finalShader.AddShaderChunk("{", false); finalShader.Indent(); finalShader.AddShaderChunk(shaderProperties.GetPropertiesBlock(2), false); finalShader.Deindent(); finalShader.AddShaderChunk("}", false); var lwSub = new LightWeightUnlitSubShader(); finalShader.AddShaderChunk(lwSub.GetSubshader(this, mode), true); //Disable HD for now as there is no mapping // for many inputs like worldSpaceNormal and similar /*var hdSub = new HDUnlitSubShader(); * finalShader.AddShaderChunk(hdSub.GetSubshader(this, mode), true);*/ finalShader.Deindent(); finalShader.AddShaderChunk("}", false); configuredTextures = shaderProperties.GetConfiguredTexutres(); return(finalShader.GetShaderString(0)); }
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 override string GetShader(GenerationMode mode, string outputName, out List <PropertyCollector.TextureInfo> configuredTextures) { var activeNodeList = ListPool <INode> .Get(); NodeUtils.DepthFirstCollectNodesFromNode(activeNodeList, this); var shaderProperties = new PropertyCollector(); var abstractMaterialGraph = owner as AbstractMaterialGraph; if (abstractMaterialGraph != null) { abstractMaterialGraph.CollectShaderProperties(shaderProperties, mode); } foreach (var activeNode in activeNodeList.OfType <AbstractMaterialNode>()) { activeNode.CollectShaderProperties(shaderProperties, mode); } var finalShader = new ShaderGenerator(); finalShader.AddShaderChunk(string.Format(@"Shader ""{0}""", outputName), false); finalShader.AddShaderChunk("{", false); finalShader.Indent(); finalShader.AddShaderChunk("Properties", false); finalShader.AddShaderChunk("{", false); finalShader.Indent(); finalShader.AddShaderChunk(shaderProperties.GetPropertiesBlock(2), false); finalShader.Deindent(); finalShader.AddShaderChunk("}", false); var lwSub = new LightWeightPBRSubShader(); foreach (var subshader in lwSub.GetSubshader(this, mode)) { finalShader.AddShaderChunk(subshader, true); } finalShader.Deindent(); finalShader.AddShaderChunk("}", false); configuredTextures = shaderProperties.GetConfiguredTexutres(); return(finalShader.GetShaderString(0)); }
public void TestCanConnectAndTraverseThreeNodesOnBaseMaterialGraph() { var graph = new GraphData(); var outputNode = new TestableNode(); graph.AddNode(outputNode); var middleNode = new TestableNode(); graph.AddNode(middleNode); var inputNode = new TestableNode(); graph.AddNode(inputNode); Assert.AreEqual(3, graph.GetNodes <AbstractMaterialNode>().Count()); graph.Connect(outputNode.GetSlotReference(TestableNode.Output0), middleNode.GetSlotReference(TestableNode.Input0)); Assert.AreEqual(1, graph.edges.Count()); graph.Connect(middleNode.GetSlotReference(TestableNode.Output0), inputNode.GetSlotReference(TestableNode.Input0)); Assert.AreEqual(2, graph.edges.Count()); var edgesOnMiddleNode = NodeUtils.GetAllEdges(middleNode); Assert.AreEqual(2, edgesOnMiddleNode.Count()); outputNode.SetOverrideActiveState(AbstractMaterialNode.ActiveState.ExplicitActive); middleNode.SetOverrideActiveState(AbstractMaterialNode.ActiveState.ExplicitActive); inputNode.SetOverrideActiveState(AbstractMaterialNode.ActiveState.ExplicitActive); List <AbstractMaterialNode> result = new List <AbstractMaterialNode>(); NodeUtils.DepthFirstCollectNodesFromNode(result, inputNode); Assert.AreEqual(3, result.Count); result.Clear(); NodeUtils.DepthFirstCollectNodesFromNode(result, inputNode, NodeUtils.IncludeSelf.Exclude); Assert.AreEqual(2, result.Count); result.Clear(); NodeUtils.DepthFirstCollectNodesFromNode(result, null); Assert.AreEqual(0, result.Count); }
public void CollectShaderProperties(PropertyCollector collector, GenerationMode generationMode) { if (!isSubGraph || generationMode == GenerationMode.Preview) { foreach (var prop in properties) { collector.AddShaderProperty(prop); } } if (isSubGraph) { List <AbstractMaterialNode> activeNodes = new List <AbstractMaterialNode>(); NodeUtils.DepthFirstCollectNodesFromNode(activeNodes, outputNode); foreach (var node in activeNodes) { node.CollectShaderProperties(collector, generationMode); } } }
public string GetShader(GenerationMode mode, string outputName, out List <PropertyCollector.TextureInfo> configuredTextures) { var activeNodeList = ListPool <INode> .Get(); NodeUtils.DepthFirstCollectNodesFromNode(activeNodeList, this); var shaderProperties = new PropertyCollector(); var abstractMaterialGraph = owner as AbstractMaterialGraph; if (abstractMaterialGraph != null) { abstractMaterialGraph.CollectShaderProperties(shaderProperties, mode); } foreach (var activeNode in activeNodeList.OfType <AbstractMaterialNode>()) { activeNode.CollectShaderProperties(shaderProperties, mode); } var finalShader = new ShaderStringBuilder(); finalShader.AppendLine(@"Shader ""{0}""", outputName); using (finalShader.BlockScope()) { finalShader.AppendLine("Properties"); using (finalShader.BlockScope()) { finalShader.AppendLine(shaderProperties.GetPropertiesBlock(0)); } foreach (var subShader in m_SubShaders) { finalShader.AppendLines(subShader.GetSubshader(this, mode)); } finalShader.AppendLine(@"FallBack ""Hidden/InternalErrorShader"""); } configuredTextures = shaderProperties.GetConfiguredTexutres(); return(finalShader.ToString()); }
static string GetShaderPassFromTemplate(string template, UnlitMasterNode masterNode, Pass pass, GenerationMode mode, SurfaceMaterialOptions materialOptions) { // ----------------------------------------------------- // // SETUP // // ----------------------------------------------------- // // ------------------------------------- // String builders var shaderProperties = new PropertyCollector(); var shaderPropertyUniforms = new ShaderStringBuilder(1); var functionBuilder = new ShaderStringBuilder(1); var functionRegistry = new FunctionRegistry(functionBuilder); var defines = new ShaderStringBuilder(1); var graph = new ShaderStringBuilder(0); var vertexDescriptionInputStruct = new ShaderStringBuilder(1); var vertexDescriptionStruct = new ShaderStringBuilder(1); var vertexDescriptionFunction = new ShaderStringBuilder(1); var surfaceDescriptionInputStruct = new ShaderStringBuilder(1); var surfaceDescriptionStruct = new ShaderStringBuilder(1); var surfaceDescriptionFunction = new ShaderStringBuilder(1); var vertexInputStruct = new ShaderStringBuilder(1); var vertexOutputStruct = new ShaderStringBuilder(2); var vertexShader = new ShaderStringBuilder(2); var vertexShaderDescriptionInputs = new ShaderStringBuilder(2); var vertexShaderOutputs = new ShaderStringBuilder(2); var pixelShader = new ShaderStringBuilder(2); var pixelShaderSurfaceInputs = new ShaderStringBuilder(2); var pixelShaderSurfaceRemap = new ShaderStringBuilder(2); // ------------------------------------- // Get Slot and Node lists per stage var vertexSlots = pass.VertexShaderSlots.Select(masterNode.FindSlot <MaterialSlot>).ToList(); var vertexNodes = ListPool <AbstractMaterialNode> .Get(); NodeUtils.DepthFirstCollectNodesFromNode(vertexNodes, masterNode, NodeUtils.IncludeSelf.Include, pass.VertexShaderSlots); var pixelSlots = pass.PixelShaderSlots.Select(masterNode.FindSlot <MaterialSlot>).ToList(); var pixelNodes = ListPool <AbstractMaterialNode> .Get(); NodeUtils.DepthFirstCollectNodesFromNode(pixelNodes, masterNode, NodeUtils.IncludeSelf.Include, pass.PixelShaderSlots); // ------------------------------------- // Get Requirements var vertexRequirements = ShaderGraphRequirements.FromNodes(vertexNodes, ShaderStageCapability.Vertex, false); var pixelRequirements = ShaderGraphRequirements.FromNodes(pixelNodes, ShaderStageCapability.Fragment); var graphRequirements = pixelRequirements.Union(vertexRequirements); var surfaceRequirements = ShaderGraphRequirements.FromNodes(pixelNodes, ShaderStageCapability.Fragment, false); var modelRequiements = ShaderGraphRequirements.none; modelRequiements.requiresNormal |= k_PixelCoordinateSpace; modelRequiements.requiresTangent |= k_PixelCoordinateSpace; modelRequiements.requiresBitangent |= k_PixelCoordinateSpace; modelRequiements.requiresPosition |= k_PixelCoordinateSpace; modelRequiements.requiresViewDir |= k_PixelCoordinateSpace; modelRequiements.requiresMeshUVs.Add(UVChannel.UV1); // ----------------------------------------------------- // // START SHADER GENERATION // // ----------------------------------------------------- // // ------------------------------------- // Calculate material options var blendingBuilder = new ShaderStringBuilder(1); var cullingBuilder = new ShaderStringBuilder(1); var zTestBuilder = new ShaderStringBuilder(1); var zWriteBuilder = new ShaderStringBuilder(1); materialOptions.GetBlend(blendingBuilder); materialOptions.GetCull(cullingBuilder); materialOptions.GetDepthTest(zTestBuilder); materialOptions.GetDepthWrite(zWriteBuilder); // ------------------------------------- // Generate defines if (masterNode.IsSlotConnected(UnlitMasterNode.AlphaThresholdSlotId)) { defines.AppendLine("#define _AlphaClip 1"); } if (masterNode.surfaceType == SurfaceType.Transparent && masterNode.alphaMode == AlphaMode.Premultiply) { defines.AppendLine("#define _ALPHAPREMULTIPLY_ON 1"); } if (graphRequirements.requiresDepthTexture) { defines.AppendLine("#define REQUIRE_DEPTH_TEXTURE"); } if (graphRequirements.requiresCameraOpaqueTexture) { defines.AppendLine("#define REQUIRE_OPAQUE_TEXTURE"); } // ----------------------------------------------------- // // START VERTEX DESCRIPTION // // ----------------------------------------------------- // // ------------------------------------- // Generate Input structure for Vertex Description function // TODO - Vertex Description Input requirements are needed to exclude intermediate translation spaces vertexDescriptionInputStruct.AppendLine("struct VertexDescriptionInputs"); using (vertexDescriptionInputStruct.BlockSemicolonScope()) { ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(vertexRequirements.requiresNormal, InterpolatorType.Normal, vertexDescriptionInputStruct); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(vertexRequirements.requiresTangent, InterpolatorType.Tangent, vertexDescriptionInputStruct); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(vertexRequirements.requiresBitangent, InterpolatorType.BiTangent, vertexDescriptionInputStruct); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(vertexRequirements.requiresViewDir, InterpolatorType.ViewDirection, vertexDescriptionInputStruct); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(vertexRequirements.requiresPosition, InterpolatorType.Position, vertexDescriptionInputStruct); if (vertexRequirements.requiresVertexColor) { vertexDescriptionInputStruct.AppendLine("float4 {0};", ShaderGeneratorNames.VertexColor); } if (vertexRequirements.requiresScreenPosition) { vertexDescriptionInputStruct.AppendLine("float4 {0};", ShaderGeneratorNames.ScreenPosition); } foreach (var channel in vertexRequirements.requiresMeshUVs.Distinct()) { vertexDescriptionInputStruct.AppendLine("half4 {0};", channel.GetUVName()); } if (vertexRequirements.requiresTime) { vertexDescriptionInputStruct.AppendLine("float3 {0};", ShaderGeneratorNames.TimeParameters); } } // ------------------------------------- // Generate Output structure for Vertex Description function GraphUtil.GenerateVertexDescriptionStruct(vertexDescriptionStruct, vertexSlots); // ------------------------------------- // Generate Vertex Description function GraphUtil.GenerateVertexDescriptionFunction( masterNode.owner as GraphData, vertexDescriptionFunction, functionRegistry, shaderProperties, mode, vertexNodes, vertexSlots); // ----------------------------------------------------- // // START SURFACE DESCRIPTION // // ----------------------------------------------------- // // ------------------------------------- // Generate Input structure for Surface Description function // Surface Description Input requirements are needed to exclude intermediate translation spaces surfaceDescriptionInputStruct.AppendLine("struct SurfaceDescriptionInputs"); using (surfaceDescriptionInputStruct.BlockSemicolonScope()) { ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(surfaceRequirements.requiresNormal, InterpolatorType.Normal, surfaceDescriptionInputStruct); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(surfaceRequirements.requiresTangent, InterpolatorType.Tangent, surfaceDescriptionInputStruct); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(surfaceRequirements.requiresBitangent, InterpolatorType.BiTangent, surfaceDescriptionInputStruct); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(surfaceRequirements.requiresViewDir, InterpolatorType.ViewDirection, surfaceDescriptionInputStruct); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(surfaceRequirements.requiresPosition, InterpolatorType.Position, surfaceDescriptionInputStruct); if (surfaceRequirements.requiresVertexColor) { surfaceDescriptionInputStruct.AppendLine("float4 {0};", ShaderGeneratorNames.VertexColor); } if (surfaceRequirements.requiresScreenPosition) { surfaceDescriptionInputStruct.AppendLine("float4 {0};", ShaderGeneratorNames.ScreenPosition); } if (surfaceRequirements.requiresFaceSign) { surfaceDescriptionInputStruct.AppendLine("float {0};", ShaderGeneratorNames.FaceSign); } foreach (var channel in surfaceRequirements.requiresMeshUVs.Distinct()) { surfaceDescriptionInputStruct.AppendLine("half4 {0};", channel.GetUVName()); } if (surfaceRequirements.requiresTime) { surfaceDescriptionInputStruct.AppendLine("float3 {0};", ShaderGeneratorNames.TimeParameters); } } // ------------------------------------- // Generate Output structure for Surface Description function GraphUtil.GenerateSurfaceDescriptionStruct(surfaceDescriptionStruct, pixelSlots); // ------------------------------------- // Generate Surface Description function GraphUtil.GenerateSurfaceDescriptionFunction( pixelNodes, masterNode, masterNode.owner as GraphData, surfaceDescriptionFunction, functionRegistry, shaderProperties, pixelRequirements, mode, "PopulateSurfaceData", "SurfaceDescription", null, pixelSlots); // ----------------------------------------------------- // // GENERATE VERTEX > PIXEL PIPELINE // // ----------------------------------------------------- // // ------------------------------------- // Property uniforms shaderProperties.GetPropertiesDeclaration(shaderPropertyUniforms, mode, masterNode.owner.concretePrecision); // ------------------------------------- // Generate Input structure for Vertex shader GraphUtil.GenerateApplicationVertexInputs(vertexRequirements.Union(pixelRequirements.Union(modelRequiements)), vertexInputStruct); // ------------------------------------- // Generate standard transformations // This method ensures all required transform data is available in vertex and pixel stages ShaderGenerator.GenerateStandardTransforms( 3, 10, vertexOutputStruct, vertexShader, vertexShaderDescriptionInputs, vertexShaderOutputs, pixelShader, pixelShaderSurfaceInputs, pixelRequirements, surfaceRequirements, modelRequiements, vertexRequirements, CoordinateSpace.World); // ------------------------------------- // Generate pixel shader surface remap foreach (var slot in pixelSlots) { pixelShaderSurfaceRemap.AppendLine("{0} = surf.{0};", slot.shaderOutputName); } // ------------------------------------- // Extra pixel shader work var faceSign = new ShaderStringBuilder(); if (pixelRequirements.requiresFaceSign) { faceSign.AppendLine(", half FaceSign : VFACE"); } // ----------------------------------------------------- // // FINALIZE // // ----------------------------------------------------- // // ------------------------------------- // Combine Graph sections graph.AppendLines(shaderPropertyUniforms.ToString()); graph.AppendLine(vertexDescriptionInputStruct.ToString()); graph.AppendLine(surfaceDescriptionInputStruct.ToString()); graph.AppendLine(functionBuilder.ToString()); graph.AppendLine(vertexDescriptionStruct.ToString()); graph.AppendLine(vertexDescriptionFunction.ToString()); graph.AppendLine(surfaceDescriptionStruct.ToString()); graph.AppendLine(surfaceDescriptionFunction.ToString()); graph.AppendLine(vertexInputStruct.ToString()); // ------------------------------------- // Generate final subshader var resultPass = template.Replace("${Tags}", string.Empty); resultPass = resultPass.Replace("${Blending}", blendingBuilder.ToString()); resultPass = resultPass.Replace("${Culling}", cullingBuilder.ToString()); resultPass = resultPass.Replace("${ZTest}", zTestBuilder.ToString()); resultPass = resultPass.Replace("${ZWrite}", zWriteBuilder.ToString()); resultPass = resultPass.Replace("${Defines}", defines.ToString()); resultPass = resultPass.Replace("${Graph}", graph.ToString()); resultPass = resultPass.Replace("${VertexOutputStruct}", vertexOutputStruct.ToString()); resultPass = resultPass.Replace("${VertexShader}", vertexShader.ToString()); resultPass = resultPass.Replace("${VertexShaderDescriptionInputs}", vertexShaderDescriptionInputs.ToString()); resultPass = resultPass.Replace("${VertexShaderOutputs}", vertexShaderOutputs.ToString()); resultPass = resultPass.Replace("${FaceSign}", faceSign.ToString()); resultPass = resultPass.Replace("${PixelShader}", pixelShader.ToString()); resultPass = resultPass.Replace("${PixelShaderSurfaceInputs}", pixelShaderSurfaceInputs.ToString()); resultPass = resultPass.Replace("${PixelShaderSurfaceRemap}", pixelShaderSurfaceRemap.ToString()); return(resultPass); }
static string GetExtraPassesFromTemplate(string template, UnlitMasterNode masterNode, Pass pass, GenerationMode mode, SurfaceMaterialOptions materialOptions) { // ----------------------------------------------------- // // SETUP // // ----------------------------------------------------- // // ------------------------------------- // String builders var dummyBuilder = new ShaderStringBuilder(0); var shaderProperties = new PropertyCollector(); var functionBuilder = new ShaderStringBuilder(1); var functionRegistry = new FunctionRegistry(functionBuilder); var defines = new ShaderStringBuilder(2); var graph = new ShaderStringBuilder(0); var vertexDescriptionInputStruct = new ShaderStringBuilder(1); var vertexDescriptionStruct = new ShaderStringBuilder(1); var vertexDescriptionFunction = new ShaderStringBuilder(1); var vertexInputStruct = new ShaderStringBuilder(1); var vertexShader = new ShaderStringBuilder(2); var vertexDescriptionInputs = new ShaderStringBuilder(2); // ------------------------------------- // Get Slot and Node lists per stage var vertexSlots = pass.VertexShaderSlots.Select(masterNode.FindSlot <MaterialSlot>).ToList(); var vertexNodes = ListPool <AbstractMaterialNode> .Get(); NodeUtils.DepthFirstCollectNodesFromNode(vertexNodes, masterNode, NodeUtils.IncludeSelf.Include, pass.VertexShaderSlots); // ------------------------------------- // Get requirements var vertexRequirements = ShaderGraphRequirements.FromNodes(vertexNodes, ShaderStageCapability.Vertex, false); var modelRequiements = ShaderGraphRequirements.none; modelRequiements.requiresNormal |= m_VertexCoordinateSpace; modelRequiements.requiresPosition |= m_VertexCoordinateSpace; modelRequiements.requiresMeshUVs.Add(UVChannel.UV1); // ----------------------------------------------------- // // START SHADER GENERATION // // ----------------------------------------------------- // // ------------------------------------- // Calculate material options var cullingBuilder = new ShaderStringBuilder(1); materialOptions.GetCull(cullingBuilder); // ------------------------------------- // Generate defines if (masterNode.IsSlotConnected(PBRMasterNode.AlphaThresholdSlotId)) { defines.AppendLine("#define _AlphaClip 1"); } // ----------------------------------------------------- // // START VERTEX DESCRIPTION // // ----------------------------------------------------- // // ------------------------------------- // Generate Input structure for Vertex Description function // TODO - Vertex Description Input requirements are needed to exclude intermediate translation spaces vertexDescriptionInputStruct.AppendLine("struct VertexDescriptionInputs"); using (vertexDescriptionInputStruct.BlockSemicolonScope()) { ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(vertexRequirements.requiresNormal, InterpolatorType.Normal, vertexDescriptionInputStruct); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(vertexRequirements.requiresTangent, InterpolatorType.Tangent, vertexDescriptionInputStruct); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(vertexRequirements.requiresBitangent, InterpolatorType.BiTangent, vertexDescriptionInputStruct); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(vertexRequirements.requiresViewDir, InterpolatorType.ViewDirection, vertexDescriptionInputStruct); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(vertexRequirements.requiresPosition, InterpolatorType.Position, vertexDescriptionInputStruct); if (vertexRequirements.requiresVertexColor) { vertexDescriptionInputStruct.AppendLine("float4 {0};", ShaderGeneratorNames.VertexColor); } if (vertexRequirements.requiresScreenPosition) { vertexDescriptionInputStruct.AppendLine("float4 {0};", ShaderGeneratorNames.ScreenPosition); } foreach (var channel in vertexRequirements.requiresMeshUVs.Distinct()) { vertexDescriptionInputStruct.AppendLine("half4 {0};", channel.GetUVName()); } } // ------------------------------------- // Generate Output structure for Vertex Description function GraphUtil.GenerateVertexDescriptionStruct(vertexDescriptionStruct, vertexSlots); // ------------------------------------- // Generate Vertex Description function GraphUtil.GenerateVertexDescriptionFunction( masterNode.owner as AbstractMaterialGraph, vertexDescriptionFunction, functionRegistry, shaderProperties, mode, vertexNodes, vertexSlots); // ----------------------------------------------------- // // GENERATE VERTEX > PIXEL PIPELINE // // ----------------------------------------------------- // // ------------------------------------- // Generate Input structure for Vertex shader GraphUtil.GenerateApplicationVertexInputs(vertexRequirements.Union(modelRequiements), vertexInputStruct); // ------------------------------------- // Generate standard transformations // This method ensures all required transform data is available in vertex and pixel stages ShaderGenerator.GenerateStandardTransforms( 3, 10, dummyBuilder, vertexShader, vertexDescriptionInputs, dummyBuilder, dummyBuilder, dummyBuilder, ShaderGraphRequirements.none, ShaderGraphRequirements.none, modelRequiements, vertexRequirements, CoordinateSpace.World); // ----------------------------------------------------- // // FINALIZE // // ----------------------------------------------------- // // ------------------------------------- // Combine Graph sections graph.AppendLine(shaderProperties.GetPropertiesDeclaration(1)); graph.AppendLine(vertexDescriptionInputStruct.ToString()); graph.AppendLine(functionBuilder.ToString()); graph.AppendLine(vertexDescriptionStruct.ToString()); graph.AppendLine(vertexDescriptionFunction.ToString()); graph.AppendLine(vertexInputStruct.ToString()); // ------------------------------------- // Generate final subshader var resultPass = template.Replace("${Culling}", cullingBuilder.ToString()); resultPass = resultPass.Replace("${Defines}", defines.ToString()); resultPass = resultPass.Replace("${Graph}", graph.ToString()); resultPass = resultPass.Replace("${VertexShader}", vertexShader.ToString()); resultPass = resultPass.Replace("${VertexShaderDescriptionInputs}", vertexDescriptionInputs.ToString()); return(resultPass); }
// TODO: Fix this static ShaderGraphVfxAsset GenerateVfxShaderGraphAsset(GraphData graph) { var target = graph.activeTargets.FirstOrDefault(x => x is VFXTarget) as VFXTarget; if (target == null) { return(null); } var nl = Environment.NewLine; var indent = new string(' ', 4); var asset = ScriptableObject.CreateInstance <ShaderGraphVfxAsset>(); var result = asset.compilationResult = new GraphCompilationResult(); var mode = GenerationMode.ForReals; asset.lit = target.lit; asset.alphaClipping = target.alphaTest; var assetGuid = graph.assetGuid; var assetPath = AssetDatabase.GUIDToAssetPath(assetGuid); var hlslName = NodeUtils.GetHLSLSafeName(Path.GetFileNameWithoutExtension(assetPath)); var ports = new List <MaterialSlot>(); var nodes = new List <AbstractMaterialNode>(); foreach (var vertexBlock in graph.vertexContext.blocks) { vertexBlock.value.GetInputSlots(ports); NodeUtils.DepthFirstCollectNodesFromNode(nodes, vertexBlock); } foreach (var fragmentBlock in graph.fragmentContext.blocks) { fragmentBlock.value.GetInputSlots(ports); NodeUtils.DepthFirstCollectNodesFromNode(nodes, fragmentBlock); } //Remove inactive blocks from generation { var tmpCtx = new TargetActiveBlockContext(new List <BlockFieldDescriptor>(), null); target.GetActiveBlocks(ref tmpCtx); ports.RemoveAll(materialSlot => { return(!tmpCtx.activeBlocks.Any(o => materialSlot.RawDisplayName() == o.displayName)); }); } var bodySb = new ShaderStringBuilder(1); var registry = new FunctionRegistry(new ShaderStringBuilder(), true); foreach (var properties in graph.properties) { properties.ValidateConcretePrecision(graph.concretePrecision); } foreach (var node in nodes) { if (node is IGeneratesBodyCode bodyGenerator) { bodySb.currentNode = node; bodyGenerator.GenerateNodeCode(bodySb, mode); bodySb.ReplaceInCurrentMapping(PrecisionUtil.Token, node.concretePrecision.ToShaderString()); } if (node is IGeneratesFunction generatesFunction) { registry.builder.currentNode = node; generatesFunction.GenerateNodeFunction(registry, mode); } } bodySb.currentNode = null; var portNodeSets = new HashSet <AbstractMaterialNode> [ports.Count]; for (var portIndex = 0; portIndex < ports.Count; portIndex++) { var port = ports[portIndex]; var nodeSet = new HashSet <AbstractMaterialNode>(); NodeUtils.CollectNodeSet(nodeSet, port); portNodeSets[portIndex] = nodeSet; } var portPropertySets = new HashSet <string> [ports.Count]; for (var portIndex = 0; portIndex < ports.Count; portIndex++) { portPropertySets[portIndex] = new HashSet <string>(); } foreach (var node in nodes) { if (!(node is PropertyNode propertyNode)) { continue; } for (var portIndex = 0; portIndex < ports.Count; portIndex++) { var portNodeSet = portNodeSets[portIndex]; if (portNodeSet.Contains(node)) { portPropertySets[portIndex].Add(propertyNode.property.objectId); } } } var shaderProperties = new PropertyCollector(); foreach (var node in nodes) { node.CollectShaderProperties(shaderProperties, GenerationMode.ForReals); } asset.SetTextureInfos(shaderProperties.GetConfiguredTexutres()); var codeSnippets = new List <string>(); var portCodeIndices = new List <int> [ports.Count]; var sharedCodeIndices = new List <int>(); for (var i = 0; i < portCodeIndices.Length; i++) { portCodeIndices[i] = new List <int>(); } sharedCodeIndices.Add(codeSnippets.Count); codeSnippets.Add($"#include \"Packages/com.unity.shadergraph/ShaderGraphLibrary/Functions.hlsl\"{nl}"); for (var registryIndex = 0; registryIndex < registry.names.Count; registryIndex++) { var name = registry.names[registryIndex]; var source = registry.sources[name]; var precision = source.nodes.First().concretePrecision; var hasPrecisionMismatch = false; var nodeNames = new HashSet <string>(); foreach (var node in source.nodes) { nodeNames.Add(node.name); if (node.concretePrecision != precision) { hasPrecisionMismatch = true; break; } } if (hasPrecisionMismatch) { var message = new StringBuilder($"Precision mismatch for function {name}:"); foreach (var node in source.nodes) { message.AppendLine($"{node.name} ({node.objectId}): {node.concretePrecision}"); } throw new InvalidOperationException(message.ToString()); } var code = source.code.Replace(PrecisionUtil.Token, precision.ToShaderString()); code = $"// Node: {string.Join(", ", nodeNames)}{nl}{code}"; var codeIndex = codeSnippets.Count; codeSnippets.Add(code + nl); for (var portIndex = 0; portIndex < ports.Count; portIndex++) { var portNodeSet = portNodeSets[portIndex]; foreach (var node in source.nodes) { if (portNodeSet.Contains(node)) { portCodeIndices[portIndex].Add(codeIndex); break; } } } } foreach (var property in graph.properties) { if (property.isExposable && property.generatePropertyBlock) { continue; } for (var portIndex = 0; portIndex < ports.Count; portIndex++) { var portPropertySet = portPropertySets[portIndex]; if (portPropertySet.Contains(property.objectId)) { portCodeIndices[portIndex].Add(codeSnippets.Count); } } ShaderStringBuilder builder = new ShaderStringBuilder(); property.ForeachHLSLProperty(h => h.AppendTo(builder)); codeSnippets.Add($"// Property: {property.displayName}{nl}{builder.ToCodeBlock()}{nl}{nl}"); } var inputStructName = $"SG_Input_{assetGuid}"; var outputStructName = $"SG_Output_{assetGuid}"; var evaluationFunctionName = $"SG_Evaluate_{assetGuid}"; #region Input Struct sharedCodeIndices.Add(codeSnippets.Count); codeSnippets.Add($"struct {inputStructName}{nl}{{{nl}"); #region Requirements var portRequirements = new ShaderGraphRequirements[ports.Count]; for (var portIndex = 0; portIndex < ports.Count; portIndex++) { var requirementsNodes = portNodeSets[portIndex].ToList(); requirementsNodes.Add(ports[portIndex].owner); portRequirements[portIndex] = ShaderGraphRequirements.FromNodes(requirementsNodes, ports[portIndex].stageCapability); } var portIndices = new List <int>(); portIndices.Capacity = ports.Count; void AddRequirementsSnippet(Func <ShaderGraphRequirements, bool> predicate, string snippet) { portIndices.Clear(); for (var portIndex = 0; portIndex < ports.Count; portIndex++) { if (predicate(portRequirements[portIndex])) { portIndices.Add(portIndex); } } if (portIndices.Count > 0) { foreach (var portIndex in portIndices) { portCodeIndices[portIndex].Add(codeSnippets.Count); } codeSnippets.Add($"{indent}{snippet};{nl}"); } } void AddCoordinateSpaceSnippets(InterpolatorType interpolatorType, Func <ShaderGraphRequirements, NeededCoordinateSpace> selector) { foreach (var space in EnumInfo <CoordinateSpace> .values) { var neededSpace = space.ToNeededCoordinateSpace(); AddRequirementsSnippet(r => (selector(r) & neededSpace) > 0, $"float3 {space.ToVariableName(interpolatorType)}"); } } // TODO: Rework requirements system to make this better AddCoordinateSpaceSnippets(InterpolatorType.Normal, r => r.requiresNormal); AddCoordinateSpaceSnippets(InterpolatorType.Tangent, r => r.requiresTangent); AddCoordinateSpaceSnippets(InterpolatorType.BiTangent, r => r.requiresBitangent); AddCoordinateSpaceSnippets(InterpolatorType.ViewDirection, r => r.requiresViewDir); AddCoordinateSpaceSnippets(InterpolatorType.Position, r => r.requiresPosition); AddRequirementsSnippet(r => r.requiresVertexColor, $"float4 {ShaderGeneratorNames.VertexColor}"); AddRequirementsSnippet(r => r.requiresScreenPosition, $"float4 {ShaderGeneratorNames.ScreenPosition}"); AddRequirementsSnippet(r => r.requiresFaceSign, $"float4 {ShaderGeneratorNames.FaceSign}"); foreach (var uvChannel in EnumInfo <UVChannel> .values) { AddRequirementsSnippet(r => r.requiresMeshUVs.Contains(uvChannel), $"half4 {uvChannel.GetUVName()}"); } AddRequirementsSnippet(r => r.requiresTime, $"float3 {ShaderGeneratorNames.TimeParameters}"); #endregion sharedCodeIndices.Add(codeSnippets.Count); codeSnippets.Add($"}};{nl}{nl}"); #endregion // VFX Code heavily relies on the slotId from the original MasterNodes // Since we keep these around for upgrades anyway, for now it is simpler to use them // Therefore we remap the output blocks back to the original Ids here var originialPortIds = new int[ports.Count]; for (int i = 0; i < originialPortIds.Length; i++) { if (!VFXTarget.s_BlockMap.TryGetValue((ports[i].owner as BlockNode).descriptor, out var originalId)) { continue; } // In Master Nodes we had a different BaseColor/Color slot id between Unlit/Lit // In the stack we use BaseColor for both cases. Catch this here. if (asset.lit && originalId == ShaderGraphVfxAsset.ColorSlotId) { originalId = ShaderGraphVfxAsset.BaseColorSlotId; } originialPortIds[i] = originalId; } #region Output Struct sharedCodeIndices.Add(codeSnippets.Count); codeSnippets.Add($"struct {outputStructName}{nl}{{"); for (var portIndex = 0; portIndex < ports.Count; portIndex++) { var port = ports[portIndex]; portCodeIndices[portIndex].Add(codeSnippets.Count); codeSnippets.Add($"{nl}{indent}{port.concreteValueType.ToShaderString(graph.concretePrecision)} {port.shaderOutputName}_{originialPortIds[portIndex]};"); } sharedCodeIndices.Add(codeSnippets.Count); codeSnippets.Add($"{nl}}};{nl}{nl}"); #endregion #region Graph Function sharedCodeIndices.Add(codeSnippets.Count); codeSnippets.Add($"{outputStructName} {evaluationFunctionName}({nl}{indent}{inputStructName} IN"); var inputProperties = new List <AbstractShaderProperty>(); var portPropertyIndices = new List <int> [ports.Count]; for (var portIndex = 0; portIndex < ports.Count; portIndex++) { portPropertyIndices[portIndex] = new List <int>(); } foreach (var property in graph.properties) { if (!property.isExposable || !property.generatePropertyBlock) { continue; } var propertyIndex = inputProperties.Count; var codeIndex = codeSnippets.Count; for (var portIndex = 0; portIndex < ports.Count; portIndex++) { var portPropertySet = portPropertySets[portIndex]; if (portPropertySet.Contains(property.objectId)) { portCodeIndices[portIndex].Add(codeIndex); portPropertyIndices[portIndex].Add(propertyIndex); } } inputProperties.Add(property); codeSnippets.Add($",{nl}{indent}/* Property: {property.displayName} */ {property.GetPropertyAsArgumentString()}"); } sharedCodeIndices.Add(codeSnippets.Count); codeSnippets.Add($"){nl}{{"); #region Node Code for (var mappingIndex = 0; mappingIndex < bodySb.mappings.Count; mappingIndex++) { var mapping = bodySb.mappings[mappingIndex]; var code = bodySb.ToString(mapping.startIndex, mapping.count); if (string.IsNullOrWhiteSpace(code)) { continue; } code = $"{nl}{indent}// Node: {mapping.node.name}{nl}{code}"; var codeIndex = codeSnippets.Count; codeSnippets.Add(code); for (var portIndex = 0; portIndex < ports.Count; portIndex++) { var portNodeSet = portNodeSets[portIndex]; if (portNodeSet.Contains(mapping.node)) { portCodeIndices[portIndex].Add(codeIndex); } } } #endregion #region Output Mapping sharedCodeIndices.Add(codeSnippets.Count); codeSnippets.Add($"{nl}{indent}// VFXMasterNode{nl}{indent}{outputStructName} OUT;{nl}"); // Output mapping for (var portIndex = 0; portIndex < ports.Count; portIndex++) { var port = ports[portIndex]; portCodeIndices[portIndex].Add(codeSnippets.Count); codeSnippets.Add($"{indent}OUT.{port.shaderOutputName}_{originialPortIds[portIndex]} = {port.owner.GetSlotValue(port.id, GenerationMode.ForReals, graph.concretePrecision)};{nl}"); } #endregion // Function end sharedCodeIndices.Add(codeSnippets.Count); codeSnippets.Add($"{indent}return OUT;{nl}}}{nl}"); #endregion result.codeSnippets = codeSnippets.ToArray(); result.sharedCodeIndices = sharedCodeIndices.ToArray(); result.outputCodeIndices = new IntArray[ports.Count]; for (var i = 0; i < ports.Count; i++) { result.outputCodeIndices[i] = portCodeIndices[i].ToArray(); } var outputMetadatas = new OutputMetadata[ports.Count]; for (int portIndex = 0; portIndex < outputMetadatas.Length; portIndex++) { outputMetadatas[portIndex] = new OutputMetadata(portIndex, ports[portIndex].shaderOutputName, originialPortIds[portIndex]); } asset.SetOutputs(outputMetadatas); asset.evaluationFunctionName = evaluationFunctionName; asset.inputStructName = inputStructName; asset.outputStructName = outputStructName; asset.portRequirements = portRequirements; asset.concretePrecision = graph.concretePrecision; asset.SetProperties(inputProperties); asset.outputPropertyIndices = new IntArray[ports.Count]; for (var portIndex = 0; portIndex < ports.Count; portIndex++) { asset.outputPropertyIndices[portIndex] = portPropertyIndices[portIndex].ToArray(); } return(asset); }
private static string GetShaderPassFromTemplate(UnlitMasterNode masterNode, Pass pass, GenerationMode mode) { var builder = new ShaderStringBuilder(); builder.IncreaseIndent(); builder.IncreaseIndent(); var surfaceDescriptionFunction = new ShaderGenerator(); var surfaceDescriptionStruct = new ShaderGenerator(); var surfaceInputs = new ShaderGenerator(); var functionRegistry = new FunctionRegistry(builder); var shaderProperties = new PropertyCollector(); surfaceInputs.AddShaderChunk("struct SurfaceInputs{", false); surfaceInputs.Indent(); var activeNodeList = ListPool <INode> .Get(); NodeUtils.DepthFirstCollectNodesFromNode(activeNodeList, masterNode, NodeUtils.IncludeSelf.Include, pass.PixelShaderSlots); var requirements = ShaderGraphRequirements.FromNodes(activeNodeList); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(requirements.requiresNormal, InterpolatorType.Normal, surfaceInputs); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(requirements.requiresTangent, InterpolatorType.Tangent, surfaceInputs); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(requirements.requiresBitangent, InterpolatorType.BiTangent, surfaceInputs); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(requirements.requiresViewDir, InterpolatorType.ViewDirection, surfaceInputs); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(requirements.requiresPosition, InterpolatorType.Position, surfaceInputs); ShaderGenerator defines = new ShaderGenerator(); defines.AddShaderChunk(string.Format("#define SHADERPASS {0}", pass.ShaderPassName), true); if (requirements.requiresVertexColor) { surfaceInputs.AddShaderChunk(string.Format("float4 {0};", ShaderGeneratorNames.VertexColor), false); } if (requirements.requiresScreenPosition) { surfaceInputs.AddShaderChunk(string.Format("float4 {0};", ShaderGeneratorNames.ScreenPosition), false); } foreach (var channel in requirements.requiresMeshUVs.Distinct()) { surfaceInputs.AddShaderChunk(string.Format("half4 {0};", channel.GetUVName()), false); defines.AddShaderChunk(string.Format("#define ATTRIBUTES_NEED_TEXCOORD{0}", (int)channel), true); defines.AddShaderChunk(string.Format("#define VARYINGS_NEED_TEXCOORD{0}", (int)channel), true); } surfaceInputs.Deindent(); surfaceInputs.AddShaderChunk("};", false); var slots = new List <MaterialSlot>(); foreach (var id in pass.PixelShaderSlots) { var slot = masterNode.FindSlot <MaterialSlot>(id); if (slot != null) { slots.Add(slot); } } GraphUtil.GenerateSurfaceDescriptionStruct(surfaceDescriptionStruct, slots, true); var usedSlots = new List <MaterialSlot>(); foreach (var id in pass.PixelShaderSlots) { usedSlots.Add(masterNode.FindSlot <MaterialSlot>(id)); } GraphUtil.GenerateSurfaceDescription( activeNodeList, masterNode, masterNode.owner as AbstractMaterialGraph, surfaceDescriptionFunction, functionRegistry, shaderProperties, requirements, mode, "PopulateSurfaceData", "SurfaceDescription", null, usedSlots); var graph = new ShaderGenerator(); graph.AddShaderChunk(shaderProperties.GetPropertiesDeclaration(2), false); graph.AddShaderChunk(surfaceInputs.GetShaderString(2), false); graph.AddShaderChunk(builder.ToString(), false); graph.AddShaderChunk(surfaceDescriptionStruct.GetShaderString(2), false); graph.AddShaderChunk(surfaceDescriptionFunction.GetShaderString(2), false); var tagsVisitor = new ShaderGenerator(); var blendingVisitor = new ShaderGenerator(); var cullingVisitor = new ShaderGenerator(); var zTestVisitor = new ShaderGenerator(); var zWriteVisitor = new ShaderGenerator(); var materialOptions = new SurfaceMaterialOptions(); materialOptions.GetTags(tagsVisitor); materialOptions.GetBlend(blendingVisitor); materialOptions.GetCull(cullingVisitor); materialOptions.GetDepthTest(zTestVisitor); materialOptions.GetDepthWrite(zWriteVisitor); var localPixelShader = new ShaderGenerator(); var localSurfaceInputs = new ShaderGenerator(); var surfaceOutputRemap = new ShaderGenerator(); foreach (var channel in requirements.requiresMeshUVs.Distinct()) { localSurfaceInputs.AddShaderChunk(string.Format("surfaceInput.{0} = {1};", channel.GetUVName(), string.Format("half4(input.texCoord{0}, 0, 0)", (int)channel)), false); } var templateLocation = ShaderGenerator.GetTemplatePath("HDUnlitPassForward.template"); foreach (var slot in usedSlots) { surfaceOutputRemap.AddShaderChunk(slot.shaderOutputName + " = surf." + slot.shaderOutputName + ";", true); } if (!File.Exists(templateLocation)) { return(string.Empty); } var subShaderTemplate = File.ReadAllText(templateLocation); var resultPass = subShaderTemplate.Replace("${Defines}", defines.GetShaderString(3)); resultPass = resultPass.Replace("${Graph}", graph.GetShaderString(3)); resultPass = resultPass.Replace("${LocalPixelShader}", localPixelShader.GetShaderString(3)); resultPass = resultPass.Replace("${SurfaceInputs}", localSurfaceInputs.GetShaderString(3)); resultPass = resultPass.Replace("${SurfaceOutputRemap}", surfaceOutputRemap.GetShaderString(3)); resultPass = resultPass.Replace("${LightMode}", pass.Name); resultPass = resultPass.Replace("${ShaderPassInclude}", pass.ShaderPassInclude); resultPass = resultPass.Replace("${Tags}", tagsVisitor.GetShaderString(2)); resultPass = resultPass.Replace("${Blending}", blendingVisitor.GetShaderString(2)); resultPass = resultPass.Replace("${Culling}", cullingVisitor.GetShaderString(2)); resultPass = resultPass.Replace("${ZTest}", zTestVisitor.GetShaderString(2)); resultPass = resultPass.Replace("${ZWrite}", zWriteVisitor.GetShaderString(2)); resultPass = resultPass.Replace("${LOD}", "" + materialOptions.lod); return(resultPass); }
private static bool GenerateShaderPass(UnlitMasterNode masterNode, Pass pass, GenerationMode mode, SurfaceMaterialOptions materialOptions, ShaderGenerator result, List <string> sourceAssetDependencyPaths) { var templateLocation = Path.Combine(Path.Combine(Path.Combine(HDEditorUtils.GetHDRenderPipelinePath(), "Editor"), "ShaderGraph"), pass.TemplateName); if (!File.Exists(templateLocation)) { // TODO: produce error here return(false); } sourceAssetDependencyPaths.Add(templateLocation); // grab all of the active nodes var activeNodeList = ListPool <INode> .Get(); NodeUtils.DepthFirstCollectNodesFromNode(activeNodeList, masterNode, NodeUtils.IncludeSelf.Include, pass.PixelShaderSlots); // graph requirements describe what the graph itself requires var graphRequirements = ShaderGraphRequirements.FromNodes(activeNodeList, ShaderStageCapability.All, true, true); ShaderStringBuilder graphNodeFunctions = new ShaderStringBuilder(); graphNodeFunctions.IncreaseIndent(); var functionRegistry = new FunctionRegistry(graphNodeFunctions); // Build the list of active slots based on what the pass requires // TODO: this can be a shared function -- From here through GraphUtil.GenerateSurfaceDescription(..) var activeSlots = new List <MaterialSlot>(); foreach (var id in pass.PixelShaderSlots) { MaterialSlot slot = masterNode.FindSlot <MaterialSlot>(id); if (slot != null) { activeSlots.Add(slot); } } // build the graph outputs structure to hold the results of each active slots (and fill out activeFields to indicate they are active) string graphInputStructName = "SurfaceDescriptionInputs"; string graphOutputStructName = "SurfaceDescription"; string graphEvalFunctionName = "SurfaceDescriptionFunction"; var graphEvalFunction = new ShaderStringBuilder(); var graphOutputs = new ShaderStringBuilder(); PropertyCollector graphProperties = new PropertyCollector(); // build the graph outputs structure, and populate activeFields with the fields of that structure HashSet <string> activeFields = new HashSet <string>(); GraphUtil.GenerateSurfaceDescriptionStruct(graphOutputs, activeSlots, true); // Build the graph evaluation code, to evaluate the specified slots GraphUtil.GenerateSurfaceDescriptionFunction( activeNodeList, masterNode, masterNode.owner as AbstractMaterialGraph, graphEvalFunction, functionRegistry, graphProperties, graphRequirements, // TODO : REMOVE UNUSED mode, graphEvalFunctionName, graphOutputStructName, null, activeSlots, graphInputStructName); var blendCode = new ShaderStringBuilder(); var cullCode = new ShaderStringBuilder(); var zTestCode = new ShaderStringBuilder(); var zWriteCode = new ShaderStringBuilder(); var stencilCode = new ShaderStringBuilder(); var colorMaskCode = new ShaderStringBuilder(); HDSubShaderUtilities.BuildRenderStatesFromPassAndMaterialOptions(pass, materialOptions, blendCode, cullCode, zTestCode, zWriteCode, stencilCode, colorMaskCode); if (masterNode.twoSided.isOn) { activeFields.Add("DoubleSided"); if (pass.ShaderPassName != "SHADERPASS_VELOCITY") // HACK to get around lack of a good interpolator dependency system { // we need to be able to build interpolators using multiple input structs // also: should only require isFrontFace if Normals are required... activeFields.Add("DoubleSided.Mirror"); // TODO: change this depending on what kind of normal flip you want.. activeFields.Add("FragInputs.isFrontFace"); // will need this for determining normal flip mode } } if (pass.PixelShaderSlots != null) { foreach (var slotId in pass.PixelShaderSlots) { var slot = masterNode.FindSlot <MaterialSlot>(slotId); if (slot != null) { var rawSlotName = slot.RawDisplayName().ToString(); var descriptionVar = string.Format("{0}.{1}", graphOutputStructName, rawSlotName); activeFields.Add(descriptionVar); } } } var packedInterpolatorCode = new ShaderGenerator(); var graphInputs = new ShaderGenerator(); HDRPShaderStructs.Generate( packedInterpolatorCode, graphInputs, graphRequirements, pass.RequiredFields, CoordinateSpace.World, activeFields); // debug output all active fields var interpolatorDefines = new ShaderGenerator(); { interpolatorDefines.AddShaderChunk("// ACTIVE FIELDS:"); foreach (string f in activeFields) { interpolatorDefines.AddShaderChunk("// " + f); } } ShaderGenerator defines = new ShaderGenerator(); { defines.AddShaderChunk(string.Format("#define SHADERPASS {0}", pass.ShaderPassName), true); if (pass.ExtraDefines != null) { foreach (var define in pass.ExtraDefines) { defines.AddShaderChunk(define); } } defines.AddGenerator(interpolatorDefines); } var shaderPassIncludes = new ShaderGenerator(); if (pass.Includes != null) { foreach (var include in pass.Includes) { shaderPassIncludes.AddShaderChunk(include); } } // build graph code var graph = new ShaderGenerator(); graph.AddShaderChunk("// Graph Inputs"); graph.Indent(); graph.AddGenerator(graphInputs); graph.Deindent(); graph.AddShaderChunk("// Graph Outputs"); graph.Indent(); graph.AddShaderChunk(graphOutputs.ToString()); //graph.AddGenerator(graphOutputs); graph.Deindent(); graph.AddShaderChunk("// Graph Properties (uniform inputs)"); graph.AddShaderChunk(graphProperties.GetPropertiesDeclaration(1)); graph.AddShaderChunk("// Graph Node Functions"); graph.AddShaderChunk(graphNodeFunctions.ToString()); graph.AddShaderChunk("// Graph Evaluation"); graph.Indent(); graph.AddShaderChunk(graphEvalFunction.ToString()); //graph.AddGenerator(graphEvalFunction); graph.Deindent(); // build the hash table of all named fragments TODO: could make this Dictionary<string, ShaderGenerator / string> ? Dictionary <string, string> namedFragments = new Dictionary <string, string>(); namedFragments.Add("${Defines}", defines.GetShaderString(2, false)); namedFragments.Add("${Graph}", graph.GetShaderString(2, false)); namedFragments.Add("${LightMode}", pass.LightMode); namedFragments.Add("${PassName}", pass.Name); namedFragments.Add("${Includes}", shaderPassIncludes.GetShaderString(2, false)); namedFragments.Add("${InterpolatorPacking}", packedInterpolatorCode.GetShaderString(2, false)); namedFragments.Add("${Blending}", blendCode.ToString()); namedFragments.Add("${Culling}", cullCode.ToString()); namedFragments.Add("${ZTest}", zTestCode.ToString()); namedFragments.Add("${ZWrite}", zWriteCode.ToString()); namedFragments.Add("${Stencil}", stencilCode.ToString()); namedFragments.Add("${ColorMask}", colorMaskCode.ToString()); namedFragments.Add("${LOD}", materialOptions.lod.ToString()); namedFragments.Add("${VariantDefines}", GetVariantDefines(masterNode)); // process the template to generate the shader code for this pass TODO: could make this a shared function string[] templateLines = File.ReadAllLines(templateLocation); System.Text.StringBuilder builder = new System.Text.StringBuilder(); foreach (string line in templateLines) { ShaderSpliceUtil.PreprocessShaderCode(line, activeFields, namedFragments, builder); builder.AppendLine(); } result.AddShaderChunk(builder.ToString(), false); return(true); }
private static string GetShaderPassFromTemplate(string template, PBRMasterNode masterNode, Pass pass, GenerationMode mode, SurfaceMaterialOptions materialOptions) { var builder = new ShaderStringBuilder(); builder.IncreaseIndent(); builder.IncreaseIndent(); var vertexInputs = new ShaderGenerator(); var surfaceVertexShader = new ShaderGenerator(); var surfaceDescriptionFunction = new ShaderGenerator(); var surfaceDescriptionStruct = new ShaderGenerator(); var functionRegistry = new FunctionRegistry(builder); var surfaceInputs = new ShaderGenerator(); var shaderProperties = new PropertyCollector(); surfaceInputs.AddShaderChunk("struct SurfaceInputs{", false); surfaceInputs.Indent(); var activeNodeList = ListPool <INode> .Get(); NodeUtils.DepthFirstCollectNodesFromNode(activeNodeList, masterNode, NodeUtils.IncludeSelf.Include, pass.PixelShaderSlots); var requirements = ShaderGraphRequirements.FromNodes(activeNodeList); var modelRequiements = ShaderGraphRequirements.none; modelRequiements.requiresNormal |= NeededCoordinateSpace.World; modelRequiements.requiresTangent |= NeededCoordinateSpace.World; modelRequiements.requiresBitangent |= NeededCoordinateSpace.World; modelRequiements.requiresPosition |= NeededCoordinateSpace.World; modelRequiements.requiresViewDir |= NeededCoordinateSpace.World; modelRequiements.requiresMeshUVs.Add(UVChannel.UV1); GraphUtil.GenerateApplicationVertexInputs(requirements.Union(modelRequiements), vertexInputs); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(requirements.requiresNormal, InterpolatorType.Normal, surfaceInputs); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(requirements.requiresTangent, InterpolatorType.Tangent, surfaceInputs); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(requirements.requiresBitangent, InterpolatorType.BiTangent, surfaceInputs); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(requirements.requiresViewDir, InterpolatorType.ViewDirection, surfaceInputs); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(requirements.requiresPosition, InterpolatorType.Position, surfaceInputs); if (requirements.requiresVertexColor) { surfaceInputs.AddShaderChunk(string.Format("float4 {0};", ShaderGeneratorNames.VertexColor), false); } if (requirements.requiresScreenPosition) { surfaceInputs.AddShaderChunk(string.Format("float4 {0};", ShaderGeneratorNames.ScreenPosition), false); } foreach (var channel in requirements.requiresMeshUVs.Distinct()) { surfaceInputs.AddShaderChunk(string.Format("half4 {0};", channel.GetUVName()), false); } surfaceInputs.Deindent(); surfaceInputs.AddShaderChunk("};", false); surfaceVertexShader.AddShaderChunk("GraphVertexInput PopulateVertexData(GraphVertexInput v){", false); surfaceVertexShader.Indent(); surfaceVertexShader.AddShaderChunk("return v;", false); surfaceVertexShader.Deindent(); surfaceVertexShader.AddShaderChunk("}", false); var slots = new List <MaterialSlot>(); foreach (var id in pass.PixelShaderSlots) { slots.Add(masterNode.FindSlot <MaterialSlot>(id)); } GraphUtil.GenerateSurfaceDescriptionStruct(surfaceDescriptionStruct, slots, true); var usedSlots = new List <MaterialSlot>(); foreach (var id in pass.PixelShaderSlots) { usedSlots.Add(masterNode.FindSlot <MaterialSlot>(id)); } GraphUtil.GenerateSurfaceDescription( activeNodeList, masterNode, masterNode.owner as AbstractMaterialGraph, surfaceDescriptionFunction, functionRegistry, shaderProperties, requirements, mode, "PopulateSurfaceData", "SurfaceDescription", null, usedSlots); var graph = new ShaderGenerator(); graph.AddShaderChunk(shaderProperties.GetPropertiesDeclaration(2), false); graph.AddShaderChunk(surfaceInputs.GetShaderString(2), false); graph.AddShaderChunk(builder.ToString(), false); graph.AddShaderChunk(vertexInputs.GetShaderString(2), false); graph.AddShaderChunk(surfaceDescriptionStruct.GetShaderString(2), false); graph.AddShaderChunk(surfaceVertexShader.GetShaderString(2), false); graph.AddShaderChunk(surfaceDescriptionFunction.GetShaderString(2), false); var blendingVisitor = new ShaderGenerator(); var cullingVisitor = new ShaderGenerator(); var zTestVisitor = new ShaderGenerator(); var zWriteVisitor = new ShaderGenerator(); materialOptions.GetBlend(blendingVisitor); materialOptions.GetCull(cullingVisitor); materialOptions.GetDepthTest(zTestVisitor); materialOptions.GetDepthWrite(zWriteVisitor); var interpolators = new ShaderGenerator(); var localVertexShader = new ShaderGenerator(); var localPixelShader = new ShaderGenerator(); var localSurfaceInputs = new ShaderGenerator(); var surfaceOutputRemap = new ShaderGenerator(); ShaderGenerator.GenerateStandardTransforms( 3, 10, interpolators, localVertexShader, localPixelShader, localSurfaceInputs, requirements, modelRequiements, CoordinateSpace.World); ShaderGenerator defines = new ShaderGenerator(); if (masterNode.IsSlotConnected(PBRMasterNode.NormalSlotId)) { defines.AddShaderChunk("#define _NORMALMAP 1", true); } if (masterNode.model == PBRMasterNode.Model.Specular) { defines.AddShaderChunk("#define _SPECULAR_SETUP 1", true); } if (masterNode.IsSlotConnected(PBRMasterNode.AlphaThresholdSlotId)) { defines.AddShaderChunk("#define _AlphaClip 1", true); } if (masterNode.surfaceType == SurfaceType.Transparent && masterNode.alphaMode == AlphaMode.Premultiply) { defines.AddShaderChunk("#define _ALPHAPREMULTIPLY_ON 1", true); } var templateLocation = ShaderGenerator.GetTemplatePath(template); foreach (var slot in usedSlots) { surfaceOutputRemap.AddShaderChunk(string.Format("{0} = surf.{0};", slot.shaderOutputName), true); } if (!File.Exists(templateLocation)) { return(string.Empty); } var subShaderTemplate = File.ReadAllText(templateLocation); var resultPass = subShaderTemplate.Replace("${Defines}", defines.GetShaderString(3)); resultPass = resultPass.Replace("${Graph}", graph.GetShaderString(3)); resultPass = resultPass.Replace("${Interpolators}", interpolators.GetShaderString(3)); resultPass = resultPass.Replace("${VertexShader}", localVertexShader.GetShaderString(3)); resultPass = resultPass.Replace("${LocalPixelShader}", localPixelShader.GetShaderString(3)); resultPass = resultPass.Replace("${SurfaceInputs}", localSurfaceInputs.GetShaderString(3)); resultPass = resultPass.Replace("${SurfaceOutputRemap}", surfaceOutputRemap.GetShaderString(3)); resultPass = resultPass.Replace("${Tags}", string.Empty); resultPass = resultPass.Replace("${Blending}", blendingVisitor.GetShaderString(2)); resultPass = resultPass.Replace("${Culling}", cullingVisitor.GetShaderString(2)); resultPass = resultPass.Replace("${ZTest}", zTestVisitor.GetShaderString(2)); resultPass = resultPass.Replace("${ZWrite}", zWriteVisitor.GetShaderString(2)); return(resultPass); }
static void ProcessSubGraph(SubGraphAsset asset, GraphData graph) { var graphIncludes = new IncludeCollection(); var registry = new FunctionRegistry(new ShaderStringBuilder(), graphIncludes, true); asset.functions.Clear(); asset.isValid = true; graph.OnEnable(); graph.messageManager.ClearAll(); graph.ValidateGraph(); var assetPath = AssetDatabase.GUIDToAssetPath(asset.assetGuid); asset.hlslName = NodeUtils.GetHLSLSafeName(Path.GetFileNameWithoutExtension(assetPath)); asset.inputStructName = $"Bindings_{asset.hlslName}_{asset.assetGuid}_$precision"; asset.functionName = $"SG_{asset.hlslName}_{asset.assetGuid}_$precision"; asset.path = graph.path; var outputNode = graph.outputNode; var outputSlots = PooledList <MaterialSlot> .Get(); outputNode.GetInputSlots(outputSlots); List <AbstractMaterialNode> nodes = new List <AbstractMaterialNode>(); NodeUtils.DepthFirstCollectNodesFromNode(nodes, outputNode); asset.effectiveShaderStage = ShaderStageCapability.All; foreach (var slot in outputSlots) { var stage = NodeUtils.GetEffectiveShaderStageCapability(slot, true); if (stage != ShaderStageCapability.All) { asset.effectiveShaderStage = stage; break; } } asset.vtFeedbackVariables = VirtualTexturingFeedbackUtils.GetFeedbackVariables(outputNode as SubGraphOutputNode); asset.requirements = ShaderGraphRequirements.FromNodes(nodes, asset.effectiveShaderStage, false); // output precision is whatever the output node has as a graph precision, falling back to the graph default asset.outputGraphPrecision = outputNode.graphPrecision.GraphFallback(graph.graphDefaultPrecision); // this saves the graph precision, which indicates whether this subgraph is switchable or not asset.subGraphGraphPrecision = graph.graphDefaultPrecision; asset.previewMode = graph.previewMode; asset.includes = graphIncludes; GatherDescendentsFromGraph(new GUID(asset.assetGuid), out var containsCircularDependency, out var descendents); asset.descendents.AddRange(descendents.Select(g => g.ToString())); asset.descendents.Sort(); // ensure deterministic order var childrenSet = new HashSet <string>(); var anyErrors = false; foreach (var node in nodes) { if (node is SubGraphNode subGraphNode) { var subGraphGuid = subGraphNode.subGraphGuid; childrenSet.Add(subGraphGuid); } if (node.hasError) { anyErrors = true; } asset.children = childrenSet.ToList(); asset.children.Sort(); // ensure deterministic order } if (!anyErrors && containsCircularDependency) { Debug.LogError($"Error in Graph at {assetPath}: Sub Graph contains a circular dependency.", asset); anyErrors = true; } if (anyErrors) { asset.isValid = false; registry.ProvideFunction(asset.functionName, sb => {}); return; } foreach (var node in nodes) { if (node is IGeneratesFunction generatesFunction) { registry.builder.currentNode = node; generatesFunction.GenerateNodeFunction(registry, GenerationMode.ForReals); } } // provide top level subgraph function // NOTE: actual concrete precision here shouldn't matter, it's irrelevant when building the subgraph asset registry.ProvideFunction(asset.functionName, asset.subGraphGraphPrecision, ConcretePrecision.Single, sb => { GenerationUtils.GenerateSurfaceInputStruct(sb, asset.requirements, asset.inputStructName); sb.AppendNewLine(); // Generate the arguments... first INPUTS var arguments = new List <string>(); foreach (var prop in graph.properties) { // apply fallback to the graph default precision (but don't convert to concrete) // this means "graph switchable" properties will use the precision token GraphPrecision propGraphPrecision = prop.precision.ToGraphPrecision(graph.graphDefaultPrecision); string precisionString = propGraphPrecision.ToGenericString(); arguments.Add(prop.GetPropertyAsArgumentString(precisionString)); if (prop.isConnectionTestable) { arguments.Add($"bool {prop.GetConnectionStateHLSLVariableName()}"); } } { var dropdowns = graph.dropdowns; foreach (var dropdown in dropdowns) { arguments.Add($"int {dropdown.referenceName}"); } } // now pass surface inputs arguments.Add(string.Format("{0} IN", asset.inputStructName)); // Now generate output arguments foreach (MaterialSlot output in outputSlots) { arguments.Add($"out {output.concreteValueType.ToShaderString(asset.outputGraphPrecision.ToGenericString())} {output.shaderOutputName}_{output.id}"); } // Vt Feedback output arguments (always full float4) foreach (var output in asset.vtFeedbackVariables) { arguments.Add($"out {ConcreteSlotValueType.Vector4.ToShaderString(ConcretePrecision.Single)} {output}_out"); } // Create the function prototype from the arguments sb.AppendLine("void {0}({1})" , asset.functionName , arguments.Aggregate((current, next) => $"{current}, {next}")); // now generate the function using (sb.BlockScope()) { // Just grab the body from the active nodes foreach (var node in nodes) { if (node is IGeneratesBodyCode generatesBodyCode) { sb.currentNode = node; generatesBodyCode.GenerateNodeCode(sb, GenerationMode.ForReals); if (node.graphPrecision == GraphPrecision.Graph) { // code generated by nodes that use graph precision stays in generic form with embedded tokens // those tokens are replaced when this subgraph function is pulled into a graph that defines the precision } else { sb.ReplaceInCurrentMapping(PrecisionUtil.Token, node.concretePrecision.ToShaderString()); } } } foreach (var slot in outputSlots) { sb.AppendLine($"{slot.shaderOutputName}_{slot.id} = {outputNode.GetSlotValue(slot.id, GenerationMode.ForReals)};"); } foreach (var slot in asset.vtFeedbackVariables) { sb.AppendLine($"{slot}_out = {slot};"); } } }); // save all of the node-declared functions to the subgraph asset foreach (var name in registry.names) { var source = registry.sources[name]; var func = new FunctionPair(name, source.code, source.graphPrecisionFlags); asset.functions.Add(func); } var collector = new PropertyCollector(); foreach (var node in nodes) { int previousPropertyCount = Math.Max(0, collector.propertyCount - 1); node.CollectShaderProperties(collector, GenerationMode.ForReals); // This is a stop-gap to prevent the autogenerated values from JsonObject and ShaderInput from // resulting in non-deterministic import data. While we should move to local ids in the future, // this will prevent cascading shader recompilations. for (int i = previousPropertyCount; i < collector.propertyCount; ++i) { var prop = collector.GetProperty(i); var namespaceId = node.objectId; var nameId = prop.referenceName; prop.OverrideObjectId(namespaceId, nameId + "_ObjectId_" + i); prop.OverrideGuid(namespaceId, nameId + "_Guid_" + i); } } asset.WriteData(graph.properties, graph.keywords, graph.dropdowns, collector.properties, outputSlots, graph.unsupportedTargets); outputSlots.Dispose(); }
public static bool GenerateShaderPass(AbstractMaterialNode masterNode, Pass pass, GenerationMode mode, SurfaceMaterialOptions materialOptions, HashSet <string> activeFields, ShaderGenerator result, List <string> sourceAssetDependencyPaths, bool vertexActive) { string templatePath = Path.Combine(HDUtils.GetHDRenderPipelinePath(), "Editor/Material"); string templateLocation = Path.Combine(Path.Combine(Path.Combine(templatePath, pass.MaterialName), "ShaderGraph"), pass.TemplateName); if (!File.Exists(templateLocation)) { // TODO: produce error here Debug.LogError("Template not found: " + templateLocation); return(false); } bool debugOutput = false; // grab all of the active nodes (for pixel and vertex graphs) var vertexNodes = ListPool <INode> .Get(); NodeUtils.DepthFirstCollectNodesFromNode(vertexNodes, masterNode, NodeUtils.IncludeSelf.Include, pass.VertexShaderSlots); var pixelNodes = ListPool <INode> .Get(); NodeUtils.DepthFirstCollectNodesFromNode(pixelNodes, masterNode, NodeUtils.IncludeSelf.Include, pass.PixelShaderSlots); // graph requirements describe what the graph itself requires var pixelRequirements = ShaderGraphRequirements.FromNodes(pixelNodes, ShaderStageCapability.Fragment, false); // TODO: is ShaderStageCapability.Fragment correct? var vertexRequirements = ShaderGraphRequirements.FromNodes(vertexNodes, ShaderStageCapability.Vertex, false); var graphRequirements = pixelRequirements.Union(vertexRequirements); // Function Registry tracks functions to remove duplicates, it wraps a string builder that stores the combined function string ShaderStringBuilder graphNodeFunctions = new ShaderStringBuilder(); graphNodeFunctions.IncreaseIndent(); var functionRegistry = new FunctionRegistry(graphNodeFunctions); // TODO: this can be a shared function for all HDRP master nodes -- From here through GraphUtil.GenerateSurfaceDescription(..) // Build the list of active slots based on what the pass requires var pixelSlots = HDSubShaderUtilities.FindMaterialSlotsOnNode(pass.PixelShaderSlots, masterNode); var vertexSlots = HDSubShaderUtilities.FindMaterialSlotsOnNode(pass.VertexShaderSlots, masterNode); // properties used by either pixel and vertex shader PropertyCollector sharedProperties = new PropertyCollector(); // build the graph outputs structure to hold the results of each active slots (and fill out activeFields to indicate they are active) string pixelGraphInputStructName = "SurfaceDescriptionInputs"; string pixelGraphOutputStructName = "SurfaceDescription"; string pixelGraphEvalFunctionName = "SurfaceDescriptionFunction"; ShaderStringBuilder pixelGraphEvalFunction = new ShaderStringBuilder(); ShaderStringBuilder pixelGraphOutputs = new ShaderStringBuilder(); // build initial requirements HDRPShaderStructs.AddActiveFieldsFromPixelGraphRequirements(activeFields, pixelRequirements); // build the graph outputs structure, and populate activeFields with the fields of that structure GraphUtil.GenerateSurfaceDescriptionStruct(pixelGraphOutputs, pixelSlots, true, pixelGraphOutputStructName, activeFields); // Build the graph evaluation code, to evaluate the specified slots GraphUtil.GenerateSurfaceDescriptionFunction( pixelNodes, masterNode, masterNode.owner as AbstractMaterialGraph, pixelGraphEvalFunction, functionRegistry, sharedProperties, pixelRequirements, // TODO : REMOVE UNUSED mode, pixelGraphEvalFunctionName, pixelGraphOutputStructName, null, pixelSlots, pixelGraphInputStructName); string vertexGraphInputStructName = "VertexDescriptionInputs"; string vertexGraphOutputStructName = "VertexDescription"; string vertexGraphEvalFunctionName = "VertexDescriptionFunction"; ShaderStringBuilder vertexGraphEvalFunction = new ShaderStringBuilder(); ShaderStringBuilder vertexGraphOutputs = new ShaderStringBuilder(); // check for vertex animation -- enables HAVE_VERTEX_MODIFICATION if (vertexActive) { vertexActive = true; activeFields.Add("features.modifyMesh"); HDRPShaderStructs.AddActiveFieldsFromVertexGraphRequirements(activeFields, vertexRequirements); // ------------------------------------- // Generate Output structure for Vertex Description function GraphUtil.GenerateVertexDescriptionStruct(vertexGraphOutputs, vertexSlots, vertexGraphOutputStructName, activeFields); // ------------------------------------- // Generate Vertex Description function GraphUtil.GenerateVertexDescriptionFunction( masterNode.owner as AbstractMaterialGraph, vertexGraphEvalFunction, functionRegistry, sharedProperties, mode, vertexNodes, vertexSlots, vertexGraphInputStructName, vertexGraphEvalFunctionName, vertexGraphOutputStructName); } var blendCode = new ShaderStringBuilder(); var cullCode = new ShaderStringBuilder(); var zTestCode = new ShaderStringBuilder(); var zWriteCode = new ShaderStringBuilder(); var zClipCode = new ShaderStringBuilder(); var stencilCode = new ShaderStringBuilder(); var colorMaskCode = new ShaderStringBuilder(); HDSubShaderUtilities.BuildRenderStatesFromPassAndMaterialOptions(pass, materialOptions, blendCode, cullCode, zTestCode, zWriteCode, zClipCode, stencilCode, colorMaskCode); HDRPShaderStructs.AddRequiredFields(pass.RequiredFields, activeFields); // propagate active field requirements using dependencies ShaderSpliceUtil.ApplyDependencies( activeFields, new List <Dependency[]>() { HDRPShaderStructs.FragInputs.dependencies, HDRPShaderStructs.VaryingsMeshToPS.standardDependencies, HDRPShaderStructs.SurfaceDescriptionInputs.dependencies, HDRPShaderStructs.VertexDescriptionInputs.dependencies }); // debug output all active fields var interpolatorDefines = new ShaderGenerator(); if (debugOutput) { interpolatorDefines.AddShaderChunk("// ACTIVE FIELDS:"); foreach (string f in activeFields) { interpolatorDefines.AddShaderChunk("// " + f); } } // build graph inputs structures ShaderGenerator pixelGraphInputs = new ShaderGenerator(); ShaderSpliceUtil.BuildType(typeof(HDRPShaderStructs.SurfaceDescriptionInputs), activeFields, pixelGraphInputs); ShaderGenerator vertexGraphInputs = new ShaderGenerator(); ShaderSpliceUtil.BuildType(typeof(HDRPShaderStructs.VertexDescriptionInputs), activeFields, vertexGraphInputs); ShaderGenerator defines = new ShaderGenerator(); { defines.AddShaderChunk(string.Format("#define SHADERPASS {0}", pass.ShaderPassName), true); if (pass.ExtraDefines != null) { foreach (var define in pass.ExtraDefines) { defines.AddShaderChunk(define); } } if (graphRequirements.requiresDepthTexture) { defines.AddShaderChunk("#define REQUIRE_DEPTH_TEXTURE"); } defines.AddGenerator(interpolatorDefines); } var shaderPassIncludes = new ShaderGenerator(); if (pass.Includes != null) { foreach (var include in pass.Includes) { shaderPassIncludes.AddShaderChunk(include); } } // build graph code var graph = new ShaderGenerator(); { graph.AddShaderChunk("// Shared Graph Properties (uniform inputs)"); graph.AddShaderChunk(sharedProperties.GetPropertiesDeclaration(1)); if (vertexActive) { graph.AddShaderChunk("// Vertex Graph Inputs"); graph.Indent(); graph.AddGenerator(vertexGraphInputs); graph.Deindent(); graph.AddShaderChunk("// Vertex Graph Outputs"); graph.Indent(); graph.AddShaderChunk(vertexGraphOutputs.ToString()); graph.Deindent(); } graph.AddShaderChunk("// Pixel Graph Inputs"); graph.Indent(); graph.AddGenerator(pixelGraphInputs); graph.Deindent(); graph.AddShaderChunk("// Pixel Graph Outputs"); graph.Indent(); graph.AddShaderChunk(pixelGraphOutputs.ToString()); graph.Deindent(); graph.AddShaderChunk("// Shared Graph Node Functions"); graph.AddShaderChunk(graphNodeFunctions.ToString()); if (vertexActive) { graph.AddShaderChunk("// Vertex Graph Evaluation"); graph.Indent(); graph.AddShaderChunk(vertexGraphEvalFunction.ToString()); graph.Deindent(); } graph.AddShaderChunk("// Pixel Graph Evaluation"); graph.Indent(); graph.AddShaderChunk(pixelGraphEvalFunction.ToString()); graph.Deindent(); } // build the hash table of all named fragments TODO: could make this Dictionary<string, ShaderGenerator / string> ? Dictionary <string, string> namedFragments = new Dictionary <string, string>(); namedFragments.Add("Defines", defines.GetShaderString(2, false)); namedFragments.Add("Graph", graph.GetShaderString(2, false)); namedFragments.Add("LightMode", pass.LightMode); namedFragments.Add("PassName", pass.Name); namedFragments.Add("Includes", shaderPassIncludes.GetShaderString(2, false)); namedFragments.Add("Blending", blendCode.ToString()); namedFragments.Add("Culling", cullCode.ToString()); namedFragments.Add("ZTest", zTestCode.ToString()); namedFragments.Add("ZWrite", zWriteCode.ToString()); namedFragments.Add("ZClip", zClipCode.ToString()); namedFragments.Add("Stencil", stencilCode.ToString()); namedFragments.Add("ColorMask", colorMaskCode.ToString()); namedFragments.Add("LOD", materialOptions.lod.ToString()); // this is the format string for building the 'C# qualified assembly type names' for $buildType() commands string buildTypeAssemblyNameFormat = "UnityEditor.Experimental.Rendering.HDPipeline.HDRPShaderStructs+{0}, " + typeof(HDSubShaderUtilities).Assembly.FullName.ToString(); string sharedTemplatePath = Path.Combine(Path.Combine(HDUtils.GetHDRenderPipelinePath(), "Editor"), "ShaderGraph"); // process the template to generate the shader code for this pass ShaderSpliceUtil.TemplatePreprocessor templatePreprocessor = new ShaderSpliceUtil.TemplatePreprocessor(activeFields, namedFragments, debugOutput, sharedTemplatePath, sourceAssetDependencyPaths, buildTypeAssemblyNameFormat); templatePreprocessor.ProcessTemplateFile(templateLocation); result.AddShaderChunk(templatePreprocessor.GetShaderCode().ToString(), false); return(true); }
static void ProcessSubGraph(SubGraphAsset asset, GraphData graph) { var registry = new FunctionRegistry(new ShaderStringBuilder(), true); registry.names.Clear(); asset.functions.Clear(); asset.nodeProperties.Clear(); asset.isValid = true; graph.OnEnable(); graph.messageManager.ClearAll(); graph.ValidateGraph(); var assetPath = AssetDatabase.GUIDToAssetPath(asset.assetGuid); asset.hlslName = NodeUtils.GetHLSLSafeName(Path.GetFileNameWithoutExtension(assetPath)); asset.inputStructName = $"Bindings_{asset.hlslName}_{asset.assetGuid}"; asset.functionName = $"SG_{asset.hlslName}_{asset.assetGuid}"; asset.path = graph.path; var outputNode = (SubGraphOutputNode)graph.outputNode; asset.outputs.Clear(); outputNode.GetInputSlots(asset.outputs); List <AbstractMaterialNode> nodes = new List <AbstractMaterialNode>(); NodeUtils.DepthFirstCollectNodesFromNode(nodes, outputNode); asset.effectiveShaderStage = ShaderStageCapability.All; foreach (var slot in asset.outputs) { var stage = NodeUtils.GetEffectiveShaderStageCapability(slot, true); if (stage != ShaderStageCapability.All) { asset.effectiveShaderStage = stage; break; } } asset.requirements = ShaderGraphRequirements.FromNodes(nodes, asset.effectiveShaderStage, false); asset.inputs = graph.properties.ToList(); asset.graphPrecision = graph.concretePrecision; asset.outputPrecision = outputNode.concretePrecision; GatherFromGraph(assetPath, out var containsCircularDependency, out var descendents); asset.descendents.AddRange(descendents); var childrenSet = new HashSet <string>(); var anyErrors = false; foreach (var node in nodes) { if (node is SubGraphNode subGraphNode) { var subGraphGuid = subGraphNode.subGraphGuid; if (childrenSet.Add(subGraphGuid)) { asset.children.Add(subGraphGuid); } } if (node.hasError) { anyErrors = true; } } if (!anyErrors && containsCircularDependency) { Debug.LogError($"Error in Graph at {assetPath}: Sub Graph contains a circular dependency.", asset); anyErrors = true; } if (anyErrors) { asset.isValid = false; registry.ProvideFunction(asset.functionName, sb => { }); return; } foreach (var node in nodes) { if (node is IGeneratesFunction generatesFunction) { registry.builder.currentNode = node; generatesFunction.GenerateNodeFunction(registry, new GraphContext(asset.inputStructName), GenerationMode.ForReals); registry.builder.ReplaceInCurrentMapping(PrecisionUtil.Token, node.concretePrecision.ToShaderString()); } } registry.ProvideFunction(asset.functionName, sb => { var graphContext = new GraphContext(asset.inputStructName); GraphUtil.GenerateSurfaceInputStruct(sb, asset.requirements, asset.inputStructName); sb.AppendNewLine(); // Generate arguments... first INPUTS var arguments = new List <string>(); foreach (var prop in asset.inputs) { prop.ValidateConcretePrecision(asset.graphPrecision); arguments.Add(string.Format("{0}", prop.GetPropertyAsArgumentString())); } // now pass surface inputs arguments.Add(string.Format("{0} IN", asset.inputStructName)); // Now generate outputs foreach (var output in asset.outputs) { arguments.Add($"out {output.concreteValueType.ToShaderString(asset.outputPrecision)} {output.shaderOutputName}_{output.id}"); } // Create the function prototype from the arguments sb.AppendLine("void {0}({1})" , asset.functionName , arguments.Aggregate((current, next) => $"{current}, {next}")); // now generate the function using (sb.BlockScope()) { // Just grab the body from the active nodes foreach (var node in nodes) { if (node is IGeneratesBodyCode generatesBodyCode) { sb.currentNode = node; generatesBodyCode.GenerateNodeCode(sb, graphContext, GenerationMode.ForReals); sb.ReplaceInCurrentMapping(PrecisionUtil.Token, node.concretePrecision.ToShaderString()); } } foreach (var slot in asset.outputs) { sb.AppendLine($"{slot.shaderOutputName}_{slot.id} = {outputNode.GetSlotValue(slot.id, GenerationMode.ForReals, asset.outputPrecision)};"); } } }); asset.functions.AddRange(registry.names.Select(x => new FunctionPair(x, registry.sources[x]))); var collector = new PropertyCollector(); asset.nodeProperties = collector.properties; foreach (var node in nodes) { node.CollectShaderProperties(collector, GenerationMode.ForReals); } asset.OnBeforeSerialize(); }
public void CanGetPermutationMapPerNode() { var previewNode = m_Graph.GetNodes <PreviewNode>().FirstOrDefault(); Assert.IsNotNull(previewNode, "Preview Node not in graph."); var descendentNodes = new List <AbstractMaterialNode>(); NodeUtils.DepthFirstCollectNodesFromNode(descendentNodes, previewNode, NodeUtils.IncludeSelf.Include); List <int>[] keywordPermutationsPerNode = new List <int> [descendentNodes.Count]; Assert.IsNotEmpty(descendentNodes, "No Nodes in graph."); for (int i = 0; i < m_Collector.permutations.Count; i++) { var localNodes = ListPool <AbstractMaterialNode> .Get(); NodeUtils.DepthFirstCollectNodesFromNode(localNodes, previewNode, NodeUtils.IncludeSelf.Include, keywordPermutation: m_Collector.permutations[i]); foreach (AbstractMaterialNode node in localNodes) { int nodeIndex = descendentNodes.IndexOf(node); if (keywordPermutationsPerNode[nodeIndex] == null) { keywordPermutationsPerNode[nodeIndex] = new List <int>(); } keywordPermutationsPerNode[nodeIndex].Add(i); } } ShaderKeyword booleanAKeyword = m_Collector.keywords.Where(x => x.displayName == "Boolean A").FirstOrDefault(); ShaderKeyword booleanBKeyword = m_Collector.keywords.Where(x => x.displayName == "Boolean B").FirstOrDefault(); ShaderKeyword enumAKeyword = m_Collector.keywords.Where(x => x.displayName == "Enum A").FirstOrDefault(); ShaderKeyword enumBKeyword = m_Collector.keywords.Where(x => x.displayName == "Enum B").FirstOrDefault(); if (booleanAKeyword == null || booleanBKeyword == null || enumAKeyword == null || enumBKeyword == null) { Assert.Fail("One or more Keywords not in graph."); } var keywordNodes = m_Graph.GetNodes <KeywordNode>().ToList(); KeywordNode booleanANode = keywordNodes.Where(x => x.keyword == booleanAKeyword).FirstOrDefault(); KeywordNode booleanBNode = keywordNodes.Where(x => x.keyword == booleanBKeyword).FirstOrDefault(); KeywordNode enumANode = keywordNodes.Where(x => x.keyword == enumAKeyword).FirstOrDefault(); KeywordNode enumBNode = keywordNodes.Where(x => x.keyword == enumBKeyword).FirstOrDefault(); if (booleanANode == null || booleanBNode == null || enumANode == null || enumBNode == null) { Assert.Fail("One or more Keywords Nodes not in graph."); } int booleanAIndex = descendentNodes.IndexOf(booleanANode); List <int> booleanAPermutations = keywordPermutationsPerNode[booleanAIndex]; Assert.AreEqual(24, booleanAPermutations.Count, "Boolean A had incorrect permutations."); int booleanBIndex = descendentNodes.IndexOf(booleanBNode); List <int> booleanBPermutations = keywordPermutationsPerNode[booleanBIndex]; Assert.AreEqual(48, booleanBPermutations.Count, "Boolean B had incorrect permutations."); int enumAIndex = descendentNodes.IndexOf(enumANode); List <int> enumAPermutations = keywordPermutationsPerNode[enumAIndex]; Assert.AreEqual(12, enumAPermutations.Count, "Enum A had incorrect permutations."); int enumBIndex = descendentNodes.IndexOf(enumBNode); List <int> enumBPermutations = keywordPermutationsPerNode[enumBIndex]; Assert.AreEqual(24, enumBPermutations.Count, "Enum B had incorrect permutations."); }
static ShaderGraphVfxAsset GenerateVfxShaderGraphAsset(VfxMasterNode masterNode) { var nl = Environment.NewLine; var indent = new string(' ', 4); var asset = ScriptableObject.CreateInstance <ShaderGraphVfxAsset>(); var result = asset.compilationResult = new GraphCompilationResult(); var mode = GenerationMode.ForReals; var graph = masterNode.owner; asset.lit = masterNode.lit.isOn; var assetGuid = masterNode.owner.assetGuid; var assetPath = AssetDatabase.GUIDToAssetPath(assetGuid); var hlslName = NodeUtils.GetHLSLSafeName(Path.GetFileNameWithoutExtension(assetPath)); var ports = new List <MaterialSlot>(); masterNode.GetInputSlots(ports); var nodes = new List <AbstractMaterialNode>(); NodeUtils.DepthFirstCollectNodesFromNode(nodes, masterNode); var bodySb = new ShaderStringBuilder(1); var registry = new FunctionRegistry(new ShaderStringBuilder(), true); foreach (var properties in graph.properties) { properties.ValidateConcretePrecision(graph.concretePrecision); } foreach (var node in nodes) { if (node is IGeneratesBodyCode bodyGenerator) { bodySb.currentNode = node; bodyGenerator.GenerateNodeCode(bodySb, mode); bodySb.ReplaceInCurrentMapping(PrecisionUtil.Token, node.concretePrecision.ToShaderString()); } if (node is IGeneratesFunction generatesFunction) { registry.builder.currentNode = node; generatesFunction.GenerateNodeFunction(registry, mode); } } bodySb.currentNode = null; var portNodeSets = new HashSet <AbstractMaterialNode> [ports.Count]; for (var portIndex = 0; portIndex < ports.Count; portIndex++) { var port = ports[portIndex]; var nodeSet = new HashSet <AbstractMaterialNode>(); NodeUtils.CollectNodeSet(nodeSet, port); portNodeSets[portIndex] = nodeSet; } var portPropertySets = new HashSet <Guid> [ports.Count]; for (var portIndex = 0; portIndex < ports.Count; portIndex++) { portPropertySets[portIndex] = new HashSet <Guid>(); } foreach (var node in nodes) { if (!(node is PropertyNode propertyNode)) { continue; } for (var portIndex = 0; portIndex < ports.Count; portIndex++) { var portNodeSet = portNodeSets[portIndex]; if (portNodeSet.Contains(node)) { portPropertySets[portIndex].Add(propertyNode.propertyGuid); } } } var shaderProperties = new PropertyCollector(); foreach (var node in nodes) { node.CollectShaderProperties(shaderProperties, GenerationMode.ForReals); } asset.SetTextureInfos(shaderProperties.GetConfiguredTexutres()); var codeSnippets = new List <string>(); var portCodeIndices = new List <int> [ports.Count]; var sharedCodeIndices = new List <int>(); for (var i = 0; i < portCodeIndices.Length; i++) { portCodeIndices[i] = new List <int>(); } sharedCodeIndices.Add(codeSnippets.Count); codeSnippets.Add($"#include \"Packages/com.unity.shadergraph/ShaderGraphLibrary/Functions.hlsl\"{nl}"); for (var registryIndex = 0; registryIndex < registry.names.Count; registryIndex++) { var name = registry.names[registryIndex]; var source = registry.sources[name]; var precision = source.nodes.First().concretePrecision; var hasPrecisionMismatch = false; var nodeNames = new HashSet <string>(); foreach (var node in source.nodes) { nodeNames.Add(node.name); if (node.concretePrecision != precision) { hasPrecisionMismatch = true; break; } } if (hasPrecisionMismatch) { var message = new StringBuilder($"Precision mismatch for function {name}:"); foreach (var node in source.nodes) { message.AppendLine($"{node.name} ({node.guid}): {node.concretePrecision}"); } throw new InvalidOperationException(message.ToString()); } var code = source.code.Replace(PrecisionUtil.Token, precision.ToShaderString()); code = $"// Node: {string.Join(", ", nodeNames)}{nl}{code}"; var codeIndex = codeSnippets.Count; codeSnippets.Add(code + nl); for (var portIndex = 0; portIndex < ports.Count; portIndex++) { var portNodeSet = portNodeSets[portIndex]; foreach (var node in source.nodes) { if (portNodeSet.Contains(node)) { portCodeIndices[portIndex].Add(codeIndex); break; } } } } foreach (var property in graph.properties) { if (property.isExposable && property.generatePropertyBlock) { continue; } for (var portIndex = 0; portIndex < ports.Count; portIndex++) { var portPropertySet = portPropertySets[portIndex]; if (portPropertySet.Contains(property.guid)) { portCodeIndices[portIndex].Add(codeSnippets.Count); } } codeSnippets.Add($"// Property: {property.displayName}{nl}{property.GetPropertyDeclarationString()}{nl}{nl}"); } var inputStructName = $"SG_Input_{assetGuid}"; var outputStructName = $"SG_Output_{assetGuid}"; var evaluationFunctionName = $"SG_Evaluate_{assetGuid}"; #region Input Struct sharedCodeIndices.Add(codeSnippets.Count); codeSnippets.Add($"struct {inputStructName}{nl}{{{nl}"); #region Requirements var portRequirements = new ShaderGraphRequirements[ports.Count]; for (var portIndex = 0; portIndex < ports.Count; portIndex++) { portRequirements[portIndex] = ShaderGraphRequirements.FromNodes(portNodeSets[portIndex].ToList(), ports[portIndex].stageCapability); } var portIndices = new List <int>(); portIndices.Capacity = ports.Count; void AddRequirementsSnippet(Func <ShaderGraphRequirements, bool> predicate, string snippet) { portIndices.Clear(); for (var portIndex = 0; portIndex < ports.Count; portIndex++) { if (predicate(portRequirements[portIndex])) { portIndices.Add(portIndex); } } if (portIndices.Count > 0) { foreach (var portIndex in portIndices) { portCodeIndices[portIndex].Add(codeSnippets.Count); } codeSnippets.Add($"{indent}{snippet};{nl}"); } } void AddCoordinateSpaceSnippets(InterpolatorType interpolatorType, Func <ShaderGraphRequirements, NeededCoordinateSpace> selector) { foreach (var space in EnumInfo <CoordinateSpace> .values) { var neededSpace = space.ToNeededCoordinateSpace(); AddRequirementsSnippet(r => (selector(r) & neededSpace) > 0, $"float3 {space.ToVariableName(interpolatorType)}"); } } // TODO: Rework requirements system to make this better AddCoordinateSpaceSnippets(InterpolatorType.Normal, r => r.requiresNormal); AddCoordinateSpaceSnippets(InterpolatorType.Tangent, r => r.requiresTangent); AddCoordinateSpaceSnippets(InterpolatorType.BiTangent, r => r.requiresBitangent); AddCoordinateSpaceSnippets(InterpolatorType.ViewDirection, r => r.requiresViewDir); AddCoordinateSpaceSnippets(InterpolatorType.Position, r => r.requiresPosition); AddRequirementsSnippet(r => r.requiresVertexColor, $"float4 {ShaderGeneratorNames.VertexColor}"); AddRequirementsSnippet(r => r.requiresScreenPosition, $"float4 {ShaderGeneratorNames.ScreenPosition}"); AddRequirementsSnippet(r => r.requiresFaceSign, $"float4 {ShaderGeneratorNames.FaceSign}"); foreach (var uvChannel in EnumInfo <UVChannel> .values) { AddRequirementsSnippet(r => r.requiresMeshUVs.Contains(uvChannel), $"half4 {uvChannel.GetUVName()}"); } AddRequirementsSnippet(r => r.requiresTime, $"float3 {ShaderGeneratorNames.TimeParameters}"); #endregion sharedCodeIndices.Add(codeSnippets.Count); codeSnippets.Add($"}};{nl}{nl}"); #endregion #region Output Struct sharedCodeIndices.Add(codeSnippets.Count); codeSnippets.Add($"struct {outputStructName}{nl}{{"); for (var portIndex = 0; portIndex < ports.Count; portIndex++) { var port = ports[portIndex]; portCodeIndices[portIndex].Add(codeSnippets.Count); codeSnippets.Add($"{nl}{indent}{port.concreteValueType.ToShaderString(graph.concretePrecision)} {port.shaderOutputName}_{port.id};"); } sharedCodeIndices.Add(codeSnippets.Count); codeSnippets.Add($"{nl}}};{nl}{nl}"); #endregion #region Graph Function sharedCodeIndices.Add(codeSnippets.Count); codeSnippets.Add($"{outputStructName} {evaluationFunctionName}({nl}{indent}{inputStructName} IN"); var inputProperties = new List <AbstractShaderProperty>(); var portPropertyIndices = new List <int> [ports.Count]; for (var portIndex = 0; portIndex < ports.Count; portIndex++) { portPropertyIndices[portIndex] = new List <int>(); } foreach (var property in graph.properties) { if (!property.isExposable || !property.generatePropertyBlock) { continue; } var propertyIndex = inputProperties.Count; var codeIndex = codeSnippets.Count; for (var portIndex = 0; portIndex < ports.Count; portIndex++) { var portPropertySet = portPropertySets[portIndex]; if (portPropertySet.Contains(property.guid)) { portCodeIndices[portIndex].Add(codeIndex); portPropertyIndices[portIndex].Add(propertyIndex); } } inputProperties.Add(property); codeSnippets.Add($",{nl}{indent}/* Property: {property.displayName} */ {property.GetPropertyAsArgumentString()}"); } sharedCodeIndices.Add(codeSnippets.Count); codeSnippets.Add($"){nl}{{"); #region Node Code for (var mappingIndex = 0; mappingIndex < bodySb.mappings.Count; mappingIndex++) { var mapping = bodySb.mappings[mappingIndex]; var code = bodySb.ToString(mapping.startIndex, mapping.count); if (string.IsNullOrWhiteSpace(code)) { continue; } code = $"{nl}{indent}// Node: {mapping.node.name}{nl}{code}"; var codeIndex = codeSnippets.Count; codeSnippets.Add(code); for (var portIndex = 0; portIndex < ports.Count; portIndex++) { var portNodeSet = portNodeSets[portIndex]; if (portNodeSet.Contains(mapping.node)) { portCodeIndices[portIndex].Add(codeIndex); } } } #endregion #region Output Mapping sharedCodeIndices.Add(codeSnippets.Count); codeSnippets.Add($"{nl}{indent}// {masterNode.name}{nl}{indent}{outputStructName} OUT;{nl}"); // Output mapping for (var portIndex = 0; portIndex < ports.Count; portIndex++) { var port = ports[portIndex]; portCodeIndices[portIndex].Add(codeSnippets.Count); codeSnippets.Add($"{indent}OUT.{port.shaderOutputName}_{port.id} = {masterNode.GetSlotValue(port.id, GenerationMode.ForReals, graph.concretePrecision)};{nl}"); } #endregion // Function end sharedCodeIndices.Add(codeSnippets.Count); codeSnippets.Add($"{indent}return OUT;{nl}}}{nl}"); #endregion result.codeSnippets = codeSnippets.ToArray(); result.sharedCodeIndices = sharedCodeIndices.ToArray(); result.outputCodeIndices = new IntArray[ports.Count]; for (var i = 0; i < ports.Count; i++) { result.outputCodeIndices[i] = portCodeIndices[i].ToArray(); } asset.SetOutputs(ports.Select((t, i) => new OutputMetadata(i, t.shaderOutputName, t.id)).ToArray()); asset.evaluationFunctionName = evaluationFunctionName; asset.inputStructName = inputStructName; asset.outputStructName = outputStructName; asset.portRequirements = portRequirements; asset.concretePrecision = graph.concretePrecision; asset.SetProperties(inputProperties); asset.outputPropertyIndices = new IntArray[ports.Count]; for (var portIndex = 0; portIndex < ports.Count; portIndex++) { asset.outputPropertyIndices[portIndex] = portPropertyIndices[portIndex].ToArray(); } return(asset); }
static string GetShaderPassFromTemplate(string template, IMasterNode iMasterNode, Pass pass, GenerationMode mode, SurfaceMaterialOptions materialOptions) { // ----------------------------------------------------- // // SETUP // // ----------------------------------------------------- // AbstractMaterialNode masterNode = iMasterNode as AbstractMaterialNode; // ------------------------------------- // String builders var shaderProperties = new PropertyCollector(); var shaderKeywords = new KeywordCollector(); var shaderPropertyUniforms = new ShaderStringBuilder(1); var shaderKeywordDeclarations = new ShaderStringBuilder(1); var functionBuilder = new ShaderStringBuilder(1); var functionRegistry = new FunctionRegistry(functionBuilder); var defines = new ShaderStringBuilder(1); var graph = new ShaderStringBuilder(0); var vertexDescriptionInputStruct = new ShaderStringBuilder(1); var vertexDescriptionStruct = new ShaderStringBuilder(1); var vertexDescriptionFunction = new ShaderStringBuilder(1); var surfaceDescriptionInputStruct = new ShaderStringBuilder(1); var surfaceDescriptionStruct = new ShaderStringBuilder(1); var surfaceDescriptionFunction = new ShaderStringBuilder(1); var vertexInputStruct = new ShaderStringBuilder(1); var vertexOutputStruct = new ShaderStringBuilder(2); var vertexShader = new ShaderStringBuilder(2); var vertexShaderDescriptionInputs = new ShaderStringBuilder(2); var vertexShaderOutputs = new ShaderStringBuilder(2); var pixelShader = new ShaderStringBuilder(2); var pixelShaderSurfaceInputs = new ShaderStringBuilder(2); var pixelShaderSurfaceRemap = new ShaderStringBuilder(2); // ------------------------------------- // Get Slot and Node lists per stage var vertexSlots = pass.VertexShaderSlots.Select(masterNode.FindSlot <MaterialSlot>).ToList(); var vertexNodes = ListPool <AbstractMaterialNode> .Get(); NodeUtils.DepthFirstCollectNodesFromNode(vertexNodes, masterNode, NodeUtils.IncludeSelf.Include, pass.VertexShaderSlots); var pixelSlots = pass.PixelShaderSlots.Select(masterNode.FindSlot <MaterialSlot>).ToList(); var pixelNodes = ListPool <AbstractMaterialNode> .Get(); NodeUtils.DepthFirstCollectNodesFromNode(pixelNodes, masterNode, NodeUtils.IncludeSelf.Include, pass.PixelShaderSlots); // ------------------------------------- // Get Requirements var vertexRequirements = ShaderGraphRequirements.FromNodes(vertexNodes, ShaderStageCapability.Vertex, false); var pixelRequirements = ShaderGraphRequirements.FromNodes(pixelNodes, ShaderStageCapability.Fragment); var graphRequirements = pixelRequirements.Union(vertexRequirements); var surfaceRequirements = ShaderGraphRequirements.FromNodes(pixelNodes, ShaderStageCapability.Fragment, false); var modelRequirements = pass.Requirements; // ----------------------------------------------------- // // START SHADER GENERATION // // ----------------------------------------------------- // // ------------------------------------- // Calculate material options var blendingBuilder = new ShaderStringBuilder(1); var cullingBuilder = new ShaderStringBuilder(1); var zTestBuilder = new ShaderStringBuilder(1); var zWriteBuilder = new ShaderStringBuilder(1); materialOptions.GetBlend(blendingBuilder); materialOptions.GetCull(cullingBuilder); materialOptions.GetDepthTest(zTestBuilder); materialOptions.GetDepthWrite(zWriteBuilder); // ------------------------------------- // Generate defines pass.OnGeneratePass(iMasterNode, graphRequirements); foreach (string define in pass.ExtraDefines) { defines.AppendLine(define); } // ----------------------------------------------------- // // KEYWORDS // // ----------------------------------------------------- // // ------------------------------------- // Get keyword permutations masterNode.owner.CollectShaderKeywords(shaderKeywords, mode); // Track permutation indices for all nodes List <int>[] keywordPermutationsPerVertexNode = new List <int> [vertexNodes.Count]; List <int>[] keywordPermutationsPerPixelNode = new List <int> [pixelNodes.Count]; // ------------------------------------- // Evaluate all permutations for (int i = 0; i < shaderKeywords.permutations.Count; i++) { // Get active nodes for this permutation var localVertexNodes = ListPool <AbstractMaterialNode> .Get(); var localPixelNodes = ListPool <AbstractMaterialNode> .Get(); NodeUtils.DepthFirstCollectNodesFromNode(localVertexNodes, masterNode, NodeUtils.IncludeSelf.Include, pass.VertexShaderSlots, shaderKeywords.permutations[i]); NodeUtils.DepthFirstCollectNodesFromNode(localPixelNodes, masterNode, NodeUtils.IncludeSelf.Include, pass.PixelShaderSlots, shaderKeywords.permutations[i]); // Track each vertex node in this permutation foreach (AbstractMaterialNode vertexNode in localVertexNodes) { int nodeIndex = vertexNodes.IndexOf(vertexNode); if (keywordPermutationsPerVertexNode[nodeIndex] == null) { keywordPermutationsPerVertexNode[nodeIndex] = new List <int>(); } keywordPermutationsPerVertexNode[nodeIndex].Add(i); } // Track each pixel node in this permutation foreach (AbstractMaterialNode pixelNode in localPixelNodes) { int nodeIndex = pixelNodes.IndexOf(pixelNode); if (keywordPermutationsPerPixelNode[nodeIndex] == null) { keywordPermutationsPerPixelNode[nodeIndex] = new List <int>(); } keywordPermutationsPerPixelNode[nodeIndex].Add(i); } } // ----------------------------------------------------- // // START VERTEX DESCRIPTION // // ----------------------------------------------------- // // ------------------------------------- // Generate Input structure for Vertex Description function // TODO - Vertex Description Input requirements are needed to exclude intermediate translation spaces vertexDescriptionInputStruct.AppendLine("struct VertexDescriptionInputs"); using (vertexDescriptionInputStruct.BlockSemicolonScope()) { ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(vertexRequirements.requiresNormal, InterpolatorType.Normal, vertexDescriptionInputStruct); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(vertexRequirements.requiresTangent, InterpolatorType.Tangent, vertexDescriptionInputStruct); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(vertexRequirements.requiresBitangent, InterpolatorType.BiTangent, vertexDescriptionInputStruct); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(vertexRequirements.requiresViewDir, InterpolatorType.ViewDirection, vertexDescriptionInputStruct); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(vertexRequirements.requiresPosition, InterpolatorType.Position, vertexDescriptionInputStruct); if (vertexRequirements.requiresVertexColor) { vertexDescriptionInputStruct.AppendLine("float4 {0};", ShaderGeneratorNames.VertexColor); } if (vertexRequirements.requiresScreenPosition) { vertexDescriptionInputStruct.AppendLine("float4 {0};", ShaderGeneratorNames.ScreenPosition); } foreach (var channel in vertexRequirements.requiresMeshUVs.Distinct()) { vertexDescriptionInputStruct.AppendLine("half4 {0};", channel.GetUVName()); } if (vertexRequirements.requiresTime) { vertexDescriptionInputStruct.AppendLine("float3 {0};", ShaderGeneratorNames.TimeParameters); } } // ------------------------------------- // Generate Output structure for Vertex Description function GraphUtil.GenerateVertexDescriptionStruct(vertexDescriptionStruct, vertexSlots); // ------------------------------------- // Generate Vertex Description function GraphUtil.GenerateVertexDescriptionFunction( masterNode.owner as GraphData, vertexDescriptionFunction, functionRegistry, shaderProperties, shaderKeywords, mode, masterNode, vertexNodes, keywordPermutationsPerVertexNode, vertexSlots); // ----------------------------------------------------- // // START SURFACE DESCRIPTION // // ----------------------------------------------------- // // ------------------------------------- // Generate Input structure for Surface Description function // Surface Description Input requirements are needed to exclude intermediate translation spaces surfaceDescriptionInputStruct.AppendLine("struct SurfaceDescriptionInputs"); using (surfaceDescriptionInputStruct.BlockSemicolonScope()) { ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(surfaceRequirements.requiresNormal, InterpolatorType.Normal, surfaceDescriptionInputStruct); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(surfaceRequirements.requiresTangent, InterpolatorType.Tangent, surfaceDescriptionInputStruct); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(surfaceRequirements.requiresBitangent, InterpolatorType.BiTangent, surfaceDescriptionInputStruct); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(surfaceRequirements.requiresViewDir, InterpolatorType.ViewDirection, surfaceDescriptionInputStruct); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(surfaceRequirements.requiresPosition, InterpolatorType.Position, surfaceDescriptionInputStruct); if (surfaceRequirements.requiresVertexColor) { surfaceDescriptionInputStruct.AppendLine("float4 {0};", ShaderGeneratorNames.VertexColor); } if (surfaceRequirements.requiresScreenPosition) { surfaceDescriptionInputStruct.AppendLine("float4 {0};", ShaderGeneratorNames.ScreenPosition); } if (surfaceRequirements.requiresFaceSign) { surfaceDescriptionInputStruct.AppendLine("float {0};", ShaderGeneratorNames.FaceSign); } foreach (var channel in surfaceRequirements.requiresMeshUVs.Distinct()) { surfaceDescriptionInputStruct.AppendLine("half4 {0};", channel.GetUVName()); } if (surfaceRequirements.requiresTime) { surfaceDescriptionInputStruct.AppendLine("float3 {0};", ShaderGeneratorNames.TimeParameters); } } // ------------------------------------- // Generate Output structure for Surface Description function GraphUtil.GenerateSurfaceDescriptionStruct(surfaceDescriptionStruct, pixelSlots); // ------------------------------------- // Generate Surface Description function GraphUtil.GenerateSurfaceDescriptionFunction( pixelNodes, keywordPermutationsPerPixelNode, masterNode, masterNode.owner as GraphData, surfaceDescriptionFunction, functionRegistry, shaderProperties, shaderKeywords, mode, "PopulateSurfaceData", "SurfaceDescription", null, pixelSlots); // ----------------------------------------------------- // // GENERATE VERTEX > PIXEL PIPELINE // // ----------------------------------------------------- // // ------------------------------------- // Keyword declarations shaderKeywords.GetKeywordsDeclaration(shaderKeywordDeclarations, mode); // ------------------------------------- // Property uniforms shaderProperties.GetPropertiesDeclaration(shaderPropertyUniforms, mode, masterNode.owner.concretePrecision); // ------------------------------------- // Generate Input structure for Vertex shader GraphUtil.GenerateApplicationVertexInputs(vertexRequirements.Union(pixelRequirements.Union(modelRequirements)), vertexInputStruct); // ------------------------------------- // Generate standard transformations // This method ensures all required transform data is available in vertex and pixel stages ShaderGenerator.GenerateStandardTransforms( 3, 10, vertexOutputStruct, vertexShader, vertexShaderDescriptionInputs, vertexShaderOutputs, pixelShader, pixelShaderSurfaceInputs, pixelRequirements, surfaceRequirements, modelRequirements, vertexRequirements, CoordinateSpace.World); // ------------------------------------- // Generate pixel shader surface remap foreach (var slot in pixelSlots) { pixelShaderSurfaceRemap.AppendLine("{0} = surf.{0};", slot.shaderOutputName); } // ------------------------------------- // Extra pixel shader work var faceSign = new ShaderStringBuilder(); if (pixelRequirements.requiresFaceSign) { faceSign.AppendLine(", half FaceSign : VFACE"); } // ----------------------------------------------------- // // FINALIZE // // ----------------------------------------------------- // // ------------------------------------- // Combine Graph sections graph.AppendLines(shaderKeywordDeclarations.ToString()); graph.AppendLines(shaderPropertyUniforms.ToString()); graph.AppendLine(vertexDescriptionInputStruct.ToString()); graph.AppendLine(surfaceDescriptionInputStruct.ToString()); graph.AppendLine(functionBuilder.ToString()); graph.AppendLine(vertexDescriptionStruct.ToString()); graph.AppendLine(vertexDescriptionFunction.ToString()); graph.AppendLine(surfaceDescriptionStruct.ToString()); graph.AppendLine(surfaceDescriptionFunction.ToString()); graph.AppendLine(vertexInputStruct.ToString()); // ------------------------------------- // Generate final subshader var resultPass = template.Replace("${Tags}", string.Empty); resultPass = resultPass.Replace("${Blending}", blendingBuilder.ToString()); resultPass = resultPass.Replace("${Culling}", cullingBuilder.ToString()); resultPass = resultPass.Replace("${ZTest}", zTestBuilder.ToString()); resultPass = resultPass.Replace("${ZWrite}", zWriteBuilder.ToString()); resultPass = resultPass.Replace("${Defines}", defines.ToString()); resultPass = resultPass.Replace("${Graph}", graph.ToString()); resultPass = resultPass.Replace("${VertexOutputStruct}", vertexOutputStruct.ToString()); resultPass = resultPass.Replace("${VertexShader}", vertexShader.ToString()); resultPass = resultPass.Replace("${VertexShaderDescriptionInputs}", vertexShaderDescriptionInputs.ToString()); resultPass = resultPass.Replace("${VertexShaderOutputs}", vertexShaderOutputs.ToString()); resultPass = resultPass.Replace("${FaceSign}", faceSign.ToString()); resultPass = resultPass.Replace("${PixelShader}", pixelShader.ToString()); resultPass = resultPass.Replace("${PixelShaderSurfaceInputs}", pixelShaderSurfaceInputs.ToString()); resultPass = resultPass.Replace("${PixelShaderSurfaceRemap}", pixelShaderSurfaceRemap.ToString()); return(resultPass); }
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 GenerationResults GetShader(this AbstractMaterialGraph graph, AbstractMaterialNode node, GenerationMode mode, string name) { var results = new GenerationResults(); bool isUber = node == null; var vertexInputs = new ShaderGenerator(); var vertexShader = new ShaderGenerator(); var surfaceDescriptionFunction = new ShaderGenerator(); var surfaceDescriptionStruct = new ShaderGenerator(); var functionBuilder = new ShaderStringBuilder(); var functionRegistry = new FunctionRegistry(functionBuilder); var surfaceInputs = new ShaderGenerator(); surfaceInputs.AddShaderChunk("struct SurfaceInputs{", false); surfaceInputs.Indent(); var activeNodeList = ListPool <INode> .Get(); if (isUber) { var unmarkedNodes = graph.GetNodes <INode>().Where(x => !(x is IMasterNode)).ToDictionary(x => x.guid); while (unmarkedNodes.Any()) { var unmarkedNode = unmarkedNodes.FirstOrDefault(); Visit(activeNodeList, unmarkedNodes, unmarkedNode.Value); } } else { NodeUtils.DepthFirstCollectNodesFromNode(activeNodeList, node); } var requirements = ShaderGraphRequirements.FromNodes(activeNodeList); GenerateApplicationVertexInputs(requirements, vertexInputs); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(requirements.requiresNormal, InterpolatorType.Normal, surfaceInputs); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(requirements.requiresTangent, InterpolatorType.Tangent, surfaceInputs); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(requirements.requiresBitangent, InterpolatorType.BiTangent, surfaceInputs); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(requirements.requiresViewDir, InterpolatorType.ViewDirection, surfaceInputs); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(requirements.requiresPosition, InterpolatorType.Position, surfaceInputs); if (requirements.requiresVertexColor) { surfaceInputs.AddShaderChunk(String.Format("float4 {0};", ShaderGeneratorNames.VertexColor), false); } if (requirements.requiresScreenPosition) { surfaceInputs.AddShaderChunk(String.Format("float4 {0};", ShaderGeneratorNames.ScreenPosition), false); } results.previewMode = PreviewMode.Preview3D; if (!isUber) { foreach (var pNode in activeNodeList.OfType <AbstractMaterialNode>()) { if (pNode.previewMode == PreviewMode.Preview3D) { results.previewMode = PreviewMode.Preview3D; break; } } } foreach (var channel in requirements.requiresMeshUVs.Distinct()) { surfaceInputs.AddShaderChunk(String.Format("half4 {0};", channel.GetUVName()), false); } surfaceInputs.Deindent(); surfaceInputs.AddShaderChunk("};", false); vertexShader.AddShaderChunk("GraphVertexInput PopulateVertexData(GraphVertexInput v){", false); vertexShader.Indent(); vertexShader.AddShaderChunk("return v;", false); vertexShader.Deindent(); vertexShader.AddShaderChunk("}", false); var slots = new List <MaterialSlot>(); foreach (var activeNode in isUber ? activeNodeList.Where(n => ((AbstractMaterialNode)n).hasPreview) : ((INode)node).ToEnumerable()) { if (activeNode is IMasterNode) { slots.AddRange(activeNode.GetInputSlots <MaterialSlot>()); } else { slots.AddRange(activeNode.GetOutputSlots <MaterialSlot>()); } } GenerateSurfaceDescriptionStruct(surfaceDescriptionStruct, slots, !isUber); var shaderProperties = new PropertyCollector(); results.outputIdProperty = new Vector1ShaderProperty { displayName = "OutputId", generatePropertyBlock = false, value = -1 }; if (isUber) { shaderProperties.AddShaderProperty(results.outputIdProperty); } GenerateSurfaceDescription( activeNodeList, node, graph, surfaceDescriptionFunction, functionRegistry, shaderProperties, requirements, mode, outputIdProperty: results.outputIdProperty); var finalBuilder = new ShaderStringBuilder(); finalBuilder.AppendLine(@"Shader ""{0}""", name); using (finalBuilder.BlockScope()) { finalBuilder.AppendLine("Properties"); using (finalBuilder.BlockScope()) { finalBuilder.AppendLines(shaderProperties.GetPropertiesBlock(0)); } finalBuilder.AppendLine(@"HLSLINCLUDE"); finalBuilder.AppendLine("#define USE_LEGACY_UNITY_MATRIX_VARIABLES"); finalBuilder.AppendLine(@"#include ""CoreRP/ShaderLibrary/Common.hlsl"""); finalBuilder.AppendLine(@"#include ""CoreRP/ShaderLibrary/Packing.hlsl"""); finalBuilder.AppendLine(@"#include ""CoreRP/ShaderLibrary/Color.hlsl"""); finalBuilder.AppendLine(@"#include ""CoreRP/ShaderLibrary/UnityInstancing.hlsl"""); finalBuilder.AppendLine(@"#include ""CoreRP/ShaderLibrary/EntityLighting.hlsl"""); finalBuilder.AppendLine(@"#include ""ShaderGraphLibrary/ShaderVariables.hlsl"""); finalBuilder.AppendLine(@"#include ""ShaderGraphLibrary/ShaderVariablesFunctions.hlsl"""); finalBuilder.AppendLine(@"#include ""ShaderGraphLibrary/Functions.hlsl"""); finalBuilder.AppendLines(shaderProperties.GetPropertiesDeclaration(0)); finalBuilder.AppendLines(surfaceInputs.GetShaderString(0)); finalBuilder.Concat(functionBuilder); finalBuilder.AppendLines(vertexInputs.GetShaderString(0)); finalBuilder.AppendLines(surfaceDescriptionStruct.GetShaderString(0)); finalBuilder.AppendLines(vertexShader.GetShaderString(0)); finalBuilder.AppendLines(surfaceDescriptionFunction.GetShaderString(0)); finalBuilder.AppendLine(@"ENDHLSL"); finalBuilder.AppendLines(ShaderGenerator.GetPreviewSubShader(node, requirements)); ListPool <INode> .Release(activeNodeList); } results.configuredTextures = shaderProperties.GetConfiguredTexutres(); ShaderSourceMap sourceMap; results.shader = finalBuilder.ToString(out sourceMap); results.sourceMap = sourceMap; return(results); }
static void ProcessSubGraph(SubGraphAsset asset, GraphData graph) { var registry = new FunctionRegistry(new ShaderStringBuilder(), true); registry.names.Clear(); asset.functions.Clear(); asset.isValid = true; graph.OnEnable(); graph.messageManager.ClearAll(); graph.ValidateGraph(); var assetPath = AssetDatabase.GUIDToAssetPath(asset.assetGuid); asset.hlslName = NodeUtils.GetHLSLSafeName(Path.GetFileNameWithoutExtension(assetPath)); asset.inputStructName = $"Bindings_{asset.hlslName}_{asset.assetGuid}"; asset.functionName = $"SG_{asset.hlslName}_{asset.assetGuid}"; asset.path = graph.path; var outputNode = graph.outputNode; var outputSlots = PooledList <MaterialSlot> .Get(); outputNode.GetInputSlots(outputSlots); List <AbstractMaterialNode> nodes = new List <AbstractMaterialNode>(); NodeUtils.DepthFirstCollectNodesFromNode(nodes, outputNode); asset.effectiveShaderStage = ShaderStageCapability.All; foreach (var slot in outputSlots) { var stage = NodeUtils.GetEffectiveShaderStageCapability(slot, true); if (stage != ShaderStageCapability.All) { asset.effectiveShaderStage = stage; break; } } asset.vtFeedbackVariables = VirtualTexturingFeedbackUtils.GetFeedbackVariables(outputNode as SubGraphOutputNode); asset.requirements = ShaderGraphRequirements.FromNodes(nodes, asset.effectiveShaderStage, false); asset.graphPrecision = graph.concretePrecision; asset.outputPrecision = outputNode.concretePrecision; asset.previewMode = graph.previewMode; GatherDescendentsFromGraph(new GUID(asset.assetGuid), out var containsCircularDependency, out var descendents); asset.descendents.AddRange(descendents.Select(g => g.ToString())); asset.descendents.Sort(); // ensure deterministic order var childrenSet = new HashSet <string>(); var anyErrors = false; foreach (var node in nodes) { if (node is SubGraphNode subGraphNode) { var subGraphGuid = subGraphNode.subGraphGuid; childrenSet.Add(subGraphGuid); } if (node.hasError) { anyErrors = true; } asset.children = childrenSet.ToList(); asset.children.Sort(); // ensure deterministic order } if (!anyErrors && containsCircularDependency) { Debug.LogError($"Error in Graph at {assetPath}: Sub Graph contains a circular dependency.", asset); anyErrors = true; } if (anyErrors) { asset.isValid = false; registry.ProvideFunction(asset.functionName, sb => { }); return; } foreach (var node in nodes) { if (node is IGeneratesFunction generatesFunction) { registry.builder.currentNode = node; generatesFunction.GenerateNodeFunction(registry, GenerationMode.ForReals); registry.builder.ReplaceInCurrentMapping(PrecisionUtil.Token, node.concretePrecision.ToShaderString()); } } // provide top level subgraph function registry.ProvideFunction(asset.functionName, sb => { GenerationUtils.GenerateSurfaceInputStruct(sb, asset.requirements, asset.inputStructName); sb.AppendNewLine(); // Generate arguments... first INPUTS var arguments = new List <string>(); foreach (var prop in graph.properties) { prop.ValidateConcretePrecision(asset.graphPrecision); arguments.Add(prop.GetPropertyAsArgumentString()); } // now pass surface inputs arguments.Add(string.Format("{0} IN", asset.inputStructName)); // Now generate outputs foreach (MaterialSlot output in outputSlots) { arguments.Add($"out {output.concreteValueType.ToShaderString(asset.outputPrecision)} {output.shaderOutputName}_{output.id}"); } // Vt Feedback arguments foreach (var output in asset.vtFeedbackVariables) { arguments.Add($"out {ConcreteSlotValueType.Vector4.ToShaderString(ConcretePrecision.Single)} {output}_out"); } // Create the function prototype from the arguments sb.AppendLine("void {0}({1})" , asset.functionName , arguments.Aggregate((current, next) => $"{current}, {next}")); // now generate the function using (sb.BlockScope()) { // Just grab the body from the active nodes foreach (var node in nodes) { if (node is IGeneratesBodyCode generatesBodyCode) { sb.currentNode = node; generatesBodyCode.GenerateNodeCode(sb, GenerationMode.ForReals); sb.ReplaceInCurrentMapping(PrecisionUtil.Token, node.concretePrecision.ToShaderString()); } } foreach (var slot in outputSlots) { sb.AppendLine($"{slot.shaderOutputName}_{slot.id} = {outputNode.GetSlotValue(slot.id, GenerationMode.ForReals, asset.outputPrecision)};"); } foreach (var slot in asset.vtFeedbackVariables) { sb.AppendLine($"{slot}_out = {slot};"); } } }); asset.functions.AddRange(registry.names.Select(x => new FunctionPair(x, registry.sources[x].code))); var collector = new PropertyCollector(); foreach (var node in nodes) { int previousPropertyCount = Math.Max(0, collector.properties.Count - 1); node.CollectShaderProperties(collector, GenerationMode.ForReals); // This is a stop-gap to prevent the autogenerated values from JsonObject and ShaderInput from // resulting in non-deterministic import data. While we should move to local ids in the future, // this will prevent cascading shader recompilations. for (int i = previousPropertyCount; i < collector.properties.Count; ++i) { var prop = collector.properties[i]; var namespaceId = node.objectId; var nameId = prop.referenceName; prop.OverrideObjectId(namespaceId, nameId + "_ObjectId_" + i); prop.OverrideGuid(namespaceId, nameId + "_Guid_" + i); } } asset.WriteData(graph.properties, graph.keywords, collector.properties, outputSlots, graph.unsupportedTargets); outputSlots.Dispose(); }
private static bool GenerateShaderPassLit(AbstractMaterialNode masterNode, Pass pass, GenerationMode mode, SurfaceMaterialOptions materialOptions, ShaderGenerator result, List <string> sourceAssetDependencyPaths) { var templateLocation = Path.Combine(Path.Combine(Path.Combine(HDEditorUtils.GetHDRenderPipelinePath(), "Editor"), "ShaderGraph"), pass.TemplateName); if (!File.Exists(templateLocation)) { // TODO: produce error here return(false); } if (sourceAssetDependencyPaths != null) { sourceAssetDependencyPaths.Add(templateLocation); } // grab all of the active nodes (for pixel and vertex graphs) var vertexNodes = ListPool <INode> .Get(); NodeUtils.DepthFirstCollectNodesFromNode(vertexNodes, masterNode, NodeUtils.IncludeSelf.Include, pass.VertexShaderSlots); var pixelNodes = ListPool <INode> .Get(); NodeUtils.DepthFirstCollectNodesFromNode(pixelNodes, masterNode, NodeUtils.IncludeSelf.Include, pass.PixelShaderSlots); // graph requirements describe what the graph itself requires var pixelRequirements = ShaderGraphRequirements.FromNodes(pixelNodes, ShaderStageCapability.Fragment, false); // TODO: is ShaderStageCapability.Fragment correct? var vertexRequirements = ShaderGraphRequirements.FromNodes(vertexNodes, ShaderStageCapability.Vertex, false); // Function Registry tracks functions to remove duplicates, it wraps a string builder that stores the combined function string ShaderStringBuilder graphNodeFunctions = new ShaderStringBuilder(); graphNodeFunctions.IncreaseIndent(); var functionRegistry = new FunctionRegistry(graphNodeFunctions); // TODO: this can be a shared function for all HDRP master nodes -- From here through GraphUtil.GenerateSurfaceDescription(..) // Build the list of active slots based on what the pass requires var pixelSlots = HDSubShaderUtilities.FindMaterialSlotsOnNode(pass.PixelShaderSlots, masterNode); var vertexSlots = HDSubShaderUtilities.FindMaterialSlotsOnNode(pass.VertexShaderSlots, masterNode); // properties used by either pixel and vertex shader PropertyCollector sharedProperties = new PropertyCollector(); // build the graph outputs structure to hold the results of each active slots (and fill out activeFields to indicate they are active) string pixelGraphInputStructName = "SurfaceDescriptionInputs"; string pixelGraphOutputStructName = "SurfaceDescription"; string pixelGraphEvalFunctionName = "SurfaceDescriptionFunction"; ShaderStringBuilder pixelGraphEvalFunction = new ShaderStringBuilder(); ShaderStringBuilder pixelGraphOutputs = new ShaderStringBuilder(); // dependency tracker -- set of active fields HashSet <string> activeFields = GetActiveFieldsFromMasterNode(masterNode, pass); // build initial requirements HDRPShaderStructs.AddActiveFieldsFromPixelGraphRequirements(activeFields, pixelRequirements); // build the graph outputs structure, and populate activeFields with the fields of that structure GraphUtil.GenerateSurfaceDescriptionStruct(pixelGraphOutputs, pixelSlots, true, pixelGraphOutputStructName, activeFields); // Build the graph evaluation code, to evaluate the specified slots GraphUtil.GenerateSurfaceDescriptionFunction( pixelNodes, masterNode, masterNode.owner as AbstractMaterialGraph, pixelGraphEvalFunction, functionRegistry, sharedProperties, pixelRequirements, // TODO : REMOVE UNUSED mode, pixelGraphEvalFunctionName, pixelGraphOutputStructName, null, pixelSlots, pixelGraphInputStructName); string vertexGraphInputStructName = "VertexDescriptionInputs"; string vertexGraphOutputStructName = "VertexDescription"; string vertexGraphEvalFunctionName = "VertexDescriptionFunction"; ShaderStringBuilder vertexGraphEvalFunction = new ShaderStringBuilder(); ShaderStringBuilder vertexGraphOutputs = new ShaderStringBuilder(); // check for vertex animation -- enables HAVE_VERTEX_MODIFICATION bool vertexActive = false; if (masterNode.IsSlotConnected(PBRMasterNode.PositionSlotId)) { vertexActive = true; activeFields.Add("features.modifyMesh"); HDRPShaderStructs.AddActiveFieldsFromVertexGraphRequirements(activeFields, vertexRequirements); // ------------------------------------- // Generate Output structure for Vertex Description function GraphUtil.GenerateVertexDescriptionStruct(vertexGraphOutputs, vertexSlots, vertexGraphOutputStructName, activeFields); // ------------------------------------- // Generate Vertex Description function GraphUtil.GenerateVertexDescriptionFunction( masterNode.owner as AbstractMaterialGraph, vertexGraphEvalFunction, functionRegistry, sharedProperties, mode, vertexNodes, vertexSlots, vertexGraphInputStructName, vertexGraphEvalFunctionName, vertexGraphOutputStructName); } var blendCode = new ShaderStringBuilder(); var cullCode = new ShaderStringBuilder(); var zTestCode = new ShaderStringBuilder(); var zWriteCode = new ShaderStringBuilder(); var stencilCode = new ShaderStringBuilder(); var colorMaskCode = new ShaderStringBuilder(); HDSubShaderUtilities.BuildRenderStatesFromPassAndMaterialOptions(pass, materialOptions, blendCode, cullCode, zTestCode, zWriteCode, stencilCode, colorMaskCode); HDRPShaderStructs.AddRequiredFields(pass.RequiredFields, activeFields); // apply dependencies to the active fields, and build interpolators (TODO: split this function) var packedInterpolatorCode = new ShaderGenerator(); HDRPShaderStructs.Generate( packedInterpolatorCode, activeFields); // debug output all active fields var interpolatorDefines = new ShaderGenerator(); { interpolatorDefines.AddShaderChunk("// ACTIVE FIELDS:"); foreach (string f in activeFields) { interpolatorDefines.AddShaderChunk("// " + f); } } // build graph inputs structures ShaderGenerator pixelGraphInputs = new ShaderGenerator(); ShaderSpliceUtil.BuildType(typeof(HDRPShaderStructs.SurfaceDescriptionInputs), activeFields, pixelGraphInputs); ShaderGenerator vertexGraphInputs = new ShaderGenerator(); ShaderSpliceUtil.BuildType(typeof(HDRPShaderStructs.VertexDescriptionInputs), activeFields, vertexGraphInputs); ShaderGenerator defines = new ShaderGenerator(); { defines.AddShaderChunk(string.Format("#define SHADERPASS {0}", pass.ShaderPassName), true); if (pass.ExtraDefines != null) { foreach (var define in pass.ExtraDefines) { defines.AddShaderChunk(define); } } defines.AddGenerator(interpolatorDefines); } var shaderPassIncludes = new ShaderGenerator(); if (pass.Includes != null) { foreach (var include in pass.Includes) { shaderPassIncludes.AddShaderChunk(include); } } // build graph code var graph = new ShaderGenerator(); { graph.AddShaderChunk("// Shared Graph Properties (uniform inputs)"); graph.AddShaderChunk(sharedProperties.GetPropertiesDeclaration(1)); if (vertexActive) { graph.AddShaderChunk("// Vertex Graph Inputs"); graph.Indent(); graph.AddGenerator(vertexGraphInputs); graph.Deindent(); graph.AddShaderChunk("// Vertex Graph Outputs"); graph.Indent(); graph.AddShaderChunk(vertexGraphOutputs.ToString()); graph.Deindent(); } graph.AddShaderChunk("// Pixel Graph Inputs"); graph.Indent(); graph.AddGenerator(pixelGraphInputs); graph.Deindent(); graph.AddShaderChunk("// Pixel Graph Outputs"); graph.Indent(); graph.AddShaderChunk(pixelGraphOutputs.ToString()); graph.Deindent(); graph.AddShaderChunk("// Shared Graph Node Functions"); graph.AddShaderChunk(graphNodeFunctions.ToString()); if (vertexActive) { graph.AddShaderChunk("// Vertex Graph Evaluation"); graph.Indent(); graph.AddShaderChunk(vertexGraphEvalFunction.ToString()); graph.Deindent(); } graph.AddShaderChunk("// Pixel Graph Evaluation"); graph.Indent(); graph.AddShaderChunk(pixelGraphEvalFunction.ToString()); graph.Deindent(); } // build the hash table of all named fragments TODO: could make this Dictionary<string, ShaderGenerator / string> ? Dictionary <string, string> namedFragments = new Dictionary <string, string>(); namedFragments.Add("${Defines}", defines.GetShaderString(2, false)); namedFragments.Add("${Graph}", graph.GetShaderString(2, false)); namedFragments.Add("${LightMode}", pass.LightMode); namedFragments.Add("${PassName}", pass.Name); namedFragments.Add("${Includes}", shaderPassIncludes.GetShaderString(2, false)); namedFragments.Add("${InterpolatorPacking}", packedInterpolatorCode.GetShaderString(2, false)); namedFragments.Add("${Blending}", blendCode.ToString()); namedFragments.Add("${Culling}", cullCode.ToString()); namedFragments.Add("${ZTest}", zTestCode.ToString()); namedFragments.Add("${ZWrite}", zWriteCode.ToString()); namedFragments.Add("${Stencil}", stencilCode.ToString()); namedFragments.Add("${ColorMask}", colorMaskCode.ToString()); namedFragments.Add("${LOD}", materialOptions.lod.ToString()); // process the template to generate the shader code for this pass TODO: could make this a shared function string[] templateLines = File.ReadAllLines(templateLocation); System.Text.StringBuilder builder = new System.Text.StringBuilder(); foreach (string line in templateLines) { ShaderSpliceUtil.PreprocessShaderCode(line, activeFields, namedFragments, builder); builder.AppendLine(); } result.AddShaderChunk(builder.ToString(), false); return(true); }
void KickOffShaderCompilations() { // Start compilation for nodes that need to recompile using (KickOffShaderCompilationsMarker.Auto()) using (var nodesToCompile = PooledHashSet <AbstractMaterialNode> .Get()) { // master node compile is first in the priority list, as it takes longer than the other previews if ((m_NodesCompiling.Count + nodesToCompile.Count < m_MaxNodesCompiling) && m_NodesNeedsRecompile.Contains(m_MasterRenderData.shaderData.node) && !m_NodesCompiling.Contains(m_MasterRenderData.shaderData.node) && ((Shader.globalRenderPipeline != null) && (Shader.globalRenderPipeline.Length > 0))) // master node requires an SRP { var renderData = GetPreviewRenderData(m_MasterRenderData.shaderData.node); Assert.IsTrue(renderData != null); nodesToCompile.Add(m_MasterRenderData.shaderData.node); } // add each node to compile list if it needs a preview, is not already compiling, and we have room // (we don't want to double kick compiles, so wait for the first one to get back before kicking another) foreach (var node in m_NodesNeedsRecompile) { if (m_NodesCompiling.Count + nodesToCompile.Count >= m_MaxNodesCompiling) { break; } if (node.hasPreview && node.previewExpanded && !m_NodesCompiling.Contains(node)) { var renderData = GetPreviewRenderData(node); if (renderData == null) // non-active output nodes can have NULL render data (no preview) { continue; } nodesToCompile.Add(node); } } // remove the selected nodes from the recompile list m_NodesNeedsRecompile.ExceptWith(nodesToCompile); // Reset error states for the UI, the shader, and all render data for nodes we're recompiling m_Messenger.ClearNodesFromProvider(this, nodesToCompile); // Force async compile on var wasAsyncAllowed = ShaderUtil.allowAsyncCompilation; ShaderUtil.allowAsyncCompilation = true; // kick async compiles for all nodes in m_NodeToCompile foreach (var node in nodesToCompile) { if (node is IMasterNode && masterRenderData != null && node == masterRenderData.shaderData.node && !(node is VfxMasterNode)) { UpdateMasterNodeShader(); continue; } Assert.IsFalse(!node.hasPreview && !(node is SubGraphOutputNode || node is VfxMasterNode)); var renderData = GetPreviewRenderData(node); // Get shader code and compile var generator = new Generator(node.owner, node, GenerationMode.Preview, $"hidden/preview/{node.GetVariableNameForNode()}"); BeginCompile(renderData, generator.generatedShader); // Calculate the PreviewMode from upstream nodes // If any upstream node is 3D that trickles downstream // TODO: not sure why this code exists here // it would make more sense in HandleGraphChanges and/or RenderPreview List <AbstractMaterialNode> upstreamNodes = new List <AbstractMaterialNode>(); NodeUtils.DepthFirstCollectNodesFromNode(upstreamNodes, node, NodeUtils.IncludeSelf.Include); renderData.previewMode = PreviewMode.Preview2D; foreach (var pNode in upstreamNodes) { if (pNode.previewMode == PreviewMode.Preview3D) { renderData.previewMode = PreviewMode.Preview3D; break; } } } ShaderUtil.allowAsyncCompilation = wasAsyncAllowed; } }
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); }