No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DecalEntityManager.cs 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541
  1. using System;
  2. using System.Collections.Generic;
  3. using Unity.Collections;
  4. using Unity.Mathematics;
  5. using UnityEngine.Assertions;
  6. using UnityEngine.Jobs;
  7. namespace UnityEngine.Rendering.Universal
  8. {
  9. internal class DecalEntityIndexer
  10. {
  11. public struct DecalEntityItem
  12. {
  13. public int chunkIndex;
  14. public int arrayIndex;
  15. public int version;
  16. }
  17. private List<DecalEntityItem> m_Entities = new List<DecalEntityItem>();
  18. private Queue<int> m_FreeIndices = new Queue<int>();
  19. public bool IsValid(DecalEntity decalEntity)
  20. {
  21. if (m_Entities.Count <= decalEntity.index)
  22. return false;
  23. return m_Entities[decalEntity.index].version == decalEntity.version;
  24. }
  25. public DecalEntity CreateDecalEntity(int arrayIndex, int chunkIndex)
  26. {
  27. // Reuse
  28. if (m_FreeIndices.Count != 0)
  29. {
  30. int entityIndex = m_FreeIndices.Dequeue();
  31. int newVersion = m_Entities[entityIndex].version + 1;
  32. m_Entities[entityIndex] = new DecalEntityItem()
  33. {
  34. arrayIndex = arrayIndex,
  35. chunkIndex = chunkIndex,
  36. version = newVersion,
  37. };
  38. return new DecalEntity()
  39. {
  40. index = entityIndex,
  41. version = newVersion,
  42. };
  43. }
  44. // Create new one
  45. {
  46. int entityIndex = m_Entities.Count;
  47. int version = 1;
  48. m_Entities.Add(new DecalEntityItem()
  49. {
  50. arrayIndex = arrayIndex,
  51. chunkIndex = chunkIndex,
  52. version = version,
  53. });
  54. return new DecalEntity()
  55. {
  56. index = entityIndex,
  57. version = version,
  58. };
  59. }
  60. }
  61. public void DestroyDecalEntity(DecalEntity decalEntity)
  62. {
  63. Assert.IsTrue(IsValid(decalEntity));
  64. m_FreeIndices.Enqueue(decalEntity.index);
  65. // Update version that everything that points to it will have outdated version
  66. var item = m_Entities[decalEntity.index];
  67. item.version++;
  68. m_Entities[decalEntity.index] = item;
  69. }
  70. public DecalEntityItem GetItem(DecalEntity decalEntity)
  71. {
  72. Assert.IsTrue(IsValid(decalEntity));
  73. return m_Entities[decalEntity.index];
  74. }
  75. public void UpdateIndex(DecalEntity decalEntity, int newArrayIndex)
  76. {
  77. Assert.IsTrue(IsValid(decalEntity));
  78. var item = m_Entities[decalEntity.index];
  79. item.arrayIndex = newArrayIndex;
  80. item.version = decalEntity.version;
  81. m_Entities[decalEntity.index] = item;
  82. }
  83. public void RemapChunkIndices(List<int> remaper)
  84. {
  85. for (int i = 0; i < m_Entities.Count; ++i)
  86. {
  87. int newChunkIndex = remaper[m_Entities[i].chunkIndex];
  88. var item = m_Entities[i];
  89. item.chunkIndex = newChunkIndex;
  90. m_Entities[i] = item;
  91. }
  92. }
  93. public void Clear()
  94. {
  95. m_Entities.Clear();
  96. m_FreeIndices.Clear();
  97. }
  98. }
  99. internal struct DecalEntity
  100. {
  101. public int index;
  102. public int version;
  103. }
  104. /// <summary>
  105. /// Contains <see cref="DecalEntity"/> and shared material.
  106. /// </summary>
  107. internal class DecalEntityChunk : DecalChunk
  108. {
  109. public Material material;
  110. public NativeArray<DecalEntity> decalEntities;
  111. public DecalProjector[] decalProjectors;
  112. public TransformAccessArray transformAccessArray;
  113. public override void Push()
  114. {
  115. count++;
  116. }
  117. public override void RemoveAtSwapBack(int entityIndex)
  118. {
  119. RemoveAtSwapBack(ref decalEntities, entityIndex, count);
  120. RemoveAtSwapBack(ref decalProjectors, entityIndex, count);
  121. transformAccessArray.RemoveAtSwapBack(entityIndex);
  122. count--;
  123. }
  124. public override void SetCapacity(int newCapacity)
  125. {
  126. decalEntities.ResizeArray(newCapacity);
  127. ResizeNativeArray(ref transformAccessArray, decalProjectors, newCapacity);
  128. ArrayExtensions.ResizeArray(ref decalProjectors, newCapacity);
  129. capacity = newCapacity;
  130. }
  131. public override void Dispose()
  132. {
  133. if (capacity == 0)
  134. return;
  135. decalEntities.Dispose();
  136. transformAccessArray.Dispose();
  137. decalProjectors = null;
  138. count = 0;
  139. capacity = 0;
  140. }
  141. }
  142. /// <summary>
  143. /// Manages lifetime between <see cref="DecalProjector"></see> and <see cref="DecalEntity"/>.
  144. /// Contains all <see cref="DecalChunk"/>.
  145. /// </summary>
  146. internal class DecalEntityManager : IDisposable
  147. {
  148. public List<DecalEntityChunk> entityChunks = new List<DecalEntityChunk>();
  149. public List<DecalCachedChunk> cachedChunks = new List<DecalCachedChunk>();
  150. public List<DecalCulledChunk> culledChunks = new List<DecalCulledChunk>();
  151. public List<DecalDrawCallChunk> drawCallChunks = new List<DecalDrawCallChunk>();
  152. public int chunkCount;
  153. private ProfilingSampler m_AddDecalSampler;
  154. private ProfilingSampler m_ResizeChunks;
  155. private ProfilingSampler m_SortChunks;
  156. private DecalEntityIndexer m_DecalEntityIndexer = new DecalEntityIndexer();
  157. private Dictionary<Material, int> m_MaterialToChunkIndex = new Dictionary<Material, int>();
  158. private struct CombinedChunks
  159. {
  160. public DecalEntityChunk entityChunk;
  161. public DecalCachedChunk cachedChunk;
  162. public DecalCulledChunk culledChunk;
  163. public DecalDrawCallChunk drawCallChunk;
  164. public int previousChunkIndex;
  165. public bool valid;
  166. }
  167. private List<CombinedChunks> m_CombinedChunks = new List<CombinedChunks>();
  168. private List<int> m_CombinedChunkRemmap = new List<int>();
  169. private Material m_ErrorMaterial;
  170. public Material errorMaterial
  171. {
  172. get
  173. {
  174. if (m_ErrorMaterial == null)
  175. m_ErrorMaterial = CoreUtils.CreateEngineMaterial(Shader.Find("Hidden/InternalErrorShader"));
  176. return m_ErrorMaterial;
  177. }
  178. }
  179. private Mesh m_DecalProjectorMesh;
  180. public Mesh decalProjectorMesh
  181. {
  182. get
  183. {
  184. if (m_DecalProjectorMesh == null)
  185. m_DecalProjectorMesh = CoreUtils.CreateCubeMesh(new Vector4(-0.5f, -0.5f, -0.5f, 1.0f), new Vector4(0.5f, 0.5f, 0.5f, 1.0f));
  186. return m_DecalProjectorMesh;
  187. }
  188. }
  189. public DecalEntityManager()
  190. {
  191. m_AddDecalSampler = new ProfilingSampler("DecalEntityManager.CreateDecalEntity");
  192. m_ResizeChunks = new ProfilingSampler("DecalEntityManager.ResizeChunks");
  193. m_SortChunks = new ProfilingSampler("DecalEntityManager.SortChunks");
  194. }
  195. public bool IsValid(DecalEntity decalEntity)
  196. {
  197. return m_DecalEntityIndexer.IsValid(decalEntity);
  198. }
  199. public DecalEntity CreateDecalEntity(DecalProjector decalProjector)
  200. {
  201. var material = decalProjector.material;
  202. if (material == null)
  203. material = errorMaterial;
  204. using (new ProfilingScope(m_AddDecalSampler))
  205. {
  206. int chunkIndex = CreateChunkIndex(material);
  207. int entityIndex = entityChunks[chunkIndex].count;
  208. DecalEntity entity = m_DecalEntityIndexer.CreateDecalEntity(entityIndex, chunkIndex);
  209. DecalEntityChunk entityChunk = entityChunks[chunkIndex];
  210. DecalCachedChunk cachedChunk = cachedChunks[chunkIndex];
  211. DecalCulledChunk culledChunk = culledChunks[chunkIndex];
  212. DecalDrawCallChunk drawCallChunk = drawCallChunks[chunkIndex];
  213. // Make sure we have space to add new entity
  214. if (entityChunks[chunkIndex].capacity == entityChunks[chunkIndex].count)
  215. {
  216. using (new ProfilingScope(m_ResizeChunks))
  217. {
  218. int newCapacity = entityChunks[chunkIndex].capacity + entityChunks[chunkIndex].capacity;
  219. newCapacity = math.max(8, newCapacity);
  220. entityChunk.SetCapacity(newCapacity);
  221. cachedChunk.SetCapacity(newCapacity);
  222. culledChunk.SetCapacity(newCapacity);
  223. drawCallChunk.SetCapacity(newCapacity);
  224. }
  225. }
  226. entityChunk.Push();
  227. cachedChunk.Push();
  228. culledChunk.Push();
  229. drawCallChunk.Push();
  230. entityChunk.decalProjectors[entityIndex] = decalProjector;
  231. entityChunk.decalEntities[entityIndex] = entity;
  232. entityChunk.transformAccessArray.Add(decalProjector.transform);
  233. UpdateDecalEntityData(entity, decalProjector);
  234. return entity;
  235. }
  236. }
  237. private int CreateChunkIndex(Material material)
  238. {
  239. if (!m_MaterialToChunkIndex.TryGetValue(material, out int chunkIndex))
  240. {
  241. var propertyBlock = new MaterialPropertyBlock();
  242. // In order instanced and non instanced rendering to work with _NormalToWorld and _DecalLayerMaskFromDecal
  243. // We need to make sure the array are created with maximum size
  244. propertyBlock.SetMatrixArray("_NormalToWorld", new Matrix4x4[DecalDrawSystem.MaxBatchSize]);
  245. propertyBlock.SetFloatArray("_DecalLayerMaskFromDecal", new float[DecalDrawSystem.MaxBatchSize]);
  246. entityChunks.Add(new DecalEntityChunk() { material = material });
  247. cachedChunks.Add(new DecalCachedChunk()
  248. {
  249. propertyBlock = propertyBlock,
  250. });
  251. culledChunks.Add(new DecalCulledChunk());
  252. drawCallChunks.Add(new DecalDrawCallChunk() { subCallCounts = new NativeArray<int>(1, Allocator.Persistent) });
  253. m_CombinedChunks.Add(new CombinedChunks());
  254. m_CombinedChunkRemmap.Add(0);
  255. m_MaterialToChunkIndex.Add(material, chunkCount);
  256. return chunkCount++;
  257. }
  258. return chunkIndex;
  259. }
  260. public void UpdateAllDecalEntitiesData()
  261. {
  262. foreach (var entityChunk in entityChunks)
  263. {
  264. for (int i = 0; i < entityChunk.count; ++i)
  265. {
  266. var decalProjector = entityChunk.decalProjectors[i];
  267. if (decalProjector == null)
  268. continue;
  269. var entity = entityChunk.decalEntities[i];
  270. if (!IsValid(entity))
  271. continue;
  272. UpdateDecalEntityData(entity, decalProjector);
  273. }
  274. }
  275. }
  276. public void UpdateDecalEntityData(DecalEntity decalEntity, DecalProjector decalProjector)
  277. {
  278. var decalItem = m_DecalEntityIndexer.GetItem(decalEntity);
  279. int chunkIndex = decalItem.chunkIndex;
  280. int arrayIndex = decalItem.arrayIndex;
  281. DecalCachedChunk cachedChunk = cachedChunks[chunkIndex];
  282. cachedChunk.sizeOffsets[arrayIndex] = Matrix4x4.Translate(decalProjector.decalOffset) * Matrix4x4.Scale(decalProjector.decalSize);
  283. float drawDistance = decalProjector.drawDistance;
  284. float fadeScale = decalProjector.fadeScale;
  285. float startAngleFade = decalProjector.startAngleFade;
  286. float endAngleFade = decalProjector.endAngleFade;
  287. Vector4 uvScaleBias = decalProjector.uvScaleBias;
  288. int layerMask = decalProjector.gameObject.layer;
  289. ulong sceneLayerMask = decalProjector.gameObject.sceneCullingMask;
  290. float fadeFactor = decalProjector.fadeFactor;
  291. cachedChunk.drawDistances[arrayIndex] = new Vector2(drawDistance, fadeScale);
  292. // In the shader to remap from cosine -1 to 1 to new range 0..1 (with 0 - 0 degree and 1 - 180 degree)
  293. // we do 1.0 - (dot() * 0.5 + 0.5) => 0.5 * (1 - dot())
  294. // we actually square that to get smoother result => x = (0.5 - 0.5 * dot())^2
  295. // Do a remap in the shader. 1.0 - saturate((x - start) / (end - start))
  296. // After simplification => saturate(a + b * dot() * (dot() - 2.0))
  297. // a = 1.0 - (0.25 - start) / (end - start), y = - 0.25 / (end - start)
  298. if (startAngleFade == 180.0f) // angle fade is disabled
  299. {
  300. cachedChunk.angleFades[arrayIndex] = new Vector2(0.0f, 0.0f);
  301. }
  302. else
  303. {
  304. float angleStart = startAngleFade / 180.0f;
  305. float angleEnd = endAngleFade / 180.0f;
  306. var range = Mathf.Max(0.0001f, angleEnd - angleStart);
  307. cachedChunk.angleFades[arrayIndex] = new Vector2(1.0f - (0.25f - angleStart) / range, -0.25f / range);
  308. }
  309. cachedChunk.uvScaleBias[arrayIndex] = uvScaleBias;
  310. cachedChunk.layerMasks[arrayIndex] = layerMask;
  311. cachedChunk.sceneLayerMasks[arrayIndex] = sceneLayerMask;
  312. cachedChunk.fadeFactors[arrayIndex] = fadeFactor;
  313. cachedChunk.scaleModes[arrayIndex] = decalProjector.scaleMode;
  314. cachedChunk.renderingLayerMasks[arrayIndex] = RenderingLayerUtils.ToValidRenderingLayers(decalProjector.renderingLayerMask);
  315. cachedChunk.positions[arrayIndex] = decalProjector.transform.position;
  316. cachedChunk.rotation[arrayIndex] = decalProjector.transform.rotation;
  317. cachedChunk.scales[arrayIndex] = decalProjector.transform.lossyScale;
  318. cachedChunk.dirty[arrayIndex] = true;
  319. }
  320. public void DestroyDecalEntity(DecalEntity decalEntity)
  321. {
  322. if (!m_DecalEntityIndexer.IsValid(decalEntity))
  323. return;
  324. var decalItem = m_DecalEntityIndexer.GetItem(decalEntity);
  325. m_DecalEntityIndexer.DestroyDecalEntity(decalEntity);
  326. int chunkIndex = decalItem.chunkIndex;
  327. int arrayIndex = decalItem.arrayIndex;
  328. DecalEntityChunk entityChunk = entityChunks[chunkIndex];
  329. DecalCachedChunk cachedChunk = cachedChunks[chunkIndex];
  330. DecalCulledChunk culledChunk = culledChunks[chunkIndex];
  331. DecalDrawCallChunk drawCallChunk = drawCallChunks[chunkIndex];
  332. int lastArrayIndex = entityChunk.count - 1;
  333. if (arrayIndex != lastArrayIndex)
  334. m_DecalEntityIndexer.UpdateIndex(entityChunk.decalEntities[lastArrayIndex], arrayIndex);
  335. entityChunk.RemoveAtSwapBack(arrayIndex);
  336. cachedChunk.RemoveAtSwapBack(arrayIndex);
  337. culledChunk.RemoveAtSwapBack(arrayIndex);
  338. drawCallChunk.RemoveAtSwapBack(arrayIndex);
  339. }
  340. public void Update()
  341. {
  342. using (new ProfilingScope(m_SortChunks))
  343. {
  344. for (int i = 0; i < chunkCount; ++i)
  345. {
  346. if (entityChunks[i].material == null)
  347. entityChunks[i].material = errorMaterial;
  348. }
  349. // Combine chunks into single array
  350. for (int i = 0; i < chunkCount; ++i)
  351. {
  352. m_CombinedChunks[i] = new CombinedChunks()
  353. {
  354. entityChunk = entityChunks[i],
  355. cachedChunk = cachedChunks[i],
  356. culledChunk = culledChunks[i],
  357. drawCallChunk = drawCallChunks[i],
  358. previousChunkIndex = i,
  359. valid = entityChunks[i].count != 0,
  360. };
  361. }
  362. // Sort
  363. m_CombinedChunks.Sort((a, b) =>
  364. {
  365. if (a.valid && !b.valid)
  366. return -1;
  367. if (!a.valid && b.valid)
  368. return 1;
  369. if (a.cachedChunk.drawOrder < b.cachedChunk.drawOrder)
  370. return -1;
  371. if (a.cachedChunk.drawOrder > b.cachedChunk.drawOrder)
  372. return 1;
  373. return a.entityChunk.material.GetHashCode().CompareTo(b.entityChunk.material.GetHashCode());
  374. });
  375. // Early out if nothing changed
  376. bool dirty = false;
  377. for (int i = 0; i < chunkCount; ++i)
  378. {
  379. if (m_CombinedChunks[i].previousChunkIndex != i || !m_CombinedChunks[i].valid)
  380. {
  381. dirty = true;
  382. break;
  383. }
  384. }
  385. if (!dirty)
  386. return;
  387. // Update chunks
  388. int count = 0;
  389. m_MaterialToChunkIndex.Clear();
  390. for (int i = 0; i < chunkCount; ++i)
  391. {
  392. var combinedChunk = m_CombinedChunks[i];
  393. // Destroy invalid chunk for cleanup
  394. if (!m_CombinedChunks[i].valid)
  395. {
  396. combinedChunk.entityChunk.currentJobHandle.Complete();
  397. combinedChunk.cachedChunk.currentJobHandle.Complete();
  398. combinedChunk.culledChunk.currentJobHandle.Complete();
  399. combinedChunk.drawCallChunk.currentJobHandle.Complete();
  400. combinedChunk.entityChunk.Dispose();
  401. combinedChunk.cachedChunk.Dispose();
  402. combinedChunk.culledChunk.Dispose();
  403. combinedChunk.drawCallChunk.Dispose();
  404. continue;
  405. }
  406. entityChunks[i] = combinedChunk.entityChunk;
  407. cachedChunks[i] = combinedChunk.cachedChunk;
  408. culledChunks[i] = combinedChunk.culledChunk;
  409. drawCallChunks[i] = combinedChunk.drawCallChunk;
  410. if (!m_MaterialToChunkIndex.ContainsKey(entityChunks[i].material))
  411. m_MaterialToChunkIndex.Add(entityChunks[i].material, i);
  412. m_CombinedChunkRemmap[combinedChunk.previousChunkIndex] = i;
  413. count++;
  414. }
  415. // In case some chunks where destroyed resize the arrays
  416. if (chunkCount > count)
  417. {
  418. entityChunks.RemoveRange(count, chunkCount - count);
  419. cachedChunks.RemoveRange(count, chunkCount - count);
  420. culledChunks.RemoveRange(count, chunkCount - count);
  421. drawCallChunks.RemoveRange(count, chunkCount - count);
  422. m_CombinedChunks.RemoveRange(count, chunkCount - count);
  423. chunkCount = count;
  424. }
  425. // Remap entities chunk index with new sorted ones
  426. m_DecalEntityIndexer.RemapChunkIndices(m_CombinedChunkRemmap);
  427. }
  428. }
  429. public void Dispose()
  430. {
  431. CoreUtils.Destroy(m_ErrorMaterial);
  432. CoreUtils.Destroy(m_DecalProjectorMesh);
  433. foreach (var entityChunk in entityChunks)
  434. entityChunk.currentJobHandle.Complete();
  435. foreach (var cachedChunk in cachedChunks)
  436. cachedChunk.currentJobHandle.Complete();
  437. foreach (var culledChunk in culledChunks)
  438. culledChunk.currentJobHandle.Complete();
  439. foreach (var drawCallChunk in drawCallChunks)
  440. drawCallChunk.currentJobHandle.Complete();
  441. foreach (var entityChunk in entityChunks)
  442. entityChunk.Dispose();
  443. foreach (var cachedChunk in cachedChunks)
  444. cachedChunk.Dispose();
  445. foreach (var culledChunk in culledChunks)
  446. culledChunk.Dispose();
  447. foreach (var drawCallChunk in drawCallChunks)
  448. drawCallChunk.Dispose();
  449. m_DecalEntityIndexer.Clear();
  450. m_MaterialToChunkIndex.Clear();
  451. entityChunks.Clear();
  452. cachedChunks.Clear();
  453. culledChunks.Clear();
  454. drawCallChunks.Clear();
  455. m_CombinedChunks.Clear();
  456. chunkCount = 0;
  457. }
  458. }
  459. }