VFXCodeGenerator.cs 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869
  1. using System;
  2. using System.IO;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Collections.Generic;
  6. using UnityEngine;
  7. using UnityEngine.VFX;
  8. using UnityEditor.ShaderGraph;
  9. using UnityEditor.Graphing.Util;
  10. using UnityEditor.ShaderGraph.Serialization;
  11. using Object = UnityEngine.Object;
  12. using System.Text.RegularExpressions;
  13. using System.Globalization;
  14. using UnityEngine.Profiling;
  15. namespace UnityEditor.VFX
  16. {
  17. static class VFXCodeGenerator
  18. {
  19. private static string GetIndent(string src, int index)
  20. {
  21. var indent = "";
  22. index--;
  23. while (index > 0 && (src[index] == ' ' || src[index] == '\t'))
  24. {
  25. indent = src[index] + indent;
  26. index--;
  27. }
  28. return indent;
  29. }
  30. //This function insure to keep padding while replacing a specific string
  31. private static void ReplaceMultiline(StringBuilder target, string targetQuery, StringBuilder value)
  32. {
  33. Profiler.BeginSample("ReplaceMultiline");
  34. string[] delim = { System.Environment.NewLine, "\n" };
  35. var valueLines = value.ToString().Split(delim, System.StringSplitOptions.None);
  36. if (valueLines.Length <= 1)
  37. {
  38. target.Replace(targetQuery, value.ToString());
  39. }
  40. else
  41. {
  42. while (true)
  43. {
  44. var targetCopy = target.ToString();
  45. var index = targetCopy.IndexOf(targetQuery);
  46. if (index == -1)
  47. {
  48. break;
  49. }
  50. var indent = GetIndent(targetCopy, index);
  51. var currentValue = new StringBuilder();
  52. foreach (var line in valueLines)
  53. {
  54. currentValue.Append(indent + line + '\n');
  55. }
  56. target.Replace(indent + targetQuery, currentValue.ToString());
  57. }
  58. }
  59. Profiler.EndSample();
  60. }
  61. internal static VFXShaderWriter GenerateLoadAttribute(string matching, VFXContext context)
  62. {
  63. var r = new VFXShaderWriter();
  64. var regex = new Regex(matching);
  65. var attributesFromContext = context.GetData().GetAttributes().Where(o => regex.IsMatch(o.attrib.name)).ToArray();
  66. var attributesSource = attributesFromContext.Where(a => context.GetData().IsSourceAttributeUsed(a.attrib, context)).ToArray();
  67. var attributesCurrent = attributesFromContext.Where(a => context.GetData().IsCurrentAttributeUsed(a.attrib, context) || (context.contextType == VFXContextType.Init && context.GetData().IsAttributeStored(a.attrib))).ToArray();
  68. //< Current Attribute
  69. foreach (var attribute in attributesCurrent.Select(o => o.attrib))
  70. {
  71. var name = attribute.GetNameInCode(VFXAttributeLocation.Current);
  72. if (attribute.name != VFXAttribute.EventCount.name)
  73. {
  74. if (context.contextType != VFXContextType.Init && context.GetData().IsAttributeStored(attribute))
  75. {
  76. r.WriteAssignement(attribute.type, name, context.GetData().GetLoadAttributeCode(attribute, VFXAttributeLocation.Current));
  77. }
  78. else
  79. {
  80. r.WriteAssignement(attribute.type, name, attribute.value.GetCodeString(null));
  81. }
  82. }
  83. else
  84. {
  85. var linkedOutCount = context.allLinkedOutputSlot.Count();
  86. r.WriteAssignement(attribute.type, name, attribute.value.GetCodeString(null));
  87. for (uint i = 0; i < linkedOutCount; ++i)
  88. {
  89. r.WriteLine();
  90. r.WriteFormat("uint {0}_{1} = 0u;", VFXAttribute.EventCount.name, VFXCodeGeneratorHelper.GeneratePrefix(i));
  91. }
  92. }
  93. r.WriteLine();
  94. }
  95. //< Source Attribute (default temporary behavior, source is always the initial current value except for init context)
  96. foreach (var attribute in attributesSource.Select(o => o.attrib))
  97. {
  98. var name = attribute.GetNameInCode(VFXAttributeLocation.Source);
  99. if (context.contextType == VFXContextType.Init)
  100. {
  101. r.WriteAssignement(attribute.type, name, context.GetData().GetLoadAttributeCode(attribute, VFXAttributeLocation.Source));
  102. }
  103. else
  104. {
  105. if (attributesCurrent.Any(o => o.attrib.name == attribute.name))
  106. {
  107. var reference = new VFXAttributeExpression(new VFXAttribute(attribute.name, attribute.value), VFXAttributeLocation.Current);
  108. r.WriteAssignement(reference.valueType, name, reference.GetCodeString(null));
  109. }
  110. else
  111. {
  112. r.WriteAssignement(attribute.type, name, attribute.value.GetCodeString(null));
  113. }
  114. }
  115. r.WriteLine();
  116. }
  117. return r;
  118. }
  119. private const string eventListOutName = "eventListOut";
  120. static private VFXShaderWriter GenerateStoreAttribute(string matching, VFXContext context, uint linkedOutCount)
  121. {
  122. var r = new VFXShaderWriter();
  123. var regex = new Regex(matching);
  124. var attributesFromContext = context.GetData().GetAttributes().Where(o => regex.IsMatch(o.attrib.name) &&
  125. context.GetData().IsAttributeStored(o.attrib) &&
  126. (context.contextType == VFXContextType.Init || context.GetData().IsCurrentAttributeWritten(o.attrib, context))).ToArray();
  127. foreach (var attribute in attributesFromContext.Select(o => o.attrib))
  128. {
  129. r.Write(context.GetData().GetStoreAttributeCode(attribute, new VFXAttributeExpression(attribute).GetCodeString(null)));
  130. r.WriteLine(';');
  131. }
  132. if (regex.IsMatch(VFXAttribute.EventCount.name))
  133. {
  134. for (uint i = 0; i < linkedOutCount; ++i)
  135. {
  136. var prefix = VFXCodeGeneratorHelper.GeneratePrefix(i);
  137. r.WriteLineFormat("for (uint i_{0} = 0; i_{0} < {1}_{0}; ++i_{0}) {2}_{0}.Append(index);", prefix, VFXAttribute.EventCount.name, eventListOutName);
  138. }
  139. }
  140. return r;
  141. }
  142. static internal VFXShaderWriter GenerateLoadParameter(string matching, VFXNamedExpression[] namedExpressions, Dictionary<VFXExpression, string> expressionToName)
  143. {
  144. var r = new VFXShaderWriter();
  145. var regex = new Regex(matching);
  146. var filteredNamedExpressions = namedExpressions.Where(o => regex.IsMatch(o.name) &&
  147. !(expressionToName.ContainsKey(o.exp) && expressionToName[o.exp] == o.name)); // if parameter already in the global scope, there's nothing to do
  148. bool needScope = false;
  149. foreach (var namedExpression in filteredNamedExpressions)
  150. {
  151. r.WriteVariable(namedExpression.exp.valueType, namedExpression.name, "0");
  152. r.WriteLine();
  153. needScope = true;
  154. }
  155. if (needScope)
  156. {
  157. var expressionToNameLocal = new Dictionary<VFXExpression, string>(expressionToName);
  158. r.EnterScope();
  159. foreach (var namedExpression in filteredNamedExpressions)
  160. {
  161. if (!expressionToNameLocal.ContainsKey(namedExpression.exp))
  162. {
  163. r.WriteVariable(namedExpression.exp, expressionToNameLocal);
  164. r.WriteLine();
  165. }
  166. r.WriteAssignement(namedExpression.exp.valueType, namedExpression.name, expressionToNameLocal[namedExpression.exp]);
  167. r.WriteLine();
  168. }
  169. r.ExitScope();
  170. }
  171. return r;
  172. }
  173. static public StringBuilder Build(VFXContext context, VFXCompilationMode compilationMode, VFXContextCompiledData contextData, HashSet<string> dependencies)
  174. {
  175. var templatePath = string.Format("{0}.template", context.codeGeneratorTemplate);
  176. dependencies.Add(AssetDatabase.AssetPathToGUID(templatePath));
  177. return Build(context, templatePath, compilationMode, contextData, dependencies);
  178. }
  179. static private void GetFunctionName(VFXBlock block, out string functionName, out string comment)
  180. {
  181. var settings = block.GetSettings(true).ToArray();
  182. if (settings.Length > 0)
  183. {
  184. comment = "";
  185. int hash = 0;
  186. foreach (var setting in settings)
  187. {
  188. var value = setting.value;
  189. hash = (hash * 397) ^ value.GetHashCode();
  190. comment += string.Format("{0}:{1} ", setting.field.Name, value.ToString());
  191. }
  192. functionName = string.Format("{0}_{1}", block.GetType().Name, hash.ToString("X"));
  193. }
  194. else
  195. {
  196. comment = null;
  197. functionName = block.GetType().Name;
  198. }
  199. }
  200. static private string FormatPath(string path)
  201. {
  202. return Path.GetFullPath(path)
  203. .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
  204. #if !UNITY_EDITOR_LINUX
  205. .ToLowerInvariant()
  206. #endif
  207. ;
  208. }
  209. static IEnumerable<Match> GetUniqueMatches(string regexStr, string src)
  210. {
  211. var regex = new Regex(regexStr);
  212. var matches = regex.Matches(src);
  213. return matches.Cast<Match>().GroupBy(m => m.Groups[0].Value).Select(g => g.First());
  214. }
  215. static private StringBuilder GetFlattenedTemplateContent(string path, List<string> includes, IEnumerable<string> defines, HashSet<string> dependencies)
  216. {
  217. var formattedPath = FormatPath(path);
  218. if (includes.Contains(formattedPath))
  219. {
  220. var includeHierarchy = new StringBuilder(string.Format("Cyclic VFXInclude dependency detected: {0}\n", formattedPath));
  221. foreach (var str in Enumerable.Reverse<string>(includes))
  222. includeHierarchy.Append(str + '\n');
  223. throw new InvalidOperationException(includeHierarchy.ToString());
  224. }
  225. includes.Add(formattedPath);
  226. var templateContent = new StringBuilder(System.IO.File.ReadAllText(formattedPath));
  227. foreach (var match in GetUniqueMatches("\\${VFXInclude(RP|)\\(\\\"(.*?)\\\"\\)(,.*)?}", templateContent.ToString()))
  228. {
  229. var groups = match.Groups;
  230. var renderPipelineInclude = groups[1].Value == "RP";
  231. var includePath = groups[2].Value;
  232. if (groups.Count > 3 && !String.IsNullOrEmpty(groups[2].Value))
  233. {
  234. var allDefines = groups[3].Value.Split(new char[] { ',', ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
  235. var neededDefines = allDefines.Where(d => d[0] != '!');
  236. var forbiddenDefines = allDefines.Except(neededDefines).Select(d => d.Substring(1));
  237. if (!neededDefines.All(d => defines.Contains(d)) || forbiddenDefines.Any(d => defines.Contains(d)))
  238. {
  239. ReplaceMultiline(templateContent, groups[0].Value, new StringBuilder());
  240. continue;
  241. }
  242. }
  243. string absolutePath;
  244. if (renderPipelineInclude)
  245. absolutePath = VFXLibrary.currentSRPBinder.templatePath + "/" + includePath;
  246. else
  247. absolutePath = VisualEffectGraphPackageInfo.assetPackagePath + "/" + includePath;
  248. dependencies.Add(AssetDatabase.AssetPathToGUID(absolutePath));
  249. var includeBuilder = GetFlattenedTemplateContent(absolutePath, includes, defines, dependencies);
  250. ReplaceMultiline(templateContent, groups[0].Value, includeBuilder);
  251. }
  252. includes.Remove(formattedPath);
  253. return templateContent;
  254. }
  255. static private void SubstituteMacros(StringBuilder builder)
  256. {
  257. var definesToCode = new Dictionary<string, string>();
  258. var source = builder.ToString();
  259. Regex beginRegex = new Regex("\\${VFXBegin:(.*)}");
  260. int currentPos = -1;
  261. int builderOffset = 0;
  262. while ((currentPos = source.IndexOf("${")) != -1)
  263. {
  264. int endPos = source.IndexOf('}', currentPos);
  265. if (endPos == -1)
  266. throw new FormatException("Ill-formed VFX tag (Missing closing brace");
  267. var tag = source.Substring(currentPos, endPos - currentPos + 1);
  268. // Replace any tag found
  269. string macro;
  270. if (definesToCode.TryGetValue(tag, out macro))
  271. {
  272. builder.Remove(currentPos + builderOffset, tag.Length);
  273. var indentedMacro = macro.Replace("\n", "\n" + GetIndent(source, currentPos));
  274. builder.Insert(currentPos + builderOffset, indentedMacro);
  275. }
  276. else
  277. {
  278. const string endStr = "${VFXEnd}";
  279. var match = beginRegex.Match(source, currentPos, tag.Length);
  280. if (match.Success)
  281. {
  282. var macroStartPos = match.Index + match.Length;
  283. var macroEndCodePos = source.IndexOf(endStr, macroStartPos);
  284. if (macroEndCodePos == -1)
  285. throw new FormatException("${VFXBegin} found without ${VFXEnd}");
  286. var defineStr = "${" + match.Groups[1].Value + "}";
  287. definesToCode[defineStr] = source.Substring(macroStartPos, macroEndCodePos - macroStartPos);
  288. // Remove the define in builder
  289. builder.Remove(match.Index + builderOffset, macroEndCodePos - match.Index + endStr.Length);
  290. }
  291. else if (tag == endStr)
  292. throw new FormatException("${VFXEnd} found without ${VFXBegin}");
  293. else // Remove undefined tag
  294. builder.Remove(currentPos + builderOffset, tag.Length);
  295. }
  296. builderOffset += currentPos;
  297. source = builder.ToString(builderOffset, builder.Length - builderOffset);
  298. }
  299. }
  300. internal static void BuildContextBlocks(VFXContext context, VFXContextCompiledData contextData,
  301. out string blockFunctionContent,
  302. out string blockCallFunctionContent)
  303. {
  304. var linkedEventOut = context.allLinkedOutputSlot.Where(s => ((VFXModel)s.owner).GetFirstOfType<VFXContext>().CanBeCompiled()).ToList();
  305. //< Block processor
  306. var blockFunction = new VFXShaderWriter();
  307. var blockCallFunction = new VFXShaderWriter();
  308. var blockDeclared = new HashSet<string>();
  309. var expressionToName = context.GetData().GetAttributes().ToDictionary(o => new VFXAttributeExpression(o.attrib) as VFXExpression, o => (new VFXAttributeExpression(o.attrib)).GetCodeString(null));
  310. expressionToName = expressionToName.Union(contextData.uniformMapper.expressionToCode).ToDictionary(s => s.Key, s => s.Value);
  311. int cpt = 0;
  312. foreach (var current in context.activeFlattenedChildrenWithImplicit)
  313. {
  314. BuildBlock(contextData, linkedEventOut, blockFunction, blockCallFunction, blockDeclared, expressionToName, current, ref cpt);
  315. }
  316. blockFunctionContent = blockFunction.builder.ToString();
  317. blockCallFunctionContent = blockCallFunction.builder.ToString();
  318. }
  319. internal static void BuildParameterBuffer(VFXContextCompiledData contextData, IEnumerable<string> filteredOutTextures, out string parameterBufferContent)
  320. {
  321. var parameterBuffer = new VFXShaderWriter();
  322. parameterBuffer.WriteCBuffer(contextData.uniformMapper, "parameters");
  323. parameterBuffer.WriteLine();
  324. parameterBuffer.WriteBufferTypeDeclaration(contextData.graphicsBufferUsage.Values.Distinct());
  325. parameterBuffer.WriteLine();
  326. parameterBuffer.WriteBuffer(contextData.uniformMapper, contextData.graphicsBufferUsage);
  327. parameterBuffer.WriteLine();
  328. parameterBuffer.WriteTexture(contextData.uniformMapper, filteredOutTextures);
  329. parameterBufferContent = parameterBuffer.ToString();
  330. }
  331. internal static void BuildVertexProperties(VFXContext context, VFXContextCompiledData contextData, out string vertexProperties)
  332. {
  333. var expressionToName = context.GetData().GetAttributes().ToDictionary(o => new VFXAttributeExpression(o.attrib) as VFXExpression, o => (new VFXAttributeExpression(o.attrib)).GetCodeString(null));
  334. expressionToName = expressionToName.Union(contextData.uniformMapper.expressionToCode).ToDictionary(s => s.Key, s => s.Value);
  335. var mainParameters = contextData.gpuMapper.CollectExpression(-1).ToArray();
  336. var additionalVertexProperties = new VFXShaderWriter();
  337. foreach (string vertexParameter in context.vertexParameters)
  338. {
  339. var filteredNamedExpression = mainParameters.FirstOrDefault(o => vertexParameter == o.name &&
  340. !(expressionToName.ContainsKey(o.exp) && expressionToName[o.exp] == o.name)); // if parameter already in the global scope, there's nothing to do
  341. if (filteredNamedExpression.exp != null)
  342. {
  343. additionalVertexProperties.WriteVariable(filteredNamedExpression.exp.valueType, filteredNamedExpression.name + "__", "0");
  344. var expressionToNameLocal = new Dictionary<VFXExpression, string>(expressionToName);
  345. additionalVertexProperties.EnterScope();
  346. {
  347. if (!expressionToNameLocal.ContainsKey(filteredNamedExpression.exp))
  348. {
  349. additionalVertexProperties.WriteVariable(filteredNamedExpression.exp, expressionToNameLocal);
  350. additionalVertexProperties.WriteLine();
  351. }
  352. additionalVertexProperties.WriteAssignement(filteredNamedExpression.exp.valueType, filteredNamedExpression.name + "__", expressionToNameLocal[filteredNamedExpression.exp]);
  353. additionalVertexProperties.WriteLine();
  354. }
  355. additionalVertexProperties.ExitScope();
  356. }
  357. }
  358. vertexProperties = additionalVertexProperties.ToString();
  359. }
  360. internal static void BuildVertexPropertiesAssign(VFXContext context, VFXContextCompiledData contextData, out string buildVertexPropertiesGeneration)
  361. {
  362. var expressionToName = context.GetData().GetAttributes().ToDictionary(o => new VFXAttributeExpression(o.attrib) as VFXExpression, o => (new VFXAttributeExpression(o.attrib)).GetCodeString(null));
  363. expressionToName = expressionToName.Union(contextData.uniformMapper.expressionToCode).ToDictionary(s => s.Key, s => s.Value);
  364. var mainParameters = contextData.gpuMapper.CollectExpression(-1).ToArray();
  365. var vertexInputsGeneration = new VFXShaderWriter();
  366. foreach (string vertexParameter in context.vertexParameters)
  367. {
  368. var filteredNamedExpression = mainParameters.FirstOrDefault(o => vertexParameter == o.name);
  369. if (filteredNamedExpression.exp == null)
  370. throw new InvalidOperationException(string.Format("Cannot find vertex property : {0}", vertexParameter));
  371. // If the parameter is in the global scope, read from the cbuffer directly (no suffix).
  372. if (!(expressionToName.ContainsKey(filteredNamedExpression.exp) && expressionToName[filteredNamedExpression.exp] == filteredNamedExpression.name))
  373. vertexInputsGeneration.WriteAssignement(filteredNamedExpression.exp.valueType, $"properties.{filteredNamedExpression.name}", $"{filteredNamedExpression.name}__");
  374. else
  375. vertexInputsGeneration.WriteAssignement(filteredNamedExpression.exp.valueType, $"properties.{filteredNamedExpression.name}", $"{filteredNamedExpression.name}");
  376. vertexInputsGeneration.WriteLine();
  377. }
  378. buildVertexPropertiesGeneration = vertexInputsGeneration.ToString();
  379. }
  380. internal static void BuildInterpolatorBlocks(VFXContext context, VFXContextCompiledData contextData,
  381. out string interpolatorsGeneration)
  382. {
  383. var expressionToName = context.GetData().GetAttributes().ToDictionary(o => new VFXAttributeExpression(o.attrib) as VFXExpression, o => (new VFXAttributeExpression(o.attrib)).GetCodeString(null));
  384. expressionToName = expressionToName.Union(contextData.uniformMapper.expressionToCode).ToDictionary(s => s.Key, s => s.Value);
  385. var mainParameters = contextData.gpuMapper.CollectExpression(-1).ToArray();
  386. var additionalInterpolantsGeneration = new VFXShaderWriter();
  387. var additionalInterpolantsPreparation = new VFXShaderWriter();
  388. foreach (string fragmentParameter in context.fragmentParameters)
  389. {
  390. var filteredNamedExpression = mainParameters.FirstOrDefault(o => fragmentParameter == o.name &&
  391. !(expressionToName.ContainsKey(o.exp) && expressionToName[o.exp] == o.name)); // if parameter already in the global scope, there's nothing to do
  392. if (filteredNamedExpression.exp != null)
  393. {
  394. additionalInterpolantsGeneration.WriteVariable(filteredNamedExpression.exp.valueType, filteredNamedExpression.name + "__", "0");
  395. var expressionToNameLocal = new Dictionary<VFXExpression, string>(expressionToName);
  396. additionalInterpolantsGeneration.EnterScope();
  397. {
  398. if (!expressionToNameLocal.ContainsKey(filteredNamedExpression.exp))
  399. {
  400. additionalInterpolantsGeneration.WriteVariable(filteredNamedExpression.exp, expressionToNameLocal);
  401. additionalInterpolantsGeneration.WriteLine();
  402. }
  403. additionalInterpolantsGeneration.WriteAssignement(filteredNamedExpression.exp.valueType, filteredNamedExpression.name + "__", expressionToNameLocal[filteredNamedExpression.exp]);
  404. additionalInterpolantsGeneration.WriteLine();
  405. }
  406. additionalInterpolantsGeneration.ExitScope();
  407. additionalInterpolantsGeneration.WriteAssignement(filteredNamedExpression.exp.valueType, "output." + filteredNamedExpression.name, filteredNamedExpression.name + "__");
  408. additionalInterpolantsPreparation.WriteVariable(filteredNamedExpression.exp.valueType, filteredNamedExpression.name, "i." + filteredNamedExpression.name);
  409. }
  410. }
  411. interpolatorsGeneration = additionalInterpolantsGeneration.ToString();
  412. }
  413. internal static void BuildFragInputsGeneration(VFXContext context, VFXContextCompiledData contextData, bool useFragInputs, out string buildFragInputsGeneration)
  414. {
  415. var expressionToName = context.GetData().GetAttributes().ToDictionary(o => new VFXAttributeExpression(o.attrib) as VFXExpression, o => (new VFXAttributeExpression(o.attrib)).GetCodeString(null));
  416. expressionToName = expressionToName.Union(contextData.uniformMapper.expressionToCode).ToDictionary(s => s.Key, s => s.Value);
  417. var mainParameters = contextData.gpuMapper.CollectExpression(-1).ToArray();
  418. var fragInputsGeneration = new VFXShaderWriter();
  419. foreach (string fragmentParameter in context.fragmentParameters)
  420. {
  421. var filteredNamedExpression = mainParameters.FirstOrDefault(o => fragmentParameter == o.name);
  422. if (filteredNamedExpression.exp == null)
  423. throw new InvalidOperationException("FragInputs generation failed to find expected parameter: " + fragmentParameter);
  424. var isInterpolant = !(expressionToName.ContainsKey(filteredNamedExpression.exp) && expressionToName[filteredNamedExpression.exp] == filteredNamedExpression.name);
  425. var surfaceSetter = useFragInputs ? "output.vfx" : "output";
  426. fragInputsGeneration.WriteAssignement(filteredNamedExpression.exp.valueType, $"{surfaceSetter}.{filteredNamedExpression.name}", $"{(isInterpolant ? "input." : string.Empty)}{filteredNamedExpression.name}");
  427. fragInputsGeneration.WriteLine();
  428. }
  429. buildFragInputsGeneration = fragInputsGeneration.ToString();
  430. }
  431. internal static void BuildPixelPropertiesAssign(VFXContext context, VFXContextCompiledData contextData, bool useFragInputs, out string buildFragInputsGeneration)
  432. {
  433. var expressionToName = context.GetData().GetAttributes().ToDictionary(o => new VFXAttributeExpression(o.attrib) as VFXExpression, o => (new VFXAttributeExpression(o.attrib)).GetCodeString(null));
  434. expressionToName = expressionToName.Union(contextData.uniformMapper.expressionToCode).ToDictionary(s => s.Key, s => s.Value);
  435. var mainParameters = contextData.gpuMapper.CollectExpression(-1).ToArray();
  436. var fragInputsGeneration = new VFXShaderWriter();
  437. foreach (string fragmentParameter in context.fragmentParameters)
  438. {
  439. var filteredNamedExpression = mainParameters.FirstOrDefault(o => fragmentParameter == o.name);
  440. var surfaceGetter = useFragInputs ? "fragInputs.vfx" : "fragInputs";
  441. fragInputsGeneration.WriteAssignement(filteredNamedExpression.exp.valueType, $"properties.{filteredNamedExpression.name}", $"{surfaceGetter}.{filteredNamedExpression.name}");
  442. fragInputsGeneration.WriteLine();
  443. }
  444. buildFragInputsGeneration = fragInputsGeneration.ToString();
  445. }
  446. static private StringBuilder Build(VFXContext context, string templatePath, VFXCompilationMode compilationMode, VFXContextCompiledData contextData, HashSet<string> dependencies)
  447. {
  448. if (!context.SetupCompilation())
  449. return null;
  450. if (context is VFXShaderGraphParticleOutput shaderGraphContext &&
  451. shaderGraphContext.GetOrRefreshShaderGraphObject() != null &&
  452. shaderGraphContext.GetOrRefreshShaderGraphObject().generatesWithShaderGraph &&
  453. VFXViewPreference.generateOutputContextWithShaderGraph)
  454. {
  455. var result = TryBuildFromShaderGraph(shaderGraphContext, contextData);
  456. // If the ShaderGraph generation path was successful, use the result, otherwise fall back to the VFX generation path.
  457. if (result != null)
  458. {
  459. context.EndCompilation();
  460. return result;
  461. }
  462. }
  463. var stringBuilder = GetFlattenedTemplateContent(templatePath, new List<string>(), context.additionalDefines, dependencies);
  464. var allCurrentAttributes = context.GetData().GetAttributes().Where(a =>
  465. (context.GetData().IsCurrentAttributeUsed(a.attrib, context)) ||
  466. (context.contextType == VFXContextType.Init && context.GetData().IsAttributeStored(a.attrib))); // In init, needs to declare all stored attributes for intialization
  467. var allSourceAttributes = context.GetData().GetAttributes().Where(a => (context.GetData().IsSourceAttributeUsed(a.attrib, context)));
  468. var globalDeclaration = new VFXShaderWriter();
  469. globalDeclaration.WriteBufferTypeDeclaration(contextData.graphicsBufferUsage.Values.Distinct());
  470. globalDeclaration.WriteLine();
  471. globalDeclaration.WriteCBuffer(contextData.uniformMapper, "parameters");
  472. globalDeclaration.WriteLine();
  473. globalDeclaration.WriteBuffer(contextData.uniformMapper, contextData.graphicsBufferUsage);
  474. globalDeclaration.WriteLine();
  475. globalDeclaration.WriteTexture(contextData.uniformMapper);
  476. globalDeclaration.WriteAttributeStruct(allCurrentAttributes.Select(a => a.attrib), "VFXAttributes");
  477. globalDeclaration.WriteLine();
  478. globalDeclaration.WriteAttributeStruct(allSourceAttributes.Select(a => a.attrib), "VFXSourceAttributes");
  479. globalDeclaration.WriteLine();
  480. var linkedEventOut = context.allLinkedOutputSlot.Where(s => ((VFXModel)s.owner).GetFirstOfType<VFXContext>().CanBeCompiled()).ToList();
  481. globalDeclaration.WriteEventBuffers(eventListOutName, linkedEventOut.Count);
  482. //< Block processor
  483. var blockFunction = new VFXShaderWriter();
  484. var blockCallFunction = new VFXShaderWriter();
  485. var blockDeclared = new HashSet<string>();
  486. var expressionToName = context.GetData().GetAttributes().ToDictionary(o => new VFXAttributeExpression(o.attrib) as VFXExpression, o => (new VFXAttributeExpression(o.attrib)).GetCodeString(null));
  487. expressionToName = expressionToName.Union(contextData.uniformMapper.expressionToCode).ToDictionary(s => s.Key, s => s.Value);
  488. int cpt = 0;
  489. foreach (var current in context.activeFlattenedChildrenWithImplicit)
  490. {
  491. BuildBlock(contextData, linkedEventOut, blockFunction, blockCallFunction, blockDeclared, expressionToName, current, ref cpt);
  492. }
  493. //< Final composition
  494. var globalIncludeContent = new VFXShaderWriter();
  495. globalIncludeContent.WriteLine("#define NB_THREADS_PER_GROUP 64");
  496. globalIncludeContent.WriteLine("#define HAS_VFX_ATTRIBUTES 1");
  497. globalIncludeContent.WriteLine("#define VFX_PASSDEPTH_ACTUAL (0)");
  498. globalIncludeContent.WriteLine("#define VFX_PASSDEPTH_MOTION_VECTOR (1)");
  499. globalIncludeContent.WriteLine("#define VFX_PASSDEPTH_SELECTION (2)");
  500. globalIncludeContent.WriteLine("#define VFX_PASSDEPTH_PICKING (3)");
  501. globalIncludeContent.WriteLine("#define VFX_PASSDEPTH_SHADOW (4)");
  502. foreach (var attribute in allCurrentAttributes)
  503. globalIncludeContent.WriteLineFormat("#define VFX_USE_{0}_{1} 1", attribute.attrib.name.ToUpper(CultureInfo.InvariantCulture), "CURRENT");
  504. foreach (var attribute in allSourceAttributes)
  505. globalIncludeContent.WriteLineFormat("#define VFX_USE_{0}_{1} 1", attribute.attrib.name.ToUpper(CultureInfo.InvariantCulture), "SOURCE");
  506. foreach (var additionnalHeader in context.additionalDataHeaders)
  507. globalIncludeContent.WriteLine(additionnalHeader);
  508. foreach (var additionnalDefine in context.additionalDefines)
  509. globalIncludeContent.WriteLineFormat("#define {0}{1}", additionnalDefine, additionnalDefine.Contains(' ') ? "" : " 1");
  510. var renderTemplatePipePath = VFXLibrary.currentSRPBinder.templatePath;
  511. var renderRuntimePipePath = VFXLibrary.currentSRPBinder.runtimePath;
  512. if (!context.codeGeneratorCompute && !string.IsNullOrEmpty(renderTemplatePipePath))
  513. {
  514. string renderPipePasses = renderTemplatePipePath + "/VFXPasses.template";
  515. globalIncludeContent.Write(GetFlattenedTemplateContent(renderPipePasses, new List<string>(), context.additionalDefines, dependencies));
  516. }
  517. if (context.GetData() is ISpaceable)
  518. {
  519. var spaceable = context.GetData() as ISpaceable;
  520. globalIncludeContent.WriteLineFormat("#define {0} 1", spaceable.space == VFXCoordinateSpace.World ? "VFX_WORLD_SPACE" : "VFX_LOCAL_SPACE");
  521. }
  522. globalIncludeContent.WriteLineFormat("#include \"{0}/VFXDefines.hlsl\"", renderRuntimePipePath);
  523. var perPassIncludeContent = new VFXShaderWriter();
  524. string renderPipeCommon = context.doesIncludeCommonCompute ? "Packages/com.unity.visualeffectgraph/Shaders/Common/VFXCommonCompute.hlsl" : renderRuntimePipePath + "/VFXCommon.hlsl";
  525. perPassIncludeContent.WriteLine("#include \"" + renderPipeCommon + "\"");
  526. perPassIncludeContent.WriteLine("#include \"Packages/com.unity.visualeffectgraph/Shaders/VFXCommon.hlsl\"");
  527. if (!context.codeGeneratorCompute)
  528. {
  529. perPassIncludeContent.WriteLine("#include \"Packages/com.unity.visualeffectgraph/Shaders/VFXCommonOutput.hlsl\"");
  530. }
  531. // Per-block includes
  532. var includes = Enumerable.Empty<string>();
  533. foreach (var block in context.activeFlattenedChildrenWithImplicit)
  534. includes = includes.Concat(block.includes);
  535. var uniqueIncludes = new HashSet<string>(includes);
  536. foreach (var includePath in uniqueIncludes)
  537. perPassIncludeContent.WriteLine(string.Format("#include \"{0}\"", includePath));
  538. ReplaceMultiline(stringBuilder, "${VFXGlobalInclude}", globalIncludeContent.builder);
  539. ReplaceMultiline(stringBuilder, "${VFXGlobalDeclaration}", globalDeclaration.builder);
  540. ReplaceMultiline(stringBuilder, "${VFXPerPassInclude}", perPassIncludeContent.builder);
  541. ReplaceMultiline(stringBuilder, "${VFXGeneratedBlockFunction}", blockFunction.builder);
  542. ReplaceMultiline(stringBuilder, "${VFXProcessBlocks}", blockCallFunction.builder);
  543. var mainParameters = contextData.gpuMapper.CollectExpression(-1).ToArray();
  544. foreach (var match in GetUniqueMatches("\\${VFXLoadParameter:{(.*?)}}", stringBuilder.ToString()))
  545. {
  546. var str = match.Groups[0].Value;
  547. var pattern = match.Groups[1].Value;
  548. var loadParameters = GenerateLoadParameter(pattern, mainParameters, expressionToName);
  549. ReplaceMultiline(stringBuilder, str, loadParameters.builder);
  550. }
  551. var additionalInterpolantsGeneration = new VFXShaderWriter();
  552. var additionalInterpolantsDeclaration = new VFXShaderWriter();
  553. var additionalInterpolantsPreparation = new VFXShaderWriter();
  554. int normSemantic = 0;
  555. foreach (string fragmentParameter in context.fragmentParameters)
  556. {
  557. var filteredNamedExpression = mainParameters.FirstOrDefault(o => fragmentParameter == o.name &&
  558. !(expressionToName.ContainsKey(o.exp) && expressionToName[o.exp] == o.name)); // if parameter already in the global scope, there's nothing to do
  559. if (filteredNamedExpression.exp != null)
  560. {
  561. if (!filteredNamedExpression.exp.Is(VFXExpression.Flags.Constant))
  562. {
  563. additionalInterpolantsDeclaration.WriteDeclaration(filteredNamedExpression.exp.valueType, filteredNamedExpression.name, $"NORMAL{normSemantic++}");
  564. additionalInterpolantsGeneration.WriteVariable(filteredNamedExpression.exp.valueType, filteredNamedExpression.name + "__", "0");
  565. var expressionToNameLocal = new Dictionary<VFXExpression, string>(expressionToName);
  566. additionalInterpolantsGeneration.EnterScope();
  567. {
  568. if (!expressionToNameLocal.ContainsKey(filteredNamedExpression.exp))
  569. {
  570. additionalInterpolantsGeneration.WriteVariable(filteredNamedExpression.exp, expressionToNameLocal);
  571. additionalInterpolantsGeneration.WriteLine();
  572. }
  573. additionalInterpolantsGeneration.WriteAssignement(filteredNamedExpression.exp.valueType, filteredNamedExpression.name + "__", expressionToNameLocal[filteredNamedExpression.exp]);
  574. additionalInterpolantsGeneration.WriteLine();
  575. }
  576. additionalInterpolantsGeneration.ExitScope();
  577. additionalInterpolantsGeneration.WriteAssignement(filteredNamedExpression.exp.valueType, "o." + filteredNamedExpression.name, filteredNamedExpression.name + "__");
  578. additionalInterpolantsPreparation.WriteVariable(filteredNamedExpression.exp.valueType, filteredNamedExpression.name, "i." + filteredNamedExpression.name);
  579. }
  580. else
  581. additionalInterpolantsPreparation.WriteVariable(filteredNamedExpression.exp.valueType, filteredNamedExpression.name, filteredNamedExpression.exp.GetCodeString(null));
  582. }
  583. }
  584. ReplaceMultiline(stringBuilder, "${VFXAdditionalInterpolantsGeneration}", additionalInterpolantsGeneration.builder);
  585. ReplaceMultiline(stringBuilder, "${VFXAdditionalInterpolantsDeclaration}", additionalInterpolantsDeclaration.builder);
  586. ReplaceMultiline(stringBuilder, "${VFXAdditionalInterpolantsPreparation}", additionalInterpolantsPreparation.builder);
  587. //< Load Attribute
  588. if (stringBuilder.ToString().Contains("${VFXLoadAttributes}"))
  589. {
  590. var loadAttributes = GenerateLoadAttribute(".*", context);
  591. ReplaceMultiline(stringBuilder, "${VFXLoadAttributes}", loadAttributes.builder);
  592. }
  593. foreach (var match in GetUniqueMatches("\\${VFXLoadAttributes:{(.*?)}}", stringBuilder.ToString()))
  594. {
  595. var str = match.Groups[0].Value;
  596. var pattern = match.Groups[1].Value;
  597. var loadAttributes = GenerateLoadAttribute(pattern, context);
  598. ReplaceMultiline(stringBuilder, str, loadAttributes.builder);
  599. }
  600. //< Store Attribute
  601. if (stringBuilder.ToString().Contains("${VFXStoreAttributes}"))
  602. {
  603. var storeAttribute = GenerateStoreAttribute(".*", context, (uint)linkedEventOut.Count);
  604. ReplaceMultiline(stringBuilder, "${VFXStoreAttributes}", storeAttribute.builder);
  605. }
  606. foreach (var match in GetUniqueMatches("\\${VFXStoreAttributes:{(.*?)}}", stringBuilder.ToString()))
  607. {
  608. var str = match.Groups[0].Value;
  609. var pattern = match.Groups[1].Value;
  610. var storeAttributes = GenerateStoreAttribute(pattern, context, (uint)linkedEventOut.Count);
  611. ReplaceMultiline(stringBuilder, str, storeAttributes.builder);
  612. }
  613. foreach (var addionalReplacement in context.additionalReplacements)
  614. {
  615. ReplaceMultiline(stringBuilder, addionalReplacement.Key, addionalReplacement.Value.builder);
  616. }
  617. // Replace defines
  618. SubstituteMacros(stringBuilder);
  619. if (VFXViewPreference.advancedLogs)
  620. Debug.LogFormat("GENERATED_OUTPUT_FILE_FOR : {0}\n{1}", context.ToString(), stringBuilder.ToString());
  621. context.EndCompilation();
  622. return stringBuilder;
  623. }
  624. private static StringBuilder TryBuildFromShaderGraph(VFXShaderGraphParticleOutput context, VFXContextCompiledData contextData)
  625. {
  626. var stringBuilder = new StringBuilder();
  627. // Reconstruct the ShaderGraph.
  628. var path = AssetDatabase.GetAssetPath(context.GetOrRefreshShaderGraphObject());
  629. List<PropertyCollector.TextureInfo> configuredTextures;
  630. AssetCollection assetCollection = new AssetCollection();
  631. MinimalGraphData.GatherMinimalDependenciesFromFile(path, assetCollection);
  632. var textGraph = File.ReadAllText(path, Encoding.UTF8);
  633. var graph = new GraphData
  634. {
  635. messageManager = new MessageManager(),
  636. assetGuid = AssetDatabase.AssetPathToGUID(path)
  637. };
  638. MultiJson.Deserialize(graph, textGraph);
  639. graph.OnEnable();
  640. graph.ValidateGraph();
  641. // Check the validity of the shader graph (unsupported keywords or shader property usage).
  642. if (VFXLibrary.currentSRPBinder == null || !VFXLibrary.currentSRPBinder.IsGraphDataValid(graph))
  643. return null;
  644. var target = graph.activeTargets.Where(o =>
  645. {
  646. if (o.SupportsVFX())
  647. {
  648. //We are assuming the target has been implemented in the same package than srp binder.
  649. var srpBinderAssembly = VFXLibrary.currentSRPBinder.GetType().Assembly;
  650. var targetAssembly = o.GetType().Assembly;
  651. if (srpBinderAssembly == targetAssembly)
  652. return true;
  653. }
  654. return false;
  655. }).FirstOrDefault();
  656. if (target == null || !target.TryConfigureContextData(context, contextData))
  657. return null;
  658. // Use ShaderGraph to generate the VFX shader.
  659. var text = ShaderGraphImporter.GetShaderText(path, out configuredTextures, assetCollection, graph, GenerationMode.VFX, new[] { target });
  660. // Append the shader + strip the name header (VFX stamps one in later on).
  661. stringBuilder.Append(text);
  662. stringBuilder.Remove(0, text.IndexOf("{", StringComparison.Ordinal));
  663. return stringBuilder;
  664. }
  665. private static void BuildBlock(VFXContextCompiledData contextData, List<VFXSlot> linkedEventOut, VFXShaderWriter blockFunction, VFXShaderWriter blockCallFunction, HashSet<string> blockDeclared, Dictionary<VFXExpression, string> expressionToName, VFXBlock block, ref int blockIndex)
  666. {
  667. var parameters = block.mergedAttributes.Select(o =>
  668. {
  669. return new VFXShaderWriter.FunctionParameter
  670. {
  671. name = o.attrib.name,
  672. expression = new VFXAttributeExpression(o.attrib) as VFXExpression,
  673. mode = o.mode
  674. };
  675. }).ToList();
  676. foreach (var parameter in block.parameters)
  677. {
  678. var expReduced = contextData.gpuMapper.FromNameAndId(parameter.name, blockIndex);
  679. if (VFXExpression.IsTypeValidOnGPU(expReduced.valueType))
  680. {
  681. parameters.Add(new VFXShaderWriter.FunctionParameter
  682. {
  683. name = parameter.name,
  684. expression = expReduced,
  685. mode = VFXAttributeMode.None
  686. });
  687. }
  688. }
  689. string methodName, commentMethod;
  690. GetFunctionName(block, out methodName, out commentMethod);
  691. if (!blockDeclared.Contains(methodName))
  692. {
  693. blockDeclared.Add(methodName);
  694. blockFunction.WriteBlockFunction(contextData.gpuMapper,
  695. methodName,
  696. block.source,
  697. parameters,
  698. commentMethod);
  699. }
  700. //< Parameters (computed and/or extracted from uniform)
  701. var expressionToNameLocal = expressionToName;
  702. bool needScope = parameters.Any(o => !expressionToNameLocal.ContainsKey(o.expression));
  703. if (needScope)
  704. {
  705. expressionToNameLocal = new Dictionary<VFXExpression, string>(expressionToNameLocal);
  706. blockCallFunction.EnterScope();
  707. foreach (var exp in parameters.Select(o => o.expression))
  708. {
  709. if (expressionToNameLocal.ContainsKey(exp))
  710. {
  711. continue;
  712. }
  713. blockCallFunction.WriteVariable(exp, expressionToNameLocal);
  714. }
  715. }
  716. var indexEventCount = parameters.FindIndex(o => o.name == VFXAttribute.EventCount.name);
  717. if (indexEventCount != -1)
  718. {
  719. if ((parameters[indexEventCount].mode & VFXAttributeMode.Read) != 0)
  720. throw new InvalidOperationException(string.Format("{0} isn't expected as read (special case)", VFXAttribute.EventCount.name));
  721. blockCallFunction.WriteLine(string.Format("{0} = 0u;", VFXAttribute.EventCount.GetNameInCode(VFXAttributeLocation.Current)));
  722. }
  723. blockCallFunction.WriteCallFunction(methodName,
  724. parameters,
  725. contextData.gpuMapper,
  726. expressionToNameLocal);
  727. if (indexEventCount != -1)
  728. {
  729. foreach (var outputSlot in block.outputSlots.SelectMany(o => o.LinkedSlots))
  730. {
  731. var eventIndex = linkedEventOut.IndexOf(outputSlot);
  732. if (eventIndex != -1)
  733. blockCallFunction.WriteLineFormat("{0}_{1} += {2};", VFXAttribute.EventCount.name, VFXCodeGeneratorHelper.GeneratePrefix((uint)eventIndex), VFXAttribute.EventCount.GetNameInCode(VFXAttributeLocation.Current));
  734. }
  735. }
  736. if (needScope)
  737. blockCallFunction.ExitScope();
  738. blockIndex++;
  739. }
  740. }
  741. }