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// +-----------------------------------------------------------------------------------------------+