ShaderGraphImporter.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569
  1. using UnityEngine;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Diagnostics.CodeAnalysis;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Text;
  8. using UnityEditor.Experimental.AssetImporters;
  9. using UnityEditor.Graphing;
  10. using UnityEditor.Graphing.Util;
  11. using UnityEditor.ShaderGraph.Internal;
  12. using Object = System.Object;
  13. namespace UnityEditor.ShaderGraph
  14. {
  15. [ScriptedImporter(31, Extension, 3)]
  16. class ShaderGraphImporter : ScriptedImporter
  17. {
  18. public const string Extension = "shadergraph";
  19. public const string k_ErrorShader = @"
  20. Shader ""Hidden/GraphErrorShader2""
  21. {
  22. SubShader
  23. {
  24. Pass
  25. {
  26. CGPROGRAM
  27. #pragma vertex vert
  28. #pragma fragment frag
  29. #pragma target 2.0
  30. #pragma multi_compile _ UNITY_SINGLE_PASS_STEREO STEREO_INSTANCING_ON STEREO_MULTIVIEW_ON
  31. #include ""UnityCG.cginc""
  32. struct appdata_t {
  33. float4 vertex : POSITION;
  34. UNITY_VERTEX_INPUT_INSTANCE_ID
  35. };
  36. struct v2f {
  37. float4 vertex : SV_POSITION;
  38. UNITY_VERTEX_OUTPUT_STEREO
  39. };
  40. v2f vert (appdata_t v)
  41. {
  42. v2f o;
  43. UNITY_SETUP_INSTANCE_ID(v);
  44. UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
  45. o.vertex = UnityObjectToClipPos(v.vertex);
  46. return o;
  47. }
  48. fixed4 frag (v2f i) : SV_Target
  49. {
  50. return fixed4(1,0,1,1);
  51. }
  52. ENDCG
  53. }
  54. }
  55. Fallback Off
  56. }";
  57. [SuppressMessage("ReSharper", "UnusedMember.Local")]
  58. static string[] GatherDependenciesFromSourceFile(string assetPath)
  59. {
  60. try
  61. {
  62. return MinimalGraphData.GetDependencyPaths(assetPath);
  63. }
  64. catch (Exception e)
  65. {
  66. Debug.LogException(e);
  67. return new string[0];
  68. }
  69. }
  70. public override void OnImportAsset(AssetImportContext ctx)
  71. {
  72. var oldShader = AssetDatabase.LoadAssetAtPath<Shader>(ctx.assetPath);
  73. if (oldShader != null)
  74. ShaderUtil.ClearShaderMessages(oldShader);
  75. List<PropertyCollector.TextureInfo> configuredTextures;
  76. string path = ctx.assetPath;
  77. var sourceAssetDependencyPaths = new List<string>();
  78. UnityEngine.Object mainObject;
  79. var textGraph = File.ReadAllText(path, Encoding.UTF8);
  80. GraphData graph = JsonUtility.FromJson<GraphData>(textGraph);
  81. graph.messageManager = new MessageManager();
  82. graph.assetGuid = AssetDatabase.AssetPathToGUID(path);
  83. graph.OnEnable();
  84. graph.ValidateGraph();
  85. if (graph.outputNode is VfxMasterNode vfxMasterNode)
  86. {
  87. var vfxAsset = GenerateVfxShaderGraphAsset(vfxMasterNode);
  88. mainObject = vfxAsset;
  89. }
  90. else
  91. {
  92. var text = GetShaderText(path, out configuredTextures, sourceAssetDependencyPaths,graph);
  93. var shader = ShaderUtil.CreateShaderAsset(text, false);
  94. if (graph != null && graph.messageManager.nodeMessagesChanged)
  95. {
  96. foreach (var pair in graph.messageManager.GetNodeMessages())
  97. {
  98. var node = graph.GetNodeFromTempId(pair.Key);
  99. MessageManager.Log(node, path, pair.Value.First(), shader);
  100. }
  101. }
  102. EditorMaterialUtility.SetShaderDefaults(
  103. shader,
  104. configuredTextures.Where(x => x.modifiable).Select(x => x.name).ToArray(),
  105. configuredTextures.Where(x => x.modifiable).Select(x => EditorUtility.InstanceIDToObject(x.textureId) as Texture).ToArray());
  106. EditorMaterialUtility.SetShaderNonModifiableDefaults(
  107. shader,
  108. configuredTextures.Where(x => !x.modifiable).Select(x => x.name).ToArray(),
  109. configuredTextures.Where(x => !x.modifiable).Select(x => EditorUtility.InstanceIDToObject(x.textureId) as Texture).ToArray());
  110. mainObject = shader;
  111. }
  112. Texture2D texture = Resources.Load<Texture2D>("Icons/sg_graph_icon@64");
  113. ctx.AddObjectToAsset("MainAsset", mainObject, texture);
  114. ctx.SetMainObject(mainObject);
  115. var metadata = ScriptableObject.CreateInstance<ShaderGraphMetadata>();
  116. metadata.hideFlags = HideFlags.HideInHierarchy;
  117. if (graph != null)
  118. {
  119. metadata.outputNodeTypeName = graph.outputNode.GetType().FullName;
  120. }
  121. ctx.AddObjectToAsset("Metadata", metadata);
  122. foreach (var sourceAssetDependencyPath in sourceAssetDependencyPaths.Distinct())
  123. {
  124. // Ensure that dependency path is relative to project
  125. if (!sourceAssetDependencyPath.StartsWith("Packages/") && !sourceAssetDependencyPath.StartsWith("Assets/"))
  126. {
  127. Debug.LogWarning($"Invalid dependency path: {sourceAssetDependencyPath}", mainObject);
  128. continue;
  129. }
  130. ctx.DependsOnSourceAsset(sourceAssetDependencyPath);
  131. }
  132. }
  133. internal static string GetShaderText(string path, out List<PropertyCollector.TextureInfo> configuredTextures, List<string> sourceAssetDependencyPaths, GraphData graph)
  134. {
  135. string shaderString = null;
  136. var shaderName = Path.GetFileNameWithoutExtension(path);
  137. try
  138. {
  139. if (!string.IsNullOrEmpty(graph.path))
  140. shaderName = graph.path + "/" + shaderName;
  141. shaderString = ((IMasterNode)graph.outputNode).GetShader(GenerationMode.ForReals, shaderName, out configuredTextures, sourceAssetDependencyPaths);
  142. if (graph.messageManager.nodeMessagesChanged)
  143. {
  144. shaderString = null;
  145. }
  146. }
  147. catch (Exception e)
  148. {
  149. Debug.LogException(e);
  150. configuredTextures = new List<PropertyCollector.TextureInfo>();
  151. // ignored
  152. }
  153. return shaderString ?? k_ErrorShader.Replace("Hidden/GraphErrorShader2", shaderName);
  154. }
  155. internal static string GetShaderText(string path, out List<PropertyCollector.TextureInfo> configuredTextures, List<string> sourceAssetDependencyPaths, out GraphData graph)
  156. {
  157. var textGraph = File.ReadAllText(path, Encoding.UTF8);
  158. graph = JsonUtility.FromJson<GraphData>(textGraph);
  159. graph.messageManager = new MessageManager();
  160. graph.assetGuid = AssetDatabase.AssetPathToGUID(path);
  161. graph.OnEnable();
  162. graph.ValidateGraph();
  163. return GetShaderText(path, out configuredTextures, sourceAssetDependencyPaths, graph);
  164. }
  165. internal static string GetShaderText(string path, out List<PropertyCollector.TextureInfo> configuredTextures)
  166. {
  167. var textGraph = File.ReadAllText(path, Encoding.UTF8);
  168. GraphData graph = JsonUtility.FromJson<GraphData>(textGraph);
  169. graph.messageManager = new MessageManager();
  170. graph.assetGuid = AssetDatabase.AssetPathToGUID(path);
  171. graph.OnEnable();
  172. graph.ValidateGraph();
  173. return GetShaderText(path, out configuredTextures, null,graph );
  174. }
  175. static ShaderGraphVfxAsset GenerateVfxShaderGraphAsset(VfxMasterNode masterNode)
  176. {
  177. var nl = Environment.NewLine;
  178. var indent = new string(' ', 4);
  179. var asset = ScriptableObject.CreateInstance<ShaderGraphVfxAsset>();
  180. var result = asset.compilationResult = new GraphCompilationResult();
  181. var mode = GenerationMode.ForReals;
  182. var graph = masterNode.owner;
  183. asset.lit = masterNode.lit.isOn;
  184. var assetGuid = masterNode.owner.assetGuid;
  185. var assetPath = AssetDatabase.GUIDToAssetPath(assetGuid);
  186. var hlslName = NodeUtils.GetHLSLSafeName(Path.GetFileNameWithoutExtension(assetPath));
  187. var ports = new List<MaterialSlot>();
  188. masterNode.GetInputSlots(ports);
  189. var nodes = new List<AbstractMaterialNode>();
  190. NodeUtils.DepthFirstCollectNodesFromNode(nodes, masterNode);
  191. var bodySb = new ShaderStringBuilder(1);
  192. var registry = new FunctionRegistry(new ShaderStringBuilder(), true);
  193. foreach (var properties in graph.properties)
  194. {
  195. properties.ValidateConcretePrecision(graph.concretePrecision);
  196. }
  197. foreach (var node in nodes)
  198. {
  199. if (node is IGeneratesBodyCode bodyGenerator)
  200. {
  201. bodySb.currentNode = node;
  202. bodyGenerator.GenerateNodeCode(bodySb, mode);
  203. bodySb.ReplaceInCurrentMapping(PrecisionUtil.Token, node.concretePrecision.ToShaderString());
  204. }
  205. if (node is IGeneratesFunction generatesFunction)
  206. {
  207. registry.builder.currentNode = node;
  208. generatesFunction.GenerateNodeFunction(registry, mode);
  209. }
  210. }
  211. bodySb.currentNode = null;
  212. var portNodeSets = new HashSet<AbstractMaterialNode>[ports.Count];
  213. for (var portIndex = 0; portIndex < ports.Count; portIndex++)
  214. {
  215. var port = ports[portIndex];
  216. var nodeSet = new HashSet<AbstractMaterialNode>();
  217. NodeUtils.CollectNodeSet(nodeSet, port);
  218. portNodeSets[portIndex] = nodeSet;
  219. }
  220. var portPropertySets = new HashSet<Guid>[ports.Count];
  221. for (var portIndex = 0; portIndex < ports.Count; portIndex++)
  222. {
  223. portPropertySets[portIndex] = new HashSet<Guid>();
  224. }
  225. foreach (var node in nodes)
  226. {
  227. if (!(node is PropertyNode propertyNode))
  228. {
  229. continue;
  230. }
  231. for (var portIndex = 0; portIndex < ports.Count; portIndex++)
  232. {
  233. var portNodeSet = portNodeSets[portIndex];
  234. if (portNodeSet.Contains(node))
  235. {
  236. portPropertySets[portIndex].Add(propertyNode.propertyGuid);
  237. }
  238. }
  239. }
  240. var shaderProperties = new PropertyCollector();
  241. foreach (var node in nodes)
  242. {
  243. node.CollectShaderProperties(shaderProperties, GenerationMode.ForReals);
  244. }
  245. asset.SetTextureInfos(shaderProperties.GetConfiguredTexutres());
  246. var codeSnippets = new List<string>();
  247. var portCodeIndices = new List<int>[ports.Count];
  248. var sharedCodeIndices = new List<int>();
  249. for (var i = 0; i < portCodeIndices.Length; i++)
  250. {
  251. portCodeIndices[i] = new List<int>();
  252. }
  253. sharedCodeIndices.Add(codeSnippets.Count);
  254. codeSnippets.Add($"#include \"Packages/com.unity.shadergraph/ShaderGraphLibrary/Functions.hlsl\"{nl}");
  255. for (var registryIndex = 0; registryIndex < registry.names.Count; registryIndex++)
  256. {
  257. var name = registry.names[registryIndex];
  258. var source = registry.sources[name];
  259. var precision = source.nodes.First().concretePrecision;
  260. var hasPrecisionMismatch = false;
  261. var nodeNames = new HashSet<string>();
  262. foreach (var node in source.nodes)
  263. {
  264. nodeNames.Add(node.name);
  265. if (node.concretePrecision != precision)
  266. {
  267. hasPrecisionMismatch = true;
  268. break;
  269. }
  270. }
  271. if (hasPrecisionMismatch)
  272. {
  273. var message = new StringBuilder($"Precision mismatch for function {name}:");
  274. foreach (var node in source.nodes)
  275. {
  276. message.AppendLine($"{node.name} ({node.guid}): {node.concretePrecision}");
  277. }
  278. throw new InvalidOperationException(message.ToString());
  279. }
  280. var code = source.code.Replace(PrecisionUtil.Token, precision.ToShaderString());
  281. code = $"// Node: {string.Join(", ", nodeNames)}{nl}{code}";
  282. var codeIndex = codeSnippets.Count;
  283. codeSnippets.Add(code + nl);
  284. for (var portIndex = 0; portIndex < ports.Count; portIndex++)
  285. {
  286. var portNodeSet = portNodeSets[portIndex];
  287. foreach (var node in source.nodes)
  288. {
  289. if (portNodeSet.Contains(node))
  290. {
  291. portCodeIndices[portIndex].Add(codeIndex);
  292. break;
  293. }
  294. }
  295. }
  296. }
  297. foreach (var property in graph.properties)
  298. {
  299. if (property.isExposable && property.generatePropertyBlock)
  300. {
  301. continue;
  302. }
  303. for (var portIndex = 0; portIndex < ports.Count; portIndex++)
  304. {
  305. var portPropertySet = portPropertySets[portIndex];
  306. if (portPropertySet.Contains(property.guid))
  307. {
  308. portCodeIndices[portIndex].Add(codeSnippets.Count);
  309. }
  310. }
  311. codeSnippets.Add($"// Property: {property.displayName}{nl}{property.GetPropertyDeclarationString()}{nl}{nl}");
  312. }
  313. var inputStructName = $"SG_Input_{assetGuid}";
  314. var outputStructName = $"SG_Output_{assetGuid}";
  315. var evaluationFunctionName = $"SG_Evaluate_{assetGuid}";
  316. #region Input Struct
  317. sharedCodeIndices.Add(codeSnippets.Count);
  318. codeSnippets.Add($"struct {inputStructName}{nl}{{{nl}");
  319. #region Requirements
  320. var portRequirements = new ShaderGraphRequirements[ports.Count];
  321. for (var portIndex = 0; portIndex < ports.Count; portIndex++)
  322. {
  323. portRequirements[portIndex] = ShaderGraphRequirements.FromNodes(portNodeSets[portIndex].ToList(), ports[portIndex].stageCapability);
  324. }
  325. var portIndices = new List<int>();
  326. portIndices.Capacity = ports.Count;
  327. void AddRequirementsSnippet(Func<ShaderGraphRequirements, bool> predicate, string snippet)
  328. {
  329. portIndices.Clear();
  330. for (var portIndex = 0; portIndex < ports.Count; portIndex++)
  331. {
  332. if (predicate(portRequirements[portIndex]))
  333. {
  334. portIndices.Add(portIndex);
  335. }
  336. }
  337. if (portIndices.Count > 0)
  338. {
  339. foreach (var portIndex in portIndices)
  340. {
  341. portCodeIndices[portIndex].Add(codeSnippets.Count);
  342. }
  343. codeSnippets.Add($"{indent}{snippet};{nl}");
  344. }
  345. }
  346. void AddCoordinateSpaceSnippets(InterpolatorType interpolatorType, Func<ShaderGraphRequirements, NeededCoordinateSpace> selector)
  347. {
  348. foreach (var space in EnumInfo<CoordinateSpace>.values)
  349. {
  350. var neededSpace = space.ToNeededCoordinateSpace();
  351. AddRequirementsSnippet(r => (selector(r) & neededSpace) > 0, $"float3 {space.ToVariableName(interpolatorType)}");
  352. }
  353. }
  354. // TODO: Rework requirements system to make this better
  355. AddCoordinateSpaceSnippets(InterpolatorType.Normal, r => r.requiresNormal);
  356. AddCoordinateSpaceSnippets(InterpolatorType.Tangent, r => r.requiresTangent);
  357. AddCoordinateSpaceSnippets(InterpolatorType.BiTangent, r => r.requiresBitangent);
  358. AddCoordinateSpaceSnippets(InterpolatorType.ViewDirection, r => r.requiresViewDir);
  359. AddCoordinateSpaceSnippets(InterpolatorType.Position, r => r.requiresPosition);
  360. AddRequirementsSnippet(r => r.requiresVertexColor, $"float4 {ShaderGeneratorNames.VertexColor}");
  361. AddRequirementsSnippet(r => r.requiresScreenPosition, $"float4 {ShaderGeneratorNames.ScreenPosition}");
  362. AddRequirementsSnippet(r => r.requiresFaceSign, $"float4 {ShaderGeneratorNames.FaceSign}");
  363. foreach (var uvChannel in EnumInfo<UVChannel>.values)
  364. {
  365. AddRequirementsSnippet(r => r.requiresMeshUVs.Contains(uvChannel), $"half4 {uvChannel.GetUVName()}");
  366. }
  367. AddRequirementsSnippet(r => r.requiresTime, $"float3 {ShaderGeneratorNames.TimeParameters}");
  368. #endregion
  369. sharedCodeIndices.Add(codeSnippets.Count);
  370. codeSnippets.Add($"}};{nl}{nl}");
  371. #endregion
  372. #region Output Struct
  373. sharedCodeIndices.Add(codeSnippets.Count);
  374. codeSnippets.Add($"struct {outputStructName}{nl}{{");
  375. for (var portIndex = 0; portIndex < ports.Count; portIndex++)
  376. {
  377. var port = ports[portIndex];
  378. portCodeIndices[portIndex].Add(codeSnippets.Count);
  379. codeSnippets.Add($"{nl}{indent}{port.concreteValueType.ToShaderString(graph.concretePrecision)} {port.shaderOutputName}_{port.id};");
  380. }
  381. sharedCodeIndices.Add(codeSnippets.Count);
  382. codeSnippets.Add($"{nl}}};{nl}{nl}");
  383. #endregion
  384. #region Graph Function
  385. sharedCodeIndices.Add(codeSnippets.Count);
  386. codeSnippets.Add($"{outputStructName} {evaluationFunctionName}({nl}{indent}{inputStructName} IN");
  387. var inputProperties = new List<AbstractShaderProperty>();
  388. var portPropertyIndices = new List<int>[ports.Count];
  389. for (var portIndex = 0; portIndex < ports.Count; portIndex++)
  390. {
  391. portPropertyIndices[portIndex] = new List<int>();
  392. }
  393. foreach (var property in graph.properties)
  394. {
  395. if (!property.isExposable || !property.generatePropertyBlock)
  396. {
  397. continue;
  398. }
  399. var propertyIndex = inputProperties.Count;
  400. var codeIndex = codeSnippets.Count;
  401. for (var portIndex = 0; portIndex < ports.Count; portIndex++)
  402. {
  403. var portPropertySet = portPropertySets[portIndex];
  404. if (portPropertySet.Contains(property.guid))
  405. {
  406. portCodeIndices[portIndex].Add(codeIndex);
  407. portPropertyIndices[portIndex].Add(propertyIndex);
  408. }
  409. }
  410. inputProperties.Add(property);
  411. codeSnippets.Add($",{nl}{indent}/* Property: {property.displayName} */ {property.GetPropertyAsArgumentString()}");
  412. }
  413. sharedCodeIndices.Add(codeSnippets.Count);
  414. codeSnippets.Add($"){nl}{{");
  415. #region Node Code
  416. for (var mappingIndex = 0; mappingIndex < bodySb.mappings.Count; mappingIndex++)
  417. {
  418. var mapping = bodySb.mappings[mappingIndex];
  419. var code = bodySb.ToString(mapping.startIndex, mapping.count);
  420. if (string.IsNullOrWhiteSpace(code))
  421. {
  422. continue;
  423. }
  424. code = $"{nl}{indent}// Node: {mapping.node.name}{nl}{code}";
  425. var codeIndex = codeSnippets.Count;
  426. codeSnippets.Add(code);
  427. for (var portIndex = 0; portIndex < ports.Count; portIndex++)
  428. {
  429. var portNodeSet = portNodeSets[portIndex];
  430. if (portNodeSet.Contains(mapping.node))
  431. {
  432. portCodeIndices[portIndex].Add(codeIndex);
  433. }
  434. }
  435. }
  436. #endregion
  437. #region Output Mapping
  438. sharedCodeIndices.Add(codeSnippets.Count);
  439. codeSnippets.Add($"{nl}{indent}// {masterNode.name}{nl}{indent}{outputStructName} OUT;{nl}");
  440. // Output mapping
  441. for (var portIndex = 0; portIndex < ports.Count; portIndex++)
  442. {
  443. var port = ports[portIndex];
  444. portCodeIndices[portIndex].Add(codeSnippets.Count);
  445. codeSnippets.Add($"{indent}OUT.{port.shaderOutputName}_{port.id} = {masterNode.GetSlotValue(port.id, GenerationMode.ForReals, graph.concretePrecision)};{nl}");
  446. }
  447. #endregion
  448. // Function end
  449. sharedCodeIndices.Add(codeSnippets.Count);
  450. codeSnippets.Add($"{indent}return OUT;{nl}}}{nl}");
  451. #endregion
  452. result.codeSnippets = codeSnippets.ToArray();
  453. result.sharedCodeIndices = sharedCodeIndices.ToArray();
  454. result.outputCodeIndices = new IntArray[ports.Count];
  455. for (var i = 0; i < ports.Count; i++)
  456. {
  457. result.outputCodeIndices[i] = portCodeIndices[i].ToArray();
  458. }
  459. asset.SetOutputs(ports.Select((t, i) => new OutputMetadata(i, t.shaderOutputName,t.id)).ToArray());
  460. asset.evaluationFunctionName = evaluationFunctionName;
  461. asset.inputStructName = inputStructName;
  462. asset.outputStructName = outputStructName;
  463. asset.portRequirements = portRequirements;
  464. asset.concretePrecision = graph.concretePrecision;
  465. asset.SetProperties(inputProperties);
  466. asset.outputPropertyIndices = new IntArray[ports.Count];
  467. for (var portIndex = 0; portIndex < ports.Count; portIndex++)
  468. {
  469. asset.outputPropertyIndices[portIndex] = portPropertyIndices[portIndex].ToArray();
  470. }
  471. return asset;
  472. }
  473. }
  474. }