A game about forced loneliness, made by TACStudios
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}