A game about forced loneliness, made by TACStudios
1using System;
2using Unity.Collections;
3using Unity.Collections.LowLevel.Unsafe;
4using Unity.Mathematics;
5using UnityEngine.Assertions;
6
7/// -----------------------------------------------------------------------
8/// See the data layout and relationship diagram at the bottom of the file.
9/// -----------------------------------------------------------------------
10
11namespace UnityEngine.Rendering
12{
13 internal struct CPUInstanceData : IDisposable
14 {
15 private const int k_InvalidIndex = -1;
16
17 private NativeArray<int> m_StructData;
18 private NativeList<int> m_InstanceIndices;
19
20 public NativeArray<InstanceHandle> instances;
21 public NativeArray<SharedInstanceHandle> sharedInstances;
22 public ParallelBitArray localToWorldIsFlippedBits;
23 public NativeArray<AABB> worldAABBs;
24 public NativeArray<int> tetrahedronCacheIndices;
25 public ParallelBitArray movedInCurrentFrameBits;
26 public ParallelBitArray movedInPreviousFrameBits;
27 public ParallelBitArray visibleInPreviousFrameBits;
28 public EditorInstanceDataArrays editorData;
29
30 public int instancesLength { get => m_StructData[0]; set => m_StructData[0] = value; }
31 public int instancesCapacity { get => m_StructData[1]; set => m_StructData[1] = value; }
32 public int handlesLength => m_InstanceIndices.Length;
33
34 public void Initialize(int initCapacity)
35 {
36 m_StructData = new NativeArray<int>(2, Allocator.Persistent);
37 instancesCapacity = initCapacity;
38 m_InstanceIndices = new NativeList<int>(Allocator.Persistent);
39 instances = new NativeArray<InstanceHandle>(instancesCapacity, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
40 instances.FillArray(InstanceHandle.Invalid);
41 sharedInstances = new NativeArray<SharedInstanceHandle>(instancesCapacity, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
42 sharedInstances.FillArray(SharedInstanceHandle.Invalid);
43 localToWorldIsFlippedBits = new ParallelBitArray(instancesCapacity, Allocator.Persistent);
44 worldAABBs = new NativeArray<AABB>(instancesCapacity, Allocator.Persistent);
45 tetrahedronCacheIndices = new NativeArray<int>(instancesCapacity, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
46 tetrahedronCacheIndices.FillArray(k_InvalidIndex);
47 movedInCurrentFrameBits = new ParallelBitArray(instancesCapacity, Allocator.Persistent);
48 movedInPreviousFrameBits = new ParallelBitArray(instancesCapacity, Allocator.Persistent);
49 visibleInPreviousFrameBits = new ParallelBitArray(instancesCapacity, Allocator.Persistent);
50 editorData.Initialize(initCapacity);
51 }
52
53 public void Dispose()
54 {
55 m_StructData.Dispose();
56 m_InstanceIndices.Dispose();
57 instances.Dispose();
58 sharedInstances.Dispose();
59 localToWorldIsFlippedBits.Dispose();
60 worldAABBs.Dispose();
61 tetrahedronCacheIndices.Dispose();
62 movedInCurrentFrameBits.Dispose();
63 movedInPreviousFrameBits.Dispose();
64 visibleInPreviousFrameBits.Dispose();
65 editorData.Dispose();
66 }
67
68 private void Grow(int newCapacity)
69 {
70 Assert.IsTrue(newCapacity > instancesCapacity);
71
72 instances.ResizeArray(newCapacity);
73 instances.FillArray(InstanceHandle.Invalid, instancesCapacity);
74 sharedInstances.ResizeArray(newCapacity);
75 sharedInstances.FillArray(SharedInstanceHandle.Invalid, instancesCapacity);
76 localToWorldIsFlippedBits.Resize(newCapacity);
77 worldAABBs.ResizeArray(newCapacity);
78 tetrahedronCacheIndices.ResizeArray(newCapacity);
79 tetrahedronCacheIndices.FillArray(k_InvalidIndex, instancesCapacity);
80 movedInCurrentFrameBits.Resize(newCapacity);
81 movedInPreviousFrameBits.Resize(newCapacity);
82 visibleInPreviousFrameBits.Resize(newCapacity);
83 editorData.Grow(newCapacity);
84
85 instancesCapacity = newCapacity;
86 }
87
88 private void AddUnsafe(InstanceHandle instance)
89 {
90 if (instance.index >= m_InstanceIndices.Length)
91 {
92 int prevLength = m_InstanceIndices.Length;
93 m_InstanceIndices.ResizeUninitialized(instance.index + 1);
94
95 for (int i = prevLength; i < m_InstanceIndices.Length - 1; ++i)
96 m_InstanceIndices[i] = k_InvalidIndex;
97 }
98
99 m_InstanceIndices[instance.index] = instancesLength;
100 instances[instancesLength] = instance;
101
102 ++instancesLength;
103 }
104
105 public int InstanceToIndex(InstanceHandle instance)
106 {
107 Assert.IsTrue(IsValidInstance(instance));
108 return m_InstanceIndices[instance.index];
109 }
110
111 public InstanceHandle IndexToInstance(int index)
112 {
113 Assert.IsTrue(IsValidIndex(index));
114 return instances[index];
115 }
116
117 public bool IsValidInstance(InstanceHandle instance)
118 {
119 if (instance.valid && instance.index < m_InstanceIndices.Length)
120 {
121 int index = m_InstanceIndices[instance.index];
122 return index >= 0 && index < instancesLength && instances[index].Equals(instance);
123 }
124 return false;
125 }
126
127 public bool IsFreeInstanceHandle(InstanceHandle instance)
128 {
129 return instance.valid && (instance.index >= m_InstanceIndices.Length || m_InstanceIndices[instance.index] == k_InvalidIndex);
130 }
131
132 public bool IsValidIndex(int index)
133 {
134 if (index >= 0 && index < instancesLength)
135 {
136 InstanceHandle instance = instances[index];
137 return index == m_InstanceIndices[instance.index];
138 }
139 return false;
140 }
141
142 public int GetFreeInstancesCount()
143 {
144 return instancesCapacity - instancesLength;
145 }
146
147 public void EnsureFreeInstances(int instancesCount)
148 {
149 int freeInstancesCount = GetFreeInstancesCount();
150 int needInstances = instancesCount - freeInstancesCount;
151
152 if (needInstances > 0)
153 Grow(instancesCapacity + needInstances + 256);
154 }
155
156 public void AddNoGrow(InstanceHandle instance)
157 {
158 Assert.IsTrue(instance.valid);
159 Assert.IsTrue(IsFreeInstanceHandle(instance));
160 Assert.IsTrue(GetFreeInstancesCount() > 0);
161
162 AddUnsafe(instance);
163 SetDefault(instance);
164 }
165
166 public void Add(InstanceHandle instance)
167 {
168 EnsureFreeInstances(1);
169 AddNoGrow(instance);
170 }
171
172 public void Remove(InstanceHandle instance)
173 {
174 Assert.IsTrue(IsValidInstance(instance));
175
176 int index = InstanceToIndex(instance);
177 int lastIndex = instancesLength - 1;
178
179 instances[index] = instances[lastIndex];
180 sharedInstances[index] = sharedInstances[lastIndex];
181 localToWorldIsFlippedBits.Set(index, localToWorldIsFlippedBits.Get(lastIndex));
182 worldAABBs[index] = worldAABBs[lastIndex];
183 tetrahedronCacheIndices[index] = tetrahedronCacheIndices[lastIndex];
184 movedInCurrentFrameBits.Set(index, movedInCurrentFrameBits.Get(lastIndex));
185 movedInPreviousFrameBits.Set(index, movedInPreviousFrameBits.Get(lastIndex));
186 visibleInPreviousFrameBits.Set(index, visibleInPreviousFrameBits.Get(lastIndex));
187 editorData.Remove(index, lastIndex);
188
189 m_InstanceIndices[instances[lastIndex].index] = index;
190 m_InstanceIndices[instance.index] = k_InvalidIndex;
191 instancesLength -= 1;
192 }
193
194 public void Set(InstanceHandle instance, SharedInstanceHandle sharedInstance, bool localToWorldIsFlipped, in AABB worldAABB, int tetrahedronCacheIndex,
195 bool movedInCurrentFrame, bool movedInPreviousFrame, bool visibleInPreviousFrame)
196 {
197 int index = InstanceToIndex(instance);
198 sharedInstances[index] = sharedInstance;
199 localToWorldIsFlippedBits.Set(index, localToWorldIsFlipped);
200 worldAABBs[index] = worldAABB;
201 tetrahedronCacheIndices[index] = tetrahedronCacheIndex;
202 movedInCurrentFrameBits.Set(index, movedInCurrentFrame);
203 movedInPreviousFrameBits.Set(index, movedInPreviousFrame);
204 visibleInPreviousFrameBits.Set(index, visibleInPreviousFrame);
205 editorData.SetDefault(index);
206 }
207
208 public void SetDefault(InstanceHandle instance)
209 {
210 Set(instance, SharedInstanceHandle.Invalid, false, new AABB(), k_InvalidIndex, false, false, false);
211 }
212
213 // These accessors just for convenience and additional safety.
214 // In general prefer converting an instance to an index and access by index.
215 public SharedInstanceHandle Get_SharedInstance(InstanceHandle instance) { return sharedInstances[InstanceToIndex(instance)]; }
216 public bool Get_LocalToWorldIsFlipped(InstanceHandle instance) { return localToWorldIsFlippedBits.Get(InstanceToIndex(instance)); }
217 public AABB Get_WorldAABB(InstanceHandle instance) { return worldAABBs[InstanceToIndex(instance)]; }
218 public int Get_TetrahedronCacheIndex(InstanceHandle instance) { return tetrahedronCacheIndices[InstanceToIndex(instance)]; }
219 public unsafe ref AABB Get_WorldBounds(InstanceHandle instance) { return ref UnsafeUtility.ArrayElementAsRef<AABB>(worldAABBs.GetUnsafePtr(), InstanceToIndex(instance)); }
220 public bool Get_MovedInCurrentFrame(InstanceHandle instance) { return movedInCurrentFrameBits.Get(InstanceToIndex(instance)); }
221 public bool Get_MovedInPreviousFrame(InstanceHandle instance) { return movedInPreviousFrameBits.Get(InstanceToIndex(instance)); }
222 public bool Get_VisibleInPreviousFrame(InstanceHandle instance) { return visibleInPreviousFrameBits.Get(InstanceToIndex(instance)); }
223
224 public void Set_SharedInstance(InstanceHandle instance, SharedInstanceHandle sharedInstance) { sharedInstances[InstanceToIndex(instance)] = sharedInstance; }
225 public void Set_LocalToWorldIsFlipped(InstanceHandle instance, bool isFlipped) { localToWorldIsFlippedBits.Set(InstanceToIndex(instance), isFlipped); }
226 public void Set_WorldAABB(InstanceHandle instance, in AABB worldBounds) { worldAABBs[InstanceToIndex(instance)] = worldBounds; }
227 public void Set_TetrahedronCacheIndex(InstanceHandle instance, int tetrahedronCacheIndex) { tetrahedronCacheIndices[InstanceToIndex(instance)] = tetrahedronCacheIndex; }
228 public void Set_MovedInCurrentFrame(InstanceHandle instance, bool movedInCurrentFrame) { movedInCurrentFrameBits.Set(InstanceToIndex(instance), movedInCurrentFrame); }
229 public void Set_MovedInPreviousFrame(InstanceHandle instance, bool movedInPreviousFrame) { movedInPreviousFrameBits.Set(InstanceToIndex(instance), movedInPreviousFrame); }
230 public void Set_VisibleInPreviousFrame(InstanceHandle instance, bool visibleInPreviousFrame) { visibleInPreviousFrameBits.Set(InstanceToIndex(instance), visibleInPreviousFrame); }
231
232 public ReadOnly AsReadOnly()
233 {
234 return new ReadOnly(this);
235 }
236
237 internal readonly struct ReadOnly
238 {
239 public readonly NativeArray<int>.ReadOnly instanceIndices;
240 public readonly NativeArray<InstanceHandle>.ReadOnly instances;
241 public readonly NativeArray<SharedInstanceHandle>.ReadOnly sharedInstances;
242 public readonly ParallelBitArray localToWorldIsFlippedBits;
243 public readonly NativeArray<AABB>.ReadOnly worldAABBs;
244 public readonly NativeArray<int>.ReadOnly tetrahedronCacheIndices;
245 public readonly ParallelBitArray movedInCurrentFrameBits;
246 public readonly ParallelBitArray movedInPreviousFrameBits;
247 public readonly ParallelBitArray visibleInPreviousFrameBits;
248 public readonly EditorInstanceDataArrays.ReadOnly editorData;
249 public readonly int handlesLength => instanceIndices.Length;
250 public readonly int instancesLength => instances.Length;
251
252 public ReadOnly(in CPUInstanceData instanceData)
253 {
254 instanceIndices = instanceData.m_InstanceIndices.AsArray().AsReadOnly();
255 instances = instanceData.instances.GetSubArray(0, instanceData.instancesLength).AsReadOnly();
256 sharedInstances = instanceData.sharedInstances.GetSubArray(0, instanceData.instancesLength).AsReadOnly();
257 localToWorldIsFlippedBits = instanceData.localToWorldIsFlippedBits.GetSubArray(instanceData.instancesLength);
258 worldAABBs = instanceData.worldAABBs.GetSubArray(0, instanceData.instancesLength).AsReadOnly();
259 tetrahedronCacheIndices = instanceData.tetrahedronCacheIndices.GetSubArray(0, instanceData.instancesLength).AsReadOnly();
260 movedInCurrentFrameBits = instanceData.movedInCurrentFrameBits.GetSubArray(instanceData.instancesLength);//.AsReadOnly(); // Implement later.
261 movedInPreviousFrameBits = instanceData.movedInPreviousFrameBits.GetSubArray(instanceData.instancesLength);//.AsReadOnly(); // Implement later.
262 visibleInPreviousFrameBits = instanceData.visibleInPreviousFrameBits.GetSubArray(instanceData.instancesLength);//.AsReadOnly(); // Implement later.
263 editorData = new EditorInstanceDataArrays.ReadOnly(instanceData);
264 }
265
266 public int InstanceToIndex(InstanceHandle instance)
267 {
268 Assert.IsTrue(IsValidInstance(instance));
269 return instanceIndices[instance.index];
270 }
271
272 public InstanceHandle IndexToInstance(int index)
273 {
274 Assert.IsTrue(IsValidIndex(index));
275 return instances[index];
276 }
277
278 public bool IsValidInstance(InstanceHandle instance)
279 {
280 if (instance.valid && instance.index < instanceIndices.Length)
281 {
282 int index = instanceIndices[instance.index];
283 return index >= 0 && index < instances.Length && instances[index].Equals(instance);
284 }
285 return false;
286 }
287
288 public bool IsValidIndex(int index)
289 {
290 if (index >= 0 && index < instances.Length)
291 {
292 InstanceHandle instance = instances[index];
293 return index == instanceIndices[instance.index];
294 }
295 return false;
296 }
297 }
298 }
299
300 internal struct CPUSharedInstanceData : IDisposable
301 {
302 private const int k_InvalidIndex = -1;
303 private const uint k_InvalidLODGroupAndMask = 0xFFFFFFFF;
304
305 private NativeArray<int> m_StructData;
306 private NativeList<int> m_InstanceIndices;
307
308 //@ Need to figure out the way to share the code with CPUInstanceData. Both structures are almost identical.
309 public NativeArray<SharedInstanceHandle> instances;
310 public NativeArray<int> rendererGroupIDs;
311
312 // For now we just use nested collections since materialIDs are only parsed rarely. E.g. when an unsupported material is detected.
313 public NativeArray<SmallIntegerArray> materialIDArrays;
314
315 public NativeArray<int> meshIDs;
316 public NativeArray<AABB> localAABBs;
317 public NativeArray<CPUSharedInstanceFlags> flags;
318 public NativeArray<uint> lodGroupAndMasks;
319 public NativeArray<int> gameObjectLayers;
320 public NativeArray<int> refCounts;
321
322 public int instancesLength { get => m_StructData[0]; set => m_StructData[0] = value; }
323 public int instancesCapacity { get => m_StructData[1]; set => m_StructData[1] = value; }
324 public int handlesLength => m_InstanceIndices.Length;
325
326 public void Initialize(int initCapacity)
327 {
328 m_StructData = new NativeArray<int>(2, Allocator.Persistent);
329 instancesCapacity = initCapacity;
330 m_InstanceIndices = new NativeList<int>(Allocator.Persistent);
331 instances = new NativeArray<SharedInstanceHandle>(instancesCapacity, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
332 instances.FillArray(SharedInstanceHandle.Invalid);
333 rendererGroupIDs = new NativeArray<int>(instancesCapacity, Allocator.Persistent);
334 materialIDArrays = new NativeArray<SmallIntegerArray>(instancesCapacity, Allocator.Persistent);
335 meshIDs = new NativeArray<int>(instancesCapacity, Allocator.Persistent);
336 localAABBs = new NativeArray<AABB>(instancesCapacity, Allocator.Persistent);
337 flags = new NativeArray<CPUSharedInstanceFlags>(instancesCapacity, Allocator.Persistent);
338 lodGroupAndMasks = new NativeArray<uint>(instancesCapacity, Allocator.Persistent);
339 lodGroupAndMasks.FillArray(k_InvalidLODGroupAndMask);
340 gameObjectLayers = new NativeArray<int>(instancesCapacity, Allocator.Persistent);
341 refCounts = new NativeArray<int>(instancesCapacity, Allocator.Persistent);
342 }
343
344 public void Dispose()
345 {
346 m_StructData.Dispose();
347 m_InstanceIndices.Dispose();
348 instances.Dispose();
349 rendererGroupIDs.Dispose();
350
351 foreach (var materialIDs in materialIDArrays)
352 {
353 materialIDs.Dispose();
354 }
355 materialIDArrays.Dispose();
356
357 meshIDs.Dispose();
358 localAABBs.Dispose();
359 flags.Dispose();
360 lodGroupAndMasks.Dispose();
361 gameObjectLayers.Dispose();
362 refCounts.Dispose();
363 }
364
365 private void Grow(int newCapacity)
366 {
367 Assert.IsTrue(newCapacity > instancesCapacity);
368
369 instances.ResizeArray(newCapacity);
370 instances.FillArray(SharedInstanceHandle.Invalid, instancesCapacity);
371 rendererGroupIDs.ResizeArray(newCapacity);
372 materialIDArrays.ResizeArray(newCapacity);
373 materialIDArrays.FillArray(default, instancesCapacity);
374 meshIDs.ResizeArray(newCapacity);
375 localAABBs.ResizeArray(newCapacity);
376 flags.ResizeArray(newCapacity);
377 lodGroupAndMasks.ResizeArray(newCapacity);
378 lodGroupAndMasks.FillArray(k_InvalidLODGroupAndMask, instancesCapacity);
379 gameObjectLayers.ResizeArray(newCapacity);
380 refCounts.ResizeArray(newCapacity);
381
382 instancesCapacity = newCapacity;
383 }
384
385 private void AddUnsafe(SharedInstanceHandle instance)
386 {
387 if (instance.index >= m_InstanceIndices.Length)
388 {
389 int prevLength = m_InstanceIndices.Length;
390 m_InstanceIndices.ResizeUninitialized(instance.index + 1);
391
392 for (int i = prevLength; i < m_InstanceIndices.Length - 1; ++i)
393 m_InstanceIndices[i] = k_InvalidIndex;
394 }
395
396 m_InstanceIndices[instance.index] = instancesLength;
397 instances[instancesLength] = instance;
398
399 ++instancesLength;
400 }
401
402 public int SharedInstanceToIndex(SharedInstanceHandle instance)
403 {
404 Assert.IsTrue(IsValidInstance(instance));
405 return m_InstanceIndices[instance.index];
406 }
407
408 public SharedInstanceHandle IndexToSharedInstance(int index)
409 {
410 Assert.IsTrue(IsValidIndex(index));
411 return instances[index];
412 }
413
414 public int InstanceToIndex(in CPUInstanceData instanceData, InstanceHandle instance)
415 {
416 int instanceIndex = instanceData.InstanceToIndex(instance);
417 SharedInstanceHandle sharedInstance = instanceData.sharedInstances[instanceIndex];
418 int sharedInstanceIndex = SharedInstanceToIndex(sharedInstance);
419 return sharedInstanceIndex;
420 }
421
422 public bool IsValidInstance(SharedInstanceHandle instance)
423 {
424 if (instance.valid && instance.index < m_InstanceIndices.Length)
425 {
426 int index = m_InstanceIndices[instance.index];
427 return index >= 0 && index < instancesLength && instances[index].Equals(instance);
428 }
429 return false;
430 }
431
432 public bool IsFreeInstanceHandle(SharedInstanceHandle instance)
433 {
434 return instance.valid && (instance.index >= m_InstanceIndices.Length || m_InstanceIndices[instance.index] == k_InvalidIndex);
435 }
436
437 public bool IsValidIndex(int index)
438 {
439 if (index >= 0 && index < instancesLength)
440 {
441 SharedInstanceHandle instance = instances[index];
442 return index == m_InstanceIndices[instance.index];
443 }
444 return false;
445 }
446
447 public int GetFreeInstancesCount()
448 {
449 return instancesCapacity - instancesLength;
450 }
451
452 public void EnsureFreeInstances(int instancesCount)
453 {
454 int freeInstancesCount = GetFreeInstancesCount();
455 int needInstances = instancesCount - freeInstancesCount;
456
457 if (needInstances > 0)
458 Grow(instancesCapacity + needInstances + 256);
459 }
460
461 public void AddNoGrow(SharedInstanceHandle instance)
462 {
463 Assert.IsTrue(instance.valid);
464 Assert.IsTrue(IsFreeInstanceHandle(instance));
465 Assert.IsTrue(GetFreeInstancesCount() > 0);
466
467 AddUnsafe(instance);
468 SetDefault(instance);
469 }
470
471 public void Add(SharedInstanceHandle instance)
472 {
473 EnsureFreeInstances(1);
474 AddNoGrow(instance);
475 }
476
477 public void Remove(SharedInstanceHandle instance)
478 {
479 Assert.IsTrue(IsValidInstance(instance));
480
481 int index = SharedInstanceToIndex(instance);
482 int lastIndex = instancesLength - 1;
483
484 instances[index] = instances[lastIndex];
485 rendererGroupIDs[index] = rendererGroupIDs[lastIndex];
486
487 materialIDArrays[index].Dispose();
488 materialIDArrays[index] = materialIDArrays[lastIndex];
489 materialIDArrays[lastIndex] = default;
490
491 meshIDs[index] = meshIDs[lastIndex];
492 localAABBs[index] = localAABBs[lastIndex];
493 flags[index] = flags[lastIndex];
494 lodGroupAndMasks[index] = lodGroupAndMasks[lastIndex];
495 gameObjectLayers[index] = gameObjectLayers[lastIndex];
496 refCounts[index] = refCounts[lastIndex];
497
498 m_InstanceIndices[instances[lastIndex].index] = index;
499 m_InstanceIndices[instance.index] = k_InvalidIndex;
500 instancesLength -= 1;
501 }
502
503 // These accessors just for convenience and additional safety.
504 // In general prefer converting an instance to an index and access by index.
505 public int Get_RendererGroupID(SharedInstanceHandle instance) { return rendererGroupIDs[SharedInstanceToIndex(instance)]; }
506 public int Get_MeshID(SharedInstanceHandle instance) { return meshIDs[SharedInstanceToIndex(instance)]; }
507 public unsafe ref AABB Get_LocalAABB(SharedInstanceHandle instance) { return ref UnsafeUtility.ArrayElementAsRef<AABB>(localAABBs.GetUnsafePtr(), SharedInstanceToIndex(instance)); }
508 public CPUSharedInstanceFlags Get_Flags(SharedInstanceHandle instance) { return flags[SharedInstanceToIndex(instance)]; }
509 public uint Get_LODGroupAndMask(SharedInstanceHandle instance) { return lodGroupAndMasks[SharedInstanceToIndex(instance)]; }
510 public int Get_GameObjectLayer(SharedInstanceHandle instance) { return gameObjectLayers[SharedInstanceToIndex(instance)]; }
511 public int Get_RefCount(SharedInstanceHandle instance) { return refCounts[SharedInstanceToIndex(instance)]; }
512 public unsafe ref SmallIntegerArray Get_MaterialIDs(SharedInstanceHandle instance) { return ref UnsafeUtility.ArrayElementAsRef<SmallIntegerArray>(materialIDArrays.GetUnsafePtr(), SharedInstanceToIndex(instance)); }
513
514 public void Set_RendererGroupID(SharedInstanceHandle instance, int rendererGroupID) { rendererGroupIDs[SharedInstanceToIndex(instance)] = rendererGroupID; }
515 public void Set_MeshID(SharedInstanceHandle instance, int meshID) { meshIDs[SharedInstanceToIndex(instance)] = meshID; }
516 public void Set_LocalAABB(SharedInstanceHandle instance, in AABB localAABB) { localAABBs[SharedInstanceToIndex(instance)] = localAABB; }
517 public void Set_Flags(SharedInstanceHandle instance, CPUSharedInstanceFlags instanceFlags) { flags[SharedInstanceToIndex(instance)] = instanceFlags; }
518 public void Set_LODGroupAndMask(SharedInstanceHandle instance, uint lodGroupAndMask) { lodGroupAndMasks[SharedInstanceToIndex(instance)] = lodGroupAndMask; }
519 public void Set_GameObjectLayer(SharedInstanceHandle instance, int gameObjectLayer) { gameObjectLayers[SharedInstanceToIndex(instance)] = gameObjectLayer; }
520 public void Set_RefCount(SharedInstanceHandle instance, int refCount) { refCounts[SharedInstanceToIndex(instance)] = refCount; }
521 public void Set_MaterialIDs(SharedInstanceHandle instance, in SmallIntegerArray materialIDs)
522 {
523 int index = SharedInstanceToIndex(instance);
524 materialIDArrays[index].Dispose();
525 materialIDArrays[index] = materialIDs;
526 }
527
528 public void Set(SharedInstanceHandle instance, int rendererGroupID, in SmallIntegerArray materialIDs, int meshID, in AABB localAABB, TransformUpdateFlags transformUpdateFlags,
529 InstanceFlags instanceFlags, uint lodGroupAndMask, int gameObjectLayer, int refCount)
530 {
531 int index = SharedInstanceToIndex(instance);
532
533 rendererGroupIDs[index] = rendererGroupID;
534 materialIDArrays[index].Dispose();
535 materialIDArrays[index] = materialIDs;
536 meshIDs[index] = meshID;
537 localAABBs[index] = localAABB;
538 flags[index] = new CPUSharedInstanceFlags { transformUpdateFlags = transformUpdateFlags, instanceFlags = instanceFlags };
539 lodGroupAndMasks[index] = lodGroupAndMask;
540 gameObjectLayers[index] = gameObjectLayer;
541 refCounts[index] = refCount;
542 }
543
544 public void SetDefault(SharedInstanceHandle instance)
545 {
546 Set(instance, 0, default, 0, new AABB(), TransformUpdateFlags.None, InstanceFlags.None, k_InvalidLODGroupAndMask, 0, 0);
547 }
548
549 public ReadOnly AsReadOnly()
550 {
551 return new ReadOnly(this);
552 }
553
554 internal readonly struct ReadOnly
555 {
556 public readonly NativeArray<int>.ReadOnly instanceIndices;
557 public readonly NativeArray<SharedInstanceHandle>.ReadOnly instances;
558 public readonly NativeArray<int>.ReadOnly rendererGroupIDs;
559 public readonly NativeArray<SmallIntegerArray>.ReadOnly materialIDArrays;
560 public readonly NativeArray<int>.ReadOnly meshIDs;
561 public readonly NativeArray<AABB>.ReadOnly localAABBs;
562 public readonly NativeArray<CPUSharedInstanceFlags>.ReadOnly flags;
563 public readonly NativeArray<uint>.ReadOnly lodGroupAndMasks;
564 public readonly NativeArray<int>.ReadOnly gameObjectLayers;
565 public readonly NativeArray<int>.ReadOnly refCounts;
566 public readonly int handlesLength => instanceIndices.Length;
567 public readonly int instancesLength => instances.Length;
568
569 public ReadOnly(in CPUSharedInstanceData instanceData)
570 {
571 instanceIndices = instanceData.m_InstanceIndices.AsArray().AsReadOnly();
572 instances = instanceData.instances.GetSubArray(0, instanceData.instancesLength).AsReadOnly();
573 rendererGroupIDs = instanceData.rendererGroupIDs.GetSubArray(0, instanceData.instancesLength).AsReadOnly();
574 materialIDArrays = instanceData.materialIDArrays.GetSubArray(0, instanceData.instancesLength).AsReadOnly();
575 meshIDs = instanceData.meshIDs.GetSubArray(0, instanceData.instancesLength).AsReadOnly();
576 localAABBs = instanceData.localAABBs.GetSubArray(0, instanceData.instancesLength).AsReadOnly();
577 flags = instanceData.flags.GetSubArray(0, instanceData.instancesLength).AsReadOnly();
578 lodGroupAndMasks = instanceData.lodGroupAndMasks.GetSubArray(0, instanceData.instancesLength).AsReadOnly();
579 gameObjectLayers = instanceData.gameObjectLayers.GetSubArray(0, instanceData.instancesLength).AsReadOnly();
580 refCounts = instanceData.refCounts.GetSubArray(0, instanceData.instancesLength).AsReadOnly();
581 }
582
583 public int SharedInstanceToIndex(SharedInstanceHandle instance)
584 {
585 Assert.IsTrue(IsValidSharedInstance(instance));
586 return instanceIndices[instance.index];
587 }
588
589 public SharedInstanceHandle IndexToSharedInstance(int index)
590 {
591 Assert.IsTrue(IsValidIndex(index));
592 return instances[index];
593 }
594
595 public bool IsValidSharedInstance(SharedInstanceHandle instance)
596 {
597 if (instance.valid && instance.index < instanceIndices.Length)
598 {
599 int index = instanceIndices[instance.index];
600 return index >= 0 && index < instances.Length && instances[index].Equals(instance);
601 }
602 return false;
603 }
604
605 public bool IsValidIndex(int index)
606 {
607 if (index >= 0 && index < instances.Length)
608 {
609 SharedInstanceHandle instance = instances[index];
610 return index == instanceIndices[instance.index];
611 }
612 return false;
613 }
614
615 public int InstanceToIndex(in CPUInstanceData.ReadOnly instanceData, InstanceHandle instance)
616 {
617 int instanceIndex = instanceData.InstanceToIndex(instance);
618 SharedInstanceHandle sharedInstance = instanceData.sharedInstances[instanceIndex];
619 int sharedInstanceIndex = SharedInstanceToIndex(sharedInstance);
620 return sharedInstanceIndex;
621 }
622 }
623 }
624
625 internal unsafe struct SmallIntegerArray : IDisposable
626 {
627 private FixedList32Bytes<int> m_FixedArray;
628 private UnsafeList<int> m_List;
629 private readonly bool m_IsEmbedded;
630
631 public bool Valid { get; private set; }
632 public readonly int Length;
633
634 public SmallIntegerArray(int length, Allocator allocator)
635 {
636 m_FixedArray = default;
637 m_List = default;
638 Length = length;
639 Valid = true;
640
641 if (Length <= m_FixedArray.Capacity)
642 {
643 m_FixedArray = new FixedList32Bytes<int>();
644 m_FixedArray.Length = Length;
645 m_IsEmbedded = true;
646 }
647 else
648 {
649 m_List = new UnsafeList<int>(Length, allocator, NativeArrayOptions.UninitializedMemory);
650 m_List.Resize(Length);
651 m_IsEmbedded = false;
652 }
653 }
654
655 public int this[int index]
656 {
657 get
658 {
659 Assert.IsTrue(Valid && index < Length);
660
661 if (m_IsEmbedded)
662 return m_FixedArray[index];
663 else
664 return m_List[index];
665 }
666 set
667 {
668 Assert.IsTrue(Valid && index < Length);
669
670 if (m_IsEmbedded)
671 m_FixedArray[index] = value;
672 else
673 m_List[index] = value;
674 }
675 }
676
677 public unsafe void Dispose()
678 {
679 if (!Valid)
680 return;
681 m_List.Dispose();
682 Valid = false;
683 }
684 }
685
686 internal interface IDataArrays
687 {
688 void Initialize(int initCapacity);
689 void Dispose();
690 void Grow(int newCapacity);
691 void Remove(int index, int lastIndex);
692 void SetDefault(int index);
693 }
694
695 internal struct EditorInstanceDataArrays : IDataArrays
696 {
697#if UNITY_EDITOR
698 public NativeArray<ulong> sceneCullingMasks;
699 public ParallelBitArray selectedBits;
700
701 public void Initialize(int initCapacity)
702 {
703 sceneCullingMasks = new NativeArray<ulong>(initCapacity, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
704 sceneCullingMasks.FillArray(ulong.MaxValue);
705 selectedBits = new ParallelBitArray(initCapacity, Allocator.Persistent);
706 }
707
708 public void Dispose()
709 {
710 sceneCullingMasks.Dispose();
711 selectedBits.Dispose();
712 }
713
714 public void Grow(int newCapacity)
715 {
716 sceneCullingMasks.ResizeArray(newCapacity);
717 selectedBits.Resize(newCapacity);
718 }
719
720 public void Remove(int index, int lastIndex)
721 {
722 sceneCullingMasks[index] = sceneCullingMasks[lastIndex];
723 selectedBits.Set(index, selectedBits.Get(lastIndex));
724 }
725
726 public void SetDefault(int index)
727 {
728 sceneCullingMasks[index] = ulong.MaxValue;
729 selectedBits.Set(index, false);
730 }
731
732 internal readonly struct ReadOnly
733 {
734 public readonly NativeArray<ulong>.ReadOnly sceneCullingMasks;
735 public readonly ParallelBitArray selectedBits;
736
737 public ReadOnly(in CPUInstanceData instanceData)
738 {
739 sceneCullingMasks = instanceData.editorData.sceneCullingMasks.GetSubArray(0, instanceData.instancesLength).AsReadOnly();
740 selectedBits = instanceData.editorData.selectedBits.GetSubArray(instanceData.instancesLength);
741 }
742 }
743#else
744 public void Initialize(int initCapacity) { }
745 public void Dispose() { }
746 public void Grow(int newCapacity) { }
747 public void Remove(int index, int lastIndex) { }
748 public void SetDefault(int index) { }
749 internal readonly struct ReadOnly { public ReadOnly(in CPUInstanceData instanceData) { } }
750#endif
751 }
752
753 [Flags]
754 internal enum TransformUpdateFlags : byte
755 {
756 None = 0,
757 HasLightProbeCombined = 1 << 0,
758 IsPartOfStaticBatch = 1 << 1
759 }
760
761 [Flags]
762 internal enum InstanceFlags : byte
763 {
764 None = 0,
765 AffectsLightmaps = 1 << 0, // either lightmapped or influence-only
766 IsShadowsOff = 1 << 1, // shadow casting mode is ShadowCastingMode.Off
767 IsShadowsOnly = 1 << 2, // shadow casting mode is ShadowCastingMode.ShadowsOnly
768 HasProgressiveLod = 1 << 3,
769 SmallMeshCulling = 1 << 4
770 }
771
772 internal struct CPUSharedInstanceFlags
773 {
774 public TransformUpdateFlags transformUpdateFlags;
775 public InstanceFlags instanceFlags;
776 }
777
778 internal struct PackedMatrix
779 {
780 /* mat4x3 packed like this:
781 p1.x, p1.w, p2.z, p3.y,
782 p1.y, p2.x, p2.w, p3.z,
783 p1.z, p2.y, p3.x, p3.w,
784 0.0, 0.0, 0.0, 1.0
785 */
786
787 public float4 packed0;
788 public float4 packed1;
789 public float4 packed2;
790
791 public static PackedMatrix FromMatrix4x4(in Matrix4x4 m)
792 {
793 return new PackedMatrix
794 {
795 packed0 = new float4(m.m00, m.m10, m.m20, m.m01),
796 packed1 = new float4(m.m11, m.m21, m.m02, m.m12),
797 packed2 = new float4(m.m22, m.m03, m.m13, m.m23)
798 };
799 }
800
801 public static PackedMatrix FromFloat4x4(in float4x4 m)
802 {
803 return new PackedMatrix
804 {
805 packed0 = new float4(m.c0.x, m.c0.y, m.c0.z, m.c1.x),
806 packed1 = new float4(m.c1.y, m.c1.z, m.c2.x, m.c2.y),
807 packed2 = new float4(m.c2.z, m.c3.x, m.c3.y, m.c3.z)
808 };
809 }
810 }
811}
812
813// +-------------+
814// | Instance |
815// | Handle 2 |
816// +------^------+
817// +-------------+ | +-------------+
818// | Instance | | | Instance |
819// | Handle 0 | | | Handle 3 |
820// +------^------+ | +---^---------+
821// | | /
822// | | /
823// +-----------------------------------------------------------------------------------------------+
824// | | | / |
825// | +-v-- ----+--v - +---v---+----+----+----+ |
826// | InstanceIndices | 0 |free| 1 | 2 |free|free|free|free|... |
827// | +--^-+----+--^-+--^-+----+----+----+----+ |
828// | | / / |
829// | | / / +------------------------- |
830// | | / / | +----------------------- |
831// | | / / | | |
832// | +--v-+--v-+--v-+--v-+--v-+----+ |
833// | Instances | 0 | 2 | 3 | | |... | |
834// | +----+----+----+----+----+----+ |
835// CPUInstanceData | LocalToWorldMatrices | | | | | |... | |
836// | +----+----+----+----+----+----+ |
837// | WorldBoundses | | | | | |... | |
838// | +----+----+----+----+----+----+ |
839// | SharedInstanceHandles | | | | | |... | |
840// | +----+----+----+----+----+----+ |
841// | MovedInCurrentFrameBits | | | | | | ...| |
842// | +----+----+----+----+----+----+ |
843// | SharedInstances | 1 | 1 | 1 | 2 | 3 | ...| |
844// | +-\--+--|-+--/-+--/-+--/-+----+ |
845// | | | / / / |
846// +-----------------------------------------------------------------------------------------------+
847// \ / | | |
848// \ | / / /
849// +-----------------------------------------------------------------------------------------------+
850// | \|/ / / |
851// | +----+-v--+-v--+-v--+----+----+----+----+ |
852// | SharedInstanceIndices |free| 0 | 1 | 2 |free|free|free|free|... |
853// | +----+-|--+--|-+--|-+----+----+----+----+ |
854// | / / / |
855// | / / / |
856// | / / / |
857// | | | | |
858// | / / / |
859// | +-v--+--v-+--v-+----+----+----+ |
860// CPUSharedInstanceData | MeshIDs | | | |... |... |... | |
861// | +----+----+----+----+----+----+ |
862// | LocalBoundses | | | |... |... | ...| |
863// | +----+----+----+----+----+----+ |
864// | RendererGroupIDs | | | | ...| ...| ...| |
865// | +----+----+----+----+----+----+ |
866// | GameObjectLayer | | | |... |... | ...| |
867// | +----+----+----+----+----+----+ |
868// | Flags | | | |... |... | ...| |
869// | +----+----+----+----+----+----+ |
870// | RefCounts | 3 | 1 | 1 |... |... | ...| |
871// | +----+----+----+----+----+----+ |
872// +-----------------------------------------------------------------------------------------------+