VFXDataParticle.cs 47 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using UnityEngine;
  6. using UnityEditor.VFX;
  7. using UnityEngine.VFX;
  8. using UnityEngine.Serialization;
  9. using Object = UnityEngine.Object;
  10. namespace UnityEditor.VFX
  11. {
  12. interface ILayoutProvider
  13. {
  14. void GenerateAttributeLayout(uint capacity, Dictionary<VFXAttribute, int> storedAttribute);
  15. string GetCodeOffset(VFXAttribute attrib, string index);
  16. uint GetBufferSize(uint capacity);
  17. VFXGPUBufferDesc GetBufferDesc(uint capacity);
  18. }
  19. class StructureOfArrayProvider : ILayoutProvider
  20. {
  21. private struct AttributeLayout
  22. {
  23. public int bucket;
  24. public int offset;
  25. public AttributeLayout(int bucket, int offset)
  26. {
  27. this.bucket = bucket;
  28. this.offset = offset;
  29. }
  30. }
  31. // return size
  32. private int GenerateBucketLayout(List<VFXAttribute> attributes, int bucketId)
  33. {
  34. var sortedAttrib = attributes.OrderByDescending(a => VFXValue.TypeToSize(a.type));
  35. var attribBlocks = new List<List<VFXAttribute>>();
  36. foreach (var value in sortedAttrib)
  37. {
  38. var block = attribBlocks.FirstOrDefault(b => b.Sum(a => VFXValue.TypeToSize(a.type)) + VFXValue.TypeToSize(value.type) <= 4);
  39. if (block != null)
  40. block.Add(value);
  41. else
  42. attribBlocks.Add(new List<VFXAttribute>() { value });
  43. }
  44. int currentOffset = 0;
  45. int minAlignment = 0;
  46. foreach (var block in attribBlocks)
  47. {
  48. foreach (var attrib in block)
  49. {
  50. int size = VFXValue.TypeToSize(attrib.type);
  51. int alignment = size > 2 ? 4 : size;
  52. minAlignment = Math.Max(alignment, minAlignment);
  53. // align offset
  54. currentOffset = (currentOffset + alignment - 1) & ~(alignment - 1);
  55. m_AttributeLayout.Add(attrib, new AttributeLayout(bucketId, currentOffset));
  56. currentOffset += size;
  57. }
  58. }
  59. return (currentOffset + minAlignment - 1) & ~(minAlignment - 1);
  60. }
  61. public void GenerateAttributeLayout(uint capacity, Dictionary<VFXAttribute, int> storedAttribute)
  62. {
  63. m_BucketSizes.Clear();
  64. m_AttributeLayout.Clear();
  65. m_BucketOffsets.Clear();
  66. var attributeBuckets = new Dictionary<int, List<VFXAttribute>>();
  67. foreach (var kvp in storedAttribute)
  68. {
  69. List<VFXAttribute> attributes;
  70. if (!attributeBuckets.ContainsKey(kvp.Value))
  71. {
  72. attributes = new List<VFXAttribute>();
  73. attributeBuckets[kvp.Value] = attributes;
  74. }
  75. else
  76. attributes = attributeBuckets[kvp.Value];
  77. attributes.Add(kvp.Key);
  78. }
  79. int bucketId = 0;
  80. foreach (var bucket in attributeBuckets)
  81. {
  82. int bucketOffset = bucketId == 0 ? 0 : m_BucketOffsets[bucketId - 1] + (int)capacity * m_BucketSizes[bucketId - 1];
  83. m_BucketOffsets.Add((bucketOffset + 3) & ~3); // align on dword;
  84. m_BucketSizes.Add(GenerateBucketLayout(bucket.Value, bucketId));
  85. ++bucketId;
  86. }
  87. // Debug log
  88. if (VFXViewPreference.advancedLogs)
  89. {
  90. var builder = new StringBuilder();
  91. builder.AppendLine("ATTRIBUTE LAYOUT");
  92. builder.Append(string.Format("NbBuckets:{0} ( ", m_BucketSizes.Count));
  93. foreach (int size in m_BucketSizes)
  94. builder.Append(size + " ");
  95. builder.AppendLine(")");
  96. foreach (var kvp in m_AttributeLayout)
  97. builder.AppendLine(string.Format("Attrib:{0} type:{1} bucket:{2} offset:{3}", kvp.Key.name, kvp.Key.type, kvp.Value.bucket, kvp.Value.offset));
  98. Debug.Log(builder.ToString());
  99. }
  100. }
  101. public string GetCodeOffset(VFXAttribute attrib, string index)
  102. {
  103. AttributeLayout layout;
  104. if (!m_AttributeLayout.TryGetValue(attrib, out layout))
  105. {
  106. throw new InvalidOperationException(string.Format("Cannot find attribute {0}", attrib.name));
  107. }
  108. return string.Format("({2} * 0x{0:X} + 0x{1:X}) << 2", m_BucketSizes[layout.bucket], m_BucketOffsets[layout.bucket] + layout.offset, index);
  109. }
  110. public uint GetBufferSize(uint capacity)
  111. {
  112. return (uint)m_BucketOffsets.LastOrDefault() + capacity * (uint)m_BucketSizes.LastOrDefault();
  113. }
  114. public VFXGPUBufferDesc GetBufferDesc(uint capacity)
  115. {
  116. var layout = m_AttributeLayout.Select(o => new VFXLayoutElementDesc()
  117. {
  118. name = o.Key.name,
  119. type = o.Key.type,
  120. offset = new VFXLayoutOffset()
  121. {
  122. structure = (uint)m_BucketSizes[o.Value.bucket],
  123. bucket = (uint)m_BucketOffsets[o.Value.bucket],
  124. element = (uint)o.Value.offset
  125. }
  126. });
  127. return new VFXGPUBufferDesc()
  128. {
  129. type = ComputeBufferType.Raw,
  130. size = GetBufferSize(capacity),
  131. stride = 4,
  132. capacity = capacity,
  133. layout = layout.ToArray()
  134. };
  135. }
  136. public struct BucketInfo
  137. {
  138. public int size;
  139. public int usedSize;
  140. public VFXAttribute[] attributes;
  141. public int[] channels;
  142. }
  143. public BucketInfo[] GetBucketLayoutInfo()
  144. {
  145. int count = m_BucketSizes.Count;
  146. BucketInfo[] buckets = new BucketInfo[count];
  147. for (int i = 0; i < count; i++)
  148. {
  149. int size = m_BucketSizes[i];
  150. buckets[i].size = size;
  151. buckets[i].usedSize = 0;
  152. buckets[i].attributes = new VFXAttribute[size];
  153. buckets[i].channels = new int[size];
  154. }
  155. foreach (var kvp in m_AttributeLayout)
  156. {
  157. var attrib = kvp.Key;
  158. int size = VFXValue.TypeToSize(attrib.type);
  159. int offset = kvp.Value.offset;
  160. for (int i = 0; i < size; i++)
  161. {
  162. buckets[kvp.Value.bucket].attributes[i + offset] = attrib;
  163. buckets[kvp.Value.bucket].channels[i + offset] = i;
  164. buckets[kvp.Value.bucket].usedSize = Math.Max(buckets[kvp.Value.bucket].usedSize, i + offset + 1);
  165. }
  166. }
  167. return buckets;
  168. }
  169. private Dictionary<VFXAttribute, AttributeLayout> m_AttributeLayout = new Dictionary<VFXAttribute, AttributeLayout>();
  170. private List<int> m_BucketSizes = new List<int>();
  171. private List<int> m_BucketOffsets = new List<int>();
  172. }
  173. internal enum BoundsSettingMode
  174. {
  175. Recorded,
  176. Manual,
  177. Automatic,
  178. }
  179. class VFXDataParticle : VFXData, ISpaceable
  180. {
  181. public override VFXDataType type { get { return hasStrip ? VFXDataType.ParticleStrip : VFXDataType.Particle; } }
  182. internal enum DataType
  183. {
  184. Particle,
  185. ParticleStrip
  186. }
  187. [VFXSetting(VFXSettingAttribute.VisibleFlags.InInspector), SerializeField]
  188. protected DataType dataType = DataType.Particle;
  189. [VFXSetting, Delayed, SerializeField, FormerlySerializedAs("m_Capacity")]
  190. [Tooltip("Sets the maximum particle capacity of this system. Particles spawned after the capacity has been reached are discarded.")]
  191. protected uint capacity = 128;
  192. [VFXSetting, Delayed, SerializeField]
  193. protected uint stripCapacity = 1;
  194. [VFXSetting, Delayed, SerializeField]
  195. protected uint particlePerStripCount = 128;
  196. [VFXSetting(VFXSettingAttribute.VisibleFlags.None), SerializeField]
  197. protected bool needsComputeBounds = false;
  198. public bool NeedsComputeBounds() => needsComputeBounds;
  199. [FormerlySerializedAs("boundsSettingMode")]
  200. [VFXSetting(VFXSettingAttribute.VisibleFlags.All),
  201. Tooltip("Specifies how the bounds are set. They can be set manually, recorded in the Target GameObject window, or computed automatically at a small performance cost."),
  202. SerializeField]
  203. public BoundsSettingMode boundsMode = BoundsSettingMode.Recorded;
  204. public bool hasStrip { get { return dataType == DataType.ParticleStrip; } }
  205. public override void OnSettingModified(VFXSetting setting)
  206. {
  207. base.OnSettingModified(setting);
  208. if (setting.name == "capacity" && capacity == 0)
  209. capacity = 1;
  210. else if (setting.name == "stripCapacity" && stripCapacity == 0)
  211. stripCapacity = 1;
  212. else if (setting.name == "particlePerStripCount" && particlePerStripCount == 0)
  213. particlePerStripCount = 1;
  214. else if (setting.name == "boundsMode")
  215. {
  216. //Refresh errors on Output contexts
  217. var allSystemOutputContexts = owners.Where(ctx => ctx is VFXAbstractParticleOutput);
  218. foreach (var ctx in allSystemOutputContexts)
  219. {
  220. ctx.RefreshErrors(GetGraph());
  221. }
  222. if (boundsMode == BoundsSettingMode.Automatic)
  223. {
  224. needsComputeBounds = true;
  225. var graph = GetGraph();
  226. graph.visualEffectResource.cullingFlags = VFXCullingFlags.CullNone;
  227. }
  228. }
  229. if (hasStrip)
  230. {
  231. if (setting.name == "dataType") // strip has just been set
  232. {
  233. stripCapacity = 1;
  234. particlePerStripCount = capacity;
  235. }
  236. capacity = stripCapacity * particlePerStripCount;
  237. }
  238. }
  239. protected override void OnInvalidate(VFXModel model, InvalidationCause cause)
  240. {
  241. base.OnInvalidate(model, cause);
  242. if (cause == InvalidationCause.kSettingChanged)
  243. UpdateValidOutputs();
  244. }
  245. protected override IEnumerable<string> filteredOutSettings
  246. {
  247. get
  248. {
  249. foreach (var s in base.filteredOutSettings)
  250. yield return s;
  251. if (!VFXViewPreference.displayExperimentalOperator) // TODO Name is bad!
  252. yield return "dataType";
  253. if (hasStrip)
  254. {
  255. yield return "capacity";
  256. }
  257. else
  258. {
  259. yield return "stripCapacity";
  260. yield return "particlePerStripCount";
  261. }
  262. }
  263. }
  264. public override IEnumerable<string> additionalHeaders
  265. {
  266. get
  267. {
  268. if (hasStrip)
  269. {
  270. yield return "#define STRIP_COUNT " + stripCapacity + "u";
  271. yield return "#define PARTICLE_PER_STRIP_COUNT " + particlePerStripCount + "u";
  272. }
  273. }
  274. }
  275. private void UpdateValidOutputs()
  276. {
  277. var toUnlink = new List<VFXContext>();
  278. foreach (var context in owners)
  279. if (context.contextType == VFXContextType.Output) // Consider only outputs
  280. {
  281. var input = context.inputContexts.FirstOrDefault(); // Consider only one input at the moment because this is ensure by the data type (even if it may change in the future)
  282. if (input != null && (input.outputType & context.inputType) != context.inputType)
  283. toUnlink.Add(context);
  284. }
  285. foreach (var context in toUnlink)
  286. context.UnlinkFrom(context.inputContexts.FirstOrDefault());
  287. }
  288. private uint alignedCapacity
  289. {
  290. get
  291. {
  292. uint paddedCapacity = capacity;
  293. const uint kThreadPerGroup = 64;
  294. if (paddedCapacity > kThreadPerGroup)
  295. paddedCapacity = (uint)((paddedCapacity + kThreadPerGroup - 1) & ~(kThreadPerGroup - 1)); // multiple of kThreadPerGroup
  296. return (paddedCapacity + 3u) & ~3u; // Align on 4 boundary
  297. }
  298. }
  299. public uint ComputeSourceCount(Dictionary<VFXContext, List<VFXContextLink>[]> effectiveFlowInputLinks)
  300. {
  301. var init = owners.FirstOrDefault(o => o.contextType == VFXContextType.Init);
  302. if (init == null)
  303. return 0u;
  304. var cpuCount = effectiveFlowInputLinks[init].SelectMany(t => t.Select(u => u.context)).Where(o => o.contextType == VFXContextType.Spawner).Count();
  305. var gpuCount = effectiveFlowInputLinks[init].SelectMany(t => t.Select(u => u.context)).Where(o => o.contextType == VFXContextType.SpawnerGPU).Count();
  306. if (cpuCount != 0 && gpuCount != 0)
  307. {
  308. throw new InvalidOperationException("Cannot mix GPU & CPU spawners in init");
  309. }
  310. if (cpuCount > 0)
  311. {
  312. return (uint)cpuCount;
  313. }
  314. else if (gpuCount > 0)
  315. {
  316. if (gpuCount > 1)
  317. {
  318. throw new InvalidOperationException("Don't support multiple GPU event (for now)");
  319. }
  320. var parent = m_DependenciesIn.OfType<VFXDataParticle>().FirstOrDefault();
  321. return parent != null ? parent.capacity : 0u;
  322. }
  323. return init != null ? (uint)effectiveFlowInputLinks[init].SelectMany(t => t.Select(u => u.context)).Where(o => o.contextType == VFXContextType.Spawner /* Explicitly ignore spawner gpu */).Count() : 0u;
  324. }
  325. public uint attributeBufferSize
  326. {
  327. get
  328. {
  329. return m_layoutAttributeCurrent.GetBufferSize(alignedCapacity);
  330. }
  331. }
  332. public VFXGPUBufferDesc attributeBufferDesc
  333. {
  334. get
  335. {
  336. return m_layoutAttributeCurrent.GetBufferDesc(alignedCapacity);
  337. }
  338. }
  339. public VFXCoordinateSpace space
  340. {
  341. get { return m_Space; }
  342. set { m_Space = value; Modified(false); }
  343. }
  344. public override bool CanBeCompiled()
  345. {
  346. // Has enough contexts and capacity
  347. if (m_Owners.Count < 1 || capacity <= 0)
  348. return false;
  349. // Has a initialize
  350. if (m_Owners[0].contextType != VFXContextType.Init)
  351. return false;
  352. // Has a spawner
  353. if (m_Owners[0].inputContexts.FirstOrDefault() == null)
  354. return false;
  355. // Has an output
  356. if (m_Owners.Last().contextType == VFXContextType.Output)
  357. return true;
  358. // Has a least one dependent compilable system
  359. if (m_Owners.SelectMany(c => c.allLinkedOutputSlot)
  360. .Select(s => ((VFXModel)s.owner).GetFirstOfType<VFXContext>())
  361. .Any(c => c.CanBeCompiled()))
  362. return true;
  363. return false;
  364. }
  365. public override VFXDeviceTarget GetCompilationTarget(VFXContext context)
  366. {
  367. return VFXDeviceTarget.GPU;
  368. }
  369. uint m_SourceCount = 0xFFFFFFFFu;
  370. public override uint staticSourceCount
  371. {
  372. get
  373. {
  374. return m_SourceCount;
  375. }
  376. }
  377. public bool hasDynamicSourceCount
  378. {
  379. get
  380. {
  381. return m_Contexts.Any(
  382. o => o.contextType == VFXContextType.Init
  383. && o.inputFlowSlot.Any(flow => flow.link.Any(link => link.context.contextType == VFXContextType.Event)));
  384. }
  385. }
  386. public override void GenerateAttributeLayout(Dictionary<VFXContext, List<VFXContextLink>[]> effectiveFlowInputLinks)
  387. {
  388. m_layoutAttributeCurrent.GenerateAttributeLayout(alignedCapacity, m_StoredCurrentAttributes);
  389. m_SourceCount = ComputeSourceCount(effectiveFlowInputLinks);
  390. var parent = m_DependenciesIn.OfType<VFXDataParticle>().FirstOrDefault();
  391. if (parent != null)
  392. {
  393. m_layoutAttributeSource.GenerateAttributeLayout(parent.alignedCapacity, parent.m_StoredCurrentAttributes);
  394. m_ownAttributeSourceBuffer = false;
  395. }
  396. else
  397. {
  398. var readSourceAttribute = m_ReadSourceAttributes.ToDictionary(o => o, _ => (int)VFXAttributeMode.ReadSource);
  399. m_layoutAttributeSource.GenerateAttributeLayout(m_SourceCount, readSourceAttribute);
  400. m_ownAttributeSourceBuffer = true;
  401. }
  402. }
  403. public override string GetAttributeDataDeclaration(VFXAttributeMode mode)
  404. {
  405. if (m_StoredCurrentAttributes.Count == 0)
  406. return string.Empty;
  407. else if ((mode & VFXAttributeMode.Write) != 0)
  408. return "RWByteAddressBuffer attributeData;";
  409. else
  410. return "ByteAddressBuffer attributeData;";
  411. }
  412. private string GetCastAttributePrefix(VFXAttribute attrib)
  413. {
  414. if (VFXExpression.IsFloatValueType(attrib.type))
  415. return "asfloat";
  416. return "";
  417. }
  418. private string GetByteAddressBufferMethodSuffix(VFXAttribute attrib)
  419. {
  420. int size = VFXExpression.TypeToSize(attrib.type);
  421. if (size == 1)
  422. return string.Empty;
  423. else if (size <= 4)
  424. return size.ToString();
  425. else
  426. throw new ArgumentException(string.Format("Attribute {0} of type {1} cannot be handled in ByteAddressBuffer due to its size of {2}", attrib.name, attrib.type, size));
  427. }
  428. public override string GetLoadAttributeCode(VFXAttribute attrib, VFXAttributeLocation location)
  429. {
  430. var attributeStore = location == VFXAttributeLocation.Current ? m_layoutAttributeCurrent : m_layoutAttributeSource;
  431. var attributeBuffer = location == VFXAttributeLocation.Current ? "attributeBuffer" : "sourceAttributeBuffer";
  432. var index = location == VFXAttributeLocation.Current ? "index" : "sourceIndex";
  433. if (location == VFXAttributeLocation.Current && !m_StoredCurrentAttributes.ContainsKey(attrib))
  434. throw new ArgumentException(string.Format("Attribute {0} does not exist in data layout", attrib.name));
  435. if (location == VFXAttributeLocation.Source && !m_ReadSourceAttributes.Any(a => a.name == attrib.name))
  436. throw new ArgumentException(string.Format("Attribute {0} does not exist in data layout", attrib.name));
  437. return string.Format("{0}({3}.Load{1}({2}))", GetCastAttributePrefix(attrib), GetByteAddressBufferMethodSuffix(attrib), attributeStore.GetCodeOffset(attrib, index), attributeBuffer);
  438. }
  439. public override string GetStoreAttributeCode(VFXAttribute attrib, string value)
  440. {
  441. if (!m_StoredCurrentAttributes.ContainsKey(attrib))
  442. throw new ArgumentException(string.Format("Attribute {0} does not exist in data layout", attrib.name));
  443. return string.Format("attributeBuffer.Store{0}({1},{3}({2}))", GetByteAddressBufferMethodSuffix(attrib), m_layoutAttributeCurrent.GetCodeOffset(attrib, "index"), value, attrib.type == VFXValueType.Boolean ? "uint" : "asuint");
  444. }
  445. public override IEnumerable<VFXContext> InitImplicitContexts()
  446. {
  447. var contexts = compilableOwners.ToList();
  448. if (!NeedsGlobalSort() &&
  449. !contexts.OfType<VFXAbstractParticleOutput>().Any(o => o.NeedsOutputUpdate()))
  450. {
  451. //Early out with the most common case
  452. m_Contexts = contexts;
  453. return Enumerable.Empty<VFXContext>();
  454. }
  455. m_Contexts = new List<VFXContext>(contexts.Count + 2); // Allocate max number
  456. int index = 0;
  457. // First add init and updates
  458. for (index = 0; index < contexts.Count; ++index)
  459. {
  460. if ((contexts[index].contextType == VFXContextType.Output))
  461. break;
  462. m_Contexts.Add(contexts[index]);
  463. }
  464. var implicitContext = new List<VFXContext>();
  465. if (NeedsGlobalSort())
  466. {
  467. // Then the camera sort
  468. var cameraSort = VFXContext.CreateImplicitContext<VFXCameraSort>(this);
  469. implicitContext.Add(cameraSort);
  470. m_Contexts.Add(cameraSort);
  471. }
  472. //additional update
  473. for (int outputIndex = index; outputIndex < contexts.Count; ++outputIndex)
  474. {
  475. var currentOutputContext = contexts[outputIndex];
  476. var abstractParticleOutput = currentOutputContext as VFXAbstractParticleOutput;
  477. if (abstractParticleOutput == null)
  478. continue;
  479. if (abstractParticleOutput.NeedsOutputUpdate())
  480. {
  481. var update = VFXContext.CreateImplicitContext<VFXOutputUpdate>(this);
  482. update.SetOutput(abstractParticleOutput);
  483. implicitContext.Add(update);
  484. m_Contexts.Add(update);
  485. }
  486. }
  487. // And finally output
  488. for (; index < contexts.Count; ++index)
  489. m_Contexts.Add(contexts[index]);
  490. return implicitContext;
  491. }
  492. public bool NeedsIndirectBuffer()
  493. {
  494. return compilableOwners.OfType<VFXAbstractParticleOutput>().Any(o => o.HasIndirectDraw());
  495. }
  496. public bool NeedsGlobalIndirectBuffer()
  497. {
  498. return compilableOwners.OfType<VFXAbstractParticleOutput>().Any(o => o.HasIndirectDraw() && !VFXOutputUpdate.HasFeature(o.outputUpdateFeatures, VFXOutputUpdate.Features.IndirectDraw));
  499. }
  500. public bool NeedsGlobalSort()
  501. {
  502. return compilableOwners.OfType<VFXAbstractParticleOutput>().Any(o => o.CanBeCompiled() && o.HasSorting() && !VFXOutputUpdate.HasFeature(o.outputUpdateFeatures, VFXOutputUpdate.Features.IndirectDraw));
  503. }
  504. public override void FillDescs(
  505. VFXCompileErrorReporter reporter,
  506. List<VFXGPUBufferDesc> outBufferDescs,
  507. List<VFXTemporaryGPUBufferDesc> outTemporaryBufferDescs,
  508. List<VFXEditorSystemDesc> outSystemDescs,
  509. VFXExpressionGraph expressionGraph,
  510. Dictionary<VFXContext, VFXContextCompiledData> contextToCompiledData,
  511. Dictionary<VFXContext, int> contextSpawnToBufferIndex,
  512. VFXDependentBuffersData dependentBuffers,
  513. Dictionary<VFXContext, List<VFXContextLink>[]> effectiveFlowInputLinks,
  514. VFXSystemNames systemNames = null)
  515. {
  516. bool hasKill = IsAttributeStored(VFXAttribute.Alive);
  517. var deadListBufferIndex = -1;
  518. var deadListCountIndex = -1;
  519. var systemBufferMappings = new List<VFXMapping>();
  520. var systemValueMappings = new List<VFXMapping>();
  521. var attributeBufferIndex = dependentBuffers.attributeBuffers[this];
  522. int attributeSourceBufferIndex = -1;
  523. int eventGPUFrom = -1;
  524. var stripDataIndex = -1;
  525. var boundsBufferIndex = -1;
  526. if (m_DependenciesIn.Any())
  527. {
  528. if (m_DependenciesIn.Count != 1)
  529. {
  530. throw new InvalidOperationException("Unexpected multiple input dependency for GPU event");
  531. }
  532. attributeSourceBufferIndex = dependentBuffers.attributeBuffers[m_DependenciesIn.FirstOrDefault()];
  533. eventGPUFrom = dependentBuffers.eventBuffers[this];
  534. }
  535. if (attributeBufferIndex != -1)
  536. {
  537. systemBufferMappings.Add(new VFXMapping("attributeBuffer", attributeBufferIndex));
  538. }
  539. if (m_ownAttributeSourceBuffer)
  540. {
  541. if (attributeSourceBufferIndex != -1)
  542. {
  543. throw new InvalidOperationException("Unexpected source while filling description of data particle");
  544. }
  545. attributeSourceBufferIndex = outBufferDescs.Count;
  546. outBufferDescs.Add(m_layoutAttributeSource.GetBufferDesc(staticSourceCount));
  547. }
  548. if (attributeSourceBufferIndex != -1)
  549. {
  550. systemBufferMappings.Add(new VFXMapping("sourceAttributeBuffer", attributeSourceBufferIndex));
  551. }
  552. var systemFlag = VFXSystemFlag.SystemDefault;
  553. if (eventGPUFrom != -1)
  554. {
  555. systemFlag |= VFXSystemFlag.SystemReceivedEventGPU;
  556. systemBufferMappings.Add(new VFXMapping("eventList", eventGPUFrom));
  557. }
  558. if (hasKill)
  559. {
  560. systemFlag |= VFXSystemFlag.SystemHasKill;
  561. deadListBufferIndex = outBufferDescs.Count;
  562. outBufferDescs.Add(new VFXGPUBufferDesc() { type = ComputeBufferType.Counter, size = capacity, stride = 4 });
  563. systemBufferMappings.Add(new VFXMapping("deadList", deadListBufferIndex));
  564. deadListCountIndex = outBufferDescs.Count;
  565. outBufferDescs.Add(new VFXGPUBufferDesc() { type = ComputeBufferType.Raw, size = 1, stride = 4 });
  566. systemBufferMappings.Add(new VFXMapping("deadListCount", deadListCountIndex));
  567. }
  568. if (hasStrip)
  569. {
  570. systemFlag |= VFXSystemFlag.SystemHasStrips;
  571. systemValueMappings.Add(new VFXMapping("stripCount", (int)stripCapacity));
  572. systemValueMappings.Add(new VFXMapping("particlePerStripCount", (int)particlePerStripCount));
  573. stripDataIndex = dependentBuffers.stripBuffers[this];
  574. systemBufferMappings.Add(new VFXMapping("stripDataBuffer", stripDataIndex));
  575. }
  576. if (hasDynamicSourceCount)
  577. {
  578. systemFlag |= VFXSystemFlag.SystemHasDirectLink;
  579. }
  580. if (needsComputeBounds || boundsMode == BoundsSettingMode.Automatic)
  581. {
  582. systemFlag |= VFXSystemFlag.SystemNeedsComputeBounds;
  583. boundsBufferIndex = dependentBuffers.boundsBuffers[this];
  584. systemBufferMappings.Add(new VFXMapping("boundsBuffer", boundsBufferIndex));
  585. }
  586. if (boundsMode == BoundsSettingMode.Automatic)
  587. {
  588. systemFlag |= VFXSystemFlag.SystemAutomaticBounds;
  589. }
  590. if (space == VFXCoordinateSpace.World)
  591. {
  592. systemFlag |= VFXSystemFlag.SystemInWorldSpace;
  593. }
  594. var initContext = m_Contexts.FirstOrDefault(o => o.contextType == VFXContextType.Init);
  595. if (initContext != null)
  596. systemBufferMappings.AddRange(effectiveFlowInputLinks[initContext].SelectMany(t => t.Select(u => u.context)).Where(o => o.contextType == VFXContextType.Spawner).Select(o => new VFXMapping("spawner_input", contextSpawnToBufferIndex[o])));
  597. if (m_Contexts.Count() > 0 && m_Contexts.First().contextType == VFXContextType.Init) // TODO This test can be removed once we ensure priorly the system is valid
  598. {
  599. var mapper = contextToCompiledData[m_Contexts.First()].cpuMapper;
  600. var boundsCenterExp = mapper.FromNameAndId("bounds_center", -1);
  601. var boundsSizeExp = mapper.FromNameAndId("bounds_size", -1);
  602. var boundsPaddingExp = mapper.FromNameAndId("boundsPadding", -1);
  603. int boundsCenterIndex = boundsCenterExp != null ? expressionGraph.GetFlattenedIndex(boundsCenterExp) : -1;
  604. int boundsSizeIndex = boundsSizeExp != null ? expressionGraph.GetFlattenedIndex(boundsSizeExp) : -1;
  605. int boundsPaddingIndex = boundsPaddingExp != null ? expressionGraph.GetFlattenedIndex(boundsPaddingExp) : -1;
  606. if (boundsCenterIndex != -1 && boundsSizeIndex != -1)
  607. {
  608. systemValueMappings.Add(new VFXMapping("bounds_center", boundsCenterIndex));
  609. systemValueMappings.Add(new VFXMapping("bounds_size", boundsSizeIndex));
  610. }
  611. if (boundsPaddingIndex != -1)
  612. {
  613. systemValueMappings.Add(new VFXMapping("boundsPadding", boundsPaddingIndex));
  614. }
  615. }
  616. Dictionary<VFXContext, VFXOutputUpdate> indirectOutputToCuller = null;
  617. bool needsIndirectBuffer = NeedsIndirectBuffer();
  618. int globalIndirectBufferIndex = -1;
  619. bool needsGlobalIndirectBuffer = false;
  620. if (needsIndirectBuffer)
  621. {
  622. indirectOutputToCuller = new Dictionary<VFXContext, VFXOutputUpdate>();
  623. foreach (var cullCompute in m_Contexts.OfType<VFXOutputUpdate>())
  624. if (cullCompute.HasFeature(VFXOutputUpdate.Features.IndirectDraw))
  625. indirectOutputToCuller.Add(cullCompute.output, cullCompute);
  626. var allIndirectOutputs = owners.OfType<VFXAbstractParticleOutput>().Where(o => o.HasIndirectDraw());
  627. needsGlobalIndirectBuffer = NeedsGlobalIndirectBuffer();
  628. if (needsGlobalIndirectBuffer)
  629. {
  630. globalIndirectBufferIndex = outBufferDescs.Count;
  631. systemBufferMappings.Add(new VFXMapping("indirectBuffer0", outBufferDescs.Count));
  632. outBufferDescs.Add(new VFXGPUBufferDesc() { type = ComputeBufferType.Counter, size = capacity, stride = 4 });
  633. }
  634. int currentIndirectBufferIndex = globalIndirectBufferIndex == -1 ? 0 : 1;
  635. foreach (var indirectOutput in allIndirectOutputs)
  636. {
  637. if (indirectOutputToCuller.ContainsKey(indirectOutput))
  638. {
  639. VFXOutputUpdate culler = indirectOutputToCuller[indirectOutput];
  640. uint bufferCount = culler.bufferCount;
  641. culler.bufferIndex = outBufferDescs.Count;
  642. bool perCamera = culler.isPerCamera;
  643. uint bufferStride = culler.HasFeature(VFXOutputUpdate.Features.Sort) ? 8u : 4u;
  644. for (uint i = 0; i < bufferCount; ++i)
  645. {
  646. string bufferName = "indirectBuffer" + currentIndirectBufferIndex++;
  647. if (perCamera)
  648. bufferName += "PerCamera";
  649. systemBufferMappings.Add(new VFXMapping(bufferName, outBufferDescs.Count));
  650. outBufferDescs.Add(new VFXGPUBufferDesc() { type = ComputeBufferType.Counter, size = capacity, stride = bufferStride });
  651. }
  652. if (culler.HasFeature(VFXOutputUpdate.Features.Sort))
  653. {
  654. culler.sortedBufferIndex = outBufferDescs.Count;
  655. for (uint i = 0; i < bufferCount; ++i)
  656. outBufferDescs.Add(new VFXGPUBufferDesc() { type = ComputeBufferType.Default, size = capacity, stride = 4 });
  657. }
  658. else
  659. culler.sortedBufferIndex = culler.bufferIndex;
  660. }
  661. }
  662. }
  663. // sort buffers
  664. int sortBufferAIndex = -1;
  665. int sortBufferBIndex = -1;
  666. bool needsSort = NeedsGlobalSort();
  667. if (needsSort)
  668. {
  669. sortBufferAIndex = outBufferDescs.Count;
  670. sortBufferBIndex = sortBufferAIndex + 1;
  671. outBufferDescs.Add(new VFXGPUBufferDesc() { type = ComputeBufferType.Default, size = capacity, stride = 8 });
  672. systemBufferMappings.Add(new VFXMapping("sortBufferA", sortBufferAIndex));
  673. outBufferDescs.Add(new VFXGPUBufferDesc() { type = ComputeBufferType.Default, size = capacity, stride = 8 });
  674. systemBufferMappings.Add(new VFXMapping("sortBufferB", sortBufferBIndex));
  675. }
  676. var elementToVFXBufferMotionVector = new Dictionary<VFXContext, int>();
  677. foreach (VFXOutputUpdate context in m_Contexts.OfType<VFXOutputUpdate>())
  678. {
  679. if (context.HasFeature(VFXOutputUpdate.Features.MotionVector))
  680. {
  681. uint sizePerElement = 12U * 4U;
  682. if (context.output.SupportsMotionVectorPerVertex(out uint vertsCount))
  683. {
  684. // 2 floats per vertex
  685. sizePerElement = vertsCount * 2U * 4U;
  686. }
  687. // add previous frame index
  688. sizePerElement += 4U;
  689. int currentElementToVFXBufferMotionVector = outTemporaryBufferDescs.Count;
  690. outTemporaryBufferDescs.Add(new VFXTemporaryGPUBufferDesc() { frameCount = 2u, desc = new VFXGPUBufferDesc { type = ComputeBufferType.Raw, size = capacity * sizePerElement, stride = 4 } });
  691. elementToVFXBufferMotionVector.Add(context.output, currentElementToVFXBufferMotionVector);
  692. }
  693. }
  694. var taskDescs = new List<VFXEditorTaskDesc>();
  695. var bufferMappings = new List<VFXMapping>();
  696. var uniformMappings = new List<VFXMapping>();
  697. var additionalParameters = new List<VFXMapping>();
  698. for (int i = 0; i < m_Contexts.Count; ++i)
  699. {
  700. var temporaryBufferMappings = new List<VFXMappingTemporary>();
  701. var context = m_Contexts[i];
  702. if (!contextToCompiledData.TryGetValue(context, out var contextData))
  703. throw new InvalidOperationException("Unexpected context which hasn't been compiled : " + context);
  704. var taskDesc = new VFXEditorTaskDesc();
  705. taskDesc.type = (UnityEngine.VFX.VFXTaskType)context.taskType;
  706. bufferMappings.Clear();
  707. additionalParameters.Clear();
  708. if (context is VFXOutputUpdate)
  709. {
  710. var update = (VFXOutputUpdate)context;
  711. if (update.HasFeature(VFXOutputUpdate.Features.MotionVector))
  712. {
  713. var currentIndex = elementToVFXBufferMotionVector[update.output];
  714. temporaryBufferMappings.Add(new VFXMappingTemporary() { pastFrameIndex = 0u, perCameraBuffer = true, mapping = new VFXMapping("elementToVFXBuffer", currentIndex) });
  715. }
  716. }
  717. else if (context.contextType == VFXContextType.Output && (context is IVFXSubRenderer) && (context as IVFXSubRenderer).hasMotionVector)
  718. {
  719. var currentIndex = elementToVFXBufferMotionVector[context];
  720. temporaryBufferMappings.Add(new VFXMappingTemporary() { pastFrameIndex = 1u, perCameraBuffer = true, mapping = new VFXMapping("elementToVFXBufferPrevious", currentIndex) });
  721. }
  722. if (attributeBufferIndex != -1)
  723. bufferMappings.Add(new VFXMapping("attributeBuffer", attributeBufferIndex));
  724. if (eventGPUFrom != -1 && context.contextType == VFXContextType.Init)
  725. bufferMappings.Add(new VFXMapping("eventList", eventGPUFrom));
  726. if (deadListBufferIndex != -1 && (context.taskType == VFXTaskType.Initialize || context.taskType == VFXTaskType.Update))
  727. bufferMappings.Add(new VFXMapping(context.contextType == VFXContextType.Update ? "deadListOut" : "deadListIn", deadListBufferIndex));
  728. if (deadListCountIndex != -1 && context.contextType == VFXContextType.Init)
  729. bufferMappings.Add(new VFXMapping("deadListCount", deadListCountIndex));
  730. if (attributeSourceBufferIndex != -1 && context.contextType == VFXContextType.Init)
  731. bufferMappings.Add(new VFXMapping("sourceAttributeBuffer", attributeSourceBufferIndex));
  732. if (stripDataIndex != -1 && context.ownedType == VFXDataType.ParticleStrip)
  733. bufferMappings.Add(new VFXMapping("stripDataBuffer", stripDataIndex));
  734. bool hasAttachedStrip = IsAttributeStored(VFXAttribute.StripAlive);
  735. if (hasAttachedStrip)
  736. {
  737. var stripData = dependenciesOut.First(d => ((VFXDataParticle)d).hasStrip); // TODO Handle several strip attached
  738. bufferMappings.Add(new VFXMapping("attachedStripDataBuffer", dependentBuffers.stripBuffers[stripData]));
  739. }
  740. if (needsIndirectBuffer)
  741. {
  742. systemFlag |= VFXSystemFlag.SystemHasIndirectBuffer;
  743. if (context.contextType == VFXContextType.Output && (context as VFXAbstractParticleOutput).HasIndirectDraw())
  744. {
  745. bool hasCuller = indirectOutputToCuller.ContainsKey(context);
  746. additionalParameters.Add(new VFXMapping("indirectIndex", hasCuller ? indirectOutputToCuller[context].bufferIndex : globalIndirectBufferIndex));
  747. bufferMappings.Add(new VFXMapping("indirectBuffer", hasCuller ? indirectOutputToCuller[context].sortedBufferIndex : globalIndirectBufferIndex));
  748. }
  749. if (context.contextType == VFXContextType.Update)
  750. {
  751. if (context.taskType == VFXTaskType.Update && needsGlobalIndirectBuffer)
  752. bufferMappings.Add(new VFXMapping("indirectBuffer", globalIndirectBufferIndex));
  753. }
  754. if (context.contextType == VFXContextType.Filter)
  755. {
  756. if (context.taskType == VFXTaskType.CameraSort && needsGlobalIndirectBuffer)
  757. bufferMappings.Add(new VFXMapping("inputBuffer", globalIndirectBufferIndex));
  758. else if (context is VFXOutputUpdate)
  759. {
  760. var outputUpdate = (VFXOutputUpdate)context;
  761. int startIndex = outputUpdate.bufferIndex;
  762. uint bufferCount = outputUpdate.bufferCount;
  763. for (int j = 0; j < bufferCount; ++j)
  764. bufferMappings.Add(new VFXMapping("outputBuffer" + j, startIndex + j));
  765. }
  766. }
  767. }
  768. if (deadListBufferIndex != -1 && context.contextType == VFXContextType.Output && (context as VFXAbstractParticleOutput).NeedsDeadListCount())
  769. bufferMappings.Add(new VFXMapping("deadListCount", deadListCountIndex));
  770. if (context.taskType == VFXTaskType.CameraSort)
  771. {
  772. bufferMappings.Add(new VFXMapping("outputBuffer", sortBufferAIndex));
  773. if (deadListCountIndex != -1)
  774. bufferMappings.Add(new VFXMapping("deadListCount", deadListCountIndex));
  775. }
  776. var gpuTarget = context.allLinkedOutputSlot.SelectMany(o => (o.owner as VFXContext).outputContexts)
  777. .Where(c => c.CanBeCompiled())
  778. .Select(o => dependentBuffers.eventBuffers[o.GetData()])
  779. .ToArray();
  780. for (uint indexTarget = 0; indexTarget < (uint)gpuTarget.Length; ++indexTarget)
  781. {
  782. var prefix = VFXCodeGeneratorHelper.GeneratePrefix(indexTarget);
  783. bufferMappings.Add(new VFXMapping(string.Format("eventListOut_{0}", prefix), gpuTarget[indexTarget]));
  784. }
  785. uniformMappings.Clear();
  786. foreach (var uniform in contextData.uniformMapper.uniforms)
  787. uniformMappings.Add(new VFXMapping(contextData.uniformMapper.GetName(uniform), expressionGraph.GetFlattenedIndex(uniform)));
  788. foreach (var buffer in contextData.uniformMapper.buffers)
  789. uniformMappings.Add(new VFXMapping(contextData.uniformMapper.GetName(buffer), expressionGraph.GetFlattenedIndex(buffer)));
  790. foreach (var texture in contextData.uniformMapper.textures)
  791. {
  792. // TODO At the moment issue all names sharing the same texture as different texture slots. This is not optimized as it required more texture binding than necessary
  793. foreach (var name in contextData.uniformMapper.GetNames(texture))
  794. uniformMappings.Add(new VFXMapping(name, expressionGraph.GetFlattenedIndex(texture)));
  795. }
  796. // Retrieve all cpu mappings at context level (-1)
  797. var cpuMappings = contextData.cpuMapper.CollectExpression(-1).Select(exp => new VFXMapping(exp.name, expressionGraph.GetFlattenedIndex(exp.exp))).ToArray();
  798. //Check potential issue with invalid operation on CPU
  799. foreach (var mapping in cpuMappings)
  800. {
  801. if (mapping.index < 0)
  802. {
  803. reporter?.RegisterError(context.GetSlotByPath(true, mapping.name), "GPUNodeLinkedTOCPUSlot", VFXErrorType.Error, "Can not link a GPU operator to a system wide (CPU) input."); ;
  804. throw new InvalidOperationException("Unable to compute CPU expression for mapping : " + mapping.name);
  805. }
  806. }
  807. taskDesc.buffers = bufferMappings.ToArray();
  808. taskDesc.temporaryBuffers = temporaryBufferMappings.ToArray();
  809. taskDesc.values = uniformMappings.ToArray();
  810. taskDesc.parameters = cpuMappings.Concat(contextData.parameters).Concat(additionalParameters).ToArray();
  811. taskDesc.shaderSourceIndex = contextToCompiledData[context].indexInShaderSource;
  812. taskDesc.model = context;
  813. if (context is IVFXMultiMeshOutput) // If the context is a multi mesh output, split and patch task desc into several tasks
  814. {
  815. var multiMeshOutput = (IVFXMultiMeshOutput)context;
  816. for (int j = (int)multiMeshOutput.meshCount - 1; j >= 0; --j) // Back to front to be consistent with LOD and alpha
  817. {
  818. VFXEditorTaskDesc singleMeshTaskDesc = taskDesc;
  819. singleMeshTaskDesc.parameters = VFXMultiMeshHelper.PatchCPUMapping(taskDesc.parameters, multiMeshOutput.meshCount, j).ToArray();
  820. singleMeshTaskDesc.buffers = VFXMultiMeshHelper.PatchBufferMapping(taskDesc.buffers, j).ToArray();
  821. taskDescs.Add(singleMeshTaskDesc);
  822. }
  823. }
  824. else
  825. taskDescs.Add(taskDesc);
  826. // if task is a per camera update with sorting, add sort tasks
  827. if (context is VFXOutputUpdate)
  828. {
  829. var update = (VFXOutputUpdate)context;
  830. if (update.HasFeature(VFXOutputUpdate.Features.Sort))
  831. {
  832. for (int j = 0; j < update.bufferCount; ++j)
  833. {
  834. VFXEditorTaskDesc sortTaskDesc = new VFXEditorTaskDesc();
  835. sortTaskDesc.type = UnityEngine.VFX.VFXTaskType.PerCameraSort;
  836. sortTaskDesc.externalProcessor = null;
  837. sortTaskDesc.model = context;
  838. sortTaskDesc.buffers = new VFXMapping[3];
  839. sortTaskDesc.buffers[0] = new VFXMapping("srcBuffer", update.bufferIndex + j);
  840. if (capacity > 4096) // Add scratch buffer
  841. {
  842. sortTaskDesc.buffers[1] = new VFXMapping("scratchBuffer", outBufferDescs.Count);
  843. outBufferDescs.Add(new VFXGPUBufferDesc() { type = ComputeBufferType.Default, size = capacity, stride = 8 });
  844. }
  845. else
  846. sortTaskDesc.buffers[1] = new VFXMapping("scratchBuffer", -1); // No scratchBuffer needed
  847. sortTaskDesc.buffers[2] = new VFXMapping("dstBuffer", update.sortedBufferIndex + j);
  848. sortTaskDesc.parameters = new VFXMapping[1];
  849. sortTaskDesc.parameters[0] = new VFXMapping("globalSort", 0);
  850. taskDescs.Add(sortTaskDesc);
  851. }
  852. }
  853. }
  854. }
  855. string nativeName = string.Empty;
  856. if (systemNames != null)
  857. nativeName = systemNames.GetUniqueSystemName(this);
  858. else
  859. throw new InvalidOperationException("system names manager cannot be null");
  860. outSystemDescs.Add(new VFXEditorSystemDesc()
  861. {
  862. flags = systemFlag,
  863. tasks = taskDescs.ToArray(),
  864. capacity = capacity,
  865. name = nativeName,
  866. buffers = systemBufferMappings.ToArray(),
  867. values = systemValueMappings.ToArray(),
  868. type = VFXSystemType.Particle,
  869. layer = m_Layer
  870. });
  871. }
  872. public override void Sanitize(int version)
  873. {
  874. if (version < 8)
  875. {
  876. SetSettingValue("boundsMode", BoundsSettingMode.Manual);
  877. }
  878. base.Sanitize(version);
  879. }
  880. public override void CopySettings<T>(T dst)
  881. {
  882. var instance = dst as VFXDataParticle;
  883. instance.m_Space = m_Space;
  884. }
  885. public StructureOfArrayProvider.BucketInfo[] GetCurrentAttributeLayout()
  886. {
  887. return m_layoutAttributeCurrent.GetBucketLayoutInfo();
  888. }
  889. public StructureOfArrayProvider.BucketInfo[] GetSourceAttributeLayout()
  890. {
  891. return m_layoutAttributeSource.GetBucketLayoutInfo();
  892. }
  893. [SerializeField]
  894. private VFXCoordinateSpace m_Space; // TODO Should be an actual setting
  895. [NonSerialized]
  896. private StructureOfArrayProvider m_layoutAttributeCurrent = new StructureOfArrayProvider();
  897. [NonSerialized]
  898. private StructureOfArrayProvider m_layoutAttributeSource = new StructureOfArrayProvider();
  899. [NonSerialized]
  900. private bool m_ownAttributeSourceBuffer;
  901. }
  902. }