Rework component ids

Remove WorldMetaStorage
This commit is contained in:
Mikhail 2023-07-04 00:00:25 +08:00
parent 5612fe047c
commit 112b96384d
10 changed files with 354 additions and 262 deletions

View File

@ -257,7 +257,7 @@ namespace DCFApixels.DragonECS
}
internal class DebuggerProxy
{
public readonly Type worldType;
public readonly EcsWorld world;
public readonly int worldID;
public readonly int[] included;
public readonly int[] excluded;
@ -265,11 +265,11 @@ namespace DCFApixels.DragonECS
public readonly Type[] excludedTypes;
public DebuggerProxy(EcsMask mask)
{
worldType = WorldMetaStorage.GetWorldType(mask.worldID);
world = EcsWorld.GetWorld(mask.worldID);
worldID = mask.worldID;
included = mask.inc;
excluded = mask.exc;
Type converter(int o) => WorldMetaStorage.GetComponentType(worldID, o);
Type converter(int o) => world.GetComponentType(o);
includedTypes = included.Select(converter).ToArray();
excludedTypes = excluded.Select(converter).ToArray();
}

View File

@ -19,23 +19,6 @@ namespace DCFApixels.DragonECS
component = default;
}
}
private TPool CreatePool<TPool>() where TPool : IEcsPoolImplementation, new()
{
int index = WorldMetaStorage.GetPoolID<TPool>(_worldTypeID);
if (index >= _pools.Length)
{
int oldCapacity = _pools.Length;
Array.Resize(ref _pools, _pools.Length << 1);
ArrayUtility.Fill(_pools, _nullPool, oldCapacity, oldCapacity - _pools.Length);
}
if (_pools[index] == _nullPool)
{
var pool = new TPool();
_pools[index] = pool;
pool.OnInit(this, index);
}
return (TPool)_pools[index];
}
internal readonly struct AspectCache<T> : IEcsWorldComponent<AspectCache<T>>
where T : EcsAspect
{

View File

@ -10,9 +10,6 @@ namespace DCFApixels.DragonECS
{
public readonly short id;
private Type _worldType;
private int _worldTypeID;
private bool _isDestroyed;
private IntDispenser _entityDispenser;
@ -25,9 +22,6 @@ namespace DCFApixels.DragonECS
private int[] _delEntBuffer;
private int _delEntBufferCount;
internal IEcsPoolImplementation[] _pools;
private EcsNullPool _nullPool = EcsNullPool.instance;
private List<WeakReference<EcsGroup>> _groups = new List<WeakReference<EcsGroup>>();
private Stack<EcsGroup> _groupsPool = new Stack<EcsGroup>(64);
@ -36,7 +30,6 @@ namespace DCFApixels.DragonECS
#region Properties
public bool IsDestroyed => _isDestroyed;
public int WorldTypeID => _worldTypeID;
public int Count => _entitiesCount;
public int Capacity => _entitesCapacity; //_denseEntities.Length;
public EcsReadonlyGroup Entities => _allEntites.Readonly;
@ -51,15 +44,12 @@ namespace DCFApixels.DragonECS
if (isIndexable)
{
id = (short)_worldIdDispenser.GetFree();
id = (short)_worldIdDispenser.UseFree();
if (id >= Worlds.Length)
Array.Resize(ref Worlds, Worlds.Length << 1);
Worlds[id] = this;
}
_worldType = this.GetType();
_worldTypeID = WorldMetaStorage.GetWorldID(_worldType);
_entityDispenser = new IntDispenser(0);
_pools = new IEcsPoolImplementation[512];
ArrayUtility.Fill(_pools, _nullPool);
@ -83,42 +73,15 @@ namespace DCFApixels.DragonECS
ReleaseData(id);
_worldIdDispenser.Release(id);
_isDestroyed = true;
_poolIds = null;
_componentIds = null;
}
#endregion
#region ComponentInfo
public int GetComponentID<T>() => WorldMetaStorage.GetComponentID<T>(_worldTypeID);
public int GetComponentID(Type type) => WorldMetaStorage.GetComponentID(type, _worldTypeID);
public Type GetComponentType(int componentID) => WorldMetaStorage.GetComponentType(_worldTypeID, componentID);
public bool IsComponentTypeDeclared<T>() => IsComponentTypeDeclared(typeof(T));
public bool IsComponentTypeDeclared(Type type) => WorldMetaStorage.IsComponentTypeDeclared(_worldTypeID, type);
#endregion
#region Getters
#if UNITY_2020_3_OR_NEWER
[UnityEngine.Scripting.Preserve]
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TPool GetPool<TPool>() where TPool : IEcsPoolImplementation, new()
{
return Get<PoolCache<TPool>>().instance;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TPool UncheckedGetPool<TPool>() where TPool : IEcsPoolImplementation, new()
{
return UncheckedGet<PoolCache<TPool>>().instance;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPool GetPool<TPool>(int worldID) where TPool : IEcsPoolImplementation, new()
{
return Get<PoolCache<TPool>>(worldID).instance;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPool UncheckedGetPool<TPool>(int worldID) where TPool : IEcsPoolImplementation, new()
{
return UncheckedGet<PoolCache<TPool>>(worldID).instance;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TAspect GetAspect<TAspect>() where TAspect : EcsAspect
{

107
src/EcsWorld.pools.cs Normal file
View File

@ -0,0 +1,107 @@
using DCFApixels.DragonECS.Internal;
using DCFApixels.DragonECS.Utils;
using System;
using System.Linq;
using System.Runtime.CompilerServices;
namespace DCFApixels.DragonECS
{
public abstract partial class EcsWorld
{
private SparseArray<int> _poolIds = new SparseArray<int>();
private SparseArray<int> _componentIds = new SparseArray<int>();
private int _poolsCount;
internal IEcsPoolImplementation[] _pools;
private EcsNullPool _nullPool = EcsNullPool.instance;
#region ComponentInfo
public int GetComponentID<T>() => DeclareComponentType(EcsTypeCode.Get<T>());
public int GetComponentID(Type type) => DeclareComponentType(EcsTypeCode.Get(type));
public bool IsComponentTypeDeclared<T>() => _componentIds.Contains(EcsTypeCode.Get<T>());
public bool IsComponentTypeDeclared(Type type) => _componentIds.Contains(EcsTypeCode.Get(type));
public Type GetComponentType(int componentID) => _pools[componentID].ComponentType;
#endregion
#region Getters
#if UNITY_2020_3_OR_NEWER
[UnityEngine.Scripting.Preserve]
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TPool GetPool<TPool>() where TPool : IEcsPoolImplementation, new()
{
return Get<PoolCache<TPool>>().instance;
}
#if UNITY_2020_3_OR_NEWER
[UnityEngine.Scripting.Preserve]
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TPool UncheckedGetPool<TPool>() where TPool : IEcsPoolImplementation, new()
{
return UncheckedGet<PoolCache<TPool>>().instance;
}
#if UNITY_2020_3_OR_NEWER
[UnityEngine.Scripting.Preserve]
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPool GetPool<TPool>(int worldID) where TPool : IEcsPoolImplementation, new()
{
return Get<PoolCache<TPool>>(worldID).instance;
}
#if UNITY_2020_3_OR_NEWER
[UnityEngine.Scripting.Preserve]
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPool UncheckedGetPool<TPool>(int worldID) where TPool : IEcsPoolImplementation, new()
{
return UncheckedGet<PoolCache<TPool>>(worldID).instance;
}
#endregion
#region Declare/Create
private int DeclareComponentType(int typeCode)
{
if (!_componentIds.TryGetValue(typeCode, out int componentId))
{
componentId = _poolsCount++;
_componentIds.Add(typeCode, componentId);
}
return componentId;
}
private TPool CreatePool<TPool>() where TPool : IEcsPoolImplementation, new()
{
if (_poolIds.Contains(EcsTypeCode.Get<TPool>()))
throw new EcsFrameworkException("The pool has already been created.");
Type componentType = typeof(TPool).GetInterfaces().First(o => o.IsGenericType && o.GetGenericTypeDefinition() == typeof(IEcsPoolImplementation<>));
int componentTypeCode = EcsTypeCode.Get(componentType);
if (_componentIds.TryGetValue(componentTypeCode, out int componentID))
{
_poolIds[componentTypeCode] = componentID;
}
else
{
componentID = _poolsCount++;
_poolIds[componentTypeCode] = componentID;
_componentIds[componentTypeCode] = componentID;
}
if (_poolsCount >= _pools.Length)
{
int oldCapacity = _pools.Length;
Array.Resize(ref _pools, _pools.Length << 1);
ArrayUtility.Fill(_pools, _nullPool, oldCapacity, oldCapacity - _pools.Length);
}
if (_pools[componentID] == _nullPool)
{
var pool = new TPool();
_pools[componentID] = pool;
pool.OnInit(this, componentID);
}
return (TPool)_pools[componentID];
}
#endregion
}
}

View File

@ -21,7 +21,7 @@ namespace DCFApixels.DragonECS
private const int DEL_ENT_BUFFER_SIZE_OFFSET = 2;
private static EcsWorld[] Worlds = new EcsWorld[4];
private static IntDispenser _worldIdDispenser = new IntDispenser(0);
private static IdDispenser _worldIdDispenser = new IdDispenser(0);
private static List<DataReleaser> _dataReleaseres = new List<DataReleaser>();
@ -101,5 +101,8 @@ namespace DCFApixels.DragonECS
}
}
}
internal sealed class EcsNullWorld : EcsWorld { }
internal sealed class EcsNullWorld : EcsWorld
{
internal EcsNullWorld() : base(false) { }
}
}

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace DCFApixels.DragonECS
{
@ -9,7 +10,8 @@ namespace DCFApixels.DragonECS
{
private static readonly Dictionary<Type, int> _codes = new Dictionary<Type, int>();
private static int _incremetn = 1;
public static int GetCode(Type type)
public static int Count => _codes.Count;
public static int Get(Type type)
{
if (!_codes.TryGetValue(type, out int code))
{
@ -18,10 +20,11 @@ namespace DCFApixels.DragonECS
}
return code;
}
public static int Count => _codes.Count;
internal static class Cache<T>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Get<T>() => Cache<T>.code;
private static class Cache<T>
{
public static readonly int code = GetCode(typeof(T));
public static readonly int code = Get(typeof(T));
}
}
}

View File

@ -7,7 +7,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace DCFApixels
namespace DCFApixels.DragonECS.Utils
{
[Serializable]
[DebuggerTypeProxy(typeof(DebuggerProxy))]

228
src/Utils/SparseArray.cs Normal file
View File

@ -0,0 +1,228 @@
//SparseArray. Analogous to Dictionary<int, T>, but faster.
//Benchmark result of indexer.get speed test with 300 elements:
//[Dictinary: 5.786us] [SparseArray: 2.047us].
using System;
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace DCFApixels.DragonECS.Utils
{
public class SparseArray<TValue>
{
public const int MIN_CAPACITY_BITS_OFFSET = 4;
public const int MIN_CAPACITY = 1 << MIN_CAPACITY_BITS_OFFSET;
private const int EMPTY = -1;
private int[] _buckets = Array.Empty<int>();
private Entry[] _entries = Array.Empty<Entry>();
private int _count;
private int _freeList;
private int _freeCount;
private int _modBitMask;
#region Properties
public TValue this[int keyX, int keyY]
{
get => _entries[FindEntry((keyX << 32) | keyY)].value;
set => Insert(keyX + (keyY << 32), value);
}
public TValue this[int key]
{
get => _entries[FindEntry(key)].value;
set => Insert(key, value);
}
public int Count => _count;
#endregion
#region Constructors
public SparseArray(int minCapacity = MIN_CAPACITY)
{
minCapacity = NormalizeCapacity(minCapacity);
_buckets = new int[minCapacity];
for (int i = 0; i < minCapacity; i++)
_buckets[i] = EMPTY;
_entries = new Entry[minCapacity];
_modBitMask = (minCapacity - 1) & 0x7FFFFFFF;
}
#endregion
#region Add/Contains/Remove
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(int keyX, int keyY, TValue value) => Add((keyX << 32) | keyY, value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(int key, TValue value)
{
#if DEBUG
if (Contains(key))
throw new ArgumentException("Contains(hashKey) is true");
#endif
Insert(key, value);
}
public bool Contains(int keyX, int keyY) => FindEntry((keyX << 32) | keyY) >= 0;
public bool Contains(int key) => FindEntry(key) >= 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Remove(int keyX, int keyY) => Remove((keyX << 32) | keyY);
public bool Remove(int key)
{
int bucket = key & _modBitMask;
int last = -1;
for (int i = _buckets[bucket]; i >= 0; last = i, i = _entries[i].next)
{
if (_entries[i].hashKey == key)
{
if (last < 0)
{
_buckets[bucket] = _entries[i].next;
}
else
{
_entries[last].next = _entries[i].next;
}
_entries[i].next = _freeList;
_entries[i].hashKey = -1;
_entries[i].value = default;
_freeList = i;
_freeCount++;
return true;
}
}
return false;
}
#endregion
#region Find/Insert
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int FindEntry(int key)
{
for (int i = _buckets[key & _modBitMask]; i >= 0; i = _entries[i].next)
if (_entries[i].hashKey == key) return i;
return -1;
}
private void Insert(int key, TValue value)
{
int targetBucket = key & _modBitMask;
for (int i = _buckets[targetBucket]; i >= 0; i = _entries[i].next)
{
if (_entries[i].hashKey == key)
{
_entries[i].value = value;
return;
}
}
int index;
if (_freeCount > 0)
{
index = _freeList;
_freeList = _entries[index].next;
_freeCount--;
}
else
{
if (_count == _entries.Length)
{
Resize();
targetBucket = key & _modBitMask;
}
index = _count++;
}
_entries[index].next = _buckets[targetBucket];
_entries[index].hashKey = key;
_entries[index].value = value;
_buckets[targetBucket] = index;
}
#endregion
#region TryGetValue
public bool TryGetValue(int keyX, int keyY, out TValue value)
{
int index = FindEntry((keyX << 32) | keyY);
if (index < 0)
{
value = default;
return false;
}
value = _entries[index].value;
return true;
}
public bool TryGetValue(int key, out TValue value)
{
int index = FindEntry(key);
if (index < 0)
{
value = default;
return false;
}
value = _entries[index].value;
return true;
}
#endregion
#region Clear
public void Clear()
{
if (_count > 0)
{
for (int i = 0; i < _buckets.Length; i++)
{
_buckets[i] = -1;
}
Array.Clear(_entries, 0, _count);
_count = 0;
}
}
#endregion
#region Resize
private void Resize()
{
int newSize = _buckets.Length << 1;
_modBitMask = (newSize - 1) & 0x7FFFFFFF;
Contract.Assert(newSize >= _entries.Length);
int[] newBuckets = new int[newSize];
for (int i = 0; i < newBuckets.Length; i++)
newBuckets[i] = EMPTY;
Entry[] newEntries = new Entry[newSize];
Array.Copy(_entries, 0, newEntries, 0, _count);
for (int i = 0; i < _count; i++)
{
if (newEntries[i].hashKey >= 0)
{
int bucket = newEntries[i].hashKey % newSize;
newEntries[i].next = newBuckets[bucket];
newBuckets[bucket] = i;
}
}
_buckets = newBuckets;
_entries = newEntries;
}
private int NormalizeCapacity(int capacity)
{
int result = MIN_CAPACITY;
while (result < capacity) result <<= 1;
return result;
}
#endregion
#region Utils
[StructLayout(LayoutKind.Sequential, Pack = 4)]
private struct Entry
{
public int next; // Index of next entry, -1 if last
public int hashKey;
public TValue value;
}
#endregion
}
}

View File

@ -1,184 +0,0 @@
using DCFApixels.DragonECS.Utils;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace DCFApixels.DragonECS
{
//TODO этот класс требует переработки, изначально такая конструкция имела хорошую производительность, но сейчас он слишком раздулся
internal static class WorldMetaStorage
{
private static int _tokenCount = 0;
private static List<ResizerBase> _resizers = new List<ResizerBase>();
private static WorldTypeMeta[] _metas = new WorldTypeMeta[0];
private static Dictionary<Type, int> _worldIds = new Dictionary<Type, int>();
private static class WorldIndex<TWorldArchetype>
{
public static int id = GetWorldID(typeof(TWorldArchetype));
}
private static int GetToken(Type worldType)
{
WorldTypeMeta meta = new WorldTypeMeta(worldType);
meta.id = _tokenCount;
Array.Resize(ref _metas, ++_tokenCount);
_metas[_tokenCount - 1] = meta;
foreach (var item in _resizers)
item.Resize(_tokenCount);
return _tokenCount - 1;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetWorldID(Type worldType)
{
if (!_worldIds.TryGetValue(worldType, out int id))
{
id = GetToken(worldType);
_worldIds.Add(worldType, id);
}
return id;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Type GetWorldType(int worldTypeID) => _metas[worldTypeID].worldType;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetWorldID<TWorldArchetype>() => WorldIndex<TWorldArchetype>.id;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetComponentID<T>(int worldID) => Component<T>.Get(worldID);
public static int GetComponentID(Type type, int worldID) => _metas[worldID].GetComponentID(type);
public static bool IsComponentTypeDeclared(int worldID, Type type) => _metas[worldID].IsDeclaredComponentType(type);
public static Type GetComponentType(int worldID, int componentID) => _metas[worldID].GetComponentType(componentID);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetPoolID<T>(int worldID) => Pool<T>.Get(worldID);
private abstract class ResizerBase
{
public abstract Type Type { get; }
public abstract int[] IDS { get; }
public abstract void Resize(int size);
}
#region Containers
public static class PoolComponentIdArrays
{
private static Dictionary<Type, int[]> _componentTypeArrayPairs = new Dictionary<Type, int[]>();
public static int[] GetIdsArray(Type type)
{
int targetSize = _tokenCount;
if (!_componentTypeArrayPairs.TryGetValue(type, out int[] result))
{
result = new int[targetSize];
for (int i = 0; i < result.Length; i++)
result[i] = -1;
_componentTypeArrayPairs.Add(type, result);
}
else
{
if (result.Length < targetSize)
{
int oldSize = result.Length;
Array.Resize(ref result, targetSize);
ArrayUtility.Fill(result, -1, oldSize, targetSize);
_componentTypeArrayPairs[type] = result;
}
}
return result;
}
public static int GetComponentID(Type type, int token)
{
GetIdsArray(type);
ref int id = ref _componentTypeArrayPairs[type][token];
if (id < 0)
id = _metas[token].DeclareComponentType(type);
return id;
}
}
private static class Pool<T>
{
public static int[] ids;
private static Type componentType = typeof(T).GetGenericArguments()[0];
static Pool()
{
ids = PoolComponentIdArrays.GetIdsArray(componentType);
_resizers.Add(new Resizer());
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Get(int token)
{
ref int id = ref ids[token];
if (id < 0)
{
id = PoolComponentIdArrays.GetComponentID(componentType, token);
}
return id;
}
private sealed class Resizer : ResizerBase
{
public override Type Type => typeof(T);
public override int[] IDS => ids;
public override void Resize(int size)
{
ids = PoolComponentIdArrays.GetIdsArray(componentType);
}
}
}
private static class Component<T>
{
public static int[] ids;
static Component()
{
ids = PoolComponentIdArrays.GetIdsArray(typeof(T));
_resizers.Add(new Resizer());
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Get(int token)
{
ref int id = ref ids[token];
if (id < 0)
{
id = PoolComponentIdArrays.GetComponentID(typeof(T), token);
}
return id;
}
private sealed class Resizer : ResizerBase
{
public override Type Type => typeof(T);
public override int[] IDS => ids;
public override void Resize(int size)
{
ids = PoolComponentIdArrays.GetIdsArray(typeof(T));
}
}
}
#endregion
private class WorldTypeMeta
{
public readonly Type worldType;
public int id;
public int componentCount;
public int worldComponentCount;
private Type[] _types = new Type[10];
private Dictionary<Type, int> _declaredComponentTypes = new Dictionary<Type, int>();
public WorldTypeMeta(Type worldType)
{
this.worldType = worldType;
}
public int DeclareComponentType(Type type)
{
int id = componentCount++;
if (_types.Length <= id)
Array.Resize(ref _types, id + 10);
_types[id] = type;
_declaredComponentTypes.Add(type, id);
return id;
}
public bool IsDeclaredComponentType(Type type) => _declaredComponentTypes.ContainsKey(type);
public Type GetComponentType(int componentID) => _types[componentID];
public int GetComponentID(Type type) => PoolComponentIdArrays.GetComponentID(type, id);
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 5e06cf4352ab9414293b145eb27daba7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: