A game about forced loneliness, made by TACStudios
at master 5.9 kB view raw
1using System; 2using System.Collections.Generic; 3using UnityEditor.ShaderGraph.Internal; 4using UnityEngine; 5 6namespace UnityEditor.ShaderGraph 7{ 8 class FunctionSource 9 { 10 public string code; 11 public HashSet<AbstractMaterialNode> nodes; 12 public bool isGeneric; 13 public int graphPrecisionFlags; // Flags<GraphPrecision> 14 public int concretePrecisionFlags; // Flags<ConcretePrecision> 15 } 16 17 class FunctionRegistry 18 { 19 Dictionary<string, FunctionSource> m_Sources = new Dictionary<string, FunctionSource>(); 20 bool m_Validate = false; 21 ShaderStringBuilder m_Builder; 22 IncludeCollection m_Includes; 23 24 public FunctionRegistry(ShaderStringBuilder builder, IncludeCollection includes, bool validate = false) 25 { 26 m_Builder = builder; 27 m_Includes = includes; 28 m_Validate = validate; 29 } 30 31 internal ShaderStringBuilder builder => m_Builder; 32 33 public Dictionary<string, FunctionSource> sources => m_Sources; 34 35 public void RequiresIncludes(IncludeCollection includes) 36 { 37 m_Includes.Add(includes); 38 } 39 40 public void RequiresIncludePath(string includePath, bool shouldIncludeWithPragmas = false) 41 { 42 m_Includes.Add(includePath, IncludeLocation.Graph, shouldIncludeWithPragmas); 43 } 44 45 // this list is somewhat redundant, but it preserves function declaration ordering 46 // (i.e. when nodes add multiple functions, they require being defined in a certain order) 47 public List<string> names { get; } = new List<string>(); 48 49 public void ProvideFunction(string name, GraphPrecision graphPrecision, ConcretePrecision concretePrecision, Action<ShaderStringBuilder> generator) 50 { 51 // appends code, construct the standalone code string 52 var originalIndex = builder.length; 53 builder.AppendNewLine(); 54 55 var startIndex = builder.length; 56 generator(builder); 57 var length = builder.length - startIndex; 58 var code = builder.ToString(startIndex, length); 59 60 // validate some assumptions around generics 61 bool isGenericName = name.Contains("$"); 62 bool isGenericFunc = code.Contains("$"); 63 bool isGeneric = isGenericName || isGenericFunc; 64 bool containsFunctionName = code.Contains(name); 65 66 var curNode = builder.currentNode; 67 if (isGenericName != isGenericFunc) 68 curNode.owner.AddValidationError(curNode.objectId, $"Function {name} provided by node {curNode.name} contains $precision tokens in the name or the code, but not both. This is very likely an error."); 69 70 if (!containsFunctionName) 71 curNode.owner.AddValidationError(curNode.objectId, $"Function {name} provided by node {curNode.name} does not contain the name of the function. This is very likely an error."); 72 73 int graphPrecisionFlag = (1 << (int)graphPrecision); 74 int concretePrecisionFlag = (1 << (int)concretePrecision); 75 76 FunctionSource existingSource; 77 if (m_Sources.TryGetValue(name, out existingSource)) 78 { 79 // function already provided 80 existingSource.nodes.Add(builder.currentNode); 81 82 // let's check if the requested precision variant has already been provided (or if it's not generic there are no variants) 83 bool concretePrecisionExists = ((existingSource.concretePrecisionFlags & concretePrecisionFlag) != 0) || !isGeneric; 84 85 // if this precision was already added -- remove the duplicate code from the builder 86 if (concretePrecisionExists) 87 builder.length -= (builder.length - originalIndex); 88 89 // save the flags 90 existingSource.graphPrecisionFlags = existingSource.graphPrecisionFlags | graphPrecisionFlag; 91 existingSource.concretePrecisionFlags = existingSource.concretePrecisionFlags | concretePrecisionFlag; 92 93 // if validate, we double check that the two function declarations are the same 94 if (m_Validate) 95 { 96 if (code != existingSource.code) 97 { 98 var errorMessage = string.Format("Function `{0}` has conflicting implementations:{1}{1}{2}{1}{1}{3}", name, Environment.NewLine, code, existingSource.code); 99 foreach (var n in existingSource.nodes) 100 n.owner.AddValidationError(n.objectId, errorMessage); 101 } 102 } 103 } 104 else 105 { 106 var newSource = new FunctionSource 107 { 108 code = code, 109 isGeneric = isGeneric, 110 graphPrecisionFlags = graphPrecisionFlag, 111 concretePrecisionFlags = concretePrecisionFlag, 112 nodes = new HashSet<AbstractMaterialNode> { builder.currentNode } 113 }; 114 115 m_Sources.Add(name, newSource); 116 names.Add(name); 117 } 118 119 // fully concretize any generic code by replacing any precision tokens by the node's concrete precision 120 if (isGeneric && (builder.length > originalIndex)) 121 { 122 int start = originalIndex; 123 int count = builder.length - start; 124 builder.Replace(PrecisionUtil.Token, concretePrecision.ToShaderString(), start, count); 125 } 126 } 127 128 public void ProvideFunction(string name, Action<ShaderStringBuilder> generator) 129 { 130 ProvideFunction(name, builder.currentNode.graphPrecision, builder.currentNode.concretePrecision, generator); 131 } 132 } 133}