using System; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Mathematics; using UnityEngine.Assertions; namespace UnityEngine.Rendering { //@ Add instance version to detect dangling instance handles. internal struct InstanceHandle : IEquatable, IComparable { // Don't use this index to reference GPU data. This index is to reference CPU data only. // To reference GPU data convert InstahceHandle to GPUInstanceIndex. public int index { get; private set; } // This is unique instance index for each instance type. public int instanceIndex => index >> InstanceTypeInfo.kInstanceTypeBitCount; // We store type bits as lower bits because this makes max InstanceHandle index bounded by how many instances we have. // So you can allocate directly indexed arrays. This is fine as long as we have only 1 to 4 instance types. // If we put type bits in higher bits then we might want to make CPUInstanceData sparse set InstanceIndices table to be paged. public InstanceType type => (InstanceType)(index & InstanceTypeInfo.kInstanceTypeMask); public bool valid => index != -1; public static readonly InstanceHandle Invalid = new InstanceHandle() { index = -1 }; public static InstanceHandle Create(int instanceIndex, InstanceType instanceType) { return new InstanceHandle() { index = instanceIndex << InstanceTypeInfo.kInstanceTypeBitCount | (int)instanceType }; } public static InstanceHandle FromInt(int value) { return new InstanceHandle() { index = value }; } public bool Equals(InstanceHandle other) => index == other.index; public int CompareTo(InstanceHandle other) { return index.CompareTo(other.index); } public override int GetHashCode() { return index; } } internal struct SharedInstanceHandle : IEquatable, IComparable { public int index { get; set; } public bool valid => index != -1; public static readonly SharedInstanceHandle Invalid = new SharedInstanceHandle() { index = -1 }; public bool Equals(SharedInstanceHandle other) => index == other.index; public int CompareTo(SharedInstanceHandle other) { return index.CompareTo(other.index); } public override int GetHashCode() { return index; } } internal struct GPUInstanceIndex : IEquatable, IComparable { public int index { get; set; } public bool valid => index != -1; public static readonly GPUInstanceIndex Invalid = new GPUInstanceIndex() { index = -1 }; public bool Equals(GPUInstanceIndex other) => index == other.index; public int CompareTo(GPUInstanceIndex other) { return index.CompareTo(other.index); } public override int GetHashCode() { return index; } } internal struct InstanceAllocator { private NativeArray m_StructData; private NativeList m_FreeInstances; private int m_BaseInstanceOffset; private int m_InstanceStride; public int length { get => m_StructData[0]; set => m_StructData[0] = value; } public bool valid => m_StructData.IsCreated; public void Initialize(int baseInstanceOffset = 0, int instanceStride = 1) { m_StructData = new NativeArray(1, Allocator.Persistent); m_FreeInstances = new NativeList(Allocator.Persistent); m_BaseInstanceOffset = baseInstanceOffset; m_InstanceStride = instanceStride; } public void Dispose() { m_StructData.Dispose(); m_FreeInstances.Dispose(); } public int AllocateInstance() { int instance; if (m_FreeInstances.Length > 0) { instance = m_FreeInstances[m_FreeInstances.Length - 1]; m_FreeInstances.RemoveAtSwapBack(m_FreeInstances.Length - 1); } else { instance = length * m_InstanceStride + m_BaseInstanceOffset; length += 1; } return instance; } public void FreeInstance(int instance) { //@ This is a bit weak validation. Need something better but fast. Assert.IsTrue(instance >= 0 && instance < length * m_InstanceStride); m_FreeInstances.Add(instance); } public int GetNumAllocated() { return length - m_FreeInstances.Length; } } internal unsafe struct InstanceAllocators { private InstanceAllocator m_InstanceAlloc_MeshRenderer; private InstanceAllocator m_InstanceAlloc_SpeedTree; private InstanceAllocator m_SharedInstanceAlloc; public void Initialize() { //@ Will keep it as two separate allocators for two types for now. Nested native containers are not allowed in burst. m_InstanceAlloc_MeshRenderer = new InstanceAllocator(); m_InstanceAlloc_SpeedTree = new InstanceAllocator(); m_InstanceAlloc_MeshRenderer.Initialize((int)InstanceType.MeshRenderer, InstanceTypeInfo.kMaxInstanceTypesCount); m_InstanceAlloc_SpeedTree.Initialize((int)InstanceType.SpeedTree, InstanceTypeInfo.kMaxInstanceTypesCount); m_SharedInstanceAlloc = new InstanceAllocator(); m_SharedInstanceAlloc.Initialize(); } public unsafe void Dispose() { m_InstanceAlloc_MeshRenderer.Dispose(); m_InstanceAlloc_SpeedTree.Dispose(); m_SharedInstanceAlloc.Dispose(); } private InstanceAllocator GetInstanceAllocator(InstanceType type) { switch (type) { case InstanceType.MeshRenderer: return m_InstanceAlloc_MeshRenderer; case InstanceType.SpeedTree: return m_InstanceAlloc_SpeedTree; default: throw new ArgumentException("Allocator for this type is not created."); } } public int GetInstanceHandlesLength(InstanceType type) { return GetInstanceAllocator(type).length; } public int GetInstancesLength(InstanceType type) { return GetInstanceAllocator(type).GetNumAllocated(); } public InstanceHandle AllocateInstance(InstanceType type) { return InstanceHandle.FromInt(GetInstanceAllocator(type).AllocateInstance()); } public void FreeInstance(InstanceHandle instance) { Assert.IsTrue(instance.valid); GetInstanceAllocator(instance.type).FreeInstance(instance.index); } public unsafe SharedInstanceHandle AllocateSharedInstance() { return new SharedInstanceHandle { index = m_SharedInstanceAlloc.AllocateInstance() }; } public void FreeSharedInstance(SharedInstanceHandle instance) { Assert.IsTrue(instance.valid); m_SharedInstanceAlloc.FreeInstance(instance.index); } } }