A game about forced loneliness, made by TACStudios
at master 1083 lines 45 kB view raw
1using System; 2using System.Threading; 3using UnityEngine.Assertions; 4using Unity.Collections; 5using Unity.Jobs; 6using Unity.Jobs.LowLevel.Unsafe; 7using Unity.Collections.LowLevel.Unsafe; 8using Unity.Burst; 9using UnityEngine.Profiling; 10 11[assembly: RegisterGenericJobType(typeof(UnityEngine.Rendering.RegisterNewInstancesJob<UnityEngine.Rendering.BatchMeshID>))] 12[assembly: RegisterGenericJobType(typeof(UnityEngine.Rendering.RegisterNewInstancesJob<UnityEngine.Rendering.BatchMaterialID>))] 13[assembly: RegisterGenericJobType(typeof(UnityEngine.Rendering.FindNonRegisteredInstancesJob<UnityEngine.Rendering.BatchMeshID>))] 14[assembly: RegisterGenericJobType(typeof(UnityEngine.Rendering.FindNonRegisteredInstancesJob<UnityEngine.Rendering.BatchMaterialID>))] 15 16namespace UnityEngine.Rendering 17{ 18 internal delegate void OnCullingCompleteCallback(JobHandle jobHandle, in BatchCullingContext cullingContext, in BatchCullingOutput cullingOutput); 19 20 internal struct InstanceCullingBatcherDesc 21 { 22 public OnCullingCompleteCallback onCompleteCallback; 23 24#if UNITY_EDITOR 25 public Shader brgPicking; 26 public Shader brgLoading; 27 public Shader brgError; 28#endif 29 30 public static InstanceCullingBatcherDesc NewDefault() 31 { 32 return new InstanceCullingBatcherDesc() 33 { 34 onCompleteCallback = null 35#if UNITY_EDITOR 36 ,brgPicking = null 37 ,brgLoading = null 38 ,brgError = null 39#endif 40 }; 41 } 42 } 43 44 internal struct MeshProceduralInfo 45 { 46 public MeshTopology topology; 47 public uint baseVertex; 48 public uint firstIndex; 49 public uint indexCount; 50 } 51 52 [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] 53 internal struct PrefixSumDrawInstancesJob : IJob 54 { 55 [ReadOnly] public NativeParallelHashMap<RangeKey, int> rangeHash; 56 57 public NativeList<DrawRange> drawRanges; 58 public NativeList<DrawBatch> drawBatches; 59 public NativeArray<int> drawBatchIndices; 60 61 public void Execute() 62 { 63 Assert.AreEqual(rangeHash.Count(), drawRanges.Length); 64 Assert.AreEqual(drawBatchIndices.Length, drawBatches.Length); 65 66 // Prefix sum to calculate draw offsets for each DrawRange 67 int drawPrefixSum = 0; 68 69 for (int i = 0; i < drawRanges.Length; ++i) 70 { 71 ref DrawRange drawRange = ref drawRanges.ElementAt(i); 72 drawRange.drawOffset = drawPrefixSum; 73 drawPrefixSum += drawRange.drawCount; 74 } 75 76 // Generate DrawBatch index ranges for each DrawRange 77 var internalRangeIndex = new NativeArray<int>(drawRanges.Length, Allocator.Temp); 78 79 for (int i = 0; i < drawBatches.Length; ++i) 80 { 81 ref DrawBatch drawBatch = ref drawBatches.ElementAt(i); 82 Assert.IsTrue(drawBatch.instanceCount > 0); 83 84 if (rangeHash.TryGetValue(drawBatch.key.range, out int drawRangeIndex)) 85 { 86 ref DrawRange drawRange = ref drawRanges.ElementAt(drawRangeIndex); 87 drawBatchIndices[drawRange.drawOffset + internalRangeIndex[drawRangeIndex]] = i; 88 internalRangeIndex[drawRangeIndex]++; 89 } 90 } 91 92 // Prefix sum to calculate instance offsets for each DrawCommand 93 int drawInstancesPrefixSum = 0; 94 95 for (int i = 0; i < drawBatchIndices.Length; ++i) 96 { 97 // DrawIndices remap to get DrawCommands ordered by DrawRange 98 var drawBatchIndex = drawBatchIndices[i]; 99 ref DrawBatch drawBatch = ref drawBatches.ElementAt(drawBatchIndex); 100 drawBatch.instanceOffset = drawInstancesPrefixSum; 101 drawInstancesPrefixSum += drawBatch.instanceCount; 102 } 103 104 internalRangeIndex.Dispose(); 105 } 106 } 107 108 [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] 109 internal unsafe struct BuildDrawListsJob : IJobParallelFor 110 { 111 public const int k_BatchSize = 128; 112 public const int k_IntsPerCacheLine = JobsUtility.CacheLineSize / sizeof(int); 113 114 [ReadOnly] public NativeParallelHashMap<DrawKey, int> batchHash; 115 [NativeDisableContainerSafetyRestriction, NoAlias] [ReadOnly] public NativeList<DrawInstance> drawInstances; 116 [NativeDisableContainerSafetyRestriction, NoAlias] [ReadOnly] public NativeList<DrawBatch> drawBatches; 117 118 [NativeDisableContainerSafetyRestriction, NoAlias] [WriteOnly] public NativeArray<int> internalDrawIndex; 119 [NativeDisableContainerSafetyRestriction, NoAlias] [WriteOnly] public NativeArray<int> drawInstanceIndices; 120 121 private unsafe static int IncrementCounter(int* counter) 122 { 123 return Interlocked.Increment(ref UnsafeUtility.AsRef<int>(counter)) - 1; 124 } 125 126 public void Execute(int index) 127 { 128 // Generate instance index ranges for each DrawCommand 129 ref DrawInstance drawInstance = ref drawInstances.ElementAt(index); 130 int drawBatchIndex = batchHash[drawInstance.key]; 131 132 ref DrawBatch drawBatch = ref drawBatches.ElementAt(drawBatchIndex); 133 var offset = IncrementCounter((int*)internalDrawIndex.GetUnsafePtr() + drawBatchIndex * k_IntsPerCacheLine); 134 var writeIndex = drawBatch.instanceOffset + offset; 135 drawInstanceIndices[writeIndex] = drawInstance.instanceIndex; 136 } 137 } 138 139 [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] 140 internal unsafe struct FindDrawInstancesJob : IJobParallelForBatch 141 { 142 public const int k_BatchSize = 128; 143 144 [ReadOnly] public NativeArray<InstanceHandle> instancesSorted; 145 [NativeDisableContainerSafetyRestriction, NoAlias] [ReadOnly] public NativeList<DrawInstance> drawInstances; 146 147 [WriteOnly] public NativeList<int>.ParallelWriter outDrawInstanceIndicesWriter; 148 149 public void Execute(int startIndex, int count) 150 { 151 int* instancesToRemove = stackalloc int[k_BatchSize]; 152 int length = 0; 153 154 for (int i = startIndex; i < startIndex + count; ++i) 155 { 156 ref DrawInstance drawInstance = ref drawInstances.ElementAt(i); 157 158 if (instancesSorted.BinarySearch(InstanceHandle.FromInt(drawInstance.instanceIndex)) >= 0) 159 instancesToRemove[length++] = i; 160 } 161 162 outDrawInstanceIndicesWriter.AddRangeNoResize(instancesToRemove, length); 163 } 164 } 165 166 [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] 167 internal unsafe struct FindMaterialDrawInstancesJob : IJobParallelForBatch 168 { 169 public const int k_BatchSize = 128; 170 171 [ReadOnly] public NativeArray<uint> materialsSorted; 172 [NativeDisableContainerSafetyRestriction, NoAlias] [ReadOnly] public NativeList<DrawInstance> drawInstances; 173 174 [WriteOnly] public NativeList<int>.ParallelWriter outDrawInstanceIndicesWriter; 175 176 public void Execute(int startIndex, int count) 177 { 178 int* instancesToRemove = stackalloc int[k_BatchSize]; 179 int length = 0; 180 181 for (int i = startIndex; i < startIndex + count; ++i) 182 { 183 ref DrawInstance drawInstance = ref drawInstances.ElementAt(i); 184 185 if (materialsSorted.BinarySearch(drawInstance.key.materialID.value) >= 0) 186 instancesToRemove[length++] = i; 187 } 188 189 outDrawInstanceIndicesWriter.AddRangeNoResize(instancesToRemove, length); 190 } 191 } 192 193 [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] 194 internal struct FindNonRegisteredInstancesJob<T> : IJobParallelForBatch where T : unmanaged 195 { 196 public const int k_BatchSize = 128; 197 198 [ReadOnly] public NativeArray<int> instanceIDs; 199 [ReadOnly] public NativeParallelHashMap<int, T> hashMap; 200 201 [WriteOnly] public NativeList<int>.ParallelWriter outInstancesWriter; 202 203 public unsafe void Execute(int startIndex, int count) 204 { 205 int* notFoundinstanceIDs = stackalloc int[k_BatchSize]; 206 int length = 0; 207 208 for (int i = startIndex; i < startIndex + count; ++i) 209 { 210 int instanceID = instanceIDs[i]; 211 212 if (!hashMap.ContainsKey(instanceID)) 213 notFoundinstanceIDs[length++] = instanceID; 214 } 215 216 outInstancesWriter.AddRangeNoResize(notFoundinstanceIDs, length); 217 } 218 } 219 220 [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] 221 internal struct RegisterNewInstancesJob<T> : IJobParallelFor where T : unmanaged 222 { 223 public const int k_BatchSize = 128; 224 225 [ReadOnly] public NativeArray<int> instanceIDs; 226 [ReadOnly] public NativeArray<T> batchIDs; 227 228 [WriteOnly] public NativeParallelHashMap<int, T>.ParallelWriter hashMap; 229 230 public unsafe void Execute(int index) 231 { 232 hashMap.TryAdd(instanceIDs[index], batchIDs[index]); 233 } 234 } 235 236 [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] 237 internal struct RemoveDrawInstanceIndicesJob : IJob 238 { 239 [NativeDisableContainerSafetyRestriction, NoAlias] [ReadOnly] public NativeArray<int> drawInstanceIndices; 240 241 public NativeList<DrawInstance> drawInstances; 242 public NativeParallelHashMap<RangeKey, int> rangeHash; 243 public NativeParallelHashMap<DrawKey, int> batchHash; 244 public NativeList<DrawRange> drawRanges; 245 public NativeList<DrawBatch> drawBatches; 246 247 public void RemoveDrawRange(in RangeKey key) 248 { 249 int drawRangeIndex = rangeHash[key]; 250 251 ref DrawRange lastDrawRange = ref drawRanges.ElementAt(drawRanges.Length - 1); 252 rangeHash[lastDrawRange.key] = drawRangeIndex; 253 254 rangeHash.Remove(key); 255 drawRanges.RemoveAtSwapBack(drawRangeIndex); 256 } 257 258 public void RemoveDrawBatch(in DrawKey key) 259 { 260 int drawBatchIndex = batchHash[key]; 261 262 ref DrawBatch drawBatch = ref drawBatches.ElementAt(drawBatchIndex); 263 264 int drawRangeIndex = rangeHash[key.range]; 265 ref DrawRange drawRange = ref drawRanges.ElementAt(drawRangeIndex); 266 267 Assert.IsTrue(drawRange.drawCount > 0); 268 269 if (--drawRange.drawCount == 0) 270 RemoveDrawRange(drawRange.key); 271 272 ref DrawBatch lastDrawBatch = ref drawBatches.ElementAt(drawBatches.Length - 1); 273 batchHash[lastDrawBatch.key] = drawBatchIndex; 274 275 batchHash.Remove(key); 276 drawBatches.RemoveAtSwapBack(drawBatchIndex); 277 } 278 279 public unsafe void Execute() 280 { 281 var drawInstancesPtr = (DrawInstance*)drawInstances.GetUnsafePtr(); 282 var drawInstancesNewBack = drawInstances.Length - 1; 283 284 for (int indexRev = drawInstanceIndices.Length - 1; indexRev >= 0; --indexRev) 285 { 286 int indexToRemove = drawInstanceIndices[indexRev]; 287 DrawInstance* drawInstance = drawInstancesPtr + indexToRemove; 288 289 int drawBatchIndex = batchHash[drawInstance->key]; 290 ref DrawBatch drawBatch = ref drawBatches.ElementAt(drawBatchIndex); 291 292 Assert.IsTrue(drawBatch.instanceCount > 0); 293 294 if (--drawBatch.instanceCount == 0) 295 RemoveDrawBatch(drawBatch.key); 296 297 UnsafeUtility.MemCpy(drawInstance, drawInstancesPtr + drawInstancesNewBack--, sizeof(DrawInstance)); 298 } 299 300 drawInstances.ResizeUninitialized(drawInstancesNewBack + 1); 301 } 302 } 303 304 [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] 305 internal struct CreateDrawBatchesJob : IJob 306 { 307 [ReadOnly] public bool implicitInstanceIndices; 308 [ReadOnly] public NativeArray<InstanceHandle> instances; 309 [ReadOnly] public GPUDrivenRendererGroupData rendererData; 310 [ReadOnly] public NativeParallelHashMap<int, BatchMeshID> batchMeshHash; 311 [ReadOnly] public NativeParallelHashMap<int, BatchMaterialID> batchMaterialHash; 312 313 public NativeParallelHashMap<RangeKey, int> rangeHash; 314 public NativeList<DrawRange> drawRanges; 315 public NativeParallelHashMap<DrawKey, int> batchHash; 316 public NativeList<DrawBatch> drawBatches; 317 318 [WriteOnly] public NativeList<DrawInstance> drawInstances; 319 320 private ref DrawRange EditDrawRange(in RangeKey key) 321 { 322 int drawRangeIndex; 323 324 if (!rangeHash.TryGetValue(key, out drawRangeIndex)) 325 { 326 var drawRange = new DrawRange { key = key, drawCount = 0, drawOffset = 0 }; 327 drawRangeIndex = drawRanges.Length; 328 rangeHash.Add(key, drawRangeIndex); 329 drawRanges.Add(drawRange); 330 } 331 332 ref DrawRange data = ref drawRanges.ElementAt(drawRangeIndex); 333 Assert.IsTrue(data.key.Equals(key)); 334 335 return ref data; 336 } 337 338 private ref DrawBatch EditDrawBatch(in DrawKey key, in SubMeshDescriptor subMeshDescriptor) 339 { 340 var procInfo = new MeshProceduralInfo(); 341 procInfo.topology = subMeshDescriptor.topology; 342 procInfo.baseVertex = (uint)subMeshDescriptor.baseVertex; 343 procInfo.firstIndex = (uint)subMeshDescriptor.indexStart; 344 procInfo.indexCount = (uint)subMeshDescriptor.indexCount; 345 346 int drawBatchIndex; 347 348 if (!batchHash.TryGetValue(key, out drawBatchIndex)) 349 { 350 var drawBatch = new DrawBatch() { key = key, instanceCount = 0, instanceOffset = 0, procInfo = procInfo }; 351 drawBatchIndex = drawBatches.Length; 352 batchHash.Add(key, drawBatchIndex); 353 drawBatches.Add(drawBatch); 354 } 355 356 ref DrawBatch data = ref drawBatches.ElementAt(drawBatchIndex); 357 Assert.IsTrue(data.key.Equals(key)); 358 359 return ref data; 360 } 361 362 public void ProcessRenderer(int i) 363 { 364 var meshIndex = rendererData.meshIndex[i]; 365 var meshID = rendererData.meshID[meshIndex]; 366 var submeshCount = rendererData.subMeshCount[meshIndex]; 367 var subMeshDescOffset = rendererData.subMeshDescOffset[meshIndex]; 368 var batchMeshID = batchMeshHash[meshID]; 369 var rendererGroupID = rendererData.rendererGroupID[i]; 370 var startSubMesh = rendererData.subMeshStartIndex[i]; 371 var gameObjectLayer = rendererData.gameObjectLayer[i]; 372 var renderingLayerMask = rendererData.renderingLayerMask[i]; 373 var materialsOffset = rendererData.materialsOffset[i]; 374 var materialsCount = rendererData.materialsCount[i]; 375 var lightmapIndex = rendererData.lightmapIndex[i]; 376 var packedRendererData = rendererData.packedRendererData[i]; 377 var rendererPriority = rendererData.rendererPriority[i]; 378 var lodGroupID = rendererData.lodGroupID[i]; 379 380 int instanceCount; 381 int instanceOffset; 382 383 if (implicitInstanceIndices) 384 { 385 instanceCount = 1; 386 instanceOffset = i; 387 } 388 else 389 { 390 instanceCount = rendererData.instancesCount[i]; 391 instanceOffset = rendererData.instancesOffset[i]; 392 } 393 394 if (instanceCount == 0) 395 return; 396 397 const int kLightmapIndexMask = 0xffff; 398 const int kLightmapIndexInfluenceOnly = 0xfffe; 399 400 var overridenComponents = InstanceComponentGroup.Default; 401 402 // Add per-instance wind parameters 403 if(packedRendererData.hasTree) 404 overridenComponents |= InstanceComponentGroup.Wind; 405 406 var lmIndexMasked = lightmapIndex & kLightmapIndexMask; 407 408 // Object doesn't have a valid lightmap Index, -> uses probes for lighting 409 if (lmIndexMasked >= kLightmapIndexInfluenceOnly) 410 { 411 // Only add the component when needed to store blended results (shader will use the ambient probe when not present) 412 if (packedRendererData.lightProbeUsage == LightProbeUsage.BlendProbes) 413 overridenComponents |= InstanceComponentGroup.LightProbe; 414 } 415 else 416 { 417 // Add per-instance lightmap parameters 418 overridenComponents |= InstanceComponentGroup.Lightmap; 419 } 420 421 // Scan all materials once to retrieve whether this renderer is indirect-compatible or not (and store it in the RangeKey). 422 var supportsIndirect = true; 423 for (int matIndex = 0; matIndex < materialsCount; ++matIndex) 424 { 425 if (matIndex >= submeshCount) 426 { 427 Debug.LogWarning("Material count in the shared material list is higher than sub mesh count for the mesh. Object may be corrupted."); 428 continue; 429 } 430 431 var materialIndex = rendererData.materialIndex[materialsOffset + matIndex]; 432 var packedMaterialData = rendererData.packedMaterialData[materialIndex]; 433 supportsIndirect &= packedMaterialData.isIndirectSupported; 434 } 435 436 var rangeKey = new RangeKey 437 { 438 layer = (byte)gameObjectLayer, 439 renderingLayerMask = renderingLayerMask, 440 motionMode = packedRendererData.motionVecGenMode, 441 shadowCastingMode = packedRendererData.shadowCastingMode, 442 staticShadowCaster = packedRendererData.staticShadowCaster, 443 rendererPriority = rendererPriority, 444 supportsIndirect = supportsIndirect 445 }; 446 447 ref DrawRange drawRange = ref EditDrawRange(rangeKey); 448 449 for (int matIndex = 0; matIndex < materialsCount; ++matIndex) 450 { 451 if (matIndex >= submeshCount) 452 { 453 Debug.LogWarning("Material count in the shared material list is higher than sub mesh count for the mesh. Object may be corrupted."); 454 continue; 455 } 456 457 var materialIndex = rendererData.materialIndex[materialsOffset + matIndex]; 458 var materialID = rendererData.materialID[materialIndex]; 459 var packedMaterialData = rendererData.packedMaterialData[materialIndex]; 460 461 if (materialID == 0) 462 { 463 Debug.LogWarning("Material in the shared materials list is null. Object will be partially rendered."); 464 continue; 465 } 466 467 batchMaterialHash.TryGetValue(materialID, out BatchMaterialID batchMaterialID); 468 469 // We always provide crossfade value packed in instance index. We don't use None even if there is no LOD to not split the batch. 470 var flags = BatchDrawCommandFlags.LODCrossFadeValuePacked; 471 472 // Let the engine know if we've opted out of lightmap texture arrays 473 flags |= BatchDrawCommandFlags.UseLegacyLightmapsKeyword; 474 475 // assume that a custom motion vectors pass contains deformation motion, so should always output motion vectors 476 // (otherwise this flag is set dynamically during culling only when the transform is changing) 477 if (packedMaterialData.isMotionVectorsPassEnabled) 478 flags |= BatchDrawCommandFlags.HasMotion; 479 480 if (packedMaterialData.isTransparent) 481 flags |= BatchDrawCommandFlags.HasSortingPosition; 482 483 { 484 var submeshIndex = startSubMesh + matIndex; 485 var subMeshDesc = rendererData.subMeshDesc[subMeshDescOffset + submeshIndex]; 486 487 var drawKey = new DrawKey 488 { 489 materialID = batchMaterialID, 490 meshID = batchMeshID, 491 submeshIndex = submeshIndex, 492 flags = flags, 493 transparentInstanceId = packedMaterialData.isTransparent ? rendererGroupID : 0, 494 range = rangeKey, 495 overridenComponents = (uint)overridenComponents, 496 // When we've opted out of lightmap texture arrays, we 497 // need to pass in a valid lightmap index. The engine 498 // uses this index for sorting and for breaking the 499 // batch when lightmaps change across draw calls, and 500 // for binding the correct light map. 501 lightmapIndex = lightmapIndex 502 }; 503 504 ref DrawBatch drawBatch = ref EditDrawBatch(drawKey, subMeshDesc); 505 506 if (drawBatch.instanceCount == 0) 507 ++drawRange.drawCount; 508 509 drawBatch.instanceCount += instanceCount; 510 511 for (int j = 0; j < instanceCount; ++j) 512 { 513 var instanceIndex = instanceOffset + j; 514 InstanceHandle instance = instances[instanceIndex]; 515 drawInstances.Add(new DrawInstance { key = drawKey, instanceIndex = instance.index }); 516 } 517 } 518 } 519 } 520 521 public void Execute() 522 { 523 { 524 for (int i = 0; i < rendererData.rendererGroupID.Length; ++i) 525 ProcessRenderer(i); 526 } 527 } 528 } 529 530 internal class CPUDrawInstanceData 531 { 532 public NativeList<DrawInstance> drawInstances => m_DrawInstances; 533 public NativeParallelHashMap<DrawKey, int> batchHash => m_BatchHash; 534 public NativeList<DrawBatch> drawBatches => m_DrawBatches; 535 public NativeParallelHashMap<RangeKey, int> rangeHash => m_RangeHash; 536 public NativeList<DrawRange> drawRanges => m_DrawRanges; 537 public NativeArray<int> drawBatchIndices => m_DrawBatchIndices.AsArray(); 538 public NativeArray<int> drawInstanceIndices => m_DrawInstanceIndices.AsArray(); 539 540 private NativeParallelHashMap<RangeKey, int> m_RangeHash; // index in m_DrawRanges, hashes by range state 541 private NativeList<DrawRange> m_DrawRanges; 542 private NativeParallelHashMap<DrawKey, int> m_BatchHash; // index in m_DrawBatches, hashed by draw state 543 private NativeList<DrawBatch> m_DrawBatches; 544 private NativeList<DrawInstance> m_DrawInstances; 545 private NativeList<int> m_DrawInstanceIndices; // DOTS instance index, arranged in contiguous blocks in m_DrawBatches order (see DrawBatch.instanceOffset, DrawBatch.instanceCount) 546 private NativeList<int> m_DrawBatchIndices; // index in m_DrawBatches, arranged in contiguous blocks in m_DrawRanges order (see DrawRange.drawOffset, DrawRange.drawCount) 547 548 private bool m_NeedsRebuild; 549 550 public bool valid => m_DrawInstances.IsCreated; 551 552 public void Initialize() 553 { 554 Assert.IsTrue(!valid); 555 m_RangeHash = new NativeParallelHashMap<RangeKey, int>(1024, Allocator.Persistent); 556 m_DrawRanges = new NativeList<DrawRange>(Allocator.Persistent); 557 m_BatchHash = new NativeParallelHashMap<DrawKey, int>(1024, Allocator.Persistent); 558 m_DrawBatches = new NativeList<DrawBatch>(Allocator.Persistent); 559 m_DrawInstances = new NativeList<DrawInstance>(1024, Allocator.Persistent); 560 m_DrawInstanceIndices = new NativeList<int>(1024, Allocator.Persistent); 561 m_DrawBatchIndices = new NativeList<int>(1024, Allocator.Persistent); 562 } 563 564 public void Dispose() 565 { 566 if (m_DrawBatchIndices.IsCreated) 567 m_DrawBatchIndices.Dispose(); 568 569 if (m_DrawInstanceIndices.IsCreated) 570 m_DrawInstanceIndices.Dispose(); 571 572 if (m_DrawInstances.IsCreated) 573 m_DrawInstances.Dispose(); 574 575 if (m_DrawBatches.IsCreated) 576 m_DrawBatches.Dispose(); 577 578 if (m_BatchHash.IsCreated) 579 m_BatchHash.Dispose(); 580 581 if (m_DrawRanges.IsCreated) 582 m_DrawRanges.Dispose(); 583 584 if (m_RangeHash.IsCreated) 585 m_RangeHash.Dispose(); 586 } 587 588 public void RebuildDrawListsIfNeeded() 589 { 590 if (!m_NeedsRebuild) 591 return; 592 593 m_NeedsRebuild = false; 594 595 Assert.IsTrue(m_RangeHash.Count() == m_DrawRanges.Length); 596 Assert.IsTrue(m_BatchHash.Count() == m_DrawBatches.Length); 597 598 m_DrawInstanceIndices.ResizeUninitialized(m_DrawInstances.Length); 599 m_DrawBatchIndices.ResizeUninitialized(m_DrawBatches.Length); 600 601 var internalDrawIndex = new NativeArray<int>(drawBatches.Length * BuildDrawListsJob.k_IntsPerCacheLine, Allocator.TempJob, NativeArrayOptions.ClearMemory); 602 603 var prefixSumDrawInstancesJob = new PrefixSumDrawInstancesJob() 604 { 605 rangeHash = m_RangeHash, 606 drawRanges = m_DrawRanges, 607 drawBatches = m_DrawBatches, 608 drawBatchIndices = m_DrawBatchIndices.AsArray() 609 }; 610 611 var prefixSumJobHandle = prefixSumDrawInstancesJob.Schedule(); 612 613 var buildDrawListsJob = new BuildDrawListsJob() 614 { 615 drawInstances = m_DrawInstances, 616 batchHash = m_BatchHash, 617 drawBatches = m_DrawBatches, 618 internalDrawIndex = internalDrawIndex, 619 drawInstanceIndices = m_DrawInstanceIndices.AsArray(), 620 }; 621 622 buildDrawListsJob.Schedule(m_DrawInstances.Length, BuildDrawListsJob.k_BatchSize, prefixSumJobHandle).Complete(); 623 624 internalDrawIndex.Dispose(); 625 } 626 627 public unsafe void DestroyDrawInstanceIndices(NativeArray<int> drawInstanceIndicesToDestroy) 628 { 629 Profiler.BeginSample("DestroyDrawInstanceIndices.ParallelSort"); 630 drawInstanceIndicesToDestroy.ParallelSort().Complete(); 631 Profiler.EndSample(); 632 633 var removeDrawInstanceIndicesJob = new RemoveDrawInstanceIndicesJob 634 { 635 drawInstanceIndices = drawInstanceIndicesToDestroy, 636 drawInstances = m_DrawInstances, 637 drawBatches = m_DrawBatches, 638 drawRanges = m_DrawRanges, 639 batchHash = m_BatchHash, 640 rangeHash = m_RangeHash 641 }; 642 643 removeDrawInstanceIndicesJob.Run(); 644 } 645 646 public unsafe void DestroyDrawInstances(NativeArray<InstanceHandle> destroyedInstances) 647 { 648 if (m_DrawInstances.IsEmpty || destroyedInstances.Length == 0) 649 return; 650 651 NeedsRebuild(); 652 653 var destroyedInstancesSorted = new NativeArray<InstanceHandle>(destroyedInstances, Allocator.TempJob); 654 Assert.AreEqual(UnsafeUtility.SizeOf<InstanceHandle>(), UnsafeUtility.SizeOf<int>()); 655 656 Profiler.BeginSample("DestroyDrawInstances.ParallelSort"); 657 destroyedInstancesSorted.Reinterpret<int>().ParallelSort().Complete(); 658 Profiler.EndSample(); 659 660 var drawInstanceIndicesToDestroy = new NativeList<int>(m_DrawInstances.Length, Allocator.TempJob); 661 662 var findDrawInstancesJobHandle = new FindDrawInstancesJob() 663 { 664 instancesSorted = destroyedInstancesSorted, 665 drawInstances = m_DrawInstances, 666 outDrawInstanceIndicesWriter = drawInstanceIndicesToDestroy.AsParallelWriter() 667 }; 668 669 findDrawInstancesJobHandle.ScheduleBatch(m_DrawInstances.Length, FindDrawInstancesJob.k_BatchSize).Complete(); 670 671 DestroyDrawInstanceIndices(drawInstanceIndicesToDestroy.AsArray()); 672 673 destroyedInstancesSorted.Dispose(); 674 drawInstanceIndicesToDestroy.Dispose(); 675 } 676 677 public unsafe void DestroyMaterialDrawInstances(NativeArray<uint> destroyedBatchMaterials) 678 { 679 if (m_DrawInstances.IsEmpty || destroyedBatchMaterials.Length == 0) 680 return; 681 682 NeedsRebuild(); 683 684 var destroyedBatchMaterialsSorted = new NativeArray<uint>(destroyedBatchMaterials, Allocator.TempJob); 685 686 Profiler.BeginSample("DestroyedBatchMaterials.ParallelSort"); 687 destroyedBatchMaterialsSorted.Reinterpret<int>().ParallelSort().Complete(); 688 Profiler.EndSample(); 689 690 var drawInstanceIndicesToDestroy = new NativeList<int>(m_DrawInstances.Length, Allocator.TempJob); 691 692 var findDrawInstancesJobHandle = new FindMaterialDrawInstancesJob() 693 { 694 materialsSorted = destroyedBatchMaterialsSorted, 695 drawInstances = m_DrawInstances, 696 outDrawInstanceIndicesWriter = drawInstanceIndicesToDestroy.AsParallelWriter() 697 }; 698 699 findDrawInstancesJobHandle.ScheduleBatch(m_DrawInstances.Length, FindMaterialDrawInstancesJob.k_BatchSize).Complete(); 700 701 DestroyDrawInstanceIndices(drawInstanceIndicesToDestroy.AsArray()); 702 703 destroyedBatchMaterialsSorted.Dispose(); 704 drawInstanceIndicesToDestroy.Dispose(); 705 } 706 707 public void NeedsRebuild() 708 { 709 m_NeedsRebuild = true; 710 } 711 } 712 713 internal class InstanceCullingBatcher : IDisposable 714 { 715 private RenderersBatchersContext m_BatchersContext; 716 private CPUDrawInstanceData m_DrawInstanceData; 717 private BatchRendererGroup m_BRG; 718 private NativeParallelHashMap<uint, BatchID> m_GlobalBatchIDs; 719 private InstanceCuller m_Culler; 720 private NativeParallelHashMap<int, BatchMaterialID> m_BatchMaterialHash; 721 private NativeParallelHashMap<int, BatchMeshID> m_BatchMeshHash; 722 723 private int m_CachedInstanceDataBufferLayoutVersion; 724 725 private OnCullingCompleteCallback m_OnCompleteCallback; 726 727 public NativeParallelHashMap<int, BatchMaterialID> batchMaterialHash => m_BatchMaterialHash; 728 729 public InstanceCullingBatcher(RenderersBatchersContext batcherContext, InstanceCullingBatcherDesc desc, BatchRendererGroup.OnFinishedCulling onFinishedCulling) 730 { 731 m_BatchersContext = batcherContext; 732 m_DrawInstanceData = new CPUDrawInstanceData(); 733 m_DrawInstanceData.Initialize(); 734 735 m_BRG = new BatchRendererGroup(new BatchRendererGroupCreateInfo() 736 { 737 cullingCallback = OnPerformCulling, 738 finishedCullingCallback = onFinishedCulling, 739 userContext = IntPtr.Zero 740 }); 741 742#if UNITY_EDITOR 743 if (desc.brgPicking != null) 744 { 745 var mat = new Material(desc.brgPicking); 746 mat.hideFlags = HideFlags.HideAndDontSave; 747 m_BRG.SetPickingMaterial(mat); 748 } 749 if (desc.brgLoading != null) 750 { 751 var mat = new Material(desc.brgLoading); 752 mat.hideFlags = HideFlags.HideAndDontSave; 753 m_BRG.SetLoadingMaterial(mat); 754 } 755 if (desc.brgError != null) 756 { 757 var mat = new Material(desc.brgError); 758 mat.hideFlags = HideFlags.HideAndDontSave; 759 m_BRG.SetErrorMaterial(mat); 760 } 761 var viewTypes = new BatchCullingViewType[] { 762 BatchCullingViewType.Light, 763 BatchCullingViewType.Camera, 764 BatchCullingViewType.Picking, 765 BatchCullingViewType.SelectionOutline, 766 BatchCullingViewType.Filtering 767 }; 768 m_BRG.SetEnabledViewTypes(viewTypes); 769#endif 770 771 m_Culler = new InstanceCuller(); 772 m_Culler.Init(batcherContext.resources, batcherContext.debugStats); 773 774 m_CachedInstanceDataBufferLayoutVersion = -1; 775 m_OnCompleteCallback = desc.onCompleteCallback; 776 m_BatchMaterialHash = new NativeParallelHashMap<int, BatchMaterialID>(64, Allocator.Persistent); 777 m_BatchMeshHash = new NativeParallelHashMap<int, BatchMeshID>(64, Allocator.Persistent); 778 779 m_GlobalBatchIDs = new NativeParallelHashMap<uint, BatchID>(6, Allocator.Persistent); 780 m_GlobalBatchIDs.Add((uint)InstanceComponentGroup.Default, GetBatchID(InstanceComponentGroup.Default)); 781 m_GlobalBatchIDs.Add((uint)InstanceComponentGroup.DefaultWind, GetBatchID(InstanceComponentGroup.DefaultWind)); 782 m_GlobalBatchIDs.Add((uint)InstanceComponentGroup.DefaultLightProbe, GetBatchID(InstanceComponentGroup.DefaultLightProbe)); 783 m_GlobalBatchIDs.Add((uint)InstanceComponentGroup.DefaultLightmap, GetBatchID(InstanceComponentGroup.DefaultLightmap)); 784 m_GlobalBatchIDs.Add((uint)InstanceComponentGroup.DefaultWindLightProbe, GetBatchID(InstanceComponentGroup.DefaultWindLightProbe)); 785 m_GlobalBatchIDs.Add((uint)InstanceComponentGroup.DefaultWindLightmap, GetBatchID(InstanceComponentGroup.DefaultWindLightmap)); 786 } 787 788 internal ref InstanceCuller culler => ref m_Culler; 789 790 public void Dispose() 791 { 792 m_OnCompleteCallback = null; 793 m_Culler.Dispose(); 794 795 foreach (var batchID in m_GlobalBatchIDs) 796 { 797 if (!batchID.Value.Equals(BatchID.Null)) 798 m_BRG.RemoveBatch(batchID.Value); 799 } 800 m_GlobalBatchIDs.Dispose(); 801 802 if (m_BRG != null) 803 m_BRG.Dispose(); 804 805 m_DrawInstanceData.Dispose(); 806 m_DrawInstanceData = null; 807 808 m_BatchMaterialHash.Dispose(); 809 m_BatchMeshHash.Dispose(); 810 } 811 812 private BatchID GetBatchID(InstanceComponentGroup componentsOverriden) 813 { 814 if (m_CachedInstanceDataBufferLayoutVersion != m_BatchersContext.instanceDataBufferLayoutVersion) 815 return BatchID.Null; 816 817 Assert.IsTrue(m_BatchersContext.defaultDescriptions.Length == m_BatchersContext.defaultMetadata.Length); 818 819 const uint kClearIsOverriddenBit = 0x4FFFFFFF; 820 var tempMetadata = new NativeList<MetadataValue>(m_BatchersContext.defaultMetadata.Length, Allocator.Temp); 821 822 for(int i = 0; i < m_BatchersContext.defaultDescriptions.Length; ++i) 823 { 824 var componentGroup = m_BatchersContext.defaultDescriptions[i].componentGroup; 825 var metadata = m_BatchersContext.defaultMetadata[i]; 826 var value = metadata.Value; 827 828 // if instances in this batch do not override the component, clear the override bit 829 if ((componentsOverriden & componentGroup) == 0) 830 value &= kClearIsOverriddenBit; 831 832 tempMetadata.Add(new MetadataValue 833 { 834 NameID = metadata.NameID, 835 Value = value 836 }); 837 } 838 839 return m_BRG.AddBatch(tempMetadata.AsArray(), m_BatchersContext.gpuInstanceDataBuffer.bufferHandle); 840 } 841 842 private void UpdateInstanceDataBufferLayoutVersion() 843 { 844 if (m_CachedInstanceDataBufferLayoutVersion != m_BatchersContext.instanceDataBufferLayoutVersion) 845 { 846 m_CachedInstanceDataBufferLayoutVersion = m_BatchersContext.instanceDataBufferLayoutVersion; 847 848 foreach (var componentsToBatchID in m_GlobalBatchIDs) 849 { 850 var batchID = componentsToBatchID.Value; 851 if (!batchID.Equals(BatchID.Null)) 852 m_BRG.RemoveBatch(batchID); 853 854 var componentsOverriden = (InstanceComponentGroup)componentsToBatchID.Key; 855 componentsToBatchID.Value = GetBatchID(componentsOverriden); 856 } 857 } 858 } 859 860 public CPUDrawInstanceData GetDrawInstanceData() 861 { 862 return m_DrawInstanceData; 863 } 864 865 public unsafe JobHandle OnPerformCulling( 866 BatchRendererGroup rendererGroup, 867 BatchCullingContext cc, 868 BatchCullingOutput cullingOutput, 869 IntPtr userContext) 870 { 871 foreach (var batchID in m_GlobalBatchIDs) 872 { 873 if (batchID.Value.Equals(BatchID.Null)) 874 return new JobHandle(); 875 } 876 877 m_DrawInstanceData.RebuildDrawListsIfNeeded(); 878 879 bool allowOcclusionCulling = m_BatchersContext.hasBoundingSpheres; 880 JobHandle jobHandle = m_Culler.CreateCullJobTree( 881 cc, 882 cullingOutput, 883 m_BatchersContext.instanceData, 884 m_BatchersContext.sharedInstanceData, 885 m_BatchersContext.instanceDataBuffer, 886 m_BatchersContext.lodGroupCullingData, 887 m_DrawInstanceData, 888 m_GlobalBatchIDs, 889 m_BatchersContext.crossfadedRendererCount, 890 m_BatchersContext.smallMeshScreenPercentage, 891 allowOcclusionCulling ? m_BatchersContext.occlusionCullingCommon : null); 892 893 if (m_OnCompleteCallback != null) 894 m_OnCompleteCallback(jobHandle, cc, cullingOutput); 895 896 return jobHandle; 897 } 898 899 public void OnFinishedCulling(IntPtr customCullingResult) 900 { 901 int viewInstanceID = (int)customCullingResult; 902 m_Culler.EnsureValidOcclusionTestResults(viewInstanceID); 903 } 904 905 public void DestroyInstances(NativeArray<InstanceHandle> instances) 906 { 907 if (instances.Length == 0) 908 return; 909 910 Profiler.BeginSample("DestroyInstances"); 911 912 m_DrawInstanceData.DestroyDrawInstances(instances); 913 914 Profiler.EndSample(); 915 } 916 917 public void DestroyMaterials(NativeArray<int> destroyedMaterials) 918 { 919 if (destroyedMaterials.Length == 0) 920 return; 921 922 Profiler.BeginSample("DestroyMaterials"); 923 924 var destroyedBatchMaterials = new NativeList<uint>(destroyedMaterials.Length, Allocator.TempJob); 925 926 foreach (int destroyedMaterial in destroyedMaterials) 927 { 928 if (m_BatchMaterialHash.TryGetValue(destroyedMaterial, out var destroyedBatchMaterial)) 929 { 930 destroyedBatchMaterials.Add(destroyedBatchMaterial.value); 931 m_BatchMaterialHash.Remove(destroyedMaterial); 932 m_BRG.UnregisterMaterial(destroyedBatchMaterial); 933 } 934 } 935 936 m_DrawInstanceData.DestroyMaterialDrawInstances(destroyedBatchMaterials.AsArray()); 937 938 destroyedBatchMaterials.Dispose(); 939 940 Profiler.EndSample(); 941 } 942 943 public void DestroyMeshes(NativeArray<int> destroyedMeshes) 944 { 945 if (destroyedMeshes.Length == 0) 946 return; 947 948 Profiler.BeginSample("DestroyMeshes"); 949 950 foreach (int destroyedMesh in destroyedMeshes) 951 { 952 if (m_BatchMeshHash.TryGetValue(destroyedMesh, out var destroyedBatchMesh)) 953 { 954 m_BatchMeshHash.Remove(destroyedMesh); 955 m_BRG.UnregisterMesh(destroyedBatchMesh); 956 } 957 } 958 959 Profiler.EndSample(); 960 } 961 962 public void PostCullBeginCameraRendering(RenderRequestBatcherContext context) 963 { 964 } 965 966 private void RegisterBatchMeshes(NativeArray<int> meshIDs) 967 { 968 var newMeshIDs = new NativeList<int>(meshIDs.Length, Allocator.TempJob); 969 new FindNonRegisteredInstancesJob<BatchMeshID> 970 { 971 instanceIDs = meshIDs, 972 hashMap = m_BatchMeshHash, 973 outInstancesWriter = newMeshIDs.AsParallelWriter() 974 } 975 .ScheduleBatch(meshIDs.Length, FindNonRegisteredInstancesJob<BatchMeshID>.k_BatchSize).Complete(); 976 var newBatchMeshIDs = new NativeArray<BatchMeshID>(newMeshIDs.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); 977 m_BRG.RegisterMeshes(newMeshIDs.AsArray(), newBatchMeshIDs); 978 979 int totalMeshesNum = m_BatchMeshHash.Count() + newBatchMeshIDs.Length; 980 m_BatchMeshHash.Capacity = Math.Max(m_BatchMeshHash.Capacity, Mathf.CeilToInt(totalMeshesNum / 1023.0f) * 1024); 981 982 new RegisterNewInstancesJob<BatchMeshID> 983 { 984 instanceIDs = newMeshIDs.AsArray(), 985 batchIDs = newBatchMeshIDs, 986 hashMap = m_BatchMeshHash.AsParallelWriter() 987 } 988 .Schedule(newMeshIDs.Length, RegisterNewInstancesJob<BatchMeshID>.k_BatchSize).Complete(); 989 990 newMeshIDs.Dispose(); 991 newBatchMeshIDs.Dispose(); 992 } 993 994 private void RegisterBatchMaterials(in NativeArray<int> usedMaterialIDs) 995 { 996 var newMaterialIDs = new NativeList<int>(usedMaterialIDs.Length, Allocator.TempJob); 997 new FindNonRegisteredInstancesJob<BatchMaterialID> 998 { 999 instanceIDs = usedMaterialIDs, 1000 hashMap = m_BatchMaterialHash, 1001 outInstancesWriter = newMaterialIDs.AsParallelWriter() 1002 } 1003 .ScheduleBatch(usedMaterialIDs.Length, FindNonRegisteredInstancesJob<BatchMaterialID>.k_BatchSize).Complete(); 1004 1005 var newBatchMaterialIDs = new NativeArray<BatchMaterialID>(newMaterialIDs.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); 1006 m_BRG.RegisterMaterials(newMaterialIDs.AsArray(), newBatchMaterialIDs); 1007 1008 int totalMaterialsNum = m_BatchMaterialHash.Count() + newMaterialIDs.Length; 1009 m_BatchMaterialHash.Capacity = Math.Max(m_BatchMaterialHash.Capacity, Mathf.CeilToInt(totalMaterialsNum / 1023.0f) * 1024); 1010 1011 new RegisterNewInstancesJob<BatchMaterialID> 1012 { 1013 instanceIDs = newMaterialIDs.AsArray(), 1014 batchIDs = newBatchMaterialIDs, 1015 hashMap = m_BatchMaterialHash.AsParallelWriter() 1016 } 1017 .Schedule(newMaterialIDs.Length, RegisterNewInstancesJob<BatchMaterialID>.k_BatchSize).Complete(); 1018 1019 newMaterialIDs.Dispose(); 1020 newBatchMaterialIDs.Dispose(); 1021 } 1022 1023 public void BuildBatch( 1024 NativeArray<InstanceHandle> instances, 1025 NativeArray<int> usedMaterialIDs, 1026 NativeArray<int> usedMeshIDs, 1027 in GPUDrivenRendererGroupData rendererData) 1028 { 1029 RegisterBatchMaterials(usedMaterialIDs); 1030 RegisterBatchMeshes(usedMeshIDs); 1031 1032 new CreateDrawBatchesJob 1033 { 1034 implicitInstanceIndices = rendererData.instancesCount.Length == 0, 1035 instances = instances, 1036 rendererData = rendererData, 1037 batchMeshHash = m_BatchMeshHash, 1038 batchMaterialHash = m_BatchMaterialHash, 1039 rangeHash = m_DrawInstanceData.rangeHash, 1040 drawRanges = m_DrawInstanceData.drawRanges, 1041 batchHash = m_DrawInstanceData.batchHash, 1042 drawBatches = m_DrawInstanceData.drawBatches, 1043 drawInstances = m_DrawInstanceData.drawInstances 1044 }.Run(); 1045 1046 m_DrawInstanceData.NeedsRebuild(); 1047 UpdateInstanceDataBufferLayoutVersion(); 1048 } 1049 1050 public void InstanceOccludersUpdated(int viewInstanceID, int subviewMask) 1051 { 1052 m_Culler.InstanceOccludersUpdated(viewInstanceID, subviewMask, m_BatchersContext); 1053 } 1054 1055 public void UpdateFrame() 1056 { 1057 m_Culler.UpdateFrame(); 1058 } 1059 1060 public ParallelBitArray GetCompactedVisibilityMasks(bool syncCullingJobs) 1061 { 1062 return m_Culler.GetCompactedVisibilityMasks(syncCullingJobs); 1063 } 1064 1065 public void OnEndContextRendering() 1066 { 1067 ParallelBitArray compactedVisibilityMasks = GetCompactedVisibilityMasks(syncCullingJobs: true); 1068 1069 if(compactedVisibilityMasks.IsCreated) 1070 m_BatchersContext.UpdatePerFrameInstanceVisibility(compactedVisibilityMasks); 1071 } 1072 1073 public void OnBeginCameraRendering(Camera camera) 1074 { 1075 m_Culler.OnBeginCameraRendering(camera); 1076 } 1077 1078 public void OnEndCameraRendering(Camera camera) 1079 { 1080 m_Culler.OnEndCameraRendering(camera); 1081 } 1082 } 1083}