public void LoadGraphData() { if (!String.IsNullOrEmpty(m_SerializedSubGraphData.JSONnodeData)) { m_SubGraphData = new SubGraphData(); MultiJson.Deserialize(m_SubGraphData, m_SerializedSubGraphData.JSONnodeData); } }
public void WriteData(IEnumerable <AbstractShaderProperty> inputs, IEnumerable <ShaderKeyword> keywords, IEnumerable <ShaderDropdown> dropdowns, IEnumerable <AbstractShaderProperty> nodeProperties, IEnumerable <MaterialSlot> outputs, IEnumerable <Target> unsupportedTargets) { if (m_SubGraphData == null) { m_SubGraphData = new SubGraphData(); m_SubGraphData.OverrideObjectId(assetGuid, "_subGraphData"); } m_SubGraphData.inputs.Clear(); m_SubGraphData.keywords.Clear(); m_SubGraphData.dropdowns.Clear(); m_SubGraphData.nodeProperties.Clear(); m_SubGraphData.outputs.Clear(); m_SubGraphData.unsupportedTargets.Clear(); foreach (var input in inputs) { m_SubGraphData.inputs.Add(input); } foreach (var keyword in keywords) { m_SubGraphData.keywords.Add(keyword); } foreach (var dropdown in dropdowns) { m_SubGraphData.dropdowns.Add(dropdown); } foreach (var nodeProperty in nodeProperties) { m_SubGraphData.nodeProperties.Add(nodeProperty); } foreach (var output in outputs) { m_SubGraphData.outputs.Add(output); } foreach (var unsupportedTarget in unsupportedTargets) { m_SubGraphData.unsupportedTargets.Add(unsupportedTarget); } var json = MultiJson.Serialize(m_SubGraphData); m_SerializedSubGraphData = new SerializationHelper.JSONSerializedElement() { JSONnodeData = json }; m_SubGraphData = null; }
public void WriteData(IEnumerable <AbstractShaderProperty> inputs, IEnumerable <ShaderKeyword> keywords, IEnumerable <AbstractShaderProperty> nodeProperties, IEnumerable <MaterialSlot> outputs) { if (m_SubGraphData == null) { m_SubGraphData = new SubGraphData(); } m_SubGraphData.inputs.Clear(); m_SubGraphData.keywords.Clear(); m_SubGraphData.nodeProperties.Clear(); m_SubGraphData.outputs.Clear(); foreach (var input in inputs) { m_SubGraphData.inputs.Add(input); } foreach (var keyword in keywords) { m_SubGraphData.keywords.Add(keyword); } foreach (var nodeProperty in nodeProperties) { m_SubGraphData.nodeProperties.Add(nodeProperty); } foreach (var output in outputs) { m_SubGraphData.outputs.Add(output); } var json = MultiJson.Serialize(m_SubGraphData); m_SerializedSubGraphData = new SerializationHelper.JSONSerializedElement() { JSONnodeData = json }; m_SubGraphData = null; }
void LoadSubGraph() { if (m_SubGraph == null) { m_SubGraphData = null; if (string.IsNullOrEmpty(m_SerializedSubGraph)) { return; } var graphGuid = subGraphGuid; var assetPath = AssetDatabase.GUIDToAssetPath(graphGuid); m_SubGraph = AssetDatabase.LoadAssetAtPath <SubGraphAsset>(assetPath); if (m_SubGraph == null) { return; } name = m_SubGraph.name; var index = SubGraphDatabase.instance.subGraphGuids.BinarySearch(graphGuid); m_SubGraphData = index < 0 ? null : SubGraphDatabase.instance.subGraphs[index]; } }
static void ProcessSubGraph(Dictionary <string, SubGraphData> subGraphMap, FunctionRegistry registry, SubGraphData subGraphData, GraphData graph) { registry.names.Clear(); subGraphData.functionNames.Clear(); subGraphData.nodeProperties.Clear(); subGraphData.isValid = true; graph.OnEnable(); graph.messageManager.ClearAll(); graph.ValidateGraph(); var assetPath = AssetDatabase.GUIDToAssetPath(subGraphData.assetGuid); subGraphData.hlslName = NodeUtils.GetHLSLSafeName(Path.GetFileNameWithoutExtension(assetPath)); subGraphData.inputStructName = $"Bindings_{subGraphData.hlslName}_{subGraphData.assetGuid}"; subGraphData.functionName = $"SG_{subGraphData.hlslName}_{subGraphData.assetGuid}"; subGraphData.path = graph.path; var outputNode = (SubGraphOutputNode)graph.outputNode; subGraphData.outputs.Clear(); outputNode.GetInputSlots(subGraphData.outputs); List <AbstractMaterialNode> nodes = new List <AbstractMaterialNode>(); NodeUtils.DepthFirstCollectNodesFromNode(nodes, outputNode); subGraphData.effectiveShaderStage = ShaderStageCapability.All; foreach (var slot in subGraphData.outputs) { var stage = NodeUtils.GetEffectiveShaderStageCapability(slot, true); if (stage != ShaderStageCapability.All) { subGraphData.effectiveShaderStage = stage; break; } } subGraphData.requirements = ShaderGraphRequirements.FromNodes(nodes, subGraphData.effectiveShaderStage, false); subGraphData.inputs = graph.properties.ToList(); foreach (var node in nodes) { if (node.hasError) { subGraphData.isValid = false; registry.ProvideFunction(subGraphData.functionName, sb => { }); return; } } foreach (var node in nodes) { if (node is SubGraphNode subGraphNode) { var nestedData = subGraphMap[subGraphNode.subGraphGuid]; foreach (var functionName in nestedData.functionNames) { registry.names.Add(functionName); } } else if (node is IGeneratesFunction generatesFunction) { generatesFunction.GenerateNodeFunction(registry, new GraphContext(subGraphData.inputStructName), GenerationMode.ForReals); } } registry.ProvideFunction(subGraphData.functionName, sb => { var graphContext = new GraphContext(subGraphData.inputStructName); GraphUtil.GenerateSurfaceInputStruct(sb, subGraphData.requirements, subGraphData.inputStructName); sb.AppendNewLine(); // Generate arguments... first INPUTS var arguments = new List <string>(); foreach (var prop in subGraphData.inputs) { arguments.Add(string.Format("{0}", prop.GetPropertyAsArgumentString())); } // now pass surface inputs arguments.Add(string.Format("{0} IN", subGraphData.inputStructName)); // Now generate outputs foreach (var output in subGraphData.outputs) { arguments.Add($"out {output.concreteValueType.ToString(outputNode.precision)} {output.shaderOutputName}_{output.id}"); } // Create the function prototype from the arguments sb.AppendLine("void {0}({1})" , subGraphData.functionName , arguments.Aggregate((current, next) => $"{current}, {next}")); // now generate the function using (sb.BlockScope()) { // Just grab the body from the active nodes var bodyGenerator = new ShaderGenerator(); foreach (var node in nodes) { if (node is IGeneratesBodyCode) { (node as IGeneratesBodyCode).GenerateNodeCode(bodyGenerator, graphContext, GenerationMode.ForReals); } } foreach (var slot in subGraphData.outputs) { bodyGenerator.AddShaderChunk($"{slot.shaderOutputName}_{slot.id} = {outputNode.GetSlotValue(slot.id, GenerationMode.ForReals)};"); } sb.Append(bodyGenerator.GetShaderString(1)); } }); subGraphData.functionNames.AddRange(registry.names.Distinct()); var collector = new PropertyCollector(); subGraphData.nodeProperties = collector.properties; foreach (var node in nodes) { node.CollectShaderProperties(collector, GenerationMode.ForReals); } subGraphData.OnBeforeSerialize(); }
public override void OnImportAsset(AssetImportContext ctx) { var currentTime = DateTime.Now.Ticks; if (ctx.assetPath != path) { ctx.LogImportError("The sgpostsubgraph extension may only be used internally by Shader Graph."); return; } if (SubGraphDatabase.instance == null) { SubGraphDatabase.instance = ScriptableObject.CreateInstance <SubGraphDatabase>(); } var database = SubGraphDatabase.instance; var allSubGraphGuids = AssetDatabase.FindAssets($"t:{nameof(SubGraphAsset)}").ToList(); allSubGraphGuids.Sort(); var subGraphMap = new Dictionary <string, SubGraphData>(); var graphDataMap = new Dictionary <string, GraphData>(); foreach (var subGraphData in database.subGraphs) { if (allSubGraphGuids.BinarySearch(subGraphData.assetGuid) >= 0) { subGraphMap.Add(subGraphData.assetGuid, subGraphData); } } var dirtySubGraphGuids = new List <string>(); foreach (var subGraphGuid in allSubGraphGuids) { var subGraphAsset = AssetDatabase.LoadAssetAtPath <SubGraphAsset>(AssetDatabase.GUIDToAssetPath(subGraphGuid)); if (!subGraphMap.TryGetValue(subGraphGuid, out var subGraphData)) { subGraphData = new SubGraphData(); } if (subGraphAsset.importedAt > subGraphData.processedAt) { dirtySubGraphGuids.Add(subGraphGuid); subGraphData.Reset(); subGraphData.processedAt = currentTime; var subGraphPath = AssetDatabase.GUIDToAssetPath(subGraphGuid); var textGraph = File.ReadAllText(subGraphPath, Encoding.UTF8); var graphData = new GraphData { isSubGraph = true, assetGuid = subGraphGuid }; JsonUtility.FromJsonOverwrite(textGraph, graphData); subGraphData.children.AddRange(graphData.GetNodes <SubGraphNode>().Select(x => x.subGraphGuid).Distinct()); subGraphData.assetGuid = subGraphGuid; subGraphMap[subGraphGuid] = subGraphData; graphDataMap[subGraphGuid] = graphData; } else { subGraphData.ancestors.Clear(); subGraphData.descendents.Clear(); subGraphData.isRecursive = false; } } database.subGraphs.Clear(); database.subGraphs.AddRange(subGraphMap.Values); database.subGraphs.Sort((s1, s2) => s1.assetGuid.CompareTo(s2.assetGuid)); database.subGraphGuids.Clear(); database.subGraphGuids.AddRange(database.subGraphs.Select(x => x.assetGuid)); var permanentMarks = new HashSet <string>(); var stack = new Stack <string>(allSubGraphGuids.Count); // Detect recursion, and populate `ancestors` and `descendents` per sub graph. foreach (var rootSubGraphData in database.subGraphs) { var rootSubGraphGuid = rootSubGraphData.assetGuid; stack.Push(rootSubGraphGuid); while (stack.Count > 0) { var subGraphGuid = stack.Pop(); if (!permanentMarks.Add(subGraphGuid)) { continue; } var subGraphData = subGraphMap[subGraphGuid]; if (subGraphData != rootSubGraphData) { subGraphData.ancestors.Add(rootSubGraphGuid); rootSubGraphData.descendents.Add(subGraphGuid); } foreach (var childSubGraphGuid in subGraphData.children) { if (childSubGraphGuid == rootSubGraphGuid) { rootSubGraphData.isRecursive = true; } else if (subGraphMap.ContainsKey(childSubGraphGuid)) { stack.Push(childSubGraphGuid); } } } permanentMarks.Clear(); } // Next up we build a list of sub graphs to be processed, which will later be sorted topologically. var sortedSubGraphs = new List <SubGraphData>(); foreach (var subGraphGuid in dirtySubGraphGuids) { var subGraphData = subGraphMap[subGraphGuid]; if (permanentMarks.Add(subGraphGuid)) { sortedSubGraphs.Add(subGraphData); } // Note that we're traversing up the graph via ancestors rather than descendents, because all Sub Graphs using the current sub graph needs to be re-processed. foreach (var ancestorGuid in subGraphData.ancestors) { if (permanentMarks.Add(ancestorGuid)) { var ancestorSubGraphData = subGraphMap[ancestorGuid]; sortedSubGraphs.Add(ancestorSubGraphData); } } } permanentMarks.Clear(); // Sort topologically. At this stage we can assume there are no loops because all recursive sub graphs have been filtered out. sortedSubGraphs.Sort((s1, s2) => s1.descendents.Contains(s2.assetGuid) ? 1 : s2.descendents.Contains(s1.assetGuid) ? -1 : 0); // Finally process the topologically sorted sub graphs without recursion. var registry = new FunctionRegistry(new ShaderStringBuilder(), true); var messageManager = new MessageManager(); foreach (var subGraphData in sortedSubGraphs) { try { var subGraphPath = AssetDatabase.GUIDToAssetPath(subGraphData.assetGuid); if (!graphDataMap.TryGetValue(subGraphData.assetGuid, out var graphData)) { var textGraph = File.ReadAllText(subGraphPath, Encoding.UTF8); graphData = new GraphData { isSubGraph = true, assetGuid = subGraphData.assetGuid }; JsonUtility.FromJsonOverwrite(textGraph, graphData); } graphData.messageManager = messageManager; ProcessSubGraph(subGraphMap, registry, subGraphData, graphData); if (messageManager.nodeMessagesChanged) { var subGraphAsset = AssetDatabase.LoadAssetAtPath <SubGraphAsset>(AssetDatabase.GUIDToAssetPath(subGraphData.assetGuid)); foreach (var pair in messageManager.GetNodeMessages()) { var node = graphData.GetNodeFromTempId(pair.Key); foreach (var message in pair.Value) { MessageManager.Log(node, subGraphPath, message, subGraphAsset); } } } } catch (Exception e) { subGraphData.isValid = false; var subGraphAsset = AssetDatabase.LoadAssetAtPath <SubGraphAsset>(AssetDatabase.GUIDToAssetPath(subGraphData.assetGuid)); Debug.LogException(e, subGraphAsset); } finally { subGraphData.processedAt = currentTime; messageManager.ClearAll(); } } // Carry over functions used by sub-graphs that were not re-processed in this import. foreach (var subGraphData in database.subGraphs) { foreach (var functionName in subGraphData.functionNames) { if (!registry.sources.ContainsKey(functionName)) { registry.sources.Add(functionName, database.functionSources[database.functionNames.BinarySearch(functionName)]); } } } var functions = registry.sources.ToList(); functions.Sort((p1, p2) => p1.Key.CompareTo(p2.Key)); database.functionNames.Clear(); database.functionSources.Clear(); foreach (var pair in functions) { database.functionNames.Add(pair.Key); database.functionSources.Add(pair.Value); } ctx.AddObjectToAsset("MainAsset", database); ctx.SetMainObject(database); SubGraphDatabase.instance = null; }