VFXExpressionAbstract.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using UnityEditor.VFX;
  5. using UnityEngine;
  6. using UnityEngine.VFX;
  7. using UnityObject = UnityEngine.Object;
  8. namespace UnityEditor.VFX
  9. {
  10. static class VFXReflectionHelper
  11. {
  12. public static T[] CollectStaticReadOnlyExpression<T>(Type expressionType, System.Reflection.BindingFlags additionnalFlag = System.Reflection.BindingFlags.Public)
  13. {
  14. var members = expressionType.GetFields(System.Reflection.BindingFlags.Static | additionnalFlag)
  15. .Where(m => m.IsInitOnly && m.FieldType == typeof(T))
  16. .ToArray();
  17. var expressions = members.Select(m => (T)m.GetValue(null)).ToArray();
  18. return expressions;
  19. }
  20. }
  21. abstract partial class VFXExpression
  22. {
  23. public struct Operands
  24. {
  25. public static readonly int OperandCount = 4;
  26. int data0;
  27. int data1;
  28. int data2;
  29. int data3;
  30. public Operands(int defaultValue)
  31. {
  32. data0 = defaultValue;
  33. data1 = defaultValue;
  34. data2 = defaultValue;
  35. data3 = defaultValue;
  36. }
  37. // This ugly code is for optimization purpose (no garbage created)
  38. public int this[int index]
  39. {
  40. get
  41. {
  42. switch (index)
  43. {
  44. case 0: return data0;
  45. case 1: return data1;
  46. case 2: return data2;
  47. case 3: return data3;
  48. default: throw new IndexOutOfRangeException();
  49. }
  50. }
  51. set
  52. {
  53. switch (index)
  54. {
  55. case 0: data0 = value; break;
  56. case 1: data1 = value; break;
  57. case 2: data2 = value; break;
  58. case 3: data3 = value; break;
  59. default: throw new IndexOutOfRangeException();
  60. }
  61. }
  62. }
  63. public int[] ToArray()
  64. {
  65. return new int[] { data0, data1, data2, data3 };
  66. }
  67. }
  68. [Flags]
  69. public enum Flags
  70. {
  71. None = 0,
  72. Value = 1 << 0, // Expression is a value, get/set can be called on it
  73. Foldable = 1 << 1, // Expression is not a constant but can be folded anyway
  74. Constant = 1 << 2, // Expression is a constant, it can be folded
  75. InvalidOnGPU = 1 << 3, // Expression can be evaluated on GPU
  76. InvalidOnCPU = 1 << 4, // Expression can be evaluated on CPU
  77. InvalidConstant = 1 << 5, // Expression can be folded (for UI) but constant folding is forbidden
  78. PerElement = 1 << 6, // Expression is per element
  79. PerSpawn = 1 << 7, // Expression relies on event attribute or spawn context
  80. NotCompilableOnCPU = InvalidOnCPU | PerElement //Helper to filter out invalid expression on CPU
  81. }
  82. public static bool IsFloatValueType(VFXValueType valueType)
  83. {
  84. return valueType == VFXValueType.Float
  85. || valueType == VFXValueType.Float2
  86. || valueType == VFXValueType.Float3
  87. || valueType == VFXValueType.Float4;
  88. }
  89. public static bool IsUIntValueType(VFXValueType valueType)
  90. {
  91. return valueType == VFXValueType.Uint32;
  92. }
  93. public static bool IsIntValueType(VFXValueType valueType)
  94. {
  95. return valueType == VFXValueType.Int32;
  96. }
  97. public static bool IsBoolValueType(VFXValueType valueType)
  98. {
  99. return valueType == VFXValueType.Boolean;
  100. }
  101. public static int TypeToSize(VFXValueType type)
  102. {
  103. return VFXExpressionHelper.GetSizeOfType(type);
  104. }
  105. public static string TypeToCode(VFXValueType type)
  106. {
  107. switch (type)
  108. {
  109. case VFXValueType.Float: return "float";
  110. case VFXValueType.Float2: return "float2";
  111. case VFXValueType.Float3: return "float3";
  112. case VFXValueType.Float4: return "float4";
  113. case VFXValueType.Int32: return "int";
  114. case VFXValueType.Uint32: return "uint";
  115. case VFXValueType.Texture2D: return "Texture2D";
  116. case VFXValueType.Texture2DArray: return "Texture2DArray";
  117. case VFXValueType.Texture3D: return "Texture3D";
  118. case VFXValueType.TextureCube: return "TextureCube";
  119. case VFXValueType.TextureCubeArray: return "TextureCubeArray";
  120. case VFXValueType.CameraBuffer: return "CameraBuffer";
  121. case VFXValueType.Matrix4x4: return "float4x4";
  122. case VFXValueType.Mesh:
  123. case VFXValueType.SkinnedMeshRenderer:
  124. case VFXValueType.Buffer: return "ByteAddressBuffer";
  125. case VFXValueType.Boolean: return "bool";
  126. }
  127. throw new NotImplementedException(type.ToString());
  128. }
  129. // As certain type of uniforms are not handled in material, we need to use floats instead
  130. public static string TypeToUniformCode(VFXValueType type)
  131. {
  132. switch (type)
  133. {
  134. case VFXValueType.Float: return "float";
  135. case VFXValueType.Float2: return "float2";
  136. case VFXValueType.Float3: return "float3";
  137. case VFXValueType.Float4: return "float4";
  138. case VFXValueType.Int32: return "float";
  139. case VFXValueType.Uint32: return "float";
  140. case VFXValueType.Matrix4x4: return "float4x4";
  141. case VFXValueType.Boolean: return "float";
  142. }
  143. throw new NotImplementedException(type.ToString());
  144. }
  145. public static Type TypeToType(VFXValueType type)
  146. {
  147. switch (type)
  148. {
  149. case VFXValueType.Float: return typeof(float);
  150. case VFXValueType.Float2: return typeof(Vector2);
  151. case VFXValueType.Float3: return typeof(Vector3);
  152. case VFXValueType.Float4: return typeof(Vector4);
  153. case VFXValueType.Int32: return typeof(int);
  154. case VFXValueType.Uint32: return typeof(uint);
  155. case VFXValueType.Texture2D: return typeof(Texture);
  156. case VFXValueType.Texture2DArray: return typeof(Texture);
  157. case VFXValueType.Texture3D: return typeof(Texture);
  158. case VFXValueType.TextureCube: return typeof(Texture);
  159. case VFXValueType.TextureCubeArray: return typeof(Texture);
  160. case VFXValueType.CameraBuffer: return typeof(CameraBuffer);
  161. case VFXValueType.Matrix4x4: return typeof(Matrix4x4);
  162. case VFXValueType.Mesh: return typeof(Mesh);
  163. case VFXValueType.Curve: return typeof(AnimationCurve);
  164. case VFXValueType.ColorGradient: return typeof(Gradient);
  165. case VFXValueType.Boolean: return typeof(bool);
  166. }
  167. throw new NotImplementedException(type.ToString());
  168. }
  169. public static bool IsTypeValidOnGPU(VFXValueType type)
  170. {
  171. switch (type)
  172. {
  173. case VFXValueType.Float:
  174. case VFXValueType.Float2:
  175. case VFXValueType.Float3:
  176. case VFXValueType.Float4:
  177. case VFXValueType.Int32:
  178. case VFXValueType.Uint32:
  179. case VFXValueType.Texture2D:
  180. case VFXValueType.Texture2DArray:
  181. case VFXValueType.Texture3D:
  182. case VFXValueType.TextureCube:
  183. case VFXValueType.TextureCubeArray:
  184. case VFXValueType.CameraBuffer:
  185. case VFXValueType.Matrix4x4:
  186. case VFXValueType.Boolean:
  187. return true;
  188. }
  189. return false;
  190. }
  191. public static bool IsTypeConstantFoldable(VFXValueType type)
  192. {
  193. switch (type)
  194. {
  195. //Mesh API can modify the vertex count & layout.
  196. //Thus, all mesh related expression should never been constant folded while generating code.
  197. // The same goes for textures
  198. case VFXValueType.Texture2D:
  199. case VFXValueType.Texture2DArray:
  200. case VFXValueType.Texture3D:
  201. case VFXValueType.TextureCube:
  202. case VFXValueType.TextureCubeArray:
  203. case VFXValueType.CameraBuffer:
  204. case VFXValueType.Mesh:
  205. case VFXValueType.SkinnedMeshRenderer:
  206. case VFXValueType.Buffer:
  207. return false;
  208. }
  209. return true;
  210. }
  211. public static bool IsTexture(Type type)
  212. {
  213. var valueType = GetVFXValueTypeFromType(type);
  214. return IsTexture(valueType);
  215. }
  216. public static bool IsTexture(VFXValueType type)
  217. {
  218. switch (type)
  219. {
  220. case VFXValueType.Texture2D:
  221. case VFXValueType.Texture2DArray:
  222. case VFXValueType.Texture3D:
  223. case VFXValueType.TextureCube:
  224. case VFXValueType.TextureCubeArray:
  225. case VFXValueType.CameraBuffer:
  226. return true;
  227. }
  228. return false;
  229. }
  230. public static bool IsBufferOnGPU(VFXValueType type)
  231. {
  232. return type == VFXValueType.Mesh || type == VFXValueType.Buffer;
  233. }
  234. public static bool IsUniform(VFXValueType type)
  235. {
  236. switch (type)
  237. {
  238. case VFXValueType.Float:
  239. case VFXValueType.Float2:
  240. case VFXValueType.Float3:
  241. case VFXValueType.Float4:
  242. case VFXValueType.Int32:
  243. case VFXValueType.Uint32:
  244. case VFXValueType.Matrix4x4:
  245. case VFXValueType.Boolean:
  246. return true;
  247. }
  248. return false;
  249. }
  250. public static Type GetMatchingScalar(Type type)
  251. {
  252. var vfxType = GetVFXValueTypeFromType(type);
  253. if (vfxType == VFXValueType.None)
  254. {
  255. var affinityFallback = VFXOperatorDynamicOperand.GetTypeAffinityList(type).GetEnumerator();
  256. while (affinityFallback.MoveNext() && vfxType == VFXValueType.None)
  257. {
  258. vfxType = GetVFXValueTypeFromType(affinityFallback.Current);
  259. }
  260. }
  261. return TypeToType(GetMatchingScalar(vfxType));
  262. }
  263. public static VFXValueType GetMatchingScalar(VFXValueType type)
  264. {
  265. if (IsFloatValueType(type))
  266. return VFXValueType.Float;
  267. if (IsUIntValueType(type))
  268. return VFXValueType.Uint32;
  269. if (IsIntValueType(type))
  270. return VFXValueType.Int32;
  271. return VFXValueType.None;
  272. }
  273. public static VFXValueType GetVFXValueTypeFromType(Type type)
  274. {
  275. if (type == typeof(float)) return VFXValueType.Float;
  276. if (type == typeof(Vector2)) return VFXValueType.Float2;
  277. if (type == typeof(Vector3)) return VFXValueType.Float3;
  278. if (type == typeof(Vector4)) return VFXValueType.Float4;
  279. if (type == typeof(Color)) return VFXValueType.Float4;
  280. if (type == typeof(int)) return VFXValueType.Int32;
  281. if (type == typeof(uint)) return VFXValueType.Uint32;
  282. if (type == typeof(Texture2D)) return VFXValueType.Texture2D;
  283. if (type == typeof(Texture2DArray)) return VFXValueType.Texture2DArray;
  284. if (type == typeof(Texture3D)) return VFXValueType.Texture3D;
  285. if (type == typeof(Cubemap)) return VFXValueType.TextureCube;
  286. if (type == typeof(CubemapArray)) return VFXValueType.TextureCubeArray;
  287. if (type == typeof(CameraBuffer)) return VFXValueType.CameraBuffer;
  288. if (type == typeof(Matrix4x4)) return VFXValueType.Matrix4x4;
  289. if (type == typeof(AnimationCurve)) return VFXValueType.Curve;
  290. if (type == typeof(Gradient)) return VFXValueType.ColorGradient;
  291. if (type == typeof(Mesh)) return VFXValueType.Mesh;
  292. if (type == typeof(SkinnedMeshRenderer)) return VFXValueType.SkinnedMeshRenderer;
  293. if (type == typeof(List<Vector3>)) return VFXValueType.Spline;
  294. if (type == typeof(bool)) return VFXValueType.Boolean;
  295. if (type == typeof(GraphicsBuffer)) return VFXValueType.Buffer;
  296. return VFXValueType.None;
  297. }
  298. private static Dictionary<VFXExpression, VFXExpression> s_ExpressionCache = new Dictionary<VFXExpression, VFXExpression>();
  299. public static void ClearCache()
  300. {
  301. s_ExpressionCache.Clear();
  302. }
  303. //Ideally, we should use HashSet<T>.TryGetValue https://msdn.microsoft.com/en-us/library/mt829070(v=vs.110).aspx
  304. //but it's available only in 4.7, Dictionary<T, T> is a workaround, sensible same performance but there is a waste of memory
  305. private void SimplifyWithCacheParents()
  306. {
  307. for (int i = 0; i < m_Parents.Length; ++i)
  308. {
  309. VFXExpression parentEq;
  310. if (!s_ExpressionCache.TryGetValue(parents[i], out parentEq))
  311. {
  312. s_ExpressionCache.Add(parents[i], parents[i]);
  313. }
  314. else
  315. {
  316. m_Parents[i] = parentEq;
  317. }
  318. }
  319. }
  320. protected VFXExpression(Flags flags, params VFXExpression[] parents)
  321. {
  322. if (parents.Length > 4)
  323. {
  324. throw new System.ArgumentException("An expression can only take up to 4 parent expressions");
  325. }
  326. m_Parents = parents;
  327. SimplifyWithCacheParents();
  328. m_Flags = flags;
  329. PropagateParentsFlags();
  330. }
  331. // Only do that when constructing an instance if needed
  332. private void Initialize(VFXExpression[] parents)
  333. {
  334. if (parents.Length > 4)
  335. {
  336. throw new System.ArgumentException("An expression can only take up to 4 parent expressions");
  337. }
  338. m_Parents = parents;
  339. SimplifyWithCacheParents();
  340. PropagateParentsFlags();
  341. m_HashCodeCached = false; // as expression is mutated
  342. }
  343. //Helper using reflection to recreate a concrete type from an abstract class (useful with reduce behavior)
  344. private static VFXExpression CreateNewInstance(Type expressionType)
  345. {
  346. var allconstructors = expressionType.GetConstructors().ToArray();
  347. if (allconstructors.Length == 0)
  348. return null; //Only static readonly expression allowed, constructors are private (attribute or builtIn)
  349. var constructor = allconstructors
  350. .OrderBy(o => o.GetParameters().Count()) //promote simplest (or default) constructors
  351. .First();
  352. var param = constructor.GetParameters().Select(o =>
  353. {
  354. var type = o.GetType();
  355. return type.IsValueType ? Activator.CreateInstance(type) : null;
  356. }).ToArray();
  357. return (VFXExpression)constructor.Invoke(param);
  358. }
  359. private VFXExpression CreateNewInstance()
  360. {
  361. return CreateNewInstance(GetType());
  362. }
  363. // Reduce the expression
  364. protected virtual VFXExpression Reduce(VFXExpression[] reducedParents)
  365. {
  366. if (reducedParents.Length == 0)
  367. return this;
  368. var reduced = CreateNewInstance();
  369. reduced.Initialize(reducedParents);
  370. return reduced;
  371. }
  372. // Evaluate the expression
  373. protected virtual VFXExpression Evaluate(VFXExpression[] constParents)
  374. {
  375. throw new NotImplementedException();
  376. }
  377. // Get the HLSL code snippet
  378. public virtual string GetCodeString(string[] parents)
  379. {
  380. throw new NotImplementedException(GetType().ToString());
  381. }
  382. // Get the operands for the runtime evaluation
  383. public Operands GetOperands(VFXExpressionGraph graph)
  384. {
  385. var addOperands = additionnalOperands;
  386. if (parents.Length + addOperands.Length > 4)
  387. throw new Exception("Too many parameters for expression : " + this);
  388. var data = new Operands(-1);
  389. if (graph != null)
  390. for (int i = 0; i < parents.Length; ++i)
  391. data[i] = graph.GetFlattenedIndex(parents[i]);
  392. for (int i = 0; i < addOperands.Length; ++i)
  393. data[Operands.OperandCount - addOperands.Length + i] = addOperands[i];
  394. return data;
  395. }
  396. public virtual IEnumerable<VFXAttributeInfo> GetNeededAttributes()
  397. {
  398. return Enumerable.Empty<VFXAttributeInfo>();
  399. }
  400. public bool Is(Flags flag) { return (m_Flags & flag) == flag; }
  401. public bool IsAny(Flags flag) { return (m_Flags & flag) != 0; }
  402. public virtual VFXValueType valueType
  403. {
  404. get
  405. {
  406. var data = GetOperands(null);
  407. return VFXExpressionHelper.GetTypeOfOperation(operation, data[0], data[1], data[2], data[3]);
  408. }
  409. }
  410. public abstract VFXExpressionOperation operation { get; }
  411. public VFXExpression[] parents { get { return m_Parents; } }
  412. public override bool Equals(object obj)
  413. {
  414. if (ReferenceEquals(this, obj))
  415. return true;
  416. var other = obj as VFXExpression;
  417. if (other == null)
  418. return false;
  419. if (GetType() != other.GetType())
  420. return false;
  421. if (operation != other.operation)
  422. return false;
  423. if (valueType != other.valueType)
  424. return false;
  425. if (m_Flags != other.m_Flags)
  426. return false;
  427. if (GetHashCode() != other.GetHashCode())
  428. return false;
  429. var operands = additionnalOperands;
  430. var otherOperands = other.additionnalOperands;
  431. if (operands.Length != otherOperands.Length)
  432. return false;
  433. for (int i = 0; i < operands.Length; ++i)
  434. if (operands[i] != otherOperands[i])
  435. return false;
  436. var thisParents = parents;
  437. var otherParents = other.parents;
  438. if (thisParents == null && otherParents == null)
  439. return true;
  440. if (thisParents == null || otherParents == null)
  441. return false;
  442. if (thisParents.Length != otherParents.Length)
  443. return false;
  444. for (int i = 0; i < thisParents.Length; ++i)
  445. if (!thisParents[i].Equals(otherParents[i]))
  446. return false;
  447. return true;
  448. }
  449. public override sealed int GetHashCode()
  450. {
  451. if (!m_HashCodeCached)
  452. {
  453. m_HashCode = GetInnerHashCode();
  454. m_HashCodeCached = true;
  455. }
  456. return m_HashCode;
  457. }
  458. protected virtual int GetInnerHashCode()
  459. {
  460. int hash = GetType().GetHashCode();
  461. var parents = this.parents;
  462. for (int i = 0; i < parents.Length; ++i)
  463. hash = (hash * 397) ^ parents[i].GetHashCode(); // 397 taken from resharper
  464. var operands = additionnalOperands;
  465. for (int i = 0; i < operands.Length; ++i)
  466. hash = (hash * 397) ^ operands[i].GetHashCode();
  467. hash = (hash * 397) ^ m_Flags.GetHashCode();
  468. hash = (hash * 397) ^ valueType.GetHashCode();
  469. hash = (hash * 397) ^ operation.GetHashCode();
  470. return hash;
  471. }
  472. private static readonly int[] k_EmptyOperands = Enumerable.Empty<int>().ToArray();
  473. protected virtual int[] additionnalOperands { get { return k_EmptyOperands; } }
  474. public virtual T Get<T>()
  475. {
  476. var value = (this as VFXValue<T>);
  477. if (value == null)
  478. {
  479. throw new ArgumentException(string.Format("Get isn't available for {0} with {1}", typeof(T).FullName, GetType().FullName));
  480. }
  481. return value.Get();
  482. }
  483. public virtual object GetContent()
  484. {
  485. throw new ArgumentException(string.Format("GetContent isn't available for {0}", GetType().FullName));
  486. }
  487. private void PropagateParentsFlags()
  488. {
  489. if (m_Parents.Length > 0)
  490. {
  491. bool foldable = true;
  492. foreach (var parent in m_Parents)
  493. {
  494. foldable &= parent.Is(Flags.Foldable);
  495. const Flags propagatedFlags = Flags.NotCompilableOnCPU | Flags.InvalidConstant | Flags.PerSpawn;
  496. m_Flags |= parent.m_Flags & propagatedFlags;
  497. if (parent.IsAny(Flags.NotCompilableOnCPU) && parent.Is(Flags.InvalidOnGPU))
  498. m_Flags |= Flags.InvalidOnGPU; // Only propagate GPU validity for per element expressions
  499. if (parent.Is(Flags.InvalidConstant))
  500. m_Flags |= Flags.InvalidConstant;
  501. }
  502. if (foldable)
  503. m_Flags |= Flags.Foldable;
  504. else
  505. m_Flags &= ~Flags.Foldable;
  506. }
  507. }
  508. public static VFXExpression operator *(VFXExpression a, VFXExpression b) { return new VFXExpressionMul(a, b); }
  509. public static VFXExpression operator /(VFXExpression a, VFXExpression b) { return new VFXExpressionDivide(a, b); }
  510. public static VFXExpression operator +(VFXExpression a, VFXExpression b) { return new VFXExpressionAdd(a, b); }
  511. public static VFXExpression operator -(VFXExpression a, VFXExpression b) { return new VFXExpressionSubtract(a, b); }
  512. public static VFXExpression operator |(VFXExpression a, VFXExpression b) { return new VFXExpressionBitwiseOr(a, b); }
  513. public static VFXExpression operator &(VFXExpression a, VFXExpression b) { return new VFXExpressionBitwiseAnd(a, b); }
  514. public static VFXExpression operator |(VFXExpression a, uint b) { return new VFXExpressionBitwiseOr(a, VFXValue.Constant(b)); }
  515. public static VFXExpression operator &(VFXExpression a, uint b) { return new VFXExpressionBitwiseAnd(a, VFXValue.Constant(b)); }
  516. public static VFXExpression operator <<(VFXExpression a, int shift) { return new VFXExpressionBitwiseLeftShift(a, VFXValue.Constant((uint)shift)); }
  517. public static VFXExpression operator >>(VFXExpression a, int shift) { return new VFXExpressionBitwiseRightShift(a, VFXValue.Constant((uint)shift)); }
  518. public VFXExpression this[int index] { get { return new VFXExpressionExtractComponent(this, index); } }
  519. public VFXExpression x { get { return new VFXExpressionExtractComponent(this, 0); } }
  520. public VFXExpression y { get { return new VFXExpressionExtractComponent(this, 1); } }
  521. public VFXExpression z { get { return new VFXExpressionExtractComponent(this, 2); } }
  522. public VFXExpression w { get { return new VFXExpressionExtractComponent(this, 3); } }
  523. public VFXExpression xxx { get { return new VFXExpressionCombine(x, x, x); } }
  524. public VFXExpression yyy { get { return new VFXExpressionCombine(y, y, y); } }
  525. public VFXExpression zzz { get { return new VFXExpressionCombine(z, z, z); } }
  526. private Flags m_Flags = Flags.None;
  527. private VFXExpression[] m_Parents;
  528. private int m_HashCode;
  529. private bool m_HashCodeCached = false;
  530. }
  531. }