VFXExpressionContext.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.ObjectModel;
  4. using System.Linq;
  5. using UnityEngine.Profiling;
  6. using UnityEngine.VFX;
  7. namespace UnityEditor.VFX
  8. {
  9. [Flags]
  10. enum VFXExpressionContextOption
  11. {
  12. None = 0,
  13. Reduction = 1 << 0,
  14. CPUEvaluation = 1 << 1,
  15. ConstantFolding = 1 << 2,
  16. GPUDataTransformation = 1 << 3,
  17. PatchReadToEventAttribute = 1 << 4
  18. }
  19. abstract partial class VFXExpression
  20. {
  21. public class Context
  22. {
  23. private bool Has(VFXExpressionContextOption options)
  24. {
  25. return (m_ReductionOptions & options) == options;
  26. }
  27. private bool HasAny(VFXExpressionContextOption options)
  28. {
  29. return (m_ReductionOptions & options) != 0;
  30. }
  31. public Context(VFXExpressionContextOption reductionOption, List<VFXLayoutElementDesc> globalEventAttibutes = null)
  32. {
  33. m_ReductionOptions = reductionOption;
  34. m_GlobalEventAttribute = globalEventAttibutes;
  35. if (Has(VFXExpressionContextOption.CPUEvaluation) && Has(VFXExpressionContextOption.GPUDataTransformation))
  36. throw new ArgumentException("Invalid reduction options");
  37. }
  38. public void RegisterExpression(VFXExpression expression)
  39. {
  40. m_EndExpressions.Add(expression);
  41. }
  42. public void UnregisterExpression(VFXExpression expression)
  43. {
  44. Invalidate(expression);
  45. m_EndExpressions.Remove(expression);
  46. }
  47. public void Compile()
  48. {
  49. Profiler.BeginSample("VFXEditor.CompileExpressionContext");
  50. try
  51. {
  52. foreach (var exp in m_EndExpressions)
  53. Compile(exp);
  54. if (HasAny(VFXExpressionContextOption.GPUDataTransformation | VFXExpressionContextOption.PatchReadToEventAttribute))
  55. {
  56. var gpuTransformation = Has(VFXExpressionContextOption.GPUDataTransformation);
  57. var spawnEventPath = Has(VFXExpressionContextOption.PatchReadToEventAttribute);
  58. foreach (var exp in m_EndExpressions)
  59. m_ReducedCache[exp] = PatchVFXExpression(GetReduced(exp), null /* no source in end expression */, gpuTransformation, spawnEventPath, m_GlobalEventAttribute);
  60. }
  61. }
  62. finally
  63. {
  64. Profiler.EndSample();
  65. }
  66. }
  67. public void Recompile()
  68. {
  69. Invalidate();
  70. Compile();
  71. }
  72. private bool ShouldEvaluate(VFXExpression exp, VFXExpression[] reducedParents)
  73. {
  74. if (!HasAny(VFXExpressionContextOption.Reduction | VFXExpressionContextOption.CPUEvaluation | VFXExpressionContextOption.ConstantFolding))
  75. return false;
  76. if (exp.IsAny(Flags.NotCompilableOnCPU))
  77. return false;
  78. if (!Has(VFXExpressionContextOption.CPUEvaluation) && exp.IsAny(Flags.InvalidConstant))
  79. return false;
  80. if (!exp.Is(Flags.Value) && reducedParents.Length == 0) // not a value
  81. return false;
  82. Flags flag = Flags.Value;
  83. if (!Has(VFXExpressionContextOption.CPUEvaluation))
  84. flag |= Has(VFXExpressionContextOption.ConstantFolding) ? Flags.Foldable : Flags.Constant;
  85. if (exp.Is(Flags.Value) && ((exp.m_Flags & (flag | Flags.InvalidOnCPU)) != flag))
  86. return false;
  87. return reducedParents.All(e => (e.m_Flags & (flag | Flags.InvalidOnCPU)) == flag);
  88. }
  89. private VFXExpression PatchVFXExpression(VFXExpression input, VFXExpression targetExpression, bool insertGPUTransformation, bool patchReadAttributeForSpawn, IEnumerable<VFXLayoutElementDesc> globalEventAttribute)
  90. {
  91. if (insertGPUTransformation)
  92. {
  93. switch (input.valueType)
  94. {
  95. case VFXValueType.ColorGradient:
  96. input = new VFXExpressionBakeGradient(input);
  97. break;
  98. case VFXValueType.Curve:
  99. input = new VFXExpressionBakeCurve(input);
  100. break;
  101. case VFXValueType.Mesh:
  102. case VFXValueType.SkinnedMeshRenderer:
  103. if (targetExpression != null)
  104. {
  105. if (input.valueType == VFXValueType.Mesh)
  106. {
  107. switch (targetExpression.operation)
  108. {
  109. case VFXExpressionOperation.SampleMeshVertexFloat:
  110. case VFXExpressionOperation.SampleMeshVertexFloat2:
  111. case VFXExpressionOperation.SampleMeshVertexFloat3:
  112. case VFXExpressionOperation.SampleMeshVertexFloat4:
  113. case VFXExpressionOperation.SampleMeshVertexColor:
  114. var channelFormatAndDimensionAndStream = targetExpression.parents[2];
  115. channelFormatAndDimensionAndStream = Compile(channelFormatAndDimensionAndStream);
  116. if (!(channelFormatAndDimensionAndStream is VFXExpressionMeshChannelInfos))
  117. throw new InvalidOperationException("Unexpected type of expression in mesh sampling : " + channelFormatAndDimensionAndStream);
  118. input = new VFXExpressionVertexBufferFromMesh(input, channelFormatAndDimensionAndStream);
  119. break;
  120. case VFXExpressionOperation.SampleMeshIndex:
  121. input = new VFXExpressionIndexBufferFromMesh(input);
  122. break;
  123. default:
  124. throw new InvalidOperationException("Unexpected source operation for InsertGPUTransformation : " + targetExpression.operation);
  125. }
  126. }
  127. else //VFXValueType.SkinnedMeshRenderer
  128. {
  129. if (targetExpression is VFXExpressionSampleSkinnedMeshRendererFloat
  130. || targetExpression is VFXExpressionSampleSkinnedMeshRendererFloat2
  131. || targetExpression is VFXExpressionSampleSkinnedMeshRendererFloat3
  132. || targetExpression is VFXExpressionSampleSkinnedMeshRendererFloat4
  133. || targetExpression is VFXExpressionSampleSkinnedMeshRendererColor)
  134. {
  135. var channelFormatAndDimensionAndStream = targetExpression.parents[2];
  136. channelFormatAndDimensionAndStream = Compile(channelFormatAndDimensionAndStream);
  137. if (!(channelFormatAndDimensionAndStream is VFXExpressionMeshChannelInfos))
  138. throw new InvalidOperationException("Unexpected type of expression in skinned mesh sampling : " + channelFormatAndDimensionAndStream);
  139. input = new VFXExpressionVertexBufferFromSkinnedMeshRenderer(input, channelFormatAndDimensionAndStream);
  140. }
  141. else
  142. {
  143. throw new InvalidOperationException("Unexpected source operation for InsertGPUTransformation : " + targetExpression);
  144. }
  145. }
  146. } //else sourceExpression is null, we can't determine usage but it's possible if value is declared but not used.
  147. break;
  148. case VFXValueType.Buffer:
  149. {
  150. //Save expression usage for later HLSL shader generation
  151. if (targetExpression is VFXExpressionSampleBuffer)
  152. {
  153. var sampledType = (targetExpression as VFXExpressionSampleBuffer).GetSampledType();
  154. if (!m_GraphicsBufferUsageType.TryGetValue(input, out var registeredType))
  155. {
  156. m_GraphicsBufferUsageType.Add(input, sampledType);
  157. }
  158. else if (registeredType != sampledType)
  159. {
  160. throw new InvalidOperationException(string.Format("Diverging type usage for GraphicsBuffer : {0}, {1}", registeredType, sampledType));
  161. }
  162. }
  163. }
  164. break;
  165. default:
  166. //Nothing to patch on this type
  167. break;
  168. }
  169. if (input.valueType == VFXValueType.Buffer && targetExpression is VFXExpressionSampleBuffer)
  170. {
  171. if (!m_GraphicsBufferUsageType.ContainsKey(input))
  172. {
  173. m_GraphicsBufferUsageType.Add(input, (targetExpression as VFXExpressionSampleBuffer).GetSampledType());
  174. }
  175. }
  176. }
  177. if (patchReadAttributeForSpawn && input is VFXAttributeExpression)
  178. {
  179. var attribute = input as VFXAttributeExpression;
  180. if (attribute.attributeLocation == VFXAttributeLocation.Current)
  181. {
  182. if (globalEventAttribute == null)
  183. throw new InvalidOperationException("m_GlobalEventAttribute is null");
  184. var layoutDesc = globalEventAttribute.FirstOrDefault(o => o.name == attribute.attributeName);
  185. if (layoutDesc.name != attribute.attributeName)
  186. throw new InvalidOperationException("Unable to find " + attribute.attributeName + " in globalEventAttribute");
  187. input = new VFXReadEventAttributeExpression(attribute.attribute, layoutDesc.offset.element);
  188. }
  189. }
  190. return input;
  191. }
  192. public VFXExpression Compile(VFXExpression expression)
  193. {
  194. var gpuTransformation = Has(VFXExpressionContextOption.GPUDataTransformation);
  195. var patchReadAttributeForSpawn = Has(VFXExpressionContextOption.PatchReadToEventAttribute);
  196. VFXExpression reduced;
  197. if (!m_ReducedCache.TryGetValue(expression, out reduced))
  198. {
  199. var parents = expression.parents.Select(e =>
  200. {
  201. var parent = Compile(e);
  202. bool currentGPUTransformation = gpuTransformation
  203. && expression.IsAny(VFXExpression.Flags.NotCompilableOnCPU)
  204. && !parent.IsAny(VFXExpression.Flags.NotCompilableOnCPU);
  205. parent = PatchVFXExpression(parent, expression, currentGPUTransformation, patchReadAttributeForSpawn, m_GlobalEventAttribute);
  206. return parent;
  207. }).ToArray();
  208. if (ShouldEvaluate(expression, parents))
  209. {
  210. reduced = expression.Evaluate(parents);
  211. }
  212. else if (HasAny(VFXExpressionContextOption.Reduction | VFXExpressionContextOption.CPUEvaluation | VFXExpressionContextOption.ConstantFolding) || !parents.SequenceEqual(expression.parents))
  213. {
  214. reduced = expression.Reduce(parents);
  215. }
  216. else
  217. {
  218. reduced = expression;
  219. }
  220. m_ReducedCache[expression] = reduced;
  221. }
  222. return reduced;
  223. }
  224. public void Invalidate()
  225. {
  226. m_ReducedCache.Clear();
  227. m_GraphicsBufferUsageType.Clear();
  228. }
  229. public void Invalidate(VFXExpression expression)
  230. {
  231. m_ReducedCache.Remove(expression);
  232. }
  233. public VFXExpression GetReduced(VFXExpression expression)
  234. {
  235. VFXExpression reduced;
  236. m_ReducedCache.TryGetValue(expression, out reduced);
  237. return reduced != null ? reduced : expression;
  238. }
  239. private void AddReducedGraph(HashSet<VFXExpression> dst, VFXExpression exp)
  240. {
  241. if (!dst.Contains(exp))
  242. {
  243. dst.Add(exp);
  244. foreach (var parent in exp.parents)
  245. AddReducedGraph(dst, parent);
  246. }
  247. }
  248. public HashSet<VFXExpression> BuildAllReduced()
  249. {
  250. var reduced = new HashSet<VFXExpression>();
  251. foreach (var exp in m_EndExpressions)
  252. if (m_ReducedCache.ContainsKey(exp))
  253. AddReducedGraph(reduced, m_ReducedCache[exp]);
  254. return reduced;
  255. }
  256. public ReadOnlyCollection<VFXExpression> RegisteredExpressions { get { return m_EndExpressions.ToList().AsReadOnly(); } }
  257. public IEnumerable<KeyValuePair<VFXExpression, Type>> GraphicsBufferUsageType { get { return m_GraphicsBufferUsageType; } }
  258. private Dictionary<VFXExpression, VFXExpression> m_ReducedCache = new Dictionary<VFXExpression, VFXExpression>();
  259. private HashSet<VFXExpression> m_EndExpressions = new HashSet<VFXExpression>();
  260. private Dictionary<VFXExpression, Type> m_GraphicsBufferUsageType = new Dictionary<VFXExpression, Type>();
  261. private IEnumerable<VFXLayoutElementDesc> m_GlobalEventAttribute;
  262. private VFXExpressionContextOption m_ReductionOptions;
  263. }
  264. }
  265. }