Brak opisu
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.

OcclusionCullingCommon.cs 26KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617
  1. using System;
  2. using System.Collections.Generic;
  3. using Unity.Collections;
  4. using Unity.Collections.LowLevel.Unsafe;
  5. using Unity.Mathematics;
  6. using UnityEngine.Rendering.RenderGraphModule;
  7. namespace UnityEngine.Rendering
  8. {
  9. [GenerateHLSL]
  10. internal enum OcclusionCullingCommonConfig
  11. {
  12. MaxOccluderMips = 8,
  13. MaxOccluderSilhouettePlanes = 6,
  14. MaxSubviewsPerView = 6,
  15. DebugPyramidOffset = 4, // TODO: rename
  16. }
  17. [GenerateHLSL(needAccessors = false)]
  18. internal enum OcclusionTestDebugFlag
  19. {
  20. AlwaysPass = (1 << 0),
  21. CountVisible = (1 << 1),
  22. }
  23. internal struct OcclusionTestComputeShader
  24. {
  25. public ComputeShader cs;
  26. public LocalKeyword occlusionDebugKeyword;
  27. public void Init(ComputeShader cs)
  28. {
  29. this.cs = cs;
  30. this.occlusionDebugKeyword = new LocalKeyword(cs, "OCCLUSION_DEBUG");
  31. }
  32. }
  33. internal struct SilhouettePlaneCache : IDisposable
  34. {
  35. private struct Slot
  36. {
  37. public bool isActive;
  38. public int viewInstanceID;
  39. public int planeCount; // planeIndex = slotIndex * kMaxSilhouettePlanes
  40. public int lastUsedFrameIndex;
  41. public Slot(int viewInstanceID, int planeCount, int frameIndex)
  42. {
  43. this.isActive = true;
  44. this.viewInstanceID = viewInstanceID;
  45. this.planeCount = planeCount;
  46. this.lastUsedFrameIndex = frameIndex;
  47. }
  48. }
  49. private const int kMaxSilhouettePlanes = (int)OcclusionCullingCommonConfig.MaxOccluderSilhouettePlanes;
  50. private NativeParallelHashMap<int, int> m_SubviewIDToIndexMap;
  51. private NativeList<int> m_SlotFreeList;
  52. private NativeList<Slot> m_Slots;
  53. private NativeList<Plane> m_PlaneStorage;
  54. public void Init()
  55. {
  56. m_SubviewIDToIndexMap = new NativeParallelHashMap<int, int>(16, Allocator.Persistent);
  57. m_SlotFreeList = new NativeList<int>(16, Allocator.Persistent);
  58. m_Slots = new NativeList<Slot>(16, Allocator.Persistent);
  59. m_PlaneStorage = new NativeList<Plane>(16 * kMaxSilhouettePlanes, Allocator.Persistent);
  60. }
  61. public void Dispose()
  62. {
  63. m_SubviewIDToIndexMap.Dispose();
  64. m_SlotFreeList.Dispose();
  65. m_Slots.Dispose();
  66. m_PlaneStorage.Dispose();
  67. }
  68. public void Update(int viewInstanceID, NativeArray<Plane> planes, int frameIndex)
  69. {
  70. int planeCount = Math.Min(planes.Length, kMaxSilhouettePlanes);
  71. if (!m_SubviewIDToIndexMap.TryGetValue(viewInstanceID, out int slotIndex))
  72. {
  73. if (m_SlotFreeList.Length > 0)
  74. {
  75. // take a free slot from the free list
  76. slotIndex = m_SlotFreeList[m_SlotFreeList.Length - 1];
  77. m_SlotFreeList.Length = m_SlotFreeList.Length - 1;
  78. }
  79. else
  80. {
  81. // ensure we have capacity for a few more
  82. if (m_Slots.Length == m_Slots.Capacity)
  83. {
  84. int newCapacity = m_Slots.Length + 8;
  85. m_Slots.SetCapacity(newCapacity);
  86. m_PlaneStorage.SetCapacity(newCapacity * kMaxSilhouettePlanes);
  87. }
  88. // use the next slot in storage
  89. slotIndex = m_Slots.Length;
  90. int newSlotCount = slotIndex + 1;
  91. m_Slots.ResizeUninitialized(newSlotCount);
  92. m_PlaneStorage.ResizeUninitialized(newSlotCount * kMaxSilhouettePlanes);
  93. }
  94. // associate with this view ID
  95. m_SubviewIDToIndexMap.Add(viewInstanceID, slotIndex);
  96. }
  97. m_Slots[slotIndex] = new Slot(viewInstanceID, planeCount, frameIndex);
  98. m_PlaneStorage.AsArray().GetSubArray(slotIndex * kMaxSilhouettePlanes, planeCount).CopyFrom(planes);
  99. }
  100. public void FreeUnusedSlots(int frameIndex, int maximumAge)
  101. {
  102. for (int slotIndex = 0; slotIndex < m_Slots.Length; ++slotIndex)
  103. {
  104. var slot = m_Slots[slotIndex];
  105. if (!slot.isActive)
  106. continue;
  107. if ((frameIndex - slot.lastUsedFrameIndex) > maximumAge)
  108. {
  109. slot.isActive = false;
  110. m_Slots[slotIndex] = slot;
  111. m_SubviewIDToIndexMap.Remove(slot.viewInstanceID);
  112. m_SlotFreeList.Add(slotIndex);
  113. }
  114. }
  115. }
  116. public NativeArray<Plane> GetSubArray(int viewInstanceID)
  117. {
  118. int planeOffset = 0;
  119. int planeCount = 0;
  120. if (m_SubviewIDToIndexMap.TryGetValue(viewInstanceID, out int slotIndex))
  121. {
  122. planeOffset = slotIndex * kMaxSilhouettePlanes;
  123. planeCount = m_Slots[slotIndex].planeCount;
  124. }
  125. return m_PlaneStorage.AsArray().GetSubArray(planeOffset, planeCount);
  126. }
  127. }
  128. internal class OcclusionCullingCommon : IDisposable
  129. {
  130. private struct OccluderContextSlot
  131. {
  132. public bool valid;
  133. public int lastUsedFrameIndex;
  134. public int viewInstanceID;
  135. }
  136. private static readonly int s_MaxContextGCFrame = 8; // Allow a few frames for alternate frame shadow updates before cleanup
  137. private Material m_DebugOcclusionTestMaterial;
  138. private Material m_OccluderDebugViewMaterial;
  139. private ComputeShader m_OcclusionDebugCS;
  140. private int m_ClearOcclusionDebugKernel;
  141. private ComputeShader m_OccluderDepthPyramidCS;
  142. private int m_OccluderDepthDownscaleKernel;
  143. private int m_FrameIndex = 0;
  144. private SilhouettePlaneCache m_SilhouettePlaneCache;
  145. private NativeParallelHashMap<int, int> m_ViewIDToIndexMap;
  146. private List<OccluderContext> m_OccluderContextData;
  147. private NativeList<OccluderContextSlot> m_OccluderContextSlots;
  148. private NativeList<int> m_FreeOccluderContexts;
  149. private NativeArray<OcclusionCullingCommonShaderVariables> m_CommonShaderVariables;
  150. private ComputeBuffer m_CommonConstantBuffer;
  151. private NativeArray<OcclusionCullingDebugShaderVariables> m_DebugShaderVariables;
  152. private ComputeBuffer m_DebugConstantBuffer;
  153. private ProfilingSampler m_ProfilingSamplerUpdateOccluders;
  154. private ProfilingSampler m_ProfilingSamplerOcclusionTestOverlay;
  155. private ProfilingSampler m_ProfilingSamplerOccluderOverlay;
  156. internal void Init(GPUResidentDrawerResources resources)
  157. {
  158. m_DebugOcclusionTestMaterial = CoreUtils.CreateEngineMaterial(resources.debugOcclusionTestPS);
  159. m_OccluderDebugViewMaterial = CoreUtils.CreateEngineMaterial(resources.debugOccluderPS);
  160. m_OcclusionDebugCS = resources.occlusionCullingDebugKernels;
  161. m_ClearOcclusionDebugKernel = m_OcclusionDebugCS.FindKernel("ClearOcclusionDebug");
  162. m_OccluderDepthPyramidCS = resources.occluderDepthPyramidKernels;
  163. m_OccluderDepthDownscaleKernel = m_OccluderDepthPyramidCS.FindKernel("OccluderDepthDownscale");
  164. m_SilhouettePlaneCache.Init();
  165. m_ViewIDToIndexMap = new NativeParallelHashMap<int, int>(64, Allocator.Persistent);
  166. m_OccluderContextData = new List<OccluderContext>();
  167. m_OccluderContextSlots = new NativeList<OccluderContextSlot>(64, Allocator.Persistent);
  168. m_FreeOccluderContexts = new NativeList<int>(64, Allocator.Persistent);
  169. m_ProfilingSamplerUpdateOccluders = new ProfilingSampler("UpdateOccluders");
  170. m_ProfilingSamplerOcclusionTestOverlay = new ProfilingSampler("OcclusionTestOverlay");
  171. m_ProfilingSamplerOccluderOverlay = new ProfilingSampler("OccluderOverlay");
  172. m_CommonShaderVariables = new NativeArray<OcclusionCullingCommonShaderVariables>(1, Allocator.Persistent);
  173. m_CommonConstantBuffer = new ComputeBuffer(1, UnsafeUtility.SizeOf<OcclusionCullingCommonShaderVariables>(), ComputeBufferType.Constant);
  174. m_DebugShaderVariables = new NativeArray<OcclusionCullingDebugShaderVariables>(1, Allocator.Persistent);
  175. m_DebugConstantBuffer = new ComputeBuffer(1, UnsafeUtility.SizeOf<OcclusionCullingDebugShaderVariables>(), ComputeBufferType.Constant);
  176. }
  177. private static class ShaderIDs
  178. {
  179. public static readonly int OcclusionCullingCommonShaderVariables = Shader.PropertyToID("OcclusionCullingCommonShaderVariables");
  180. public static readonly int _OccluderDepthPyramid = Shader.PropertyToID("_OccluderDepthPyramid");
  181. public static readonly int _OcclusionDebugOverlay = Shader.PropertyToID("_OcclusionDebugOverlay");
  182. public static readonly int OcclusionCullingDebugShaderVariables = Shader.PropertyToID("OcclusionCullingDebugShaderVariables");
  183. }
  184. internal static bool UseOcclusionDebug(in OccluderContext occluderCtx)
  185. {
  186. return occluderCtx.occlusionDebugOverlaySize != 0;
  187. }
  188. internal void PrepareCulling(ComputeCommandBuffer cmd, in OccluderContext occluderCtx, in OcclusionCullingSettings settings, in InstanceOcclusionTestSubviewSettings subviewSettings, in OcclusionTestComputeShader shader, bool useOcclusionDebug)
  189. {
  190. OccluderContext.SetKeyword(cmd, shader.cs, shader.occlusionDebugKeyword, useOcclusionDebug);
  191. var debugStats = GPUResidentDrawer.GetDebugStats();
  192. m_CommonShaderVariables[0] = new OcclusionCullingCommonShaderVariables(
  193. in occluderCtx,
  194. subviewSettings,
  195. debugStats?.occlusionOverlayCountVisible ?? false,
  196. debugStats?.overrideOcclusionTestToAlwaysPass ?? false);
  197. cmd.SetBufferData(m_CommonConstantBuffer, m_CommonShaderVariables);
  198. cmd.SetComputeConstantBufferParam(shader.cs, ShaderIDs.OcclusionCullingCommonShaderVariables, m_CommonConstantBuffer, 0, m_CommonConstantBuffer.stride);
  199. DispatchDebugClear(cmd, settings.viewInstanceID);
  200. }
  201. internal static void SetDepthPyramid(ComputeCommandBuffer cmd, in OcclusionTestComputeShader shader, int kernel, in OccluderHandles occluderHandles)
  202. {
  203. cmd.SetComputeTextureParam(shader.cs, kernel, ShaderIDs._OccluderDepthPyramid, occluderHandles.occluderDepthPyramid);
  204. }
  205. internal static void SetDebugPyramid(ComputeCommandBuffer cmd, in OcclusionTestComputeShader shader, int kernel, in OccluderHandles occluderHandles)
  206. {
  207. cmd.SetComputeBufferParam(shader.cs, kernel, ShaderIDs._OcclusionDebugOverlay, occluderHandles.occlusionDebugOverlay);
  208. }
  209. private class OcclusionTestOverlaySetupPassData
  210. {
  211. public OcclusionCullingDebugShaderVariables cb;
  212. }
  213. private class OcclusionTestOverlayPassData
  214. {
  215. public BufferHandle debugPyramid;
  216. }
  217. public void RenderDebugOcclusionTestOverlay(RenderGraph renderGraph, DebugDisplayGPUResidentDrawer debugSettings, int viewInstanceID, TextureHandle colorBuffer)
  218. {
  219. if (debugSettings == null)
  220. return;
  221. if (!debugSettings.occlusionTestOverlayEnable)
  222. return;
  223. OcclusionCullingDebugOutput debugOutput = GetOcclusionTestDebugOutput(viewInstanceID);
  224. if (debugOutput.occlusionDebugOverlay == null)
  225. return;
  226. using (var builder = renderGraph.AddComputePass<OcclusionTestOverlaySetupPassData>("OcclusionTestOverlay", out var passData, m_ProfilingSamplerOcclusionTestOverlay))
  227. {
  228. builder.AllowPassCulling(false);
  229. passData.cb = debugOutput.cb;
  230. builder.SetRenderFunc(
  231. (OcclusionTestOverlaySetupPassData data, ComputeGraphContext ctx) =>
  232. {
  233. var occ = GPUResidentDrawer.instance.batcher.occlusionCullingCommon;
  234. occ.m_DebugShaderVariables[0] = data.cb;
  235. ctx.cmd.SetBufferData(occ.m_DebugConstantBuffer, occ.m_DebugShaderVariables);
  236. occ.m_DebugOcclusionTestMaterial.SetConstantBuffer(
  237. ShaderIDs.OcclusionCullingDebugShaderVariables,
  238. occ.m_DebugConstantBuffer,
  239. 0,
  240. occ.m_DebugConstantBuffer.stride);
  241. });
  242. }
  243. using (var builder = renderGraph.AddRasterRenderPass<OcclusionTestOverlayPassData>("OcclusionTestOverlay", out var passData, m_ProfilingSamplerOcclusionTestOverlay))
  244. {
  245. builder.AllowGlobalStateModification(true);
  246. passData.debugPyramid = renderGraph.ImportBuffer(debugOutput.occlusionDebugOverlay);
  247. builder.SetRenderAttachment(colorBuffer, 0);
  248. builder.UseBuffer(passData.debugPyramid);
  249. builder.SetRenderFunc(
  250. (OcclusionTestOverlayPassData data, RasterGraphContext ctx) =>
  251. {
  252. ctx.cmd.SetGlobalBuffer(ShaderIDs._OcclusionDebugOverlay, data.debugPyramid);
  253. CoreUtils.DrawFullScreen(ctx.cmd, m_DebugOcclusionTestMaterial);
  254. });
  255. }
  256. }
  257. private struct DebugOccluderViewData
  258. {
  259. public int passIndex;
  260. public Rect viewport;
  261. public bool valid;
  262. }
  263. class OccluderOverlayPassData
  264. {
  265. public Material debugMaterial;
  266. public RTHandle occluderTexture;
  267. public Rect viewport;
  268. public int passIndex;
  269. public Vector2 validRange;
  270. }
  271. public void RenderDebugOccluderOverlay(RenderGraph renderGraph, DebugDisplayGPUResidentDrawer debugSettings, Vector2 screenPos, float maxHeight, TextureHandle colorBuffer)
  272. {
  273. if (debugSettings == null)
  274. return;
  275. if (!debugSettings.occluderDebugViewEnable)
  276. return;
  277. if (!debugSettings.GetOccluderViewInstanceID(out var viewInstanceID))
  278. return;
  279. var occluderTexture = GetOcclusionTestDebugOutput(viewInstanceID).occluderDepthPyramid;
  280. if (occluderTexture == null)
  281. return;
  282. Material debugMaterial = m_OccluderDebugViewMaterial;
  283. int passIndex = debugMaterial.FindPass("DebugOccluder");
  284. Vector2 outputSize = occluderTexture.referenceSize;
  285. float scaleFactor = maxHeight / outputSize.y;
  286. outputSize *= scaleFactor;
  287. Rect viewport = new Rect(screenPos.x, screenPos.y, outputSize.x, outputSize.y);
  288. using (var builder = renderGraph.AddRasterRenderPass<OccluderOverlayPassData>("OccluderOverlay", out var passData, m_ProfilingSamplerOccluderOverlay))
  289. {
  290. builder.AllowGlobalStateModification(true);
  291. builder.SetRenderAttachment(colorBuffer, 0);
  292. passData.debugMaterial = debugMaterial;
  293. passData.occluderTexture = occluderTexture;
  294. passData.viewport = viewport;
  295. passData.passIndex = passIndex;
  296. passData.validRange = debugSettings.occluderDebugViewRange;
  297. builder.SetRenderFunc(
  298. (OccluderOverlayPassData data, RasterGraphContext ctx) =>
  299. {
  300. var mpb = ctx.renderGraphPool.GetTempMaterialPropertyBlock();
  301. mpb.SetTexture("_OccluderTexture", data.occluderTexture);
  302. mpb.SetVector("_ValidRange", data.validRange);
  303. ctx.cmd.SetViewport(data.viewport);
  304. ctx.cmd.DrawProcedural(Matrix4x4.identity, data.debugMaterial, data.passIndex, MeshTopology.Triangles, 3, 1, mpb);
  305. });
  306. }
  307. }
  308. private void DispatchDebugClear(ComputeCommandBuffer cmd, int viewInstanceID)
  309. {
  310. if (!m_ViewIDToIndexMap.TryGetValue(viewInstanceID, out var contextIndex))
  311. return;
  312. OccluderContext occluderCtx = m_OccluderContextData[contextIndex];
  313. if (UseOcclusionDebug(in occluderCtx) && occluderCtx.debugNeedsClear)
  314. {
  315. var cs = m_OcclusionDebugCS;
  316. int kernel = m_ClearOcclusionDebugKernel;
  317. cmd.SetComputeConstantBufferParam(cs, ShaderIDs.OcclusionCullingCommonShaderVariables, m_CommonConstantBuffer, 0, m_CommonConstantBuffer.stride);
  318. cmd.SetComputeBufferParam(cs, kernel, ShaderIDs._OcclusionDebugOverlay, occluderCtx.occlusionDebugOverlay);
  319. Vector2Int mip0Size = occluderCtx.occluderMipBounds[0].size;
  320. cmd.DispatchCompute(cs, kernel, (mip0Size.x + 7) / 8, (mip0Size.y + 7) / 8, occluderCtx.subviewCount);
  321. // mark as cleared in the dictionary
  322. occluderCtx.debugNeedsClear = false;
  323. m_OccluderContextData[contextIndex] = occluderCtx;
  324. }
  325. }
  326. private OccluderHandles PrepareOccluders(RenderGraph renderGraph, in OccluderParameters occluderParams)
  327. {
  328. OccluderHandles occluderHandles = new OccluderHandles();
  329. if (occluderParams.depthTexture.IsValid())
  330. {
  331. if (!m_ViewIDToIndexMap.TryGetValue(occluderParams.viewInstanceID, out var contextIndex))
  332. contextIndex = NewContext(occluderParams.viewInstanceID);
  333. OccluderContext ctx = m_OccluderContextData[contextIndex];
  334. ctx.PrepareOccluders(occluderParams);
  335. occluderHandles = ctx.Import(renderGraph);
  336. m_OccluderContextData[contextIndex] = ctx;
  337. }
  338. else
  339. {
  340. DeleteContext(occluderParams.viewInstanceID);
  341. }
  342. return occluderHandles;
  343. }
  344. private void CreateFarDepthPyramid(ComputeCommandBuffer cmd, in OccluderParameters occluderParams, ReadOnlySpan<OccluderSubviewUpdate> occluderSubviewUpdates, in OccluderHandles occluderHandles)
  345. {
  346. if (!m_ViewIDToIndexMap.TryGetValue(occluderParams.viewInstanceID, out var contextIndex))
  347. return;
  348. var silhouettePlanes = m_SilhouettePlaneCache.GetSubArray(occluderParams.viewInstanceID);
  349. OccluderContext ctx = m_OccluderContextData[contextIndex];
  350. ctx.CreateFarDepthPyramid(cmd, occluderParams, occluderSubviewUpdates, occluderHandles, silhouettePlanes, m_OccluderDepthPyramidCS, m_OccluderDepthDownscaleKernel);
  351. ctx.version++;
  352. m_OccluderContextData[contextIndex] = ctx;
  353. var slot = m_OccluderContextSlots[contextIndex];
  354. slot.lastUsedFrameIndex = m_FrameIndex;
  355. m_OccluderContextSlots[contextIndex] = slot;
  356. }
  357. private class UpdateOccludersPassData
  358. {
  359. public OccluderParameters occluderParams;
  360. public List<OccluderSubviewUpdate> occluderSubviewUpdates;
  361. public OccluderHandles occluderHandles;
  362. }
  363. public bool UpdateInstanceOccluders(RenderGraph renderGraph, in OccluderParameters occluderParams, ReadOnlySpan<OccluderSubviewUpdate> occluderSubviewUpdates)
  364. {
  365. var occluderHandles = PrepareOccluders(renderGraph, occluderParams);
  366. if (!occluderHandles.occluderDepthPyramid.IsValid())
  367. return false;
  368. using (var builder = renderGraph.AddComputePass<UpdateOccludersPassData>("Update Occluders", out var passData, m_ProfilingSamplerUpdateOccluders))
  369. {
  370. builder.AllowGlobalStateModification(true);
  371. passData.occluderParams = occluderParams;
  372. if (passData.occluderSubviewUpdates is null)
  373. passData.occluderSubviewUpdates = new List<OccluderSubviewUpdate>();
  374. else
  375. passData.occluderSubviewUpdates.Clear();
  376. for (int i = 0; i < occluderSubviewUpdates.Length; ++i)
  377. passData.occluderSubviewUpdates.Add(occluderSubviewUpdates[i]);
  378. passData.occluderHandles = occluderHandles;
  379. builder.UseTexture(passData.occluderParams.depthTexture);
  380. passData.occluderHandles.UseForOccluderUpdate(builder);
  381. builder.SetRenderFunc(
  382. (UpdateOccludersPassData data, ComputeGraphContext context) =>
  383. {
  384. Span<OccluderSubviewUpdate> occluderSubviewUpdates = stackalloc OccluderSubviewUpdate[data.occluderSubviewUpdates.Count];
  385. int subviewMask = 0;
  386. for (int i = 0; i < data.occluderSubviewUpdates.Count; ++i)
  387. {
  388. occluderSubviewUpdates[i] = data.occluderSubviewUpdates[i];
  389. subviewMask |= 1 << data.occluderSubviewUpdates[i].subviewIndex;
  390. }
  391. var batcher = GPUResidentDrawer.instance.batcher;
  392. batcher.occlusionCullingCommon.CreateFarDepthPyramid(context.cmd, in data.occluderParams, occluderSubviewUpdates, in data.occluderHandles);
  393. batcher.instanceCullingBatcher.InstanceOccludersUpdated(data.occluderParams.viewInstanceID, subviewMask);
  394. });
  395. }
  396. return true;
  397. }
  398. internal void UpdateSilhouettePlanes(int viewInstanceID, NativeArray<Plane> planes)
  399. {
  400. m_SilhouettePlaneCache.Update(viewInstanceID, planes, m_FrameIndex);
  401. }
  402. internal OcclusionCullingDebugOutput GetOcclusionTestDebugOutput(int viewInstanceID)
  403. {
  404. if (m_ViewIDToIndexMap.TryGetValue(viewInstanceID, out var contextIndex) && m_OccluderContextSlots[contextIndex].valid)
  405. return m_OccluderContextData[contextIndex].GetDebugOutput();
  406. return new OcclusionCullingDebugOutput();
  407. }
  408. public void UpdateOccluderStats(DebugRendererBatcherStats debugStats)
  409. {
  410. debugStats.occluderStats.Clear();
  411. foreach (var pair in m_ViewIDToIndexMap)
  412. {
  413. if (pair.Value < m_OccluderContextSlots.Length && m_OccluderContextSlots[pair.Value].valid)
  414. {
  415. debugStats.occluderStats.Add(new DebugOccluderStats
  416. {
  417. viewInstanceID = pair.Key,
  418. subviewCount = m_OccluderContextData[pair.Value].subviewCount,
  419. occluderMipLayoutSize = m_OccluderContextData[pair.Value].occluderMipLayoutSize,
  420. });
  421. }
  422. }
  423. }
  424. internal bool HasOccluderContext(int viewInstanceID)
  425. {
  426. return m_ViewIDToIndexMap.ContainsKey(viewInstanceID);
  427. }
  428. internal bool GetOccluderContext(int viewInstanceID, out OccluderContext occluderContext)
  429. {
  430. if (m_ViewIDToIndexMap.TryGetValue(viewInstanceID, out var contextIndex) && m_OccluderContextSlots[contextIndex].valid)
  431. {
  432. occluderContext = m_OccluderContextData[contextIndex];
  433. return true;
  434. }
  435. occluderContext = new OccluderContext();
  436. return false;
  437. }
  438. internal void UpdateFrame()
  439. {
  440. for (int i = 0; i < m_OccluderContextData.Count; ++i)
  441. {
  442. if (!m_OccluderContextSlots[i].valid)
  443. continue;
  444. OccluderContext occluderCtx = m_OccluderContextData[i];
  445. var slot = m_OccluderContextSlots[i];
  446. //Garbage collect unused contexts for a long time:
  447. if ((m_FrameIndex - slot.lastUsedFrameIndex) >= s_MaxContextGCFrame)
  448. {
  449. DeleteContext(slot.viewInstanceID);
  450. continue;
  451. }
  452. occluderCtx.debugNeedsClear = true;
  453. m_OccluderContextData[i] = occluderCtx;
  454. }
  455. m_SilhouettePlaneCache.FreeUnusedSlots(m_FrameIndex, s_MaxContextGCFrame);
  456. ++m_FrameIndex;
  457. }
  458. private int NewContext(int viewInstanceID)
  459. {
  460. int newSlot = -1;
  461. var newCtxSlot = new OccluderContextSlot { valid = true, viewInstanceID = viewInstanceID, lastUsedFrameIndex = m_FrameIndex };
  462. var newCtx = new OccluderContext() {};
  463. if (m_FreeOccluderContexts.Length > 0)
  464. {
  465. newSlot = m_FreeOccluderContexts[m_FreeOccluderContexts.Length - 1];
  466. m_FreeOccluderContexts.RemoveAt(m_FreeOccluderContexts.Length - 1);
  467. m_OccluderContextData[newSlot] = newCtx;
  468. m_OccluderContextSlots[newSlot] = newCtxSlot;
  469. }
  470. else
  471. {
  472. newSlot = m_OccluderContextData.Count;
  473. m_OccluderContextData.Add(newCtx);
  474. m_OccluderContextSlots.Add(newCtxSlot);
  475. }
  476. m_ViewIDToIndexMap.Add(viewInstanceID, newSlot);
  477. return newSlot;
  478. }
  479. private void DeleteContext(int viewInstanceID)
  480. {
  481. if (!m_ViewIDToIndexMap.TryGetValue(viewInstanceID, out var contextIndex) || !m_OccluderContextSlots[contextIndex].valid)
  482. return;
  483. m_OccluderContextData[contextIndex].Dispose();
  484. m_OccluderContextSlots[contextIndex] = new OccluderContextSlot { valid = false };
  485. m_FreeOccluderContexts.Add(contextIndex);
  486. m_ViewIDToIndexMap.Remove(viewInstanceID);
  487. }
  488. public void Dispose()
  489. {
  490. CoreUtils.Destroy(m_DebugOcclusionTestMaterial);
  491. CoreUtils.Destroy(m_OccluderDebugViewMaterial);
  492. for (int i = 0; i < m_OccluderContextData.Count; ++i)
  493. {
  494. if (m_OccluderContextSlots[i].valid)
  495. m_OccluderContextData[i].Dispose();
  496. }
  497. m_SilhouettePlaneCache.Dispose();
  498. m_ViewIDToIndexMap.Dispose();
  499. m_FreeOccluderContexts.Dispose();
  500. m_OccluderContextData.Clear();
  501. m_OccluderContextSlots.Dispose();
  502. m_CommonShaderVariables.Dispose();
  503. m_CommonConstantBuffer.Release();
  504. m_DebugShaderVariables.Dispose();
  505. m_DebugConstantBuffer.Release();
  506. }
  507. }
  508. }