A game about forced loneliness, made by TACStudios
at master 982 lines 46 kB view raw
1using System; 2using System.Collections.Generic; 3using System.Threading; 4using Unity.Burst; 5using Unity.Collections; 6using Unity.Collections.LowLevel.Unsafe; 7using Unity.Jobs; 8using Unity.Mathematics; 9using Unity.Profiling; 10using UnityEngine.Assertions; 11using UnityEngine.Profiling; 12 13namespace UnityEngine.Rendering 14{ 15 internal partial class InstanceDataSystem : IDisposable 16 { 17 private unsafe static int AtomicAddLengthNoResize<T>(in NativeList<T> list, int count) where T : unmanaged 18 { 19 UnsafeList<T>* unsafeList = list.GetUnsafeList(); 20 var newLength = Interlocked.Add(ref unsafeList->m_length, count); 21 Assert.IsTrue(unsafeList->Capacity >= newLength); 22 return newLength - count; 23 } 24 25 [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] 26 private unsafe struct QueryRendererGroupInstancesCountJob : IJobParallelForBatch 27 { 28 public const int k_BatchSize = 128; 29 30 [ReadOnly] public CPUInstanceData instanceData; 31 [ReadOnly] public CPUSharedInstanceData sharedInstanceData; 32 [ReadOnly] public NativeParallelMultiHashMap<int, InstanceHandle> rendererGroupInstanceMultiHash; 33 [NativeDisableContainerSafetyRestriction, NoAlias][ReadOnly] public NativeArray<int> rendererGroupIDs; 34 35 [NativeDisableContainerSafetyRestriction, NoAlias][WriteOnly] public NativeArray<int> instancesCount; 36 37 public void Execute(int startIndex, int count) 38 { 39 for (int i = startIndex; i < startIndex + count; ++i) 40 { 41 var rendererGroupID = rendererGroupIDs[i]; 42 43 if (rendererGroupInstanceMultiHash.TryGetFirstValue(rendererGroupID, out var instance, out var it)) 44 { 45 var sharedInstance = instanceData.Get_SharedInstance(instance); 46 var refCount = sharedInstanceData.Get_RefCount(sharedInstance); 47 instancesCount[i] = refCount; 48 } 49 else 50 { 51 instancesCount[i] = 0; 52 } 53 } 54 } 55 } 56 57 [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] 58 private unsafe struct ComputeInstancesOffsetAndResizeInstancesArrayJob : IJob 59 { 60 [ReadOnly] public NativeArray<int> instancesCount; 61 [WriteOnly] public NativeArray<int> instancesOffset; 62 public NativeList<InstanceHandle> instances; 63 64 public void Execute() 65 { 66 int totalInstancesCount = 0; 67 68 for (int i = 0; i < instancesCount.Length; ++i) 69 { 70 instancesOffset[i] = totalInstancesCount; 71 totalInstancesCount += instancesCount[i]; 72 } 73 74 instances.ResizeUninitialized(totalInstancesCount); 75 } 76 } 77 78 [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] 79 private unsafe struct QueryRendererGroupInstancesJob : IJobParallelForBatch 80 { 81 public const int k_BatchSize = 128; 82 83 [ReadOnly] public NativeParallelMultiHashMap<int, InstanceHandle> rendererGroupInstanceMultiHash; 84 [NativeDisableContainerSafetyRestriction, NoAlias][ReadOnly] public NativeArray<int> rendererGroupIDs; 85 86 [NativeDisableContainerSafetyRestriction, NoAlias][WriteOnly] public NativeArray<InstanceHandle> instances; 87 [NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 atomicNonFoundInstancesCount; 88 89 public void Execute(int startIndex, int count) 90 { 91 int newInstancesCountJob = 0; 92 93 for (int i = startIndex; i < startIndex + count; ++i) 94 { 95 if (rendererGroupInstanceMultiHash.TryGetFirstValue(rendererGroupIDs[i], out var instance, out var it)) 96 { 97 instances[i] = instance; 98 } 99 else 100 { 101 newInstancesCountJob += 1; 102 instances[i] = InstanceHandle.Invalid; 103 } 104 } 105 106 if (atomicNonFoundInstancesCount.Counter != null && newInstancesCountJob > 0) 107 atomicNonFoundInstancesCount.Add(newInstancesCountJob); 108 } 109 } 110 111 [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] 112 private unsafe struct QueryRendererGroupInstancesMultiJob : IJobParallelForBatch 113 { 114 public const int k_BatchSize = 128; 115 116 [ReadOnly] public NativeParallelMultiHashMap<int, InstanceHandle> rendererGroupInstanceMultiHash; 117 [NativeDisableContainerSafetyRestriction, NoAlias][ReadOnly] public NativeArray<int> rendererGroupIDs; 118 [NativeDisableContainerSafetyRestriction, NoAlias][ReadOnly] public NativeArray<int> instancesOffsets; 119 [NativeDisableContainerSafetyRestriction, NoAlias][ReadOnly] public NativeArray<int> instancesCounts; 120 121 [NativeDisableContainerSafetyRestriction, NoAlias][WriteOnly] public NativeArray<InstanceHandle> instances; 122 [NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 atomicNonFoundSharedInstancesCount; 123 [NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 atomicNonFoundInstancesCount; 124 125 public void Execute(int startIndex, int count) 126 { 127 int newSharedInstancesCountJob = 0; 128 int newInstancesCountJob = 0; 129 130 for (int i = startIndex; i < startIndex + count; ++i) 131 { 132 var rendererGroupID = rendererGroupIDs[i]; 133 int instancesOffset = instancesOffsets[i]; 134 int instancesCount = instancesCounts[i]; 135 136 bool success = rendererGroupInstanceMultiHash.TryGetFirstValue(rendererGroupID, out var storedInstance, out var it); 137 138 if (!success) 139 newSharedInstancesCountJob += 1; 140 141 for (int j = 0; j < instancesCount; ++j) 142 { 143 int index = instancesOffset + j; 144 145 if (success) 146 { 147 instances[index] = storedInstance; 148 success = rendererGroupInstanceMultiHash.TryGetNextValue(out storedInstance, ref it); 149 } 150 else 151 { 152 newInstancesCountJob += 1; 153 instances[index] = InstanceHandle.Invalid; 154 } 155 } 156 } 157 158 if (atomicNonFoundSharedInstancesCount.Counter != null && newSharedInstancesCountJob > 0) 159 atomicNonFoundSharedInstancesCount.Add(newSharedInstancesCountJob); 160 161 if (atomicNonFoundInstancesCount.Counter != null && newInstancesCountJob > 0) 162 atomicNonFoundInstancesCount.Add(newInstancesCountJob); 163 } 164 } 165 166 [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] 167 private struct QuerySortedMeshInstancesJob : IJobParallelForBatch 168 { 169 public const int k_BatchSize = 64; 170 171 [ReadOnly] public CPUInstanceData instanceData; 172 [ReadOnly] public CPUSharedInstanceData sharedInstanceData; 173 [ReadOnly] public NativeArray<int> sortedMeshID; 174 175 [NativeDisableParallelForRestriction][WriteOnly] public NativeList<InstanceHandle> instances; 176 177 public void Execute(int startIndex, int count) 178 { 179 ulong validBits = 0; 180 181 for (int i = 0; i < count; ++i) 182 { 183 int instanceIndex = startIndex + i; 184 InstanceHandle instance = instanceData.instances[instanceIndex]; 185 Assert.IsTrue(instanceData.IsValidInstance(instance)); 186 SharedInstanceHandle sharedInstance = instanceData.sharedInstances[instanceIndex]; 187 var meshID = sharedInstanceData.Get_MeshID(sharedInstance); 188 189 if (sortedMeshID.BinarySearch(meshID) >= 0) 190 validBits |= 1ul << i; 191 } 192 193 int validBitCount = math.countbits(validBits); 194 195 if (validBitCount > 0) 196 { 197 int writeIndex = AtomicAddLengthNoResize(instances, validBitCount); 198 int validBitIndex = math.tzcnt(validBits); 199 200 while (validBits != 0) 201 { 202 int instanceIndex = startIndex + validBitIndex; 203 instances[writeIndex] = instanceData.instances[instanceIndex]; 204 205 writeIndex += 1; 206 validBits &= ~(1ul << validBitIndex); 207 validBitIndex = math.tzcnt(validBits); 208 } 209 } 210 } 211 } 212 213 [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] 214 private struct CalculateInterpolatedLightAndOcclusionProbesBatchJob : IJobParallelFor 215 { 216 public const int k_BatchSize = 1; 217 public const int k_CalculatedProbesPerBatch = 8; 218 219 [ReadOnly] public int probesCount; 220 [ReadOnly] public LightProbesQuery lightProbesQuery; 221 222 [NativeDisableParallelForRestriction][ReadOnly] public NativeArray<Vector3> queryPostitions; 223 [NativeDisableParallelForRestriction] public NativeArray<int> compactTetrahedronCache; 224 [NativeDisableParallelForRestriction][WriteOnly] public NativeArray<SphericalHarmonicsL2> probesSphericalHarmonics; 225 [NativeDisableParallelForRestriction][WriteOnly] public NativeArray<Vector4> probesOcclusion; 226 227 public void Execute(int index) 228 { 229 var startIndex = index * k_CalculatedProbesPerBatch; 230 var endIndex = math.min(probesCount, startIndex + k_CalculatedProbesPerBatch); 231 var count = endIndex - startIndex; 232 233 var compactTetrahedronCacheSubArray = compactTetrahedronCache.GetSubArray(startIndex, count); 234 var queryPostitionsSubArray = queryPostitions.GetSubArray(startIndex, count); 235 var probesSphericalHarmonicsSubArray = probesSphericalHarmonics.GetSubArray(startIndex, count); 236 var probesOcclusionSubArray = probesOcclusion.GetSubArray(startIndex, count); 237 lightProbesQuery.CalculateInterpolatedLightAndOcclusionProbes(queryPostitionsSubArray, compactTetrahedronCacheSubArray, probesSphericalHarmonicsSubArray, probesOcclusionSubArray); 238 } 239 } 240 241 [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] 242 private struct ScatterTetrahedronCacheIndicesJob : IJobParallelFor 243 { 244 public const int k_BatchSize = 128; 245 246 [ReadOnly] public NativeArray<InstanceHandle> probeInstances; 247 [ReadOnly] public NativeArray<int> compactTetrahedronCache; 248 249 [NativeDisableContainerSafetyRestriction, NoAlias][NativeDisableParallelForRestriction] public CPUInstanceData instanceData; 250 251 public void Execute(int index) 252 { 253 InstanceHandle instance = probeInstances[index]; 254 instanceData.Set_TetrahedronCacheIndex(instance, compactTetrahedronCache[index]); 255 } 256 } 257 258 [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] 259 private unsafe struct TransformUpdateJob : IJobParallelForBatch 260 { 261 public const int k_BatchSize = 64; 262 263 [ReadOnly] public bool initialize; 264 [ReadOnly] public bool enableBoundingSpheres; 265 [ReadOnly] public NativeArray<InstanceHandle> instances; 266 [ReadOnly] public NativeArray<Matrix4x4> localToWorldMatrices; 267 [ReadOnly] public NativeArray<Matrix4x4> prevLocalToWorldMatrices; 268 269 [NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 atomicTransformQueueCount; 270 271 [NativeDisableParallelForRestriction] public CPUSharedInstanceData sharedInstanceData; 272 [NativeDisableParallelForRestriction] public CPUInstanceData instanceData; 273 274 [NativeDisableParallelForRestriction] public NativeArray<InstanceHandle> transformUpdateInstanceQueue; 275 [NativeDisableParallelForRestriction] public NativeArray<TransformUpdatePacket> transformUpdateDataQueue; 276 [NativeDisableParallelForRestriction] public NativeArray<float4> boundingSpheresDataQueue; 277 278 public void Execute(int startIndex, int count) 279 { 280 ulong validBits = 0; 281 282 for (int i = 0; i < count; ++i) 283 { 284 InstanceHandle instance = instances[startIndex + i]; 285 286 if (!instance.valid) 287 continue; 288 289 if (!initialize) 290 { 291 int instanceIndex = instanceData.InstanceToIndex(instance); 292 int sharedInstanceIndex = sharedInstanceData.InstanceToIndex(instanceData, instance); 293 294 TransformUpdateFlags flags = sharedInstanceData.flags[sharedInstanceIndex].transformUpdateFlags; 295 bool movedCurrentFrame = instanceData.movedInCurrentFrameBits.Get(instanceIndex); 296 bool isStaticObject = (flags & TransformUpdateFlags.IsPartOfStaticBatch) != 0; 297 298 if (isStaticObject || movedCurrentFrame) 299 continue; 300 } 301 302 validBits |= 1ul << i; 303 } 304 305 int validBitCount = math.countbits(validBits); 306 307 if (validBitCount > 0) 308 { 309 int writeIndex = atomicTransformQueueCount.Add(validBitCount); 310 int validBitIndex = math.tzcnt(validBits); 311 312 while (validBits != 0) 313 { 314 int index = startIndex + validBitIndex; 315 InstanceHandle instance = instances[index]; 316 int instanceIndex = instanceData.InstanceToIndex(instance); 317 int sharedInstanceIndex = sharedInstanceData.InstanceToIndex(instanceData, instance); 318 319 TransformUpdateFlags flags = sharedInstanceData.flags[sharedInstanceIndex].transformUpdateFlags; 320 bool isStaticObject = (flags & TransformUpdateFlags.IsPartOfStaticBatch) != 0; 321 322 instanceData.movedInCurrentFrameBits.Set(instanceIndex, !isStaticObject); 323 transformUpdateInstanceQueue[writeIndex] = instance; 324 325 ref float4x4 l2w = ref UnsafeUtility.ArrayElementAsRef<float4x4>(localToWorldMatrices.GetUnsafeReadOnlyPtr(), index); 326 ref AABB localAABB = ref UnsafeUtility.ArrayElementAsRef<AABB>(sharedInstanceData.localAABBs.GetUnsafePtr(), sharedInstanceIndex); 327 AABB worldAABB = AABB.Transform(l2w, localAABB); 328 instanceData.worldAABBs[instanceIndex] = worldAABB; 329 330 if (initialize) 331 { 332 PackedMatrix l2wPacked = PackedMatrix.FromFloat4x4(l2w); 333 PackedMatrix l2wPrevPacked = PackedMatrix.FromMatrix4x4(prevLocalToWorldMatrices[index]); 334 335 transformUpdateDataQueue[writeIndex * 2] = new TransformUpdatePacket() 336 { 337 localToWorld0 = l2wPacked.packed0, 338 localToWorld1 = l2wPacked.packed1, 339 localToWorld2 = l2wPacked.packed2, 340 }; 341 transformUpdateDataQueue[writeIndex * 2 + 1] = new TransformUpdatePacket() 342 { 343 localToWorld0 = l2wPrevPacked.packed0, 344 localToWorld1 = l2wPrevPacked.packed1, 345 localToWorld2 = l2wPrevPacked.packed2, 346 }; 347 348 // no need to set instanceData.localToWorldMatrices or instanceData.localToWorldIsFlippedBits 349 // they have been set up already by UpdateRendererInstancesJob 350 } 351 else 352 { 353 PackedMatrix l2wPacked = PackedMatrix.FromMatrix4x4(l2w); 354 355 transformUpdateDataQueue[writeIndex] = new TransformUpdatePacket() 356 { 357 localToWorld0 = l2wPacked.packed0, 358 localToWorld1 = l2wPacked.packed1, 359 localToWorld2 = l2wPacked.packed2, 360 }; 361 362 float det = math.determinant((float3x3)l2w); 363 instanceData.localToWorldIsFlippedBits.Set(instanceIndex, det < 0.0f); 364 } 365 366 if (enableBoundingSpheres) 367 boundingSpheresDataQueue[writeIndex] = new float4(worldAABB.center.x, worldAABB.center.y, worldAABB.center.z, math.distance(worldAABB.max, worldAABB.min) * 0.5f); 368 369 writeIndex += 1; 370 validBits &= ~(1ul << validBitIndex); 371 validBitIndex = math.tzcnt(validBits); 372 } 373 } 374 } 375 } 376 377 [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] 378 private unsafe struct ProbesUpdateJob : IJobParallelForBatch 379 { 380 public const int k_BatchSize = 64; 381 382 [ReadOnly] public bool initialize; 383 [NativeDisableContainerSafetyRestriction, NoAlias][ReadOnly] public NativeArray<InstanceHandle> instances; 384 [NativeDisableParallelForRestriction][NativeDisableContainerSafetyRestriction, NoAlias] public CPUInstanceData instanceData; 385 [ReadOnly] public CPUSharedInstanceData sharedInstanceData; 386 387 [NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 atomicProbesQueueCount; 388 389 [NativeDisableParallelForRestriction] public NativeArray<InstanceHandle> probeInstanceQueue; 390 [NativeDisableParallelForRestriction] public NativeArray<int> compactTetrahedronCache; 391 [NativeDisableParallelForRestriction] public NativeArray<Vector3> probeQueryPosition; 392 393 public void Execute(int startIndex, int count) 394 { 395 ulong validBits = 0; 396 397 for (int i = 0; i < count; ++i) 398 { 399 InstanceHandle instance = instances[startIndex + i]; 400 401 if (!instance.valid) 402 continue; 403 404 int sharedInstanceIndex = sharedInstanceData.InstanceToIndex(instanceData, instance); 405 TransformUpdateFlags flags = sharedInstanceData.flags[sharedInstanceIndex].transformUpdateFlags; 406 bool isStaticObject = (flags & TransformUpdateFlags.IsPartOfStaticBatch) != 0; 407 408 if (!initialize && isStaticObject) 409 continue; 410 411 bool hasLightProbe = (flags & TransformUpdateFlags.HasLightProbeCombined) != 0; 412 413 if (!hasLightProbe) 414 continue; 415 416 validBits |= 1ul << i; 417 } 418 419 int validBitCount = math.countbits(validBits); 420 421 if (validBitCount > 0) 422 { 423 int writeIndex = atomicProbesQueueCount.Add(validBitCount); 424 int validBitIndex = math.tzcnt(validBits); 425 426 while (validBits != 0) 427 { 428 InstanceHandle instance = instances[startIndex + validBitIndex]; 429 int instanceIndex = instanceData.InstanceToIndex(instance); 430 ref AABB worldAABB = ref UnsafeUtility.ArrayElementAsRef<AABB>(instanceData.worldAABBs.GetUnsafePtr(), instanceIndex); 431 432 probeInstanceQueue[writeIndex] = instance; 433 probeQueryPosition[writeIndex] = worldAABB.center; 434 compactTetrahedronCache[writeIndex] = instanceData.tetrahedronCacheIndices[instanceIndex]; 435 436 writeIndex += 1; 437 validBits &= ~(1ul << validBitIndex); 438 validBitIndex = math.tzcnt(validBits); 439 } 440 } 441 } 442 } 443 444 [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] 445 private struct MotionUpdateJob : IJobParallelFor 446 { 447 public const int k_BatchSize = 16; 448 449 [ReadOnly] public int queueWriteBase; 450 451 [NativeDisableParallelForRestriction] public CPUInstanceData instanceData; 452 [NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 atomicUpdateQueueCount; 453 [NativeDisableParallelForRestriction][WriteOnly] public NativeArray<InstanceHandle> transformUpdateInstanceQueue; 454 455 public void Execute(int chunk_index) 456 { 457 int maxChunkBitCount = math.min(instanceData.instancesLength - 64 * chunk_index, 64); 458 ulong chunkBitMask = ~0ul >> (64 - maxChunkBitCount); 459 460 ulong currentChunkBits = instanceData.movedInCurrentFrameBits.GetChunk(chunk_index) & chunkBitMask; 461 ulong prevChunkBits = instanceData.movedInPreviousFrameBits.GetChunk(chunk_index) & chunkBitMask; 462 463 // update state in memory for the next frame 464 instanceData.movedInCurrentFrameBits.SetChunk(chunk_index, 0); 465 instanceData.movedInPreviousFrameBits.SetChunk(chunk_index, currentChunkBits); 466 467 // ensure that objects that were moved last frame update their previous world matrix, if not already fully updated 468 ulong remainingChunkBits = prevChunkBits & ~currentChunkBits; 469 470 // allocate space for all the writes from this chunk 471 int chunkBitCount = math.countbits(remainingChunkBits); 472 int writeIndex = queueWriteBase; 473 if (chunkBitCount > 0) 474 writeIndex += atomicUpdateQueueCount.Add(chunkBitCount); 475 476 // loop over set bits to do the writes 477 int indexInChunk = math.tzcnt(remainingChunkBits); 478 479 while (indexInChunk < 64) 480 { 481 int instanceIndex = 64 * chunk_index + indexInChunk; 482 transformUpdateInstanceQueue[writeIndex] = instanceData.IndexToInstance(instanceIndex); 483 484 writeIndex += 1; 485 remainingChunkBits &= ~(1ul << indexInChunk); 486 indexInChunk = math.tzcnt(remainingChunkBits); 487 } 488 } 489 } 490 491 [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] 492 private struct ReallocateInstancesJob : IJob 493 { 494 [ReadOnly] public bool implicitInstanceIndices; 495 [ReadOnly] public NativeArray<int> rendererGroupIDs; 496 [ReadOnly] public NativeArray<GPUDrivenPackedRendererData> packedRendererData; 497 [ReadOnly] public NativeArray<int> instanceOffsets; 498 [ReadOnly] public NativeArray<int> instanceCounts; 499 500 public InstanceAllocators instanceAllocators; 501 public CPUInstanceData instanceData; 502 public CPUSharedInstanceData sharedInstanceData; 503 public NativeArray<InstanceHandle> instances; 504 public NativeParallelMultiHashMap<int, InstanceHandle> rendererGroupInstanceMultiHash; 505 506 public void Execute() 507 { 508 for (int i = 0; i < rendererGroupIDs.Length; ++i) 509 { 510 var rendererGroupID = rendererGroupIDs[i]; 511 var hasTree = packedRendererData[i].hasTree; 512 513 int instanceCount; 514 int instanceOffset; 515 516 if (implicitInstanceIndices) 517 { 518 instanceCount = 1; 519 instanceOffset = i; 520 } 521 else 522 { 523 instanceCount = instanceCounts[i]; 524 instanceOffset = instanceOffsets[i]; 525 } 526 527 SharedInstanceHandle sharedInstance; 528 529 if (rendererGroupInstanceMultiHash.TryGetFirstValue(rendererGroupID, out var instance, out var it)) 530 { 531 sharedInstance = instanceData.Get_SharedInstance(instance); 532 533 int currentInstancesCount = sharedInstanceData.Get_RefCount(sharedInstance); 534 int instancesToFreeCount = currentInstancesCount - instanceCount; 535 536 if (instancesToFreeCount > 0) 537 { 538 bool success = true; 539 int freedInstancesCount = 0; 540 541 for (int j = 0; j < instanceCount; ++j) 542 success = rendererGroupInstanceMultiHash.TryGetNextValue(out instance, ref it); 543 544 Assert.IsTrue(success); 545 546 while (success) 547 { 548 instanceData.Remove(instance); 549 instanceAllocators.FreeInstance(instance); 550 551 rendererGroupInstanceMultiHash.Remove(it); 552 ++freedInstancesCount; 553 success = rendererGroupInstanceMultiHash.TryGetNextValue(out instance, ref it); 554 } 555 556 Assert.AreEqual(instancesToFreeCount, freedInstancesCount); 557 } 558 } 559 else 560 { 561 sharedInstance = instanceAllocators.AllocateSharedInstance(); 562 sharedInstanceData.AddNoGrow(sharedInstance); 563 } 564 565 if (instanceCount > 0) 566 { 567 sharedInstanceData.Set_RefCount(sharedInstance, instanceCount); 568 569 for (int j = 0; j < instanceCount; ++j) 570 { 571 int instanceIndex = instanceOffset + j; 572 573 if (instances[instanceIndex].valid) 574 continue; 575 576 InstanceHandle newInstance; 577 578 if (!hasTree) 579 newInstance = instanceAllocators.AllocateInstance(InstanceType.MeshRenderer); 580 else 581 newInstance = instanceAllocators.AllocateInstance(InstanceType.SpeedTree); 582 583 instanceData.AddNoGrow(newInstance); 584 int index = instanceData.InstanceToIndex(newInstance); 585 instanceData.sharedInstances[index] = sharedInstance; 586 instanceData.movedInCurrentFrameBits.Set(index, false); 587 instanceData.movedInPreviousFrameBits.Set(index, false); 588 instanceData.visibleInPreviousFrameBits.Set(index, false); 589 590 rendererGroupInstanceMultiHash.Add(rendererGroupID, newInstance); 591 instances[instanceIndex] = newInstance; 592 } 593 } 594 else 595 { 596 sharedInstanceData.Remove(sharedInstance); 597 instanceAllocators.FreeSharedInstance(sharedInstance); 598 } 599 } 600 } 601 } 602 603 [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] 604 private struct FreeInstancesJob : IJob 605 { 606 [ReadOnly] public NativeArray<InstanceHandle> instances; 607 608 public InstanceAllocators instanceAllocators; 609 public CPUInstanceData instanceData; 610 public CPUSharedInstanceData sharedInstanceData; 611 public NativeParallelMultiHashMap<int, InstanceHandle> rendererGroupInstanceMultiHash; 612 613 public void Execute() 614 { 615 foreach (var instance in instances) 616 { 617 if (!instanceData.IsValidInstance(instance)) 618 continue; 619 620 int instanceIndex = instanceData.InstanceToIndex(instance); 621 SharedInstanceHandle sharedInstance = instanceData.sharedInstances[instanceIndex]; 622 int sharedInstanceIndex = sharedInstanceData.SharedInstanceToIndex(sharedInstance); 623 int refCount = sharedInstanceData.refCounts[sharedInstanceIndex]; 624 var rendererGroupID = sharedInstanceData.rendererGroupIDs[sharedInstanceIndex]; 625 626 Assert.IsTrue(refCount > 0); 627 628 if (refCount > 1) 629 { 630 sharedInstanceData.refCounts[sharedInstanceIndex] = refCount - 1; 631 } 632 else 633 { 634 sharedInstanceData.Remove(sharedInstance); 635 instanceAllocators.FreeSharedInstance(sharedInstance); 636 } 637 638 instanceData.Remove(instance); 639 instanceAllocators.FreeInstance(instance); 640 641 //@ This will have quadratic cost. Optimize later. 642 for (bool success = rendererGroupInstanceMultiHash.TryGetFirstValue(rendererGroupID, out var i, out var it); success;) 643 { 644 if (instance.Equals(i)) 645 { 646 rendererGroupInstanceMultiHash.Remove(it); 647 break; 648 } 649 success = rendererGroupInstanceMultiHash.TryGetNextValue(out i, ref it); 650 } 651 } 652 } 653 } 654 655 [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] 656 private struct FreeRendererGroupInstancesJob : IJob 657 { 658 [ReadOnly] public NativeArray<int> rendererGroupsID; 659 660 public InstanceAllocators instanceAllocators; 661 public CPUInstanceData instanceData; 662 public CPUSharedInstanceData sharedInstanceData; 663 public NativeParallelMultiHashMap<int, InstanceHandle> rendererGroupInstanceMultiHash; 664 665 public void Execute() 666 { 667 foreach (var rendererGroupID in rendererGroupsID) 668 { 669 for (bool success = rendererGroupInstanceMultiHash.TryGetFirstValue(rendererGroupID, out var instance, out var it); success;) 670 { 671 SharedInstanceHandle sharedInstance = instanceData.Get_SharedInstance(instance); 672 int sharedInstanceIndex = sharedInstanceData.SharedInstanceToIndex(sharedInstance); 673 int refCount = sharedInstanceData.refCounts[sharedInstanceIndex]; 674 675 Assert.IsTrue(refCount > 0); 676 677 if (refCount > 1) 678 { 679 sharedInstanceData.refCounts[sharedInstanceIndex] = refCount - 1; 680 } 681 else 682 { 683 sharedInstanceData.Remove(sharedInstance); 684 instanceAllocators.FreeSharedInstance(sharedInstance); 685 } 686 687 instanceData.Remove(instance); 688 instanceAllocators.FreeInstance(instance); 689 690 success = rendererGroupInstanceMultiHash.TryGetNextValue(out instance, ref it); 691 } 692 693 rendererGroupInstanceMultiHash.Remove(rendererGroupID); 694 } 695 } 696 } 697 698 [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] 699 private unsafe struct UpdateRendererInstancesJob : IJobParallelFor 700 { 701 public const int k_BatchSize = 128; 702 703 [ReadOnly] public bool implicitInstanceIndices; 704 [ReadOnly] public GPUDrivenRendererGroupData rendererData; 705 [ReadOnly] public NativeArray<InstanceHandle> instances; 706 [ReadOnly] public NativeParallelHashMap<int, GPUInstanceIndex> lodGroupDataMap; 707 708 [NativeDisableParallelForRestriction][NativeDisableContainerSafetyRestriction, NoAlias] public CPUInstanceData instanceData; 709 [NativeDisableParallelForRestriction][NativeDisableContainerSafetyRestriction, NoAlias] public CPUSharedInstanceData sharedInstanceData; 710 711 public void Execute(int index) 712 { 713 var rendererGroupID = rendererData.rendererGroupID[index]; 714 int meshIndex = rendererData.meshIndex[index]; 715 var packedRendererData = rendererData.packedRendererData[index]; 716 var lodGroupID = rendererData.lodGroupID[index]; 717 var gameObjectLayer = rendererData.gameObjectLayer[index]; 718 var lightmapIndex = rendererData.lightmapIndex[index]; 719 var localAABB = rendererData.localBounds[index].ToAABB(); 720 int materialOffset = rendererData.materialsOffset[index]; 721 int materialCount = rendererData.materialsCount[index]; 722 723 int meshID = rendererData.meshID[meshIndex]; 724 725 const int k_LightmapIndexMask = 0xFFFF; 726 const int k_LightmapIndexNotLightmapped = 0xFFFF; 727 const int k_LightmapIndexInfluenceOnly = 0xFFFE; 728 const uint k_InvalidLODGroupAndMask = 0xFFFFFFFF; 729 730 var instanceFlags = InstanceFlags.None; 731 var transformUpdateFlags = TransformUpdateFlags.None; 732 733 var lmIndexMasked = lightmapIndex & k_LightmapIndexMask; 734 735 // Object doesn't have a valid lightmap Index, -> uses probes for lighting 736 if (lmIndexMasked >= k_LightmapIndexInfluenceOnly) 737 { 738 // Only add the component when needed to store blended results (shader will use the ambient probe when not present) 739 if (packedRendererData.lightProbeUsage == LightProbeUsage.BlendProbes) 740 transformUpdateFlags |= TransformUpdateFlags.HasLightProbeCombined; 741 } 742 743 if (packedRendererData.isPartOfStaticBatch) 744 transformUpdateFlags |= TransformUpdateFlags.IsPartOfStaticBatch; 745 746 switch (packedRendererData.shadowCastingMode) 747 { 748 case ShadowCastingMode.Off: 749 instanceFlags |= InstanceFlags.IsShadowsOff; 750 break; 751 case ShadowCastingMode.ShadowsOnly: 752 instanceFlags |= InstanceFlags.IsShadowsOnly; 753 break; 754 default: 755 break; 756 } 757 758 // If the object is light mapped, or has the special influence-only value, it affects lightmaps 759 if (lmIndexMasked != k_LightmapIndexNotLightmapped) 760 instanceFlags |= InstanceFlags.AffectsLightmaps; 761 762 // Mark if it should perform the small-mesh culling test 763 if (packedRendererData.smallMeshCulling) 764 instanceFlags |= InstanceFlags.SmallMeshCulling; 765 766 uint lodGroupAndMask = k_InvalidLODGroupAndMask; 767 768 // Renderer's LODGroup could be disabled which means that the renderer is not managed by it. 769 if (lodGroupDataMap.TryGetValue(lodGroupID, out var lodGroupHandle)) 770 { 771 if (packedRendererData.lodMask > 0) 772 lodGroupAndMask = (uint)lodGroupHandle.index << 8 | packedRendererData.lodMask; 773 } 774 775 int instancesCount; 776 int instancesOffset; 777 778 if (implicitInstanceIndices) 779 { 780 instancesCount = 1; 781 instancesOffset = index; 782 } 783 else 784 { 785 instancesCount = rendererData.instancesCount[index]; 786 instancesOffset = rendererData.instancesOffset[index]; 787 } 788 789 if (instancesCount > 0) 790 { 791 InstanceHandle instance = instances[instancesOffset]; 792 SharedInstanceHandle sharedInstance = instanceData.Get_SharedInstance(instance); 793 Assert.IsTrue(sharedInstance.valid); 794 795 var materialIDs = new SmallIntegerArray(materialCount, Allocator.Persistent); 796 for (int i = 0; i < materialCount; i++) 797 { 798 int matIndex = rendererData.materialIndex[materialOffset + i]; 799 int materialInstanceID = rendererData.materialID[matIndex]; 800 materialIDs[i] = materialInstanceID; 801 } 802 803 sharedInstanceData.Set(sharedInstance, rendererGroupID, materialIDs, meshID, localAABB, transformUpdateFlags, instanceFlags, lodGroupAndMask, gameObjectLayer, 804 sharedInstanceData.Get_RefCount(sharedInstance)); 805 806 for (int i = 0; i < instancesCount; ++i) 807 { 808 int inputIndex = instancesOffset + i; 809 810 ref Matrix4x4 l2w = ref UnsafeUtility.ArrayElementAsRef<Matrix4x4>(rendererData.localToWorldMatrix.GetUnsafeReadOnlyPtr(), inputIndex); 811 var worldAABB = AABB.Transform(l2w, localAABB); 812 813 instance = instances[inputIndex]; 814 Assert.IsTrue(instance.valid); 815 816 float det = math.determinant((float3x3)(float4x4)l2w); 817 bool isFlipped = (det < 0.0f); 818 819 int instanceIndex = instanceData.InstanceToIndex(instance); 820 instanceData.localToWorldIsFlippedBits.Set(instanceIndex, isFlipped); 821 instanceData.worldAABBs[instanceIndex] = worldAABB; 822 instanceData.tetrahedronCacheIndices[instanceIndex] = -1; 823#if UNITY_EDITOR 824 instanceData.editorData.sceneCullingMasks[instanceIndex] = rendererData.editorData[index].sceneCullingMask; 825 // Store more editor instance data here if needed. 826#endif 827 } 828 } 829 } 830 } 831 832 [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] 833 private struct CollectInstancesLODGroupsAndMasksJob : IJobParallelFor 834 { 835 public const int k_BatchSize = 128; 836 837 [ReadOnly] public NativeArray<InstanceHandle> instances; 838 [ReadOnly] public CPUInstanceData.ReadOnly instanceData; 839 [ReadOnly] public CPUSharedInstanceData.ReadOnly sharedInstanceData; 840 841 [WriteOnly] public NativeArray<uint> lodGroupAndMasks; 842 843 public void Execute(int index) 844 { 845 var instance = instances[index]; 846 var sharedInstanceIndex = sharedInstanceData.InstanceToIndex(instanceData, instance); 847 lodGroupAndMasks[index] = sharedInstanceData.lodGroupAndMasks[sharedInstanceIndex]; 848 } 849 } 850 851 [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] 852 private struct GetVisibleNonProcessedTreeInstancesJob : IJobParallelForBatch 853 { 854 public const int k_BatchSize = 64; 855 856 [ReadOnly] public CPUInstanceData instanceData; 857 [ReadOnly] public CPUSharedInstanceData sharedInstanceData; 858 [ReadOnly][NativeDisableContainerSafetyRestriction, NoAlias] public ParallelBitArray compactedVisibilityMasks; 859 [ReadOnly] public bool becomeVisible; 860 861 [NativeDisableParallelForRestriction] public ParallelBitArray processedBits; 862 863 [NativeDisableParallelForRestriction][WriteOnly] public NativeArray<int> rendererIDs; 864 [NativeDisableParallelForRestriction][WriteOnly] public NativeArray<InstanceHandle> instances; 865 866 [NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 atomicTreeInstancesCount; 867 868 public void Execute(int startIndex, int count) 869 { 870 var chunkIndex = startIndex / 64; 871 var visibleInPrevFrameChunk = instanceData.visibleInPreviousFrameBits.GetChunk(chunkIndex); 872 var processedChunk = processedBits.GetChunk(chunkIndex); 873 874 ulong validBits = 0; 875 876 for (int i = 0; i < count; ++i) 877 { 878 int instanceIndex = startIndex + i; 879 InstanceHandle instance = instanceData.IndexToInstance(instanceIndex); 880 bool hasTree = instance.type == InstanceType.SpeedTree; 881 882 if (hasTree && compactedVisibilityMasks.Get(instance.index)) 883 { 884 var bitMask = 1ul << i; 885 886 var processedInCurrentFrame = (processedChunk & bitMask) != 0; 887 888 if (!processedInCurrentFrame) 889 { 890 bool visibleInPrevFrame = (visibleInPrevFrameChunk & bitMask) != 0; 891 892 if (becomeVisible) 893 { 894 if (!visibleInPrevFrame) 895 validBits |= bitMask; 896 } 897 else 898 { 899 if (visibleInPrevFrame) 900 validBits |= bitMask; 901 } 902 } 903 } 904 } 905 906 int validBitsCount = math.countbits(validBits); 907 908 if (validBitsCount > 0) 909 { 910 processedBits.SetChunk(chunkIndex, processedChunk | validBits); 911 912 int writeIndex = atomicTreeInstancesCount.Add(validBitsCount); 913 int validBitIndex = math.tzcnt(validBits); 914 915 while (validBits != 0) 916 { 917 int instanceIndex = startIndex + validBitIndex; 918 InstanceHandle instance = instanceData.IndexToInstance(instanceIndex); 919 SharedInstanceHandle sharedInstanceHandle = instanceData.Get_SharedInstance(instance); 920 int rendererID = sharedInstanceData.Get_RendererGroupID(sharedInstanceHandle); 921 922 rendererIDs[writeIndex] = rendererID; 923 instances[writeIndex] = instance; 924 925 writeIndex += 1; 926 validBits &= ~(1ul << validBitIndex); 927 validBitIndex = math.tzcnt(validBits); 928 } 929 } 930 } 931 } 932 933 [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] 934 private struct UpdateCompactedInstanceVisibilityJob : IJobParallelForBatch 935 { 936 public const int k_BatchSize = 64; 937 938 [ReadOnly] public ParallelBitArray compactedVisibilityMasks; 939 940 [NativeDisableContainerSafetyRestriction, NoAlias][NativeDisableParallelForRestriction] public CPUInstanceData instanceData; 941 942 public void Execute(int startIndex, int count) 943 { 944 ulong visibleBits = 0; 945 946 for (int i = 0; i < count; ++i) 947 { 948 int instanceIndex = startIndex + i; 949 InstanceHandle instance = instanceData.IndexToInstance(instanceIndex); 950 bool visible = compactedVisibilityMasks.Get(instance.index); 951 952 if (visible) 953 visibleBits |= 1ul << i; 954 } 955 956 instanceData.visibleInPreviousFrameBits.SetChunk(startIndex / 64, visibleBits); 957 } 958 } 959 960#if UNITY_EDITOR 961 962 [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] 963 private struct UpdateSelectedInstancesJob : IJobParallelFor 964 { 965 public const int k_BatchSize = 64; 966 967 [ReadOnly] public NativeArray<InstanceHandle> instances; 968 969 [NativeDisableParallelForRestriction] public CPUInstanceData instanceData; 970 971 public void Execute(int index) 972 { 973 InstanceHandle instance = instances[index]; 974 975 if (instance.valid) 976 instanceData.editorData.selectedBits.Set(instanceData.InstanceToIndex(instance), true); 977 } 978 } 979 980#endif 981 } 982}