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.

GeometryPool.cs 41KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923
  1. using System;
  2. using System.Collections.Generic;
  3. using Unity.Collections;
  4. using UnityEngine.Assertions;
  5. // This file is a fork of the GeometryPool used by the GPU Driven Pipeline
  6. // TODO: remove that file and use GeometryPool v2 (written in C++)
  7. namespace UnityEngine.Rendering.UnifiedRayTracing
  8. {
  9. // Initial description of geometry pool, contains memory limits to hold cluster / index & vertex data.
  10. internal struct GeometryPoolDesc
  11. {
  12. public int vertexPoolByteSize;
  13. public int indexPoolByteSize;
  14. public int meshChunkTablesByteSize;
  15. public static GeometryPoolDesc NewDefault()
  16. {
  17. return new GeometryPoolDesc()
  18. {
  19. vertexPoolByteSize = 256 * 1024 * 1024, //256 mb
  20. indexPoolByteSize = 32 * 1024 * 1024, //32 mb
  21. meshChunkTablesByteSize = 4 * 1024 * 1024
  22. };
  23. }
  24. }
  25. // Handle to a piece of geo. Geometry meshes registered are ref counted.
  26. // Each handle allocation must be deallocated manually.
  27. internal struct GeometryPoolHandle : IEquatable<GeometryPoolHandle>
  28. {
  29. public int index;
  30. public static GeometryPoolHandle Invalid = new GeometryPoolHandle() { index = -1 };
  31. public bool valid => index != -1;
  32. public bool Equals(GeometryPoolHandle other) => index == other.index;
  33. }
  34. // Entry information of a geometry handle.
  35. // Use this helper to check validity, material hashes and active refcounts.
  36. internal struct GeometryPoolEntryInfo
  37. {
  38. public bool valid;
  39. public uint refCount;
  40. public static GeometryPoolEntryInfo NewDefault()
  41. {
  42. return new GeometryPoolEntryInfo()
  43. {
  44. valid = false,
  45. refCount = 0
  46. };
  47. }
  48. }
  49. // Descriptor of piece of geometry (the submesh information).
  50. internal struct GeometryPoolSubmeshData
  51. {
  52. public int submeshIndex;
  53. public Material material;
  54. }
  55. // Description of the geometry pool entry.
  56. // Contains master list and the submesh data information.
  57. internal struct GeometryPoolEntryDesc
  58. {
  59. public Mesh mesh;
  60. public GeometryPoolSubmeshData[] submeshData;
  61. }
  62. // Geometry pool container. Contains a global set of geometry accessible from the GPU.
  63. internal class GeometryPool : IDisposable
  64. {
  65. private const int kMaxThreadGroupsPerDispatch = 65535; // Counted in groups, not threads.
  66. private const int kThreadGroupSize = 256; // Counted in threads
  67. private static class GeoPoolShaderIDs
  68. {
  69. // MainUpdateIndexBuffer32 and MainUpdateIndexBuffer16 Kernel Strings
  70. public static readonly int _InputIBBaseOffset = Shader.PropertyToID("_InputIBBaseOffset");
  71. public static readonly int _DispatchIndexOffset = Shader.PropertyToID("_DispatchIndexOffset");
  72. public static readonly int _InputIBCount = Shader.PropertyToID("_InputIBCount");
  73. public static readonly int _OutputIBOffset = Shader.PropertyToID("_OutputIBOffset");
  74. public static readonly int _InputFirstVertex = Shader.PropertyToID("_InputFirstVertex");
  75. public static readonly int _InputIndexBuffer = Shader.PropertyToID("_InputIndexBuffer");
  76. public static readonly int _OutputIndexBuffer = Shader.PropertyToID("_OutputIndexBuffer");
  77. // MainUpdateVertexBuffer Kernel Strings
  78. public static readonly int _InputVBCount = Shader.PropertyToID("_InputVBCount");
  79. public static readonly int _InputBaseVertexOffset = Shader.PropertyToID("_InputBaseVertexOffset");
  80. public static readonly int _DispatchVertexOffset = Shader.PropertyToID("_DispatchVertexOffset");
  81. public static readonly int _OutputVBSize = Shader.PropertyToID("_OutputVBSize");
  82. public static readonly int _OutputVBOffset = Shader.PropertyToID("_OutputVBOffset");
  83. public static readonly int _InputPosBufferStride = Shader.PropertyToID("_InputPosBufferStride");
  84. public static readonly int _InputPosBufferOffset = Shader.PropertyToID("_InputPosBufferOffset");
  85. public static readonly int _InputUv0BufferStride = Shader.PropertyToID("_InputUv0BufferStride");
  86. public static readonly int _InputUv0BufferOffset = Shader.PropertyToID("_InputUv0BufferOffset");
  87. public static readonly int _InputUv1BufferStride = Shader.PropertyToID("_InputUv1BufferStride");
  88. public static readonly int _InputUv1BufferOffset = Shader.PropertyToID("_InputUv1BufferOffset");
  89. public static readonly int _InputNormalBufferStride = Shader.PropertyToID("_InputNormalBufferStride");
  90. public static readonly int _InputNormalBufferOffset = Shader.PropertyToID("_InputNormalBufferOffset");
  91. public static readonly int _InputTangentBufferStride = Shader.PropertyToID("_InputTangentBufferStride");
  92. public static readonly int _InputTangentBufferOffset = Shader.PropertyToID("_InputTangentBufferOffset");
  93. public static readonly int _PosBuffer = Shader.PropertyToID("_PosBuffer");
  94. public static readonly int _Uv0Buffer = Shader.PropertyToID("_Uv0Buffer");
  95. public static readonly int _Uv1Buffer = Shader.PropertyToID("_Uv1Buffer");
  96. public static readonly int _NormalBuffer = Shader.PropertyToID("_NormalBuffer");
  97. public static readonly int _TangentBuffer = Shader.PropertyToID("_TangentBuffer");
  98. public static readonly int _OutputVB = Shader.PropertyToID("_OutputVB");
  99. public static readonly int _AttributesMask = Shader.PropertyToID("_AttributesMask");
  100. }
  101. // Geometry slot represents a set of pointers to the blobs of vertex, index and cluster information
  102. private const int InvalidHandle = -1;
  103. public struct MeshChunk
  104. {
  105. public BlockAllocator.Allocation vertexAlloc;
  106. public BlockAllocator.Allocation indexAlloc;
  107. public GeoPoolMeshChunk EncodeGPUEntry()
  108. {
  109. return new GeoPoolMeshChunk() {
  110. indexOffset = indexAlloc.block.offset,
  111. indexCount = indexAlloc.block.count,
  112. vertexOffset = vertexAlloc.block.offset,
  113. vertexCount = vertexAlloc.block.count,
  114. };
  115. }
  116. public static MeshChunk Invalid => new MeshChunk()
  117. {
  118. vertexAlloc = BlockAllocator.Allocation.Invalid,
  119. indexAlloc = BlockAllocator.Allocation.Invalid
  120. };
  121. }
  122. public struct GeometrySlot
  123. {
  124. public uint refCount;
  125. public uint hash;
  126. public BlockAllocator.Allocation meshChunkTableAlloc;
  127. public NativeArray<MeshChunk> meshChunks;
  128. public bool hasGPUData;
  129. public static GeometrySlot Invalid = new GeometrySlot()
  130. {
  131. meshChunkTableAlloc = BlockAllocator.Allocation.Invalid,
  132. hasGPUData = false,
  133. };
  134. public bool valid => meshChunkTableAlloc.valid;
  135. }
  136. private struct GeoPoolEntrySlot
  137. {
  138. public uint refCount;
  139. public uint hash;
  140. public int geoSlotHandle;
  141. public static GeoPoolEntrySlot Invalid = new GeoPoolEntrySlot()
  142. {
  143. refCount = 0u,
  144. hash = 0u,
  145. geoSlotHandle = InvalidHandle,
  146. };
  147. public bool valid => geoSlotHandle != InvalidHandle;
  148. }
  149. private struct VertexBufferAttribInfo
  150. {
  151. public GraphicsBuffer buffer;
  152. public int stride;
  153. public int offset;
  154. public int byteCount;
  155. public bool valid => buffer != null;
  156. }
  157. public static int GetVertexByteSize() => GeometryPoolConstants.GeoPoolVertexByteSize;
  158. public static int GetIndexByteSize() => GeometryPoolConstants.GeoPoolIndexByteSize;
  159. public static int GetMeshChunkTableEntryByteSize() => System.Runtime.InteropServices.Marshal.SizeOf<GeoPoolMeshChunk>();
  160. private int GetFormatByteCount(VertexAttributeFormat format)
  161. {
  162. switch (format)
  163. {
  164. case VertexAttributeFormat.Float32: return 4;
  165. case VertexAttributeFormat.Float16: return 2;
  166. case VertexAttributeFormat.UNorm8: return 1;
  167. case VertexAttributeFormat.SNorm8: return 1;
  168. case VertexAttributeFormat.UNorm16: return 2;
  169. case VertexAttributeFormat.SNorm16: return 2;
  170. case VertexAttributeFormat.UInt8: return 1;
  171. case VertexAttributeFormat.SInt8: return 1;
  172. case VertexAttributeFormat.UInt16: return 2;
  173. case VertexAttributeFormat.SInt16: return 2;
  174. case VertexAttributeFormat.UInt32: return 4;
  175. case VertexAttributeFormat.SInt32: return 4;
  176. }
  177. return 4;
  178. }
  179. private static int DivUp(int x, int y) => (x + y - 1) / y;
  180. private const GraphicsBuffer.Target VertexBufferTarget = GraphicsBuffer.Target.Structured;
  181. private const GraphicsBuffer.Target IndexBufferTarget = GraphicsBuffer.Target.Structured;
  182. private int m_GeoPoolEntriesCount;
  183. public GraphicsBuffer globalIndexBuffer { get { return m_GlobalIndexBuffer; } }
  184. public GraphicsBuffer globalVertexBuffer { get { return m_GlobalVertexBuffer; } }
  185. public int globalVertexBufferStrideBytes { get { return GetVertexByteSize(); } }
  186. public GraphicsBuffer globalMeshChunkTableEntryBuffer { get { return m_GlobalMeshChunkTableEntryBuffer; } }
  187. public int indicesCount => m_MaxIndexCounts;
  188. public int verticesCount => m_MaxVertCounts;
  189. public int meshChunkTablesEntryCount => m_MaxMeshChunkTableEntriesCount;
  190. private GraphicsBuffer m_GlobalIndexBuffer = null;
  191. private GraphicsBuffer m_GlobalVertexBuffer = null;
  192. private GraphicsBuffer m_GlobalMeshChunkTableEntryBuffer = null;
  193. private GraphicsBuffer m_DummyBuffer = null;
  194. private int m_MaxVertCounts;
  195. private int m_MaxIndexCounts;
  196. private int m_MaxMeshChunkTableEntriesCount;
  197. private BlockAllocator m_VertexAllocator;
  198. private BlockAllocator m_IndexAllocator;
  199. private BlockAllocator m_MeshChunkTableAllocator;
  200. private NativeParallelHashMap<uint, int> m_MeshHashToGeoSlot;
  201. private List<GeometrySlot> m_GeoSlots;
  202. private NativeList<int> m_FreeGeoSlots;
  203. private NativeParallelHashMap<uint, GeometryPoolHandle> m_GeoPoolEntryHashToSlot;
  204. private NativeList<GeoPoolEntrySlot> m_GeoPoolEntrySlots;
  205. private NativeList<GeometryPoolHandle> m_FreeGeoPoolEntrySlots;
  206. private List<GraphicsBuffer> m_InputBufferReferences;
  207. private ComputeShader m_CopyShader;
  208. private ComputeShader m_GeometryPoolKernelsCS;
  209. private int m_KernelMainUpdateIndexBuffer16;
  210. private int m_KernelMainUpdateIndexBuffer32;
  211. private int m_KernelMainUpdateVertexBuffer;
  212. private CommandBuffer m_CmdBuffer;
  213. private bool m_MustClearCmdBuffer;
  214. private int m_PendingCmds;
  215. public GeometryPool(in GeometryPoolDesc desc, ComputeShader geometryPoolShader, ComputeShader copyShader)
  216. {
  217. m_CopyShader = copyShader;
  218. LoadKernels(geometryPoolShader);
  219. m_CmdBuffer = new CommandBuffer();
  220. m_InputBufferReferences = new List<GraphicsBuffer>();
  221. m_MustClearCmdBuffer = false;
  222. m_PendingCmds = 0;
  223. m_GeoPoolEntriesCount = 0;
  224. m_MaxVertCounts = CalcVertexCount(desc.vertexPoolByteSize);
  225. m_MaxIndexCounts = CalcIndexCount(desc.indexPoolByteSize);
  226. m_MaxMeshChunkTableEntriesCount = CalcMeshChunkTablesCount(desc.meshChunkTablesByteSize);
  227. m_GlobalVertexBuffer = new GraphicsBuffer(VertexBufferTarget, DivUp(m_MaxVertCounts * GetVertexByteSize(), 4), 4);
  228. m_GlobalIndexBuffer = new GraphicsBuffer(IndexBufferTarget, m_MaxIndexCounts, 4);
  229. m_GlobalMeshChunkTableEntryBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, m_MaxMeshChunkTableEntriesCount, GetMeshChunkTableEntryByteSize());
  230. m_DummyBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, 16, 4);
  231. var initialCapacity = 4096;
  232. m_MeshHashToGeoSlot = new NativeParallelHashMap<uint, int>(initialCapacity, Allocator.Persistent);
  233. m_GeoSlots = new List<GeometrySlot>();
  234. m_FreeGeoSlots = new NativeList<int>(Allocator.Persistent);
  235. m_GeoPoolEntryHashToSlot = new NativeParallelHashMap<uint, GeometryPoolHandle>(initialCapacity, Allocator.Persistent);
  236. m_GeoPoolEntrySlots = new NativeList<GeoPoolEntrySlot>(Allocator.Persistent);
  237. m_FreeGeoPoolEntrySlots = new NativeList<GeometryPoolHandle>(Allocator.Persistent);
  238. m_VertexAllocator = new BlockAllocator();
  239. m_VertexAllocator.Initialize(m_MaxVertCounts);
  240. m_IndexAllocator = new BlockAllocator();
  241. m_IndexAllocator.Initialize(m_MaxIndexCounts);
  242. m_MeshChunkTableAllocator = new BlockAllocator();
  243. m_MeshChunkTableAllocator.Initialize(m_MaxMeshChunkTableEntriesCount);
  244. }
  245. void DisposeInputBuffers()
  246. {
  247. if (m_InputBufferReferences.Count == 0)
  248. return;
  249. foreach (var b in m_InputBufferReferences)
  250. b.Dispose();
  251. m_InputBufferReferences.Clear();
  252. }
  253. public void Dispose()
  254. {
  255. m_IndexAllocator.Dispose();
  256. m_VertexAllocator.Dispose();
  257. m_MeshChunkTableAllocator.Dispose();
  258. m_DummyBuffer.Dispose();
  259. m_MeshHashToGeoSlot.Dispose();
  260. foreach (var geoSlot in m_GeoSlots)
  261. {
  262. if (geoSlot.valid)
  263. geoSlot.meshChunks.Dispose();
  264. }
  265. m_GeoSlots = null;
  266. m_FreeGeoSlots.Dispose();
  267. m_GeoPoolEntryHashToSlot.Dispose();
  268. m_GeoPoolEntrySlots.Dispose();
  269. m_FreeGeoPoolEntrySlots.Dispose();
  270. m_GlobalIndexBuffer.Dispose();
  271. m_GlobalVertexBuffer.Release();
  272. m_GlobalMeshChunkTableEntryBuffer.Dispose();
  273. m_CmdBuffer.Release();
  274. DisposeInputBuffers();
  275. }
  276. private void LoadKernels(ComputeShader geometryPoolShader)
  277. {
  278. m_GeometryPoolKernelsCS = geometryPoolShader;
  279. m_KernelMainUpdateIndexBuffer16 = m_GeometryPoolKernelsCS.FindKernel("MainUpdateIndexBuffer16");
  280. m_KernelMainUpdateIndexBuffer32 = m_GeometryPoolKernelsCS.FindKernel("MainUpdateIndexBuffer32");
  281. m_KernelMainUpdateVertexBuffer = m_GeometryPoolKernelsCS.FindKernel("MainUpdateVertexBuffer");
  282. }
  283. private int CalcVertexCount(int bufferByteSize) => DivUp(bufferByteSize, GetVertexByteSize());
  284. private int CalcIndexCount(int bufferByteSize) => DivUp(bufferByteSize, GetIndexByteSize());
  285. private int CalcMeshChunkTablesCount(int bufferByteSize) => DivUp(bufferByteSize, GetMeshChunkTableEntryByteSize());
  286. private void DeallocateGeometrySlot(ref GeometrySlot slot)
  287. {
  288. if (slot.meshChunkTableAlloc.valid)
  289. {
  290. m_MeshChunkTableAllocator.FreeAllocation(slot.meshChunkTableAlloc);
  291. if (slot.meshChunks.IsCreated)
  292. {
  293. for (int i = 0; i < slot.meshChunks.Length; ++i)
  294. {
  295. var meshChunk = slot.meshChunks[i];
  296. if (meshChunk.vertexAlloc.valid)
  297. m_VertexAllocator.FreeAllocation(meshChunk.vertexAlloc);
  298. if (meshChunk.indexAlloc.valid)
  299. m_IndexAllocator.FreeAllocation(meshChunk.indexAlloc);
  300. }
  301. slot.meshChunks.Dispose();
  302. }
  303. }
  304. slot = GeometrySlot.Invalid;
  305. }
  306. private void DeallocateGeometrySlot(int geoSlotHandle)
  307. {
  308. var geoSlot = m_GeoSlots[geoSlotHandle];
  309. Assertions.Assert.IsTrue(geoSlot.valid);
  310. --geoSlot.refCount;
  311. if (geoSlot.refCount == 0)
  312. {
  313. m_MeshHashToGeoSlot.Remove(geoSlot.hash);
  314. DeallocateGeometrySlot(ref geoSlot);
  315. m_FreeGeoSlots.Add(geoSlotHandle);
  316. }
  317. m_GeoSlots[geoSlotHandle] = geoSlot;
  318. }
  319. private bool AllocateGeo(Mesh mesh, out int allocationHandle)
  320. {
  321. uint meshHash = (uint)mesh.GetHashCode();
  322. int vertexCount = mesh.vertexCount;
  323. int indexCount = 0;
  324. for (int submeshIndex = 0; submeshIndex < mesh.subMeshCount; ++submeshIndex)
  325. {
  326. indexCount += (int)mesh.GetIndexCount(submeshIndex);
  327. }
  328. if (m_MeshHashToGeoSlot.TryGetValue(meshHash, out allocationHandle))
  329. {
  330. var geoSlot = m_GeoSlots[allocationHandle];
  331. Assertions.Assert.IsTrue(geoSlot.hash == meshHash);
  332. Assertions.Assert.IsTrue(geoSlot.meshChunkTableAlloc.block.count == mesh.subMeshCount);
  333. ++geoSlot.refCount;
  334. m_GeoSlots[allocationHandle] = geoSlot;
  335. return true;
  336. }
  337. allocationHandle = InvalidHandle;
  338. var newSlot = GeometrySlot.Invalid;
  339. newSlot.refCount = 1;
  340. newSlot.hash = meshHash;
  341. bool allocationSuccess = true;
  342. if (mesh.subMeshCount > 0)
  343. {
  344. newSlot.meshChunkTableAlloc = m_MeshChunkTableAllocator.Allocate(mesh.subMeshCount);
  345. if (!newSlot.meshChunkTableAlloc.valid)
  346. {
  347. var oldChunkCount = m_MeshChunkTableAllocator.capacity;
  348. var newChunkCount = m_MeshChunkTableAllocator.Grow(m_MeshChunkTableAllocator.capacity + mesh.subMeshCount);
  349. var newChunkTableBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, newChunkCount, GetMeshChunkTableEntryByteSize());
  350. GraphicsHelpers.CopyBuffer(m_CopyShader, m_GlobalMeshChunkTableEntryBuffer, 0, newChunkTableBuffer, 0, DivUp(oldChunkCount * GetMeshChunkTableEntryByteSize(), 4));
  351. m_GlobalMeshChunkTableEntryBuffer.Dispose();
  352. m_GlobalMeshChunkTableEntryBuffer = newChunkTableBuffer;
  353. m_MaxMeshChunkTableEntriesCount = newChunkCount;
  354. newSlot.meshChunkTableAlloc = m_MeshChunkTableAllocator.Allocate(mesh.subMeshCount);
  355. Assert.IsTrue(newSlot.meshChunkTableAlloc.valid);
  356. }
  357. newSlot.meshChunks = new NativeArray<MeshChunk>(mesh.subMeshCount, Allocator.Persistent);
  358. for (int submeshIndex = 0; submeshIndex < mesh.subMeshCount; ++submeshIndex)
  359. {
  360. SubMeshDescriptor submeshDescriptor = mesh.GetSubMesh(submeshIndex);
  361. var newMeshChunk = MeshChunk.Invalid;
  362. newMeshChunk.vertexAlloc = m_VertexAllocator.Allocate(submeshDescriptor.vertexCount);
  363. if (!newMeshChunk.vertexAlloc.valid)
  364. {
  365. var oldVertexCount = m_VertexAllocator.capacity;
  366. var newVertexCount = m_VertexAllocator.Grow(m_VertexAllocator.capacity + submeshDescriptor.vertexCount);
  367. var newVertexBuffer = new GraphicsBuffer(VertexBufferTarget, DivUp(newVertexCount * GetVertexByteSize(), 4), 4);
  368. GraphicsHelpers.CopyBuffer(m_CopyShader, m_GlobalVertexBuffer, 0, newVertexBuffer, 0, DivUp(oldVertexCount * GetVertexByteSize(), 4));
  369. m_GlobalVertexBuffer.Dispose();
  370. m_GlobalVertexBuffer = newVertexBuffer;
  371. m_MaxVertCounts = newVertexCount;
  372. newMeshChunk.vertexAlloc = m_VertexAllocator.Allocate(submeshDescriptor.vertexCount);
  373. Assert.IsTrue(newMeshChunk.vertexAlloc.valid);
  374. }
  375. newMeshChunk.indexAlloc = m_IndexAllocator.Allocate(submeshDescriptor.indexCount);
  376. if (!newMeshChunk.indexAlloc.valid)
  377. {
  378. var oldIndexcount = m_IndexAllocator.capacity;
  379. var newIndexCount = m_IndexAllocator.Grow(m_IndexAllocator.capacity + submeshDescriptor.indexCount);
  380. var newIndexBuffer = new GraphicsBuffer(IndexBufferTarget, newIndexCount, 4);
  381. GraphicsHelpers.CopyBuffer(m_CopyShader, m_GlobalIndexBuffer, 0, newIndexBuffer, 0, oldIndexcount);
  382. m_GlobalIndexBuffer.Dispose();
  383. m_GlobalIndexBuffer = newIndexBuffer;
  384. m_MaxIndexCounts = newIndexCount;
  385. newMeshChunk.indexAlloc = m_IndexAllocator.Allocate(submeshDescriptor.indexCount);
  386. Assert.IsTrue(newMeshChunk.indexAlloc.valid);
  387. }
  388. newSlot.meshChunks[submeshIndex] = newMeshChunk;
  389. }
  390. }
  391. if (!allocationSuccess)
  392. {
  393. DeallocateGeometrySlot(ref newSlot);
  394. return false;
  395. }
  396. if (m_FreeGeoSlots.IsEmpty)
  397. {
  398. allocationHandle = m_GeoSlots.Count;
  399. m_GeoSlots.Add(newSlot);
  400. }
  401. else
  402. {
  403. allocationHandle = m_FreeGeoSlots[m_FreeGeoSlots.Length - 1];
  404. m_FreeGeoSlots.RemoveAtSwapBack(m_FreeGeoSlots.Length - 1);
  405. Assertions.Assert.IsTrue(!m_GeoSlots[allocationHandle].valid);
  406. m_GeoSlots[allocationHandle] = newSlot;
  407. }
  408. m_MeshHashToGeoSlot.Add(newSlot.hash, allocationHandle);
  409. return true;
  410. }
  411. private void DeallocateGeoPoolEntrySlot(GeometryPoolHandle handle)
  412. {
  413. var slot = m_GeoPoolEntrySlots[handle.index];
  414. --slot.refCount;
  415. if (slot.refCount == 0)
  416. {
  417. m_GeoPoolEntryHashToSlot.Remove(slot.hash);
  418. DeallocateGeoPoolEntrySlot(ref slot);
  419. m_FreeGeoPoolEntrySlots.Add(handle);
  420. }
  421. m_GeoPoolEntrySlots[handle.index] = slot;
  422. }
  423. private void DeallocateGeoPoolEntrySlot(ref GeoPoolEntrySlot geoPoolEntrySlot)
  424. {
  425. if (geoPoolEntrySlot.geoSlotHandle != InvalidHandle)
  426. DeallocateGeometrySlot(geoPoolEntrySlot.geoSlotHandle);
  427. geoPoolEntrySlot = GeoPoolEntrySlot.Invalid;
  428. }
  429. public GeometryPoolEntryInfo GetEntryInfo(GeometryPoolHandle handle)
  430. {
  431. if (!handle.valid)
  432. return GeometryPoolEntryInfo.NewDefault();
  433. GeoPoolEntrySlot slot = m_GeoPoolEntrySlots[handle.index];
  434. if (!slot.valid)
  435. return GeometryPoolEntryInfo.NewDefault();
  436. if (slot.geoSlotHandle == -1)
  437. Debug.LogErrorFormat("Found invalid geometry slot handle with handle id {0}.", handle.index);
  438. return new GeometryPoolEntryInfo()
  439. {
  440. valid = slot.valid,
  441. refCount = slot.refCount
  442. };
  443. }
  444. public GeometrySlot GetEntryGeomAllocation(GeometryPoolHandle handle)
  445. {
  446. var slot = m_GeoPoolEntrySlots[handle.index];
  447. Assertions.Assert.IsTrue(slot.valid);
  448. var geoSlot = m_GeoSlots[slot.geoSlotHandle];
  449. Assertions.Assert.IsTrue(geoSlot.valid);
  450. return geoSlot;
  451. }
  452. private void UpdateGeoGpuState(Mesh mesh, List<GeometryPoolSubmeshData> submeshData, GeometryPoolHandle handle)
  453. {
  454. var entrySlot = m_GeoPoolEntrySlots[handle.index];
  455. var geoSlot = m_GeoSlots[entrySlot.geoSlotHandle];
  456. CommandBuffer cmdBuffer = AllocateCommandBuffer(); //clear any previous cmd buffers.
  457. //Upload mesh information.
  458. if (!geoSlot.hasGPUData)
  459. {
  460. //Load index buffer
  461. GraphicsBuffer buffer = LoadIndexBuffer(cmdBuffer, mesh);
  462. Assertions.Assert.IsTrue((buffer.target & GraphicsBuffer.Target.Raw) != 0);
  463. // Load attribute buffers
  464. var posAttrib = new VertexBufferAttribInfo();
  465. LoadVertexAttribInfo(mesh, VertexAttribute.Position, out posAttrib);
  466. var uv0Attrib = new VertexBufferAttribInfo();
  467. LoadVertexAttribInfo(mesh, VertexAttribute.TexCoord0, out uv0Attrib);
  468. var uv1Attrib = new VertexBufferAttribInfo();
  469. LoadVertexAttribInfo(mesh, VertexAttribute.TexCoord1, out uv1Attrib);
  470. var normalAttrib = new VertexBufferAttribInfo();
  471. LoadVertexAttribInfo(mesh, VertexAttribute.Normal, out normalAttrib);
  472. var meshChunkAllocationTable = new NativeArray<GeoPoolMeshChunk>(geoSlot.meshChunks.Length, Allocator.Temp);
  473. for (int submeshIndex = 0; submeshIndex < mesh.subMeshCount; ++submeshIndex)
  474. {
  475. SubMeshDescriptor submeshDescriptor = mesh.GetSubMesh(submeshIndex);
  476. MeshChunk targetMeshChunk = geoSlot.meshChunks[submeshIndex];
  477. //Update mesh chunk vertex offset
  478. AddVertexUpdateCommand(
  479. cmdBuffer, submeshDescriptor.baseVertex + submeshDescriptor.firstVertex,
  480. posAttrib, uv0Attrib, uv1Attrib, normalAttrib,
  481. targetMeshChunk.vertexAlloc, m_GlobalVertexBuffer);
  482. //Update mesh chunk index offset
  483. AddIndexUpdateCommand(
  484. cmdBuffer,
  485. mesh.indexFormat, buffer, targetMeshChunk.indexAlloc, submeshDescriptor.firstVertex,
  486. submeshDescriptor.indexStart, submeshDescriptor.indexCount, 0,
  487. m_GlobalIndexBuffer);
  488. meshChunkAllocationTable[submeshIndex] = targetMeshChunk.EncodeGPUEntry();
  489. }
  490. cmdBuffer.SetBufferData(m_GlobalMeshChunkTableEntryBuffer, meshChunkAllocationTable, 0, geoSlot.meshChunkTableAlloc.block.offset, meshChunkAllocationTable.Length);
  491. meshChunkAllocationTable.Dispose();
  492. geoSlot.hasGPUData = true;
  493. m_GeoSlots[entrySlot.geoSlotHandle] = geoSlot;
  494. }
  495. }
  496. private uint FNVHash(uint prevHash, uint dword)
  497. {
  498. //https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
  499. const uint fnvPrime = 0x811C9DC5;
  500. for (int i = 0; i < 4; ++i)
  501. {
  502. prevHash ^= ((dword >> (i * 8)) & 0xFF);
  503. prevHash *= fnvPrime;
  504. }
  505. return prevHash;
  506. }
  507. private uint CalculateClusterHash(Mesh mesh, GeometryPoolSubmeshData[] submeshData)
  508. {
  509. uint meshHash = (uint)mesh.GetHashCode();
  510. uint clusterHash = meshHash;
  511. if (submeshData != null)
  512. {
  513. foreach (var data in submeshData)
  514. {
  515. clusterHash = FNVHash(clusterHash, (uint)data.submeshIndex);
  516. clusterHash = FNVHash(clusterHash, (uint)(data.material == null ? 0 : data.material.GetHashCode()));
  517. }
  518. }
  519. return clusterHash;
  520. }
  521. public bool Register(Mesh mesh, out GeometryPoolHandle outHandle)
  522. {
  523. return Register(new GeometryPoolEntryDesc()
  524. {
  525. mesh = mesh,
  526. submeshData = null
  527. }, out outHandle);
  528. }
  529. public GeometryPoolHandle GetHandle(Mesh mesh)
  530. {
  531. uint geoPoolEntryHash = CalculateClusterHash(mesh, null);
  532. if (m_GeoPoolEntryHashToSlot.TryGetValue(geoPoolEntryHash, out GeometryPoolHandle outHandle))
  533. return outHandle;
  534. else
  535. return GeometryPoolHandle.Invalid;
  536. }
  537. private static int FindSubmeshEntryInDesc(int submeshIndex, in GeometryPoolSubmeshData[] submeshData)
  538. {
  539. if (submeshData == null)
  540. return -1;
  541. for (int i = 0; i < submeshData.Length; ++i)
  542. {
  543. if (submeshData[i].submeshIndex == submeshIndex)
  544. return i;
  545. }
  546. return -1;
  547. }
  548. public bool Register(in GeometryPoolEntryDesc entryDesc, out GeometryPoolHandle outHandle)
  549. {
  550. outHandle = GeometryPoolHandle.Invalid;
  551. if (entryDesc.mesh == null)
  552. {
  553. return false;
  554. }
  555. Mesh mesh = entryDesc.mesh;
  556. uint geoPoolEntryHash = CalculateClusterHash(entryDesc.mesh, entryDesc.submeshData);
  557. if (m_GeoPoolEntryHashToSlot.TryGetValue(geoPoolEntryHash, out outHandle))
  558. {
  559. GeoPoolEntrySlot geoPoolEntrySlot = m_GeoPoolEntrySlots[outHandle.index];
  560. Assertions.Assert.IsTrue(geoPoolEntrySlot.hash == geoPoolEntryHash);
  561. GeometrySlot geoSlot = m_GeoSlots[geoPoolEntrySlot.geoSlotHandle];
  562. Assertions.Assert.IsTrue(geoSlot.hash == (uint)mesh.GetHashCode());
  563. ++geoPoolEntrySlot.refCount;
  564. m_GeoPoolEntrySlots[outHandle.index] = geoPoolEntrySlot;
  565. return true;
  566. }
  567. var newSlot = GeoPoolEntrySlot.Invalid;
  568. newSlot.refCount = 1;
  569. newSlot.hash = geoPoolEntryHash;
  570. // Validate submesh information
  571. var validSubmeshData = new List<GeometryPoolSubmeshData>(mesh.subMeshCount);
  572. if (mesh.subMeshCount > 0 && entryDesc.submeshData != null)
  573. {
  574. for (int submeshIndex = 0; submeshIndex < mesh.subMeshCount; ++submeshIndex)
  575. {
  576. int entryIndex = FindSubmeshEntryInDesc(submeshIndex, entryDesc.submeshData);
  577. if (entryIndex == -1)
  578. {
  579. Debug.LogErrorFormat("Could not find submesh index {0} for mesh entry descriptor of mesh {1}.", submeshIndex, mesh.name);
  580. continue;
  581. }
  582. validSubmeshData.Add(entryDesc.submeshData[entryIndex]);
  583. }
  584. }
  585. if (!AllocateGeo(mesh, out newSlot.geoSlotHandle))
  586. {
  587. DeallocateGeoPoolEntrySlot(ref newSlot);
  588. return false;
  589. }
  590. if (m_FreeGeoPoolEntrySlots.IsEmpty)
  591. {
  592. outHandle = new GeometryPoolHandle() { index = m_GeoPoolEntrySlots.Length };
  593. m_GeoPoolEntrySlots.Add(newSlot);
  594. }
  595. else
  596. {
  597. outHandle = m_FreeGeoPoolEntrySlots[m_FreeGeoPoolEntrySlots.Length - 1];
  598. m_FreeGeoPoolEntrySlots.RemoveAtSwapBack(m_FreeGeoPoolEntrySlots.Length - 1);
  599. Assertions.Assert.IsTrue(!m_GeoPoolEntrySlots[outHandle.index].valid);
  600. m_GeoPoolEntrySlots[outHandle.index] = newSlot;
  601. }
  602. m_GeoPoolEntryHashToSlot.Add(newSlot.hash, outHandle);
  603. UpdateGeoGpuState(mesh, validSubmeshData, outHandle);
  604. ++m_GeoPoolEntriesCount;
  605. return true;
  606. }
  607. public void Unregister(GeometryPoolHandle handle)
  608. {
  609. var slot = m_GeoPoolEntrySlots[handle.index];
  610. Assertions.Assert.IsTrue(slot.valid);
  611. DeallocateGeoPoolEntrySlot(handle);
  612. --m_GeoPoolEntriesCount;
  613. }
  614. public void SendGpuCommands()
  615. {
  616. if (m_PendingCmds != 0)
  617. {
  618. Graphics.ExecuteCommandBuffer(m_CmdBuffer);
  619. m_MustClearCmdBuffer = true;
  620. m_PendingCmds = 0;
  621. }
  622. DisposeInputBuffers();
  623. }
  624. private GraphicsBuffer LoadIndexBuffer(CommandBuffer cmdBuffer, Mesh mesh)
  625. {
  626. if ((mesh.indexBufferTarget & GraphicsBuffer.Target.Raw) == 0 && (mesh.GetIndices(0) == null || mesh.GetIndices(0).Length == 0))
  627. {
  628. throw new Exception("Cant use a mesh buffer that is not raw and has no CPU index information.");
  629. }
  630. else
  631. {
  632. mesh.indexBufferTarget |= GraphicsBuffer.Target.Raw;
  633. mesh.vertexBufferTarget |= GraphicsBuffer.Target.Raw;
  634. var idxBuffer = mesh.GetIndexBuffer();
  635. m_InputBufferReferences.Add(idxBuffer);
  636. return idxBuffer;
  637. }
  638. }
  639. void LoadVertexAttribInfo(Mesh mesh, VertexAttribute attribute, out VertexBufferAttribInfo output)
  640. {
  641. if (!mesh.HasVertexAttribute(attribute))
  642. {
  643. output.buffer = null;
  644. output.stride = output.offset = output.byteCount = 0;
  645. return;
  646. }
  647. int stream = mesh.GetVertexAttributeStream(attribute);
  648. output.stride = mesh.GetVertexBufferStride(stream);
  649. output.offset = mesh.GetVertexAttributeOffset(attribute);
  650. output.byteCount = GetFormatByteCount(mesh.GetVertexAttributeFormat(attribute)) * mesh.GetVertexAttributeDimension(attribute);
  651. output.buffer = mesh.GetVertexBuffer(stream);
  652. m_InputBufferReferences.Add(output.buffer);
  653. Assertions.Assert.IsTrue((output.buffer.target & GraphicsBuffer.Target.Raw) != 0);
  654. }
  655. private CommandBuffer AllocateCommandBuffer()
  656. {
  657. if (m_MustClearCmdBuffer)
  658. {
  659. m_CmdBuffer.Clear();
  660. m_MustClearCmdBuffer = false;
  661. }
  662. ++m_PendingCmds;
  663. return m_CmdBuffer;
  664. }
  665. private void AddIndexUpdateCommand(
  666. CommandBuffer cmdBuffer,
  667. IndexFormat inputFormat,
  668. in GraphicsBuffer inputBuffer,
  669. in BlockAllocator.Allocation location,
  670. int firstVertex,
  671. int inputOffset, int indexCount, int outputOffset,
  672. GraphicsBuffer outputIdxBuffer)
  673. {
  674. if (location.block.count == 0)
  675. return;
  676. Assertions.Assert.IsTrue(indexCount <= location.block.count);
  677. Assertions.Assert.IsTrue(outputOffset < location.block.count);
  678. cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputIBBaseOffset, inputOffset);
  679. cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputIBCount, indexCount);
  680. cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputFirstVertex, firstVertex);
  681. cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._OutputIBOffset, location.block.offset + outputOffset);
  682. int kernel = inputFormat == IndexFormat.UInt16 ? m_KernelMainUpdateIndexBuffer16 : m_KernelMainUpdateIndexBuffer32;
  683. cmdBuffer.SetComputeBufferParam(m_GeometryPoolKernelsCS, kernel, GeoPoolShaderIDs._InputIndexBuffer, inputBuffer);
  684. cmdBuffer.SetComputeBufferParam(m_GeometryPoolKernelsCS, kernel, GeoPoolShaderIDs._OutputIndexBuffer, outputIdxBuffer);
  685. for (int uvChannel = 0; uvChannel < 2; uvChannel++)
  686. {
  687. cmdBuffer.EnableKeyword(m_GeometryPoolKernelsCS, new LocalKeyword(m_GeometryPoolKernelsCS, $"UV{uvChannel}_DIM2"));
  688. cmdBuffer.DisableKeyword(m_GeometryPoolKernelsCS, new LocalKeyword(m_GeometryPoolKernelsCS, $"UV{uvChannel}_DIM3"));
  689. cmdBuffer.DisableKeyword(m_GeometryPoolKernelsCS, new LocalKeyword(m_GeometryPoolKernelsCS, $"UV{uvChannel}_DIM4"));
  690. }
  691. int totalGroupCount = DivUp(location.block.count, kThreadGroupSize);
  692. int dispatchCount = DivUp(totalGroupCount, kMaxThreadGroupsPerDispatch);
  693. for (int dispatchIndex = 0; dispatchIndex < dispatchCount; ++dispatchIndex)
  694. {
  695. int indexOffset = dispatchIndex * kMaxThreadGroupsPerDispatch * kThreadGroupSize;
  696. int dispatchGroupCount = Math.Min(kMaxThreadGroupsPerDispatch, totalGroupCount - dispatchIndex * kMaxThreadGroupsPerDispatch);
  697. cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._DispatchIndexOffset, indexOffset);
  698. cmdBuffer.DispatchCompute(m_GeometryPoolKernelsCS, kernel, dispatchGroupCount, 1, 1);
  699. }
  700. }
  701. private void AddVertexUpdateCommand(
  702. CommandBuffer cmdBuffer, int baseVertexOffset,
  703. in VertexBufferAttribInfo pos, in VertexBufferAttribInfo uv0, in VertexBufferAttribInfo uv1, in VertexBufferAttribInfo n,
  704. in BlockAllocator.Allocation location,
  705. GraphicsBuffer outputVertexBuffer)
  706. {
  707. if (location.block.count == 0)
  708. return;
  709. GeoPoolVertexAttribs attributes = 0;
  710. if (pos.valid)
  711. attributes |= GeoPoolVertexAttribs.Position;
  712. if (uv0.valid)
  713. attributes |= GeoPoolVertexAttribs.Uv0;
  714. if (uv1.valid)
  715. attributes |= GeoPoolVertexAttribs.Uv1;
  716. if (n.valid)
  717. attributes |= GeoPoolVertexAttribs.Normal;
  718. int vertexCount = location.block.count;
  719. cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputVBCount, vertexCount);
  720. cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputBaseVertexOffset, baseVertexOffset);
  721. cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._OutputVBSize, m_MaxVertCounts);
  722. cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._OutputVBOffset, location.block.offset);
  723. cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputPosBufferStride, pos.stride);
  724. cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputPosBufferOffset, pos.offset);
  725. cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputUv0BufferStride, uv0.stride);
  726. cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputUv0BufferOffset, uv0.offset);
  727. cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputUv1BufferStride, uv1.stride);
  728. cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputUv1BufferOffset, uv1.offset);
  729. cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputNormalBufferStride, n.stride);
  730. cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputNormalBufferOffset, n.offset);
  731. cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._AttributesMask, (int)attributes);
  732. int kernel = m_KernelMainUpdateVertexBuffer;
  733. cmdBuffer.SetComputeBufferParam(m_GeometryPoolKernelsCS, kernel, GeoPoolShaderIDs._PosBuffer, pos.valid ? pos.buffer : m_DummyBuffer);
  734. cmdBuffer.SetComputeBufferParam(m_GeometryPoolKernelsCS, kernel, GeoPoolShaderIDs._Uv0Buffer, uv0.valid ? uv0.buffer : m_DummyBuffer);
  735. cmdBuffer.SetComputeBufferParam(m_GeometryPoolKernelsCS, kernel, GeoPoolShaderIDs._Uv1Buffer, uv1.valid ? uv1.buffer : m_DummyBuffer);
  736. cmdBuffer.SetComputeBufferParam(m_GeometryPoolKernelsCS, kernel, GeoPoolShaderIDs._NormalBuffer, n.valid ? n.buffer : m_DummyBuffer);
  737. cmdBuffer.SetComputeBufferParam(m_GeometryPoolKernelsCS, kernel, GeoPoolShaderIDs._OutputVB, outputVertexBuffer);
  738. for (int uvChannel = 0; uvChannel < 2; uvChannel++)
  739. {
  740. cmdBuffer.DisableKeyword(m_GeometryPoolKernelsCS, new LocalKeyword(m_GeometryPoolKernelsCS, $"UV{uvChannel}_DIM2"));
  741. cmdBuffer.DisableKeyword(m_GeometryPoolKernelsCS, new LocalKeyword(m_GeometryPoolKernelsCS, $"UV{uvChannel}_DIM3"));
  742. cmdBuffer.DisableKeyword(m_GeometryPoolKernelsCS, new LocalKeyword(m_GeometryPoolKernelsCS, $"UV{uvChannel}_DIM4"));
  743. ref readonly VertexBufferAttribInfo uv = ref (uvChannel == 0) ? ref uv0 : ref uv1;
  744. switch (uv.byteCount / 4)
  745. {
  746. case 0:
  747. // if there are no UVs, we still have to enabe a UV_DIM* keyword, but because of the _AttributesMask no UVs will be processed. This way we save a shader variant.
  748. case 2:
  749. cmdBuffer.EnableKeyword(m_GeometryPoolKernelsCS, new LocalKeyword(m_GeometryPoolKernelsCS, $"UV{uvChannel}_DIM2"));
  750. break;
  751. case 3:
  752. cmdBuffer.EnableKeyword(m_GeometryPoolKernelsCS, new LocalKeyword(m_GeometryPoolKernelsCS, $"UV{uvChannel}_DIM3"));
  753. break;
  754. case 4:
  755. cmdBuffer.EnableKeyword(m_GeometryPoolKernelsCS, new LocalKeyword(m_GeometryPoolKernelsCS, $"UV{uvChannel}_DIM4"));
  756. break;
  757. }
  758. }
  759. int totalGroupCount = DivUp(vertexCount, kThreadGroupSize);
  760. int dispatchCount = DivUp(totalGroupCount, kMaxThreadGroupsPerDispatch);
  761. for (int dispatchIndex = 0; dispatchIndex < dispatchCount; ++dispatchIndex)
  762. {
  763. int vertexOffset = dispatchIndex * kMaxThreadGroupsPerDispatch * kThreadGroupSize;
  764. int dispatchGroupCount = Math.Min(kMaxThreadGroupsPerDispatch, totalGroupCount - dispatchIndex * kMaxThreadGroupsPerDispatch);
  765. cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._DispatchVertexOffset, vertexOffset);
  766. cmdBuffer.DispatchCompute(m_GeometryPoolKernelsCS, kernel, dispatchGroupCount, 1, 1);
  767. }
  768. }
  769. }
  770. }