Skip to content

Commit

Permalink
Merge pull request #30 from andreakarasho/rules
Browse files Browse the repository at this point in the history
Rules
  • Loading branch information
andreakarasho authored Aug 2, 2024
2 parents dc43b6f + 6efc75d commit 06e5314
Show file tree
Hide file tree
Showing 12 changed files with 477 additions and 354 deletions.
125 changes: 80 additions & 45 deletions src/Archetype.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ internal ArchetypeChunk(ImmutableArray<ComponentInfo> sign, int chunkSize)
Entities = new EntityView[chunkSize];
Data = new Array[sign.Length];
for (var i = 0; i < sign.Length; ++i)
Data[i] = sign[i].Size > 0 ? Lookup.GetArray(sign[i].ID, chunkSize)! : null!;
Data[i] = Lookup.GetArray(sign[i].ID, chunkSize)!;
}

public int Count { get; internal set; }
Expand Down Expand Up @@ -75,19 +75,19 @@ public sealed class Archetype
{
const int ARCHETYPE_INITIAL_CAPACITY = 4;

internal const int CHUNK_THRESHOLD = 0xFFF;
internal const int CHUNK_SIZE = 4096;
private const int CHUNK_LOG2 = 12;
internal const int CHUNK_THRESHOLD = CHUNK_SIZE - 1;


private readonly World _world;
private ArchetypeChunk[] _chunks;
private readonly ComponentComparer _comparer;
private readonly FrozenDictionary<EcsID, int> _lookup;
private readonly FrozenDictionary<EcsID, int> _componentsLookup, _pairsLookup, _allLookup;
private readonly EcsID[] _ids;
private readonly List<EcsEdge> _add, _remove;
private int _count;

private readonly int[] _fastLookup;

internal Archetype(
World world,
Expand All @@ -103,17 +103,37 @@ ComponentComparer comparer
Pairs = All.Where(x => x.ID.IsPair()).ToImmutableArray();
_chunks = new ArchetypeChunk[ARCHETYPE_INITIAL_CAPACITY];


var roll = new RollingHash();
var dict = new Dictionary<EcsID, int>();
for (var i = 0; i < sign.Length; ++i)
var allDict = new Dictionary<EcsID, int>();
var maxId = -1;
for (int i = 0, cur = 0; i < sign.Length; ++i)
{
dict.Add(sign[i].ID, i);
roll.Add(sign[i].ID);

if (sign[i].Size > 0)
{
dict.Add(sign[i].ID, cur);
cur += 1;
maxId = Math.Max(maxId, (int)sign[i].ID);
}

allDict.Add(sign[i].ID, i);
}

Id = roll.Hash;

_lookup = dict.ToFrozenDictionary();
_fastLookup = new int[maxId + 1];
_fastLookup.AsSpan().Fill(-1);
foreach ((var id, var i) in dict)
{
_fastLookup[(int)id] = i;
}

_componentsLookup = dict.ToFrozenDictionary();
_allLookup = allDict.ToFrozenDictionary();
_pairsLookup = allDict.Where(s => s.Key.IsPair()).GroupBy(s => s.Key.First()).ToFrozenDictionary(s => s.Key, v => v.First().Value);

_ids = All.Select(s => s.ID).ToArray();
_add = new ();
Expand All @@ -128,7 +148,7 @@ ComponentComparer comparer
internal Span<ArchetypeChunk> Chunks => _chunks.AsSpan(0, (_count + CHUNK_SIZE - 1) >> CHUNK_LOG2);
internal Memory<ArchetypeChunk> MemChunks => _chunks.AsMemory(0, (_count + CHUNK_SIZE - 1) >> CHUNK_LOG2);
internal int EmptyChunks => _chunks.Length - ((_count + CHUNK_SIZE - 1) >> CHUNK_LOG2);

internal EcsID[] Sign => _ids;

private ref ArchetypeChunk GetOrCreateChunk(int index)
{
Expand All @@ -140,7 +160,7 @@ private ref ArchetypeChunk GetOrCreateChunk(int index)
ref var chunk = ref _chunks[index];
if (chunk.Data == null)
{
chunk = new ArchetypeChunk(All, CHUNK_SIZE);
chunk = new ArchetypeChunk(Components, CHUNK_SIZE);
}

return ref chunk;
Expand All @@ -158,41 +178,62 @@ public ChunkEnumerator GetEnumerator()
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal int GetComponentIndex(EcsID id)
{
ref readonly var idx = ref _lookup.GetValueRefOrNullRef(id);
return Unsafe.IsNullRef(ref Unsafe.AsRef(in idx))? -1 : idx;
if (id.IsPair())
{
if (_componentsLookup.TryGetValue(id, out var index))
return index;
return -1;
}

return (int)id >= _fastLookup.Length ? -1 : _fastLookup[(int)id];
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int GetComponentIndex<T>() where T : struct
internal int GetPairIndex(EcsID id)
{
var id = Lookup.Component<T>.HashCode;
return GetComponentIndex(id);
if (_pairsLookup.TryGetValue(id, out var index))
return index;
return -1;
}

internal int Add(EntityView ent)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal int GetAnyIndex(EcsID id)
{
ref var chunk = ref GetOrCreateChunk(_count);
chunk.EntityAt(chunk.Count++) = ent;
return _count++;
if (_allLookup.TryGetValue(id, out var index))
return index;
return -1;
}

internal bool HasIndex(EcsID id)
{
return _allLookup.ContainsKey(id);
}

internal ref ArchetypeChunk Add2(EntityView ent, out int newRow)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int GetComponentIndex<T>() where T : struct
{
var id = Lookup.Component<T>.HashCode;
var size = Lookup.Component<T>.Size;
return size > 0 ? GetComponentIndex(id) : GetAnyIndex(id);
}

internal ref ArchetypeChunk Add(EntityView ent, out int row)
{
ref var chunk = ref GetOrCreateChunk(_count);
chunk.EntityAt(chunk.Count++) = ent;
newRow = _count++;
row = _count++;
return ref chunk;
}

internal int Add(EcsID id)
=> Add(new(_world, id));
internal ref ArchetypeChunk Add(EcsID id, out int newRow)
=> ref Add(new(_world, id), out newRow);

private EcsID RemoveByRow(int row)
private EcsID RemoveByRow(ref ArchetypeChunk chunk, int row)
{
_count -= 1;
EcsAssert.Assert(_count >= 0, "Negative count");

ref var chunk = ref GetChunk(row);
// ref var chunk = ref GetChunk(row);
ref var lastChunk = ref GetChunk(_count);
var removed = chunk.EntityAt(row).ID;

Expand All @@ -204,20 +245,18 @@ private EcsID RemoveByRow(int row)

var srcIdx = _count & CHUNK_THRESHOLD;
var dstIdx = row & CHUNK_THRESHOLD;
var items = All;
var items = Components;
for (var i = 0; i < items.Length; ++i)
{
var size = items[i].Size;
if (size <= 0)
continue;

var arrayToBeRemoved = chunk.Data![i];
var lastValidArray = lastChunk.Data![i];

CopyFast(lastValidArray, srcIdx, arrayToBeRemoved, dstIdx, 1, size, items[i].IsManaged);
CopyFast(lastValidArray, srcIdx, arrayToBeRemoved, dstIdx, 1, items[i].Size, items[i].IsManaged);
}

_world.GetRecord(chunk.EntityAt(row).ID).Row = row;
ref var rec = ref _world.GetRecord(chunk.EntityAt(row).ID);
rec.Chunk = chunk;
rec.Row = row;
}

// lastChunk.EntityAt(_count) = EntityView.Invalid;
Expand All @@ -240,7 +279,7 @@ private EcsID RemoveByRow(int row)
}

internal EcsID Remove(ref EcsRecord record)
=> RemoveByRow(record.Row);
=> RemoveByRow(ref record.Chunk, record.Row);

internal Archetype InsertVertex(
Archetype left,
Expand All @@ -256,21 +295,20 @@ EcsID id
return vertex;
}

internal int MoveEntity(Archetype newArch, int oldRow, bool isRemove)
internal ref ArchetypeChunk MoveEntity(Archetype newArch, ref ArchetypeChunk fromChunk, int oldRow, bool isRemove, out int newRow)
{
ref var fromChunk = ref GetChunk(oldRow);
ref var toChunk = ref newArch.Add2(fromChunk.EntityAt(oldRow), out var newRow);
ref var toChunk = ref newArch.Add(fromChunk.EntityAt(oldRow), out newRow);

int i = 0, j = 0;
var count = isRemove ? newArch.All.Length : All.Length;
var count = isRemove ? newArch.Components.Length : Components.Length;

ref var x = ref (isRemove ? ref j : ref i);
ref var y = ref (!isRemove ? ref j : ref i);

var srcIdx = oldRow & CHUNK_THRESHOLD;
var dstIdx = newRow & CHUNK_THRESHOLD;
var items = All;
var newItems = newArch.All;
var items = Components;
var newItems = newArch.Components;
for (; x < count; ++x, ++y)
{
while (items[i].ID != newItems[j].ID)
Expand All @@ -279,19 +317,16 @@ internal int MoveEntity(Archetype newArch, int oldRow, bool isRemove)
++y;
}

var size = items[i].Size;
if (size <= 0)
continue;

var fromArray = fromChunk.Data![i];
var toArray = toChunk.Data![j];

// copy the moved entity to the target archetype
CopyFast(fromArray!, srcIdx, toArray!, dstIdx, 1, size, items[i].IsManaged);
CopyFast(fromArray!, srcIdx, toArray!, dstIdx, 1, items[i].Size, items[i].IsManaged);
}

_ = RemoveByRow(oldRow);
return newRow;
_ = RemoveByRow(ref fromChunk, oldRow);

return ref toChunk;
}

internal void Clear()
Expand Down
13 changes: 9 additions & 4 deletions src/Default.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public static class Defaults
/// <summary>
/// Built-in tag.<br/>Shortcut for child.Add{ChildOf}(parent);
/// </summary>
public struct ChildOf { }
public readonly struct ChildOf { public static readonly EcsID ID = Lookup.Component<ChildOf>.Value.ID; }

/// <summary>
/// Built-in component.<br/>Used in combination with <see cref="Name"/>
Expand Down Expand Up @@ -50,7 +50,7 @@ public readonly struct Name
/// </code>
/// </example>
/// </summary>
public struct Unique { }
public readonly struct Unique { public static readonly EcsID ID = Lookup.Component<Unique>.Value.ID; }

/// <summary>
/// Built-in tag.<br/>Used to add the same rule to the target entity.<para/>
Expand All @@ -70,7 +70,7 @@ public struct Unique { }
/// </code>
/// </example>
/// </summary>
public struct Symmetric { }
public readonly struct Symmetric { public static readonly EcsID ID = Lookup.Component<Symmetric>.Value.ID; }

/// <summary>
/// Built-in tag.<br/>Mark a component/tag to not be deleted<para/>
Expand Down Expand Up @@ -153,5 +153,10 @@ public struct Panic { }
/// </code>
/// </example>
/// </summary>
public struct Unset { }
public readonly struct Unset { public static readonly EcsID ID = Lookup.Component<Unset>.Value.ID; }




internal readonly struct Rule { public static readonly EcsID ID = Lookup.Component<Rule>.Value.ID; }
}
71 changes: 10 additions & 61 deletions src/EcsID.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,83 +2,32 @@ namespace TinyEcs;

public static class EcsIdEx
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsPair(this EcsID id)
=> IDOp.IsPair(id);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static EcsID First(this EcsID id)
=> id.IsPair() ? IDOp.GetPairFirst(id) : 0;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static EcsID Second(this EcsID id)
=> id.IsPair() ? IDOp.GetPairSecond(id) : 0;

public static (EcsID, EcsID) Pair(this EcsID id)
=> (id.First(), id.Second());
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static (EcsID first, EcsID second) Pair(this EcsID id)
=> (IDOp.GetPairFirst(id), IDOp.GetPairSecond(id));
// => id.IsPair() ? (IDOp.GetPairFirst(id), IDOp.GetPairSecond(id)) : (0, 0);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsValid(this EcsID id)
=> id != 0;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static EcsID RealId(this EcsID id)
=> IDOp.RealID(id);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Generation(this EcsID id)
=> (int) IDOp.GetGeneration(id);
}

/// <summary>
/// The ecs entity rappresentation which is a wrapper around an ulong
/// </summary>
// [StructLayout(LayoutKind.Explicit)]
// public readonly struct EcsID : IEquatable<ulong>, IComparable<ulong>, IEquatable<EcsID>, IComparable<EcsID>
// {
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
// internal EcsID(ulong value) => Value = value;
//
//
// /// <summary>
// /// ID + Generation
// /// </summary>
// [FieldOffset(0)]
// public readonly ulong Value;
//
// /// <summary>
// /// ID only
// /// </summary>
// [FieldOffset(0)]
// internal readonly int ID;
//
// /// <summary>
// /// Generation count.<br/>
// /// This number rappresent how many times the real ID has been recycled.
// /// </summary>
// [FieldOffset(4)]
// internal readonly int Generation;
//
//
// public readonly bool IsValid => Value != 0;
// public readonly bool IsPair => IDOp.IsPair(Value);
// public readonly EcsID First => IsPair ? IDOp.GetPairFirst(Value) : 0;
// public readonly EcsID Second => IsPair ? IDOp.GetPairSecond(Value) : 0;
// public readonly (EcsID, EcsID) Pair => (First, Second);
//
//
// public readonly int CompareTo(ulong other) => Value.CompareTo(other);
// public readonly int CompareTo(EcsID other) => Value.CompareTo(other.Value);
// public readonly bool Equals(ulong other) => Value == other;
// public readonly bool Equals(EcsID other) => Value == other.Value;
//
//
// // public static implicit operator ulong(EcsID id) => id.Value;
// // public static implicit operator EcsID(ulong value) => new (value);
// public static bool operator ==(EcsID id, EcsID other) => id.Value.Equals(other.Value);
// public static bool operator !=(EcsID id, EcsID other) => !id.Value.Equals(other.Value);
//
//
// public readonly override bool Equals(object? obj) => obj is EcsID ent && Equals(ent);
// public readonly override int GetHashCode() => Value.GetHashCode();
// public readonly override string ToString()
// {
// if (IsPair)
// return $"({First}, {Second}) | {Value}";
// return $"[{ID} - @{Generation} | {Value}]";
// }
// }
Loading

0 comments on commit 06e5314

Please sign in to comment.