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.

InstanceOcclusionCuller.cs 33KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Runtime.InteropServices;
  4. using System.Threading;
  5. using UnityEngine.Assertions;
  6. using Unity.Burst;
  7. using Unity.Burst.CompilerServices;
  8. using Unity.Mathematics;
  9. using Unity.Collections;
  10. using Unity.Collections.LowLevel.Unsafe;
  11. using Unity.Jobs;
  12. using Unity.Jobs.LowLevel.Unsafe;
  13. using UnityEngine.Experimental.Rendering;
  14. using UnityEngine.Rendering.RenderGraphModule;
  15. namespace UnityEngine.Rendering
  16. {
  17. internal struct OccluderDerivedData
  18. {
  19. /// <summary></summary>
  20. public Matrix4x4 viewProjMatrix; // from view-centered world space
  21. /// <summary></summary>
  22. public Vector4 viewOriginWorldSpace;
  23. /// <summary></summary>
  24. public Vector4 radialDirWorldSpace;
  25. /// <summary></summary>
  26. public Vector4 facingDirWorldSpace;
  27. public static OccluderDerivedData FromParameters(in OccluderSubviewUpdate occluderSubviewUpdate)
  28. {
  29. var origin = occluderSubviewUpdate.viewOffsetWorldSpace + (Vector3)occluderSubviewUpdate.invViewMatrix.GetColumn(3); // view origin in world space
  30. var xViewVec = (Vector3)occluderSubviewUpdate.invViewMatrix.GetColumn(0); // positive x axis in world space
  31. var yViewVec = (Vector3)occluderSubviewUpdate.invViewMatrix.GetColumn(1); // positive y axis in world space
  32. var towardsVec = (Vector3)occluderSubviewUpdate.invViewMatrix.GetColumn(2); // positive z axis in world space
  33. var viewMatrixNoTranslation = occluderSubviewUpdate.viewMatrix;
  34. viewMatrixNoTranslation.SetColumn(3, new Vector4(0.0f, 0.0f, 0.0f, 1.0f));
  35. return new OccluderDerivedData
  36. {
  37. viewOriginWorldSpace = origin,
  38. facingDirWorldSpace = towardsVec.normalized,
  39. radialDirWorldSpace = (xViewVec + yViewVec).normalized,
  40. viewProjMatrix = occluderSubviewUpdate.gpuProjMatrix * viewMatrixNoTranslation,
  41. };
  42. }
  43. }
  44. internal struct OccluderHandles
  45. {
  46. public TextureHandle occluderDepthPyramid;
  47. public BufferHandle occlusionDebugOverlay;
  48. public bool IsValid()
  49. {
  50. return occluderDepthPyramid.IsValid();
  51. }
  52. public void UseForOcclusionTest(IBaseRenderGraphBuilder builder)
  53. {
  54. builder.UseTexture(occluderDepthPyramid, AccessFlags.Read);
  55. if (occlusionDebugOverlay.IsValid())
  56. builder.UseBuffer(occlusionDebugOverlay, AccessFlags.ReadWrite);
  57. }
  58. public void UseForOccluderUpdate(IBaseRenderGraphBuilder builder)
  59. {
  60. builder.UseTexture(occluderDepthPyramid, AccessFlags.ReadWrite);
  61. if (occlusionDebugOverlay.IsValid())
  62. builder.UseBuffer(occlusionDebugOverlay, AccessFlags.ReadWrite);
  63. }
  64. }
  65. [GenerateHLSL(needAccessors = false)]
  66. internal enum InstanceOcclusionTestDebugCounter
  67. {
  68. Occluded,
  69. NotOccluded,
  70. Count,
  71. }
  72. [GenerateHLSL(needAccessors = false)]
  73. internal struct IndirectInstanceInfo
  74. {
  75. public int drawOffsetAndSplitMask; // [31:8]=draw_offset, [7:0]=split_mask
  76. public int instanceIndexAndCrossFade; // DOTS instance index
  77. }
  78. [GenerateHLSL(needAccessors = false)]
  79. internal struct IndirectDrawInfo
  80. {
  81. public uint indexCount;
  82. public uint firstIndex;
  83. public uint baseVertex;
  84. public uint firstInstanceGlobalIndex;
  85. public uint maxInstanceCount;
  86. }
  87. internal struct IndirectBufferAllocInfo
  88. {
  89. public int drawAllocIndex;
  90. public int drawCount;
  91. public int instanceAllocIndex;
  92. public int instanceCount;
  93. public bool IsEmpty()
  94. {
  95. return drawCount == 0;
  96. }
  97. public bool IsWithinLimits(in IndirectBufferLimits limits)
  98. {
  99. return drawAllocIndex + drawCount <= limits.maxDrawCount
  100. && instanceAllocIndex + instanceCount <= limits.maxInstanceCount;
  101. }
  102. public int GetExtraDrawInfoSlotIndex()
  103. {
  104. return drawAllocIndex + drawCount;
  105. }
  106. }
  107. internal struct IndirectBufferContext
  108. {
  109. public JobHandle cullingJobHandle;
  110. public enum BufferState
  111. {
  112. Pending, // Not synced with culling output yet
  113. Zeroed, // All draws have zero instances
  114. NoOcclusionTest, // Copy the results of CPU frustum/LOD culling
  115. AllInstancesOcclusionTested, // Occlusion test the results of CPU frustum/LOD culling
  116. OccludedInstancesReTested, // Re-test previously occluded instances (against updated occluders)
  117. }
  118. public BufferState bufferState;
  119. public int occluderVersion;
  120. public int subviewMask;
  121. public IndirectBufferContext(JobHandle cullingJobHandle)
  122. {
  123. this.cullingJobHandle = cullingJobHandle;
  124. this.bufferState = BufferState.Pending;
  125. this.occluderVersion = 0;
  126. this.subviewMask = 0;
  127. }
  128. public bool Matches(BufferState bufferState, int occluderVersion, int subviewMask)
  129. {
  130. return this.bufferState == bufferState
  131. && this.occluderVersion == occluderVersion
  132. && this.subviewMask == subviewMask;
  133. }
  134. }
  135. internal struct OccluderMipBounds
  136. {
  137. public Vector2Int offset;
  138. public Vector2Int size;
  139. }
  140. internal struct OccluderContext : IDisposable
  141. {
  142. private static class ShaderIDs
  143. {
  144. public static readonly int _SrcDepth = Shader.PropertyToID("_SrcDepth");
  145. public static readonly int _DstDepth = Shader.PropertyToID("_DstDepth");
  146. public static readonly int OccluderDepthPyramidConstants = Shader.PropertyToID("OccluderDepthPyramidConstants");
  147. }
  148. public const int k_FirstDepthMipIndex = 3; // 8x8 tiles
  149. public const int k_MaxOccluderMips = (int)OcclusionCullingCommonConfig.MaxOccluderMips;
  150. public const int k_MaxSilhouettePlanes = (int)OcclusionCullingCommonConfig.MaxOccluderSilhouettePlanes;
  151. public const int k_MaxSubviewsPerView = (int)OcclusionCullingCommonConfig.MaxSubviewsPerView;
  152. public int version;
  153. public Vector2Int depthBufferSize;
  154. public NativeArray<OccluderDerivedData> subviewData;
  155. public int subviewCount { get { return subviewData.Length; } }
  156. public int subviewValidMask;
  157. public bool IsSubviewValid(int subviewIndex)
  158. {
  159. return subviewIndex < subviewCount && (subviewValidMask & (1 << subviewIndex)) != 0;
  160. }
  161. public NativeArray<OccluderMipBounds> occluderMipBounds;
  162. public Vector2Int occluderMipLayoutSize; // total size of 2D layout specified by occluderMipBounds
  163. public Vector2Int occluderDepthPyramidSize; // at least the size of N mip layouts tiled vertically (one per subview)
  164. public RTHandle occluderDepthPyramid;
  165. public int occlusionDebugOverlaySize;
  166. public GraphicsBuffer occlusionDebugOverlay;
  167. public bool debugNeedsClear;
  168. public ComputeBuffer constantBuffer;
  169. public NativeArray<OccluderDepthPyramidConstants> constantBufferData;
  170. public Vector2 depthBufferSizeInOccluderPixels {
  171. get
  172. {
  173. int occluderPixelSize = 1 << k_FirstDepthMipIndex;
  174. return new Vector2(
  175. (float)depthBufferSize.x / (float)occluderPixelSize,
  176. (float)depthBufferSize.y / (float)occluderPixelSize);
  177. }
  178. }
  179. public void Dispose()
  180. {
  181. if (subviewData.IsCreated)
  182. subviewData.Dispose();
  183. if (occluderMipBounds.IsCreated)
  184. occluderMipBounds.Dispose();
  185. if (occluderDepthPyramid != null)
  186. {
  187. occluderDepthPyramid.Release();
  188. occluderDepthPyramid = null;
  189. }
  190. if (occlusionDebugOverlay != null)
  191. {
  192. occlusionDebugOverlay.Release();
  193. occlusionDebugOverlay = null;
  194. }
  195. if (constantBuffer != null)
  196. {
  197. constantBuffer.Release();
  198. constantBuffer = null;
  199. }
  200. if (constantBufferData.IsCreated)
  201. constantBufferData.Dispose();
  202. }
  203. private void UpdateMipBounds()
  204. {
  205. int occluderPixelSize = 1 << k_FirstDepthMipIndex;
  206. Vector2Int topMipSize = (depthBufferSize + (occluderPixelSize - 1) * Vector2Int.one) / occluderPixelSize;
  207. Vector2Int totalSize = Vector2Int.zero;
  208. Vector2Int mipOffset = Vector2Int.zero;
  209. Vector2Int mipSize = topMipSize;
  210. if (!occluderMipBounds.IsCreated)
  211. occluderMipBounds = new NativeArray<OccluderMipBounds>(k_MaxOccluderMips, Allocator.Persistent);
  212. for (int mipIndex = 0; mipIndex < k_MaxOccluderMips; ++mipIndex)
  213. {
  214. occluderMipBounds[mipIndex] = new OccluderMipBounds { offset = mipOffset, size = mipSize };
  215. totalSize.x = Mathf.Max(totalSize.x, mipOffset.x + mipSize.x);
  216. totalSize.y = Mathf.Max(totalSize.y, mipOffset.y + mipSize.y);
  217. if (mipIndex == 0)
  218. {
  219. mipOffset.x = 0;
  220. mipOffset.y += mipSize.y;
  221. }
  222. else
  223. {
  224. mipOffset.x += mipSize.x;
  225. }
  226. mipSize.x = (mipSize.x + 1) / 2;
  227. mipSize.y = (mipSize.y + 1) / 2;
  228. }
  229. occluderMipLayoutSize = totalSize;
  230. }
  231. private void AllocateTexturesIfNecessary(bool debugOverlayEnabled)
  232. {
  233. Vector2Int minDepthPyramidSize = new Vector2Int(occluderMipLayoutSize.x, occluderMipLayoutSize.y * subviewCount);
  234. if (occluderDepthPyramidSize.x < minDepthPyramidSize.x || occluderDepthPyramidSize.y < minDepthPyramidSize.y)
  235. {
  236. if (occluderDepthPyramid != null)
  237. occluderDepthPyramid.Release();
  238. occluderDepthPyramidSize = minDepthPyramidSize;
  239. occluderDepthPyramid = RTHandles.Alloc(
  240. occluderDepthPyramidSize.x, occluderDepthPyramidSize.y,
  241. dimension: TextureDimension.Tex2D,
  242. colorFormat: GraphicsFormat.R32_SFloat,
  243. filterMode: FilterMode.Point,
  244. wrapMode: TextureWrapMode.Clamp,
  245. enableRandomWrite: true,
  246. name: "Occluder Depths");
  247. }
  248. int newDebugOverlaySize = debugOverlayEnabled ? (minDepthPyramidSize.x * minDepthPyramidSize.y) : 0;
  249. if (occlusionDebugOverlaySize < newDebugOverlaySize)
  250. {
  251. if (occlusionDebugOverlay != null)
  252. occlusionDebugOverlay.Release();
  253. occlusionDebugOverlaySize = newDebugOverlaySize;
  254. debugNeedsClear = true;
  255. // We use buffer instead of texture, because some platforms don't support atmoic operations for Texture2D<uint>
  256. occlusionDebugOverlay = new GraphicsBuffer(GraphicsBuffer.Target.Structured, GraphicsBuffer.UsageFlags.None,
  257. occlusionDebugOverlaySize + (int)OcclusionCullingCommonConfig.DebugPyramidOffset, sizeof(uint));
  258. }
  259. if (newDebugOverlaySize == 0)
  260. {
  261. if (occlusionDebugOverlay != null)
  262. {
  263. occlusionDebugOverlay.Release();
  264. occlusionDebugOverlay = null;
  265. }
  266. occlusionDebugOverlaySize = newDebugOverlaySize;
  267. }
  268. if (constantBuffer == null)
  269. constantBuffer = new ComputeBuffer(1, UnsafeUtility.SizeOf<OccluderDepthPyramidConstants>(), ComputeBufferType.Constant);
  270. if (!constantBufferData.IsCreated)
  271. constantBufferData = new NativeArray<OccluderDepthPyramidConstants>(1, Allocator.Persistent);
  272. }
  273. internal static void SetKeyword(ComputeCommandBuffer cmd, ComputeShader cs, in LocalKeyword keyword, bool value)
  274. {
  275. if (value)
  276. cmd.EnableKeyword(cs, keyword);
  277. else
  278. cmd.DisableKeyword(cs, keyword);
  279. }
  280. private OccluderDepthPyramidConstants SetupFarDepthPyramidConstants(ReadOnlySpan<OccluderSubviewUpdate> occluderSubviewUpdates, NativeArray<Plane> silhouettePlanes)
  281. {
  282. OccluderDepthPyramidConstants cb = new OccluderDepthPyramidConstants();
  283. // write globals
  284. cb._OccluderMipLayoutSizeX = (uint)occluderMipLayoutSize.x;
  285. cb._OccluderMipLayoutSizeY = (uint)occluderMipLayoutSize.y;
  286. // write per-subview data
  287. int updateCount = occluderSubviewUpdates.Length;
  288. for (int updateIndex = 0; updateIndex < updateCount; ++updateIndex)
  289. {
  290. ref readonly OccluderSubviewUpdate update = ref occluderSubviewUpdates[updateIndex];
  291. int subviewIndex = update.subviewIndex;
  292. subviewData[subviewIndex] = OccluderDerivedData.FromParameters(update);
  293. subviewValidMask |= 1 << update.subviewIndex;
  294. Matrix4x4 viewProjMatrix
  295. = update.gpuProjMatrix
  296. * update.viewMatrix
  297. * Matrix4x4.Translate(-update.viewOffsetWorldSpace);
  298. Matrix4x4 invViewProjMatrix = viewProjMatrix.inverse;
  299. unsafe
  300. {
  301. for (int j = 0; j < 16; ++j)
  302. cb._InvViewProjMatrix[16 * updateIndex + j] = invViewProjMatrix[j];
  303. cb._SrcOffset[4 * updateIndex + 0] = (uint)update.depthOffset.x;
  304. cb._SrcOffset[4 * updateIndex + 1] = (uint)update.depthOffset.y;
  305. cb._SrcOffset[4 * updateIndex + 2] = 0;
  306. cb._SrcOffset[4 * updateIndex + 3] = 0;
  307. }
  308. cb._SrcSliceIndices |= (((uint)update.depthSliceIndex & 0xf) << (4 * updateIndex));
  309. cb._DstSubviewIndices |= ((uint)subviewIndex << (4 * updateIndex));
  310. }
  311. // TODO: transform these planes from world space into NDC space planes
  312. for (int i = 0; i < k_MaxSilhouettePlanes; ++i)
  313. {
  314. Plane plane = new Plane(Vector3.zero, 0.0f);
  315. if (i < silhouettePlanes.Length)
  316. plane = silhouettePlanes[i];
  317. unsafe
  318. {
  319. cb._SilhouettePlanes[4 * i + 0] = plane.normal.x;
  320. cb._SilhouettePlanes[4 * i + 1] = plane.normal.y;
  321. cb._SilhouettePlanes[4 * i + 2] = plane.normal.z;
  322. cb._SilhouettePlanes[4 * i + 3] = plane.distance;
  323. }
  324. }
  325. cb._SilhouettePlaneCount = (uint)silhouettePlanes.Length;
  326. return cb;
  327. }
  328. public void CreateFarDepthPyramid(ComputeCommandBuffer cmd, in OccluderParameters occluderParams, ReadOnlySpan<OccluderSubviewUpdate> occluderSubviewUpdates, in OccluderHandles occluderHandles, NativeArray<Plane> silhouettePlanes, ComputeShader occluderDepthPyramidCS, int occluderDepthDownscaleKernel)
  329. {
  330. OccluderDepthPyramidConstants cb = SetupFarDepthPyramidConstants(occluderSubviewUpdates, silhouettePlanes);
  331. var cs = occluderDepthPyramidCS;
  332. int kernel = occluderDepthDownscaleKernel;
  333. var srcKeyword = new LocalKeyword(cs, "USE_SRC");
  334. var srcIsArrayKeyword = new LocalKeyword(cs, "SRC_IS_ARRAY");
  335. var srcIsMsaaKeyword = new LocalKeyword(cs, "SRC_IS_MSAA");
  336. bool srcIsArray = occluderParams.depthIsArray;
  337. RTHandle depthTexture = (RTHandle)occluderParams.depthTexture;
  338. bool srcIsMsaa = depthTexture?.isMSAAEnabled ?? false;
  339. int mipCount = k_FirstDepthMipIndex + k_MaxOccluderMips;
  340. for (int mipIndexBase = 0; mipIndexBase < mipCount - 1; mipIndexBase += 4)
  341. {
  342. cmd.SetComputeTextureParam(cs, kernel, ShaderIDs._DstDepth, occluderHandles.occluderDepthPyramid);
  343. bool useSrc = (mipIndexBase == 0);
  344. SetKeyword(cmd, cs, srcKeyword, useSrc);
  345. SetKeyword(cmd, cs, srcIsArrayKeyword, useSrc && srcIsArray);
  346. SetKeyword(cmd, cs, srcIsMsaaKeyword, useSrc && srcIsMsaa);
  347. if (useSrc)
  348. cmd.SetComputeTextureParam(cs, kernel, ShaderIDs._SrcDepth, occluderParams.depthTexture);
  349. cb._MipCount = (uint)Math.Min(mipCount - 1 - mipIndexBase, 4);
  350. Vector2Int srcSize = Vector2Int.zero;
  351. for (int i = 0; i < 5; ++i)
  352. {
  353. Vector2Int offset = Vector2Int.zero;
  354. Vector2Int size = Vector2Int.zero;
  355. int mipIndex = mipIndexBase + i;
  356. if (mipIndex == 0)
  357. {
  358. size = occluderParams.depthSize;
  359. }
  360. else
  361. {
  362. int occMipIndex = mipIndex - k_FirstDepthMipIndex;
  363. if (0 <= occMipIndex && occMipIndex < k_MaxOccluderMips)
  364. {
  365. offset = occluderMipBounds[occMipIndex].offset;
  366. size = occluderMipBounds[occMipIndex].size;
  367. }
  368. }
  369. if (i == 0)
  370. srcSize = size;
  371. unsafe
  372. {
  373. cb._MipOffsetAndSize[4 * i + 0] = (uint)offset.x;
  374. cb._MipOffsetAndSize[4 * i + 1] = (uint)offset.y;
  375. cb._MipOffsetAndSize[4 * i + 2] = (uint)size.x;
  376. cb._MipOffsetAndSize[4 * i + 3] = (uint)size.y;
  377. }
  378. }
  379. constantBufferData[0] = cb;
  380. cmd.SetBufferData(constantBuffer, constantBufferData);
  381. cmd.SetComputeConstantBufferParam(cs, ShaderIDs.OccluderDepthPyramidConstants, constantBuffer, 0, constantBuffer.stride);
  382. cmd.DispatchCompute(cs, kernel, (srcSize.x + 15) / 16, (srcSize.y + 15) / 16, occluderSubviewUpdates.Length);
  383. }
  384. }
  385. public OccluderHandles Import(RenderGraph renderGraph)
  386. {
  387. RenderTargetInfo rtInfo = new RenderTargetInfo
  388. {
  389. width = occluderDepthPyramidSize.x,
  390. height = occluderDepthPyramidSize.y,
  391. volumeDepth = 1,
  392. msaaSamples = 1,
  393. format = GraphicsFormat.R32_SFloat,
  394. bindMS = false,
  395. };
  396. OccluderHandles occluderHandles = new OccluderHandles()
  397. {
  398. occluderDepthPyramid = renderGraph.ImportTexture(occluderDepthPyramid, rtInfo)
  399. };
  400. if (occlusionDebugOverlay != null)
  401. occluderHandles.occlusionDebugOverlay = renderGraph.ImportBuffer(occlusionDebugOverlay);
  402. return occluderHandles;
  403. }
  404. public void PrepareOccluders(in OccluderParameters occluderParams)
  405. {
  406. if (subviewCount != occluderParams.subviewCount)
  407. {
  408. if (subviewData.IsCreated)
  409. subviewData.Dispose();
  410. subviewData = new NativeArray<OccluderDerivedData>(occluderParams.subviewCount, Allocator.Persistent);
  411. subviewValidMask = 0;
  412. }
  413. depthBufferSize = occluderParams.depthSize;
  414. // enable debug counters for cameras when the overlay is enabled
  415. bool debugOverlayEnabled = GPUResidentDrawer.GetDebugStats()?.occlusionOverlayEnabled ?? false;
  416. UpdateMipBounds();
  417. AllocateTexturesIfNecessary(debugOverlayEnabled);
  418. }
  419. internal OcclusionCullingDebugOutput GetDebugOutput()
  420. {
  421. var debugOutput = new OcclusionCullingDebugOutput
  422. {
  423. occluderDepthPyramid = occluderDepthPyramid,
  424. occlusionDebugOverlay = occlusionDebugOverlay,
  425. };
  426. debugOutput.cb._DepthSizeInOccluderPixels = depthBufferSizeInOccluderPixels;
  427. debugOutput.cb._OccluderMipLayoutSizeX = (uint)occluderMipLayoutSize.x;
  428. debugOutput.cb._OccluderMipLayoutSizeY = (uint)occluderMipLayoutSize.y;
  429. for (int i = 0; i < occluderMipBounds.Length; ++i)
  430. {
  431. var mipBounds = occluderMipBounds[i];
  432. unsafe
  433. {
  434. debugOutput.cb._OccluderMipBounds[4 * i + 0] = (uint)mipBounds.offset.x;
  435. debugOutput.cb._OccluderMipBounds[4 * i + 1] = (uint)mipBounds.offset.y;
  436. debugOutput.cb._OccluderMipBounds[4 * i + 2] = (uint)mipBounds.size.x;
  437. debugOutput.cb._OccluderMipBounds[4 * i + 3] = (uint)mipBounds.size.y;
  438. }
  439. }
  440. return debugOutput;
  441. }
  442. }
  443. internal enum IndirectAllocator
  444. {
  445. NextInstanceIndex,
  446. NextDrawIndex,
  447. Count // keep last
  448. }
  449. internal struct IndirectBufferLimits
  450. {
  451. public int maxInstanceCount;
  452. public int maxDrawCount;
  453. }
  454. internal struct InstanceOcclusionTestSubviewSettings
  455. {
  456. public int testCount;
  457. public int occluderSubviewIndices;
  458. public int occluderSubviewMask;
  459. public int cullingSplitIndices;
  460. public int cullingSplitMask;
  461. public static InstanceOcclusionTestSubviewSettings FromSpan(ReadOnlySpan<SubviewOcclusionTest> subviewOcclusionTests)
  462. {
  463. InstanceOcclusionTestSubviewSettings settings = new InstanceOcclusionTestSubviewSettings();
  464. for (int testIndex = 0; testIndex < subviewOcclusionTests.Length; ++testIndex)
  465. {
  466. SubviewOcclusionTest subviewTest = subviewOcclusionTests[testIndex];
  467. settings.occluderSubviewIndices |= subviewTest.occluderSubviewIndex << (4 * testIndex);
  468. settings.occluderSubviewMask |= 1 << subviewTest.occluderSubviewIndex;
  469. settings.cullingSplitIndices |= subviewTest.cullingSplitIndex << (4 * testIndex);
  470. settings.cullingSplitMask |= 1 << subviewTest.cullingSplitIndex;
  471. }
  472. settings.testCount = subviewOcclusionTests.Length;
  473. return settings;
  474. }
  475. }
  476. internal struct IndirectBufferContextHandles
  477. {
  478. public BufferHandle instanceBuffer;
  479. public BufferHandle instanceInfoBuffer;
  480. public BufferHandle argsBuffer;
  481. public BufferHandle drawInfoBuffer;
  482. public void UseForOcclusionTest(IBaseRenderGraphBuilder builder)
  483. {
  484. instanceBuffer = builder.UseBuffer(instanceBuffer, AccessFlags.ReadWrite);
  485. instanceInfoBuffer = builder.UseBuffer(instanceInfoBuffer, AccessFlags.Read);
  486. argsBuffer = builder.UseBuffer(argsBuffer, AccessFlags.ReadWrite);
  487. drawInfoBuffer = builder.UseBuffer(drawInfoBuffer, AccessFlags.Read);
  488. }
  489. }
  490. internal struct IndirectBufferContextStorage : IDisposable
  491. {
  492. private const int kAllocatorCount = (int)IndirectAllocator.Count;
  493. internal const int kExtraDrawAllocationCount = 1; // over-allocate by one for indirect args scratch space GPU-side
  494. internal const int kInstanceInfoGpuOffsetMultiplier = 2; // GPU side allocates storage for extra copy of instance list
  495. private IndirectBufferLimits m_BufferLimits;
  496. private GraphicsBuffer m_InstanceBuffer;
  497. private GraphicsBuffer m_InstanceInfoBuffer;
  498. private NativeArray<IndirectInstanceInfo> m_InstanceInfoStaging;
  499. private GraphicsBuffer m_ArgsBuffer;
  500. private GraphicsBuffer m_DrawInfoBuffer;
  501. private NativeArray<IndirectDrawInfo> m_DrawInfoStaging;
  502. private int m_ContextAllocCounter;
  503. private NativeHashMap<int, int> m_ContextIndexFromViewID;
  504. private NativeList<IndirectBufferContext> m_Contexts;
  505. private NativeArray<IndirectBufferAllocInfo> m_ContextAllocInfo;
  506. private NativeArray<int> m_AllocationCounters;
  507. public GraphicsBuffer instanceBuffer { get { return m_InstanceBuffer; } }
  508. public GraphicsBuffer instanceInfoBuffer { get { return m_InstanceInfoBuffer; } }
  509. public GraphicsBuffer argsBuffer { get { return m_ArgsBuffer; } }
  510. public GraphicsBuffer drawInfoBuffer { get { return m_DrawInfoBuffer; } }
  511. public GraphicsBufferHandle visibleInstanceBufferHandle { get { return m_InstanceBuffer.bufferHandle; } }
  512. public GraphicsBufferHandle indirectArgsBufferHandle { get { return m_ArgsBuffer.bufferHandle; } }
  513. public IndirectBufferContextHandles ImportBuffers(RenderGraph renderGraph)
  514. {
  515. return new IndirectBufferContextHandles()
  516. {
  517. instanceBuffer = renderGraph.ImportBuffer(m_InstanceBuffer),
  518. instanceInfoBuffer = renderGraph.ImportBuffer(m_InstanceInfoBuffer),
  519. argsBuffer = renderGraph.ImportBuffer(m_ArgsBuffer),
  520. drawInfoBuffer = renderGraph.ImportBuffer(m_DrawInfoBuffer),
  521. };
  522. }
  523. public NativeArray<IndirectInstanceInfo> instanceInfoGlobalArray { get { return m_InstanceInfoStaging; } }
  524. public NativeArray<IndirectDrawInfo> drawInfoGlobalArray { get { return m_DrawInfoStaging; } }
  525. public NativeArray<int> allocationCounters { get { return m_AllocationCounters; } }
  526. public void Init()
  527. {
  528. int initialDrawCount = 256;
  529. int initialInstanceCount = 64 * initialDrawCount;
  530. int initialContextCount = 8;
  531. AllocateInstanceBuffers(initialInstanceCount);
  532. AllocateDrawBuffers(initialDrawCount);
  533. m_ContextIndexFromViewID = new NativeHashMap<int, int>(initialContextCount, Allocator.Persistent);
  534. m_Contexts = new NativeList<IndirectBufferContext>(initialContextCount, Allocator.Persistent);
  535. m_ContextAllocInfo = new NativeArray<IndirectBufferAllocInfo>(initialContextCount, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
  536. m_AllocationCounters = new NativeArray<int>(kAllocatorCount, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
  537. ResetAllocators();
  538. }
  539. void AllocateInstanceBuffers(int maxInstanceCount)
  540. {
  541. m_InstanceBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, maxInstanceCount, sizeof(int));
  542. m_InstanceInfoBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, kInstanceInfoGpuOffsetMultiplier * maxInstanceCount, System.Runtime.InteropServices.Marshal.SizeOf<IndirectInstanceInfo>());
  543. m_InstanceInfoStaging = new NativeArray<IndirectInstanceInfo>(maxInstanceCount, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
  544. m_BufferLimits.maxInstanceCount = maxInstanceCount;
  545. }
  546. void FreeInstanceBuffers()
  547. {
  548. m_InstanceBuffer.Release();
  549. m_InstanceInfoBuffer.Release();
  550. m_InstanceInfoStaging.Dispose();
  551. m_BufferLimits.maxInstanceCount = 0;
  552. }
  553. void AllocateDrawBuffers(int maxDrawCount)
  554. {
  555. m_ArgsBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured | GraphicsBuffer.Target.IndirectArguments, maxDrawCount * (GraphicsBuffer.IndirectDrawIndexedArgs.size / sizeof(int)), sizeof(int));
  556. m_DrawInfoBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, maxDrawCount, System.Runtime.InteropServices.Marshal.SizeOf<IndirectDrawInfo>());
  557. m_DrawInfoStaging = new NativeArray<IndirectDrawInfo>(maxDrawCount, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
  558. m_BufferLimits.maxDrawCount = maxDrawCount;
  559. }
  560. void FreeDrawBuffers()
  561. {
  562. m_ArgsBuffer.Release();
  563. m_DrawInfoBuffer.Release();
  564. m_DrawInfoStaging.Dispose();
  565. m_BufferLimits.maxDrawCount = 0;
  566. }
  567. public void Dispose()
  568. {
  569. SyncContexts();
  570. FreeInstanceBuffers();
  571. FreeDrawBuffers();
  572. m_ContextIndexFromViewID.Dispose();
  573. m_Contexts.Dispose();
  574. m_ContextAllocInfo.Dispose();
  575. m_AllocationCounters.Dispose();
  576. }
  577. private void SyncContexts()
  578. {
  579. for (int contextIndex = 0; contextIndex < m_Contexts.Length; ++contextIndex)
  580. m_Contexts[contextIndex].cullingJobHandle.Complete();
  581. }
  582. private void ResetAllocators()
  583. {
  584. m_ContextAllocCounter = 0;
  585. m_ContextIndexFromViewID.Clear();
  586. m_Contexts.Clear();
  587. m_AllocationCounters.FillArray(0);
  588. }
  589. private void GrowBuffers()
  590. {
  591. if (m_ContextAllocCounter > m_ContextAllocInfo.Length)
  592. {
  593. // allocate 20% more than the high water mark
  594. int newContextCount = (m_ContextAllocCounter * 6) / 5;
  595. m_Contexts.Clear();
  596. m_Contexts.SetCapacity(newContextCount);
  597. m_ContextAllocInfo.Dispose();
  598. m_ContextAllocInfo = new NativeArray<IndirectBufferAllocInfo>(newContextCount, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
  599. //Debug.Log("Raised indirect context count to " + newContextCount);
  600. }
  601. int instanceAllocCounter = m_AllocationCounters[(int)IndirectAllocator.NextInstanceIndex];
  602. if (instanceAllocCounter > m_BufferLimits.maxInstanceCount)
  603. {
  604. // allocate 20% more than the high water mark
  605. int newInstanceCount = (instanceAllocCounter * 6) / 5;
  606. FreeInstanceBuffers();
  607. AllocateInstanceBuffers(newInstanceCount);
  608. //Debug.Log("Raised indirect instance count to " + newInstanceCount);
  609. }
  610. int drawAllocCounter = m_AllocationCounters[(int)IndirectAllocator.NextDrawIndex];
  611. if (drawAllocCounter > m_BufferLimits.maxDrawCount)
  612. {
  613. // allocate 20% more than the high water mark
  614. int newDrawCount = (drawAllocCounter * 6) / 5;
  615. FreeDrawBuffers();
  616. AllocateDrawBuffers(newDrawCount);
  617. //Debug.Log("Raised indirect draw count to " + newDrawCount);
  618. }
  619. }
  620. public void ClearContextsAndGrowBuffers()
  621. {
  622. SyncContexts();
  623. GrowBuffers();
  624. ResetAllocators();
  625. }
  626. public int TryAllocateContext(int viewID)
  627. {
  628. // Disallow using the same viewID multiple times for a frame, since it is used as a UID to update indirect args
  629. // This will prevent multiple context being created for example if a custom pass is being used
  630. if (m_ContextIndexFromViewID.ContainsKey(viewID))
  631. return -1;
  632. int contextIndex = -1;
  633. m_ContextAllocCounter += 1;
  634. if (m_Contexts.Length < m_ContextAllocInfo.Length)
  635. {
  636. contextIndex = m_Contexts.Length;
  637. m_Contexts.Add(new IndirectBufferContext());
  638. m_ContextIndexFromViewID.Add(viewID, contextIndex);
  639. }
  640. return contextIndex;
  641. }
  642. public int TryGetContextIndex(int viewID)
  643. {
  644. if (!m_ContextIndexFromViewID.TryGetValue(viewID, out var contextIndex))
  645. contextIndex = -1;
  646. return contextIndex;
  647. }
  648. public NativeArray<IndirectBufferAllocInfo> GetAllocInfoSubArray(int contextIndex)
  649. {
  650. int safeIndex = Mathf.Max(contextIndex, 0);
  651. return m_ContextAllocInfo.GetSubArray(safeIndex, 1);
  652. }
  653. public IndirectBufferAllocInfo GetAllocInfo(int contextIndex)
  654. {
  655. IndirectBufferAllocInfo allocInfo = new IndirectBufferAllocInfo();
  656. if (0 <= contextIndex && contextIndex < m_Contexts.Length)
  657. allocInfo = m_ContextAllocInfo[contextIndex];
  658. return allocInfo;
  659. }
  660. public void CopyFromStaging(CommandBuffer cmd, in IndirectBufferAllocInfo allocInfo)
  661. {
  662. if (!allocInfo.IsEmpty())
  663. {
  664. cmd.SetBufferData(
  665. m_DrawInfoBuffer,
  666. m_DrawInfoStaging,
  667. allocInfo.drawAllocIndex,
  668. allocInfo.drawAllocIndex,
  669. allocInfo.drawCount);
  670. cmd.SetBufferData(
  671. m_InstanceInfoBuffer,
  672. m_InstanceInfoStaging,
  673. allocInfo.instanceAllocIndex,
  674. kInstanceInfoGpuOffsetMultiplier * allocInfo.instanceAllocIndex,
  675. allocInfo.instanceCount);
  676. }
  677. }
  678. public IndirectBufferLimits GetLimits(int contextIndex)
  679. {
  680. IndirectBufferLimits limits = new IndirectBufferLimits();
  681. if (contextIndex >= 0)
  682. limits = m_BufferLimits;
  683. return limits;
  684. }
  685. public IndirectBufferContext GetBufferContext(int contextIndex)
  686. {
  687. IndirectBufferContext ctx = new IndirectBufferContext();
  688. if (0 <= contextIndex && contextIndex < m_Contexts.Length)
  689. ctx = m_Contexts[contextIndex];
  690. return ctx;
  691. }
  692. public void SetBufferContext(int contextIndex, IndirectBufferContext ctx)
  693. {
  694. if (0 <= contextIndex && contextIndex < m_Contexts.Length)
  695. m_Contexts[contextIndex] = ctx;
  696. }
  697. }
  698. }