예제 #1
0
        private string GetFunctionBody(MethodInfo info)
        {
            var args = new List <object>();

            foreach (var param in info.GetParameters())
            {
                args.Add(GetDefault(param.ParameterType));
            }

            var result = info.Invoke(this, args.ToArray()) as string;

            if (string.IsNullOrEmpty(result))
            {
                return(string.Empty);
            }

            using (var tempSlots = PooledList <MaterialSlot> .Get())
            {
                GetSlots(tempSlots);
                foreach (var slot in tempSlots)
                {
                    var toReplace   = string.Format("{{slot{0}dimension}}", slot.id);
                    var replacement = NodeUtils.GetSlotDimension(slot.concreteValueType);
                    result = result.Replace(toReplace, replacement);
                }
            }

            return(result);
        }
예제 #2
0
        private string GetFunctionHeader()
        {
            string header = "void " + GetFunctionName() + "(";

            using (var tempSlots = PooledList <MaterialSlot> .Get())
            {
                GetSlots(tempSlots);
                tempSlots.Sort((slot1, slot2) => slot1.id.CompareTo(slot2.id));
                var first = true;
                foreach (var slot in tempSlots)
                {
                    if (!first)
                    {
                        header += ", ";
                    }

                    first = false;

                    if (slot.isOutputSlot)
                    {
                        header += "out ";
                    }

                    header += slot.concreteValueType.ToShaderString() + " " + slot.shaderOutputName;
                }

                header += ")";
            }

            return(header);
        }
예제 #3
0
        private string GetFunctionHeader()
        {
            string header = "void " + GetFunctionName() + "(";

            using (var tempSlots = PooledList <MaterialSlot> .Get())
            {
                GetSlots(tempSlots);
                tempSlots.Sort((slot1, slot2) => slot1.id.CompareTo(slot2.id));
                var first = true;
                foreach (var slot in tempSlots)
                {
                    if (!first)
                    {
                        header += ", ";
                    }

                    first = false;

                    if (slot.isOutputSlot)
                    {
                        header += "out ";
                    }

                    // always use generic precisions for parameters, they will get concretized by the system
                    header += slot.concreteValueType.ToShaderString(PrecisionUtil.Token) + " " + slot.shaderOutputName;
                }

                header += ")";
            }

            return(header);
        }
예제 #4
0
 public NeededCoordinateSpace RequiresBitangent(ShaderStageCapability stageCapability)
 {
     using (var tempSlots = PooledList <MaterialSlot> .Get())
     {
         GetInputSlots(tempSlots);
         var binding = NeededCoordinateSpace.None;
         foreach (var slot in tempSlots)
         {
             binding |= slot.RequiresBitangent();
         }
         return(binding);
     }
 }
예제 #5
0
 public bool RequiresMeshUV(Internal.UVChannel channel, ShaderStageCapability stageCapability)
 {
     using (var tempSlots = PooledList <MaterialSlot> .Get())
     {
         GetInputSlots(tempSlots);
         foreach (var slot in tempSlots)
         {
             if (slot.RequiresMeshUV(channel))
             {
                 return(true);
             }
         }
         return(false);
     }
 }
예제 #6
0
 public bool RequiresPixelPosition(ShaderStageCapability stageCapability)
 {
     using (var tempSlots = PooledList <MaterialSlot> .Get())
     {
         GetInputSlots(tempSlots);
         foreach (var slot in tempSlots)
         {
             if (slot.RequiresPixelPosition(stageCapability))
             {
                 return(true);
             }
         }
         return(false);
     }
 }
예제 #7
0
        public bool RequiresMeshUV(UVChannel channel, ShaderStageCapability stageCapability)
        {
            var result = false;

            using (var tempSlots = PooledList <MaterialSlot> .Get())
            {
                GetInputSlots(tempSlots);
                foreach (var slot in tempSlots)
                {
                    if (slot.RequiresMeshUV(channel))
                    {
                        result = true;
                        break;
                    }
                }
            }

            return(result);
        }
예제 #8
0
        public void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode generationMode)
        {
            using (var tempSlots = PooledList <MaterialSlot> .Get())
            {
                GetOutputSlots(tempSlots);
                foreach (var outSlot in tempSlots)
                {
                    sb.AppendLine(outSlot.concreteValueType.ToShaderString(PrecisionUtil.Token) + " " + GetVariableNameForSlot(outSlot.id) + ";");
                }

                string call  = GetFunctionName() + "(";
                bool   first = true;
                tempSlots.Clear();
                GetSlots(tempSlots);
                tempSlots.Sort((slot1, slot2) => slot1.id.CompareTo(slot2.id));
                foreach (var slot in tempSlots)
                {
                    if (!first)
                    {
                        call += ", ";
                    }
                    first = false;

                    if (slot.isInputSlot)
                    {
                        call += GetSlotValue(slot.id, generationMode);
                    }
                    else
                    {
                        call += GetVariableNameForSlot(slot.id);
                    }
                }
                call += ");";

                sb.AppendLine(call);
            }
        }
예제 #9
0
        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();
        }
예제 #10
0
        void UpdatePorts()
        {
            switch (keyword.keywordType)
            {
            case KeywordType.Boolean:
            {
                // Boolean type has preset slots
                PooledList <MaterialSlot> temp = PooledList <MaterialSlot> .Get();

                GetInputSlots(temp);
                if (temp.Any())
                {
                    temp.Dispose();
                    break;
                }
                else
                {
                    temp.Dispose();
                }
                AddSlot(new DynamicVectorMaterialSlot(OutputSlotId, "Out", "Out", SlotType.Output, Vector4.zero));
                AddSlot(new DynamicVectorMaterialSlot(1, "On", "On", SlotType.Input, Vector4.zero));
                AddSlot(new DynamicVectorMaterialSlot(2, "Off", "Off", SlotType.Input, Vector4.zero));
                RemoveSlotsNameNotMatching(new int[] { 0, 1, 2 });
                break;
            }

            case KeywordType.Enum:
            {
                // Get slots
                List <MaterialSlot> inputSlots = new List <MaterialSlot>();
                GetInputSlots(inputSlots);

                // Store the edges
                Dictionary <MaterialSlot, List <IEdge> > edgeDict = new Dictionary <MaterialSlot, List <IEdge> >();
                foreach (MaterialSlot slot in inputSlots)
                {
                    edgeDict.Add(slot, (List <IEdge>)slot.owner.owner.GetEdges(slot.slotReference));
                }

                // Remove old slots
                for (int i = 0; i < inputSlots.Count; i++)
                {
                    RemoveSlot(inputSlots[i].id);
                }

                // Add output slot
                AddSlot(new DynamicVectorMaterialSlot(OutputSlotId, "Out", "Out", SlotType.Output, Vector4.zero));

                // Add input slots
                int[] slotIds = new int[keyword.entries.Count + 1];
                slotIds[keyword.entries.Count] = OutputSlotId;
                for (int i = 0; i < keyword.entries.Count; i++)
                {
                    // Get slot based on entry id
                    MaterialSlot slot = inputSlots.Where(x =>
                                                         x.id == keyword.entries[i].id &&
                                                         x.RawDisplayName() == keyword.entries[i].displayName &&
                                                         x.shaderOutputName == keyword.entries[i].referenceName).FirstOrDefault();

                    // If slot doesnt exist its new so create it
                    if (slot == null)
                    {
                        slot = new DynamicVectorMaterialSlot(keyword.entries[i].id, keyword.entries[i].displayName, keyword.entries[i].referenceName, SlotType.Input, Vector4.zero);
                    }

                    AddSlot(slot);
                    slotIds[i] = keyword.entries[i].id;
                }
                RemoveSlotsNameNotMatching(slotIds);

                // Reconnect the edges
                foreach (KeyValuePair <MaterialSlot, List <IEdge> > entry in edgeDict)
                {
                    foreach (IEdge edge in entry.Value)
                    {
                        owner.Connect(edge.outputSlot, edge.inputSlot);
                    }
                }
                break;
            }
            }

            ValidateNode();
        }
예제 #11
0
        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();
        }
예제 #12
0
        public override void EvaluateDynamicMaterialSlots()
        {
            var dynamicInputSlotsToCompare = DictionaryPool <DynamicVectorMaterialSlot, ConcreteSlotValueType> .Get();

            var skippedDynamicSlots = ListPool <DynamicVectorMaterialSlot> .Get();

            var dynamicMatrixInputSlotsToCompare = DictionaryPool <DynamicMatrixMaterialSlot, ConcreteSlotValueType> .Get();

            var skippedDynamicMatrixSlots = ListPool <DynamicMatrixMaterialSlot> .Get();

            // iterate the input slots
            using (var tempSlots = PooledList <MaterialSlot> .Get())
            {
                GetInputSlots(tempSlots);
                foreach (var inputSlot in tempSlots)
                {
                    inputSlot.hasError = false;

                    // if there is a connection
                    var edges = owner.GetEdges(inputSlot.slotReference).ToList();
                    if (!edges.Any())
                    {
                        if (inputSlot is DynamicVectorMaterialSlot)
                        {
                            skippedDynamicSlots.Add(inputSlot as DynamicVectorMaterialSlot);
                        }
                        if (inputSlot is DynamicMatrixMaterialSlot)
                        {
                            skippedDynamicMatrixSlots.Add(inputSlot as DynamicMatrixMaterialSlot);
                        }
                        continue;
                    }

                    // get the output details
                    var outputSlotRef = edges[0].outputSlot;
                    var outputNode    = owner.GetNodeFromGuid(outputSlotRef.nodeGuid);
                    if (outputNode == null)
                    {
                        continue;
                    }

                    var outputSlot = outputNode.FindOutputSlot <MaterialSlot>(outputSlotRef.slotId);
                    if (outputSlot == null)
                    {
                        continue;
                    }

                    if (outputSlot.hasError)
                    {
                        inputSlot.hasError = true;
                        continue;
                    }

                    var outputConcreteType = outputSlot.concreteValueType;
                    // dynamic input... depends on output from other node.
                    // we need to compare ALL dynamic inputs to make sure they
                    // are compatable.
                    if (inputSlot is DynamicVectorMaterialSlot)
                    {
                        dynamicInputSlotsToCompare.Add((DynamicVectorMaterialSlot)inputSlot, outputConcreteType);
                        continue;
                    }
                    else if (inputSlot is DynamicMatrixMaterialSlot)
                    {
                        dynamicMatrixInputSlotsToCompare.Add((DynamicMatrixMaterialSlot)inputSlot, outputConcreteType);
                        continue;
                    }
                }

                // and now dynamic matrices
                var dynamicMatrixType = ConvertDynamicMatrixInputTypeToConcrete(dynamicMatrixInputSlotsToCompare.Values);
                foreach (var dynamicKvP in dynamicMatrixInputSlotsToCompare)
                {
                    dynamicKvP.Key.SetConcreteType(dynamicMatrixType);
                }
                foreach (var skippedSlot in skippedDynamicMatrixSlots)
                {
                    skippedSlot.SetConcreteType(dynamicMatrixType);
                }

                // we can now figure out the dynamic slotType
                // from here set all the
                var dynamicType = SlotValueHelper.ConvertMatrixToVectorType(dynamicMatrixType);
                foreach (var dynamicKvP in dynamicInputSlotsToCompare)
                {
                    dynamicKvP.Key.SetConcreteType(dynamicType);
                }
                foreach (var skippedSlot in skippedDynamicSlots)
                {
                    skippedSlot.SetConcreteType(dynamicType);
                }

                tempSlots.Clear();
                GetInputSlots(tempSlots);
                bool inputError = tempSlots.Any(x => x.hasError);
                if (inputError)
                {
                    owner.AddConcretizationError(guid, string.Format("Node {0} had input error", guid));
                    hasError = true;
                }
                // configure the output slots now
                // their slotType will either be the default output slotType
                // or the above dynanic slotType for dynamic nodes
                // or error if there is an input error
                tempSlots.Clear();
                GetOutputSlots(tempSlots);
                foreach (var outputSlot in tempSlots)
                {
                    outputSlot.hasError = false;

                    if (inputError)
                    {
                        outputSlot.hasError = true;
                        continue;
                    }

                    if (outputSlot is DynamicVectorMaterialSlot)
                    {
                        (outputSlot as DynamicVectorMaterialSlot).SetConcreteType(dynamicType);
                        continue;
                    }
                    else if (outputSlot is DynamicMatrixMaterialSlot)
                    {
                        (outputSlot as DynamicMatrixMaterialSlot).SetConcreteType(dynamicMatrixType);
                        continue;
                    }
                }


                tempSlots.Clear();
                GetOutputSlots(tempSlots);
                if (tempSlots.Any(x => x.hasError))
                {
                    owner.AddConcretizationError(guid, string.Format("Node {0} had output error", guid));
                    hasError = true;
                }
            }

            CalculateNodeHasError();

            ListPool <DynamicVectorMaterialSlot> .Release(skippedDynamicSlots);

            DictionaryPool <DynamicVectorMaterialSlot, ConcreteSlotValueType> .Release(dynamicInputSlotsToCompare);

            ListPool <DynamicMatrixMaterialSlot> .Release(skippedDynamicMatrixSlots);

            DictionaryPool <DynamicMatrixMaterialSlot, ConcreteSlotValueType> .Release(dynamicMatrixInputSlotsToCompare);
        }
예제 #13
0
        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 = (SubGraphOutputNode)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);
            asset.requirements        = ShaderGraphRequirements.FromNodes(nodes, asset.effectiveShaderStage, false);
            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, GenerationMode.ForReals);
                    registry.builder.ReplaceInCurrentMapping(PrecisionUtil.Token, node.concretePrecision.ToShaderString());
                }
            }

            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(string.Format("{0}", prop.GetPropertyAsArgumentString()));
                }

                // now pass surface inputs
                arguments.Add(string.Format("{0} IN", asset.inputStructName));

                // Now generate outputs
                foreach (var 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.Float)} {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)
            {
                node.CollectShaderProperties(collector, GenerationMode.ForReals);
            }
            asset.WriteData(graph.properties, graph.keywords, collector.properties, outputSlots);
            outputSlots.Dispose();
        }
예제 #14
0
        // Internal validation
        // -------------------------------------------------

        public override void EvaluateDynamicMaterialSlots()
        {
            var dynamicInputSlotsToCompare = DictionaryPool <DynamicValueMaterialSlot, ConcreteSlotValueType> .Get();

            var skippedDynamicSlots = ListPool <DynamicValueMaterialSlot> .Get();

            // iterate the input slots
            using (var tempSlots = PooledList <MaterialSlot> .Get())
            {
                GetInputSlots(tempSlots);
                foreach (var inputSlot in tempSlots)
                {
                    inputSlot.hasError = false;

                    // if there is a connection
                    var edges = owner.GetEdges(inputSlot.slotReference).ToList();
                    if (!edges.Any())
                    {
                        if (inputSlot is DynamicValueMaterialSlot)
                        {
                            skippedDynamicSlots.Add(inputSlot as DynamicValueMaterialSlot);
                        }
                        continue;
                    }

                    // get the output details
                    var outputSlotRef = edges[0].outputSlot;
                    var outputNode    = owner.GetNodeFromGuid(outputSlotRef.nodeGuid);
                    if (outputNode == null)
                    {
                        continue;
                    }

                    var outputSlot = outputNode.FindOutputSlot <MaterialSlot>(outputSlotRef.slotId);
                    if (outputSlot == null)
                    {
                        continue;
                    }

                    if (outputSlot.hasError)
                    {
                        inputSlot.hasError = true;
                        continue;
                    }

                    var outputConcreteType = outputSlot.concreteValueType;
                    // dynamic input... depends on output from other node.
                    // we need to compare ALL dynamic inputs to make sure they
                    // are compatable.
                    if (inputSlot is DynamicValueMaterialSlot)
                    {
                        dynamicInputSlotsToCompare.Add((DynamicValueMaterialSlot)inputSlot, outputConcreteType);
                        continue;
                    }
                }

                m_MultiplyType = GetMultiplyType(dynamicInputSlotsToCompare.Values);

                // Resolve dynamics depending on matrix/vector configuration
                switch (m_MultiplyType)
                {
                // If all matrix resolve as per dynamic matrix
                case MultiplyType.Matrix:
                    var dynamicMatrixType = ConvertDynamicMatrixInputTypeToConcrete(dynamicInputSlotsToCompare.Values);
                    foreach (var dynamicKvP in dynamicInputSlotsToCompare)
                    {
                        dynamicKvP.Key.SetConcreteType(dynamicMatrixType);
                    }
                    foreach (var skippedSlot in skippedDynamicSlots)
                    {
                        skippedSlot.SetConcreteType(dynamicMatrixType);
                    }
                    break;

                // If mixed handle differently:
                // Iterate all slots and set their concretes based on their edges
                // Find matrix slot and convert its type to a vector type
                // Reiterate all slots and set non matrix slots to the vector type
                case MultiplyType.Mixed:
                    foreach (var dynamicKvP in dynamicInputSlotsToCompare)
                    {
                        SetConcreteValueTypeFromEdge(dynamicKvP.Key);
                    }
                    MaterialSlot          matrixSlot = GetMatrixSlot();
                    ConcreteSlotValueType vectorType = SlotValueHelper.ConvertMatrixToVectorType(matrixSlot.concreteValueType);
                    foreach (var dynamicKvP in dynamicInputSlotsToCompare)
                    {
                        if (dynamicKvP.Key != matrixSlot)
                        {
                            dynamicKvP.Key.SetConcreteType(vectorType);
                        }
                    }
                    foreach (var skippedSlot in skippedDynamicSlots)
                    {
                        skippedSlot.SetConcreteType(vectorType);
                    }
                    break;

                // If all vector resolve as per dynamic vector
                default:
                    var dynamicVectorType = ConvertDynamicVectorInputTypeToConcrete(dynamicInputSlotsToCompare.Values);
                    foreach (var dynamicKvP in dynamicInputSlotsToCompare)
                    {
                        dynamicKvP.Key.SetConcreteType(dynamicVectorType);
                    }
                    foreach (var skippedSlot in skippedDynamicSlots)
                    {
                        skippedSlot.SetConcreteType(dynamicVectorType);
                    }
                    break;
                }

                tempSlots.Clear();
                GetInputSlots(tempSlots);
                bool inputError = tempSlots.Any(x => x.hasError);
                if (inputError)
                {
                    owner.AddConcretizationError(guid, string.Format("Node {0} had input error", guid));
                    hasError = true;
                }
                // configure the output slots now
                // their slotType will either be the default output slotType
                // or the above dynanic slotType for dynamic nodes
                // or error if there is an input error
                tempSlots.Clear();
                GetOutputSlots(tempSlots);
                foreach (var outputSlot in tempSlots)
                {
                    outputSlot.hasError = false;

                    if (inputError)
                    {
                        outputSlot.hasError = true;
                        continue;
                    }

                    if (outputSlot is DynamicValueMaterialSlot)
                    {
                        // Apply similar logic to output slot
                        switch (m_MultiplyType)
                        {
                        // As per dynamic matrix
                        case MultiplyType.Matrix:
                            var dynamicMatrixType = ConvertDynamicMatrixInputTypeToConcrete(dynamicInputSlotsToCompare.Values);
                            (outputSlot as DynamicValueMaterialSlot).SetConcreteType(dynamicMatrixType);
                            break;

                        // Mixed configuration
                        // Find matrix slot and convert type to vector
                        // Set output concrete to vector
                        case MultiplyType.Mixed:
                            MaterialSlot          matrixSlot = GetMatrixSlot();
                            ConcreteSlotValueType vectorType = SlotValueHelper.ConvertMatrixToVectorType(matrixSlot.concreteValueType);
                            (outputSlot as DynamicValueMaterialSlot).SetConcreteType(vectorType);
                            break;

                        // As per dynamic vector
                        default:
                            var dynamicVectorType = ConvertDynamicVectorInputTypeToConcrete(dynamicInputSlotsToCompare.Values);
                            (outputSlot as DynamicValueMaterialSlot).SetConcreteType(dynamicVectorType);
                            break;
                        }
                        continue;
                    }
                }

                tempSlots.Clear();
                GetOutputSlots(tempSlots);
                if (tempSlots.Any(x => x.hasError))
                {
                    owner.AddConcretizationError(guid, string.Format("Node {0} had output error", guid));
                    hasError = true;
                }
            }

            CalculateNodeHasError();

            ListPool <DynamicValueMaterialSlot> .Release(skippedDynamicSlots);

            DictionaryPool <DynamicValueMaterialSlot, ConcreteSlotValueType> .Release(dynamicInputSlotsToCompare);
        }
예제 #15
0
        // TODO: could get rid of this if we could run a codegen prepass (with proper keyword #ifdef)
        public static void GenerateVirtualTextureFeedback(
            List <AbstractMaterialNode> downstreamNodesIncludingRoot,
            List <int>[] keywordPermutationsPerNode,
            ShaderStringBuilder surfaceDescriptionFunction,
            KeywordCollector shaderKeywords)
        {
            // A note on how we handle vt feedback in combination with keywords:
            // We essentially generate a fully separate feedback path for each permutation of keywords
            // so per permutation we gather variables contribution to feedback and we generate
            // feedback gathering for each permutation individually.

            var feedbackVariablesPerPermutation = PooledList <PooledList <string> > .Get();

            try
            {
                if (shaderKeywords.permutations.Count >= 1)
                {
                    for (int i = 0; i < shaderKeywords.permutations.Count; i++)
                    {
                        feedbackVariablesPerPermutation.Add(PooledList <string> .Get());
                    }
                }
                else
                {
                    // Create a dummy single permutation
                    feedbackVariablesPerPermutation.Add(PooledList <string> .Get());
                }

                int index = 0; //for keywordPermutationsPerNode
                foreach (var node in downstreamNodesIncludingRoot)
                {
                    if (node is SampleVirtualTextureNode vtNode)
                    {
                        if (vtNode.noFeedback)
                        {
                            continue;
                        }
                        if (keywordPermutationsPerNode[index] == null)
                        {
                            Debug.Assert(shaderKeywords.permutations.Count == 0, $"Shader has {shaderKeywords.permutations.Count} permutations but keywordPermutationsPerNode of some nodes are null.");
                            feedbackVariablesPerPermutation[0].Add(vtNode.GetFeedbackVariableName());
                        }
                        else
                        {
                            foreach (int perm in keywordPermutationsPerNode[index])
                            {
                                feedbackVariablesPerPermutation[perm].Add(vtNode.GetFeedbackVariableName());
                            }
                        }
                    }

                    if (node is SubGraphNode sgNode)
                    {
                        if (sgNode.asset == null)
                        {
                            continue;
                        }
                        if (keywordPermutationsPerNode[index] == null)
                        {
                            Debug.Assert(shaderKeywords.permutations.Count == 0, $"Shader has {shaderKeywords.permutations.Count} permutations but keywordPermutationsPerNode of some nodes are null.");
                            foreach (var feedbackSlot in sgNode.asset.vtFeedbackVariables)
                            {
                                feedbackVariablesPerPermutation[0].Add(node.GetVariableNameForNode() + "_" + feedbackSlot);
                            }
                        }
                        else
                        {
                            foreach (var feedbackSlot in sgNode.asset.vtFeedbackVariables)
                            {
                                foreach (int perm in keywordPermutationsPerNode[index])
                                {
                                    feedbackVariablesPerPermutation[perm].Add(node.GetVariableNameForNode() + "_" + feedbackSlot);
                                }
                            }
                        }
                    }

                    index++;
                }

                index = 0;
                foreach (var feedbackVariables in feedbackVariablesPerPermutation)
                {
                    // If it's a dummy single always-on permutation don't put an ifdef around the code
                    if (shaderKeywords.permutations.Count >= 1)
                    {
                        surfaceDescriptionFunction.AppendLine(KeywordUtil.GetKeywordPermutationConditional(index));
                    }

                    using (surfaceDescriptionFunction.BlockScope())
                    {
                        if (feedbackVariables.Count == 0)
                        {
                            string feedBackCode = "surface.VTPackedFeedback = float4(1.0f,1.0f,1.0f,1.0f);";
                            surfaceDescriptionFunction.AppendLine(feedBackCode);
                        }
                        else if (feedbackVariables.Count == 1)
                        {
                            string feedBackCode = "surface.VTPackedFeedback = GetPackedVTFeedback(" + feedbackVariables[0] + ");";
                            surfaceDescriptionFunction.AppendLine(feedBackCode);
                        }
                        else if (feedbackVariables.Count > 1)
                        {
                            surfaceDescriptionFunction.AppendLine("float4 VTFeedback_array[" + feedbackVariables.Count + "];");

                            int arrayIndex = 0;
                            foreach (var variable in feedbackVariables)
                            {
                                surfaceDescriptionFunction.AppendLine("VTFeedback_array[" + arrayIndex + "] = " + variable + ";");
                                arrayIndex++;
                            }

                            // TODO: should read from NDCPosition instead...
                            surfaceDescriptionFunction.AppendLine("uint pixelColumn = (IN.ScreenPosition.x / IN.ScreenPosition.w) * _ScreenParams.x;");
                            surfaceDescriptionFunction.AppendLine(
                                "surface.VTPackedFeedback = GetPackedVTFeedback(VTFeedback_array[(pixelColumn + _FrameCount) % (uint)" + feedbackVariables.Count + "]);");
                        }
                    }

                    if (shaderKeywords.permutations.Count >= 1)
                    {
                        surfaceDescriptionFunction.AppendLine("#endif");
                    }

                    index++;
                }
            }
            finally
            {
                foreach (var list in feedbackVariablesPerPermutation)
                {
                    list.Dispose();
                }
                feedbackVariablesPerPermutation.Dispose();
            }
        }
예제 #16
0
        void UpdatePorts()
        {
            switch (keyword.keywordType)
            {
            case KeywordType.Boolean:
            {
                // Boolean type has preset slots
                PooledList <MaterialSlot> temp = PooledList <MaterialSlot> .Get();

                GetInputSlots(temp);
                if (temp.Any())
                {
                    temp.Dispose();
                    break;
                }
                else
                {
                    temp.Dispose();
                }
                AddSlot(new DynamicVectorMaterialSlot(OutputSlotId, "Out", "Out", SlotType.Output, Vector4.zero));
                AddSlot(new DynamicVectorMaterialSlot(1, "On", "On", SlotType.Input, Vector4.zero));
                AddSlot(new DynamicVectorMaterialSlot(2, "Off", "Off", SlotType.Input, Vector4.zero));
                RemoveSlotsNameNotMatching(new int[] { 0, 1, 2 });
                break;
            }

            case KeywordType.Enum:
                using (var inputSlots = PooledList <MaterialSlot> .Get())
                    using (var slotIDs = PooledList <int> .Get())
                    {
                        // Get slots
                        GetInputSlots(inputSlots);


                        // Add output slot
                        AddSlot(new DynamicVectorMaterialSlot(OutputSlotId, "Out", "Out", SlotType.Output, Vector4.zero));
                        slotIDs.Add(OutputSlotId);

                        // Add input slots
                        for (int i = 0; i < keyword.entries.Count; i++)
                        {
                            // Get slot based on entry id
                            MaterialSlot slot = inputSlots.Find(x =>
                                                                x.id == keyword.entries[i].id &&
                                                                x.RawDisplayName() == keyword.entries[i].displayName &&
                                                                x.shaderOutputName == keyword.entries[i].referenceName);

                            // If slot doesn't exist, it's new so create it
                            if (slot == null)
                            {
                                slot = new DynamicVectorMaterialSlot(keyword.entries[i].id, keyword.entries[i].displayName, keyword.entries[i].referenceName, SlotType.Input, Vector4.zero);
                            }

                            AddSlot(slot);
                            slotIDs.Add(keyword.entries[i].id);
                        }
                        RemoveSlotsNameNotMatching(slotIDs);
                        bool orderChanged = SetSlotOrder(slotIDs);
                        if (orderChanged)
                        {
                            // unfortunately there is no way to get the view to update slot order other than Topological
                            Dirty(ModificationScope.Topological);
                        }
                        break;
                    }
            }

            ValidateNode();
        }