Skip to content

Commit

Permalink
experimental query tuple
Browse files Browse the repository at this point in the history
  • Loading branch information
andreakarasho committed Feb 11, 2024
1 parent bb7d3e2 commit 661fa0c
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 85 deletions.
34 changes: 33 additions & 1 deletion samples/MyBattleground/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,22 @@
using var ecs = new World();


ecs.Entity()
.Set<Position>(new Position())
.Set<Velocity>(new Velocity());

ecs.Entity()
.Set<Position>(new Position())
.Set<Velocity>(new Velocity());

for (int i = 0; i < ENTITIES_COUNT; i++)
ecs.Entity()
.Set<Position>(new Position())
.Set<Velocity>(new Velocity())
.Set<PlayerTag>();



ecs.Query()
.With<PlayerTag>()
.EachWithEntity(static (ref readonly EntityView e, ref Velocity pos, ref Position vel) =>
Expand Down Expand Up @@ -43,7 +52,7 @@
// pos.Y *= vel.Y;
// });

foreach (var archetype in query)
foreach (var archetype in ecs.Query<(Position, Velocity)>())
{
var column0 = archetype.GetComponentIndex<Position>();
var column1 = archetype.GetComponentIndex<Velocity>();
Expand All @@ -65,6 +74,29 @@
}
}
}

// foreach (var archetype in query)
// {
// var column0 = archetype.GetComponentIndex<Position>();
// var column1 = archetype.GetComponentIndex<Velocity>();
//
// foreach (ref readonly var chunk in archetype)
// {
// ref var pos = ref chunk.GetReference<Position>(column0);
// ref var vel = ref chunk.GetReference<Velocity>(column1);
//
// ref var last2 = ref Unsafe.Add(ref pos, chunk.Count);
//
// while (Unsafe.IsAddressLessThan(ref pos, ref last2))
// {
// pos.X *= vel.X;
// pos.Y *= vel.Y;
//
// pos = ref Unsafe.Add(ref pos, 1);
// vel = ref Unsafe.Add(ref vel, 1);
// }
// }
// }
}

last = start;
Expand Down
17 changes: 1 addition & 16 deletions src/Commands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,6 @@ public void Delete(EcsID id)
}
}

public unsafe void Set(EcsID id, EcsID tag)
{
// EcsAssert.Assert(!IDOp.IsPair(tag));
//
// if (_main.Exists(tag) && Has<EcsComponent>(tag))
// {
// ref readonly var cmp2 = ref _main.Component<EcsComponent>();
// Set(id, in cmp2);
// return;
// }
//
// var cmp = new EcsComponent(tag, 0);
// Set(id, ref cmp);
}

public void Set<T>(EcsID id) where T : struct
{
ref readonly var cmp = ref _main.Component<T>();
Expand Down Expand Up @@ -180,7 +165,7 @@ public void Clear()
_despawn.Clear();
}

private unsafe struct SetComponent
private struct SetComponent
{
public EcsID Entity;
public int Component;
Expand Down
4 changes: 3 additions & 1 deletion src/DictionarySlim.cs
Original file line number Diff line number Diff line change
Expand Up @@ -215,9 +215,10 @@ public bool Remove(TKey key)
/// </summary>
/// <param name="key">Key to look for</param>
/// <returns>Reference to the new or existing value</returns>
public ref TValue GetOrAddValueRef(TKey key)
public ref TValue GetOrAddValueRef(TKey key, out bool exists)
{
if (key == null) ThrowHelper.ThrowKeyArgumentNullException();
exists = true;
Entry[] entries = _entries;
int collisionCount = 0;
int bucketIndex = key.GetHashCode() & (_buckets.Length - 1);
Expand All @@ -235,6 +236,7 @@ public ref TValue GetOrAddValueRef(TKey key)
collisionCount++;
}

exists = false;
return ref AddKey(key, bucketIndex);
}

Expand Down
62 changes: 1 addition & 61 deletions src/EntityView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace TinyEcs;
#endif
[StructLayout(LayoutKind.Sequential)]
[DebuggerDisplay("ID: {ID}")]
public readonly unsafe struct EntityView : IEquatable<EcsID>, IEquatable<EntityView>
public readonly struct EntityView : IEquatable<EcsID>, IEquatable<EntityView>
{
public static readonly EntityView Invalid = new(null, 0);

Check warning on line 10 in src/EntityView.cs

View workflow job for this annotation

GitHub Actions / build

Cannot convert null literal to non-nullable reference type.

Check warning on line 10 in src/EntityView.cs

View workflow job for this annotation

GitHub Actions / build

Cannot convert null literal to non-nullable reference type.

Expand Down Expand Up @@ -35,36 +35,12 @@ public readonly EntityView Set<T>() where T : struct
return this;
}

public readonly EntityView Set(EcsID id)
{
World.Set(ID, id);
return this;
}

public readonly EntityView Set<T>(T component) where T : struct
{
World.Set(ID, component);
return this;
}

//public readonly EntityView Set<TKind, TTarget>()
// where TKind : unmanaged
// where TTarget : unmanaged
//{
// return Set(World.Entity<TKind>(), World.Entity<TTarget>());
//}

//public readonly EntityView Set<TKind>(EcsID target) where TKind : unmanaged
//{
// return Set(World.Entity<TKind>(), target);
//}

//public readonly EntityView Set(EcsID first, EcsID second)
//{
// World.Set(ID, first, second);
// return this;
//}

public readonly EntityView Unset<T>() where T : struct
{
World.Unset<T>(ID);
Expand All @@ -89,42 +65,6 @@ public readonly EntityView Disable()

public readonly bool Has<T>() where T : struct => World.Has<T>(ID);

//public readonly bool Has<TKind, TTarget>()
// where TKind : unmanaged
// where TTarget : unmanaged
//{
// return World.Has(ID, World.Entity<TKind>(), World.Entity<TTarget>());
//}

//public readonly EntityView ChildOf(EcsID parent)
//{
// World.Set<EcsChildOf>(ID, parent);
// return this;
//}

//public readonly void Children(Action<EntityView> action)
//{
// World
// .Query()
// .With<EcsChildOf>(ID)
// .Iterate(
// (ref Iterator it) =>
// {
// for (int i = 0, count = it.Count; i < count; ++i)
// action(it.Entity(i));
// }
// );
//}

//public readonly void ClearChildren()
//{
// var id = World.Entity<EcsChildOf>();
// var myID = ID; // lol
// Children(v => v.Unset(id, myID));
//}

//public readonly EntityView Parent() => World.Entity(World.GetParent(ID));

public readonly void Delete() => World.Delete(ID);

public readonly bool Exists() => World.Exists(ID);
Expand Down
4 changes: 4 additions & 0 deletions src/Query.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ private Query With(int id)
term.ID = id;
term.Op = TermOp.With;

_terms.Span.Sort();

return this;
}

Expand All @@ -43,6 +45,8 @@ private Query Without(int id)
term.ID = id;
term.Op = TermOp.Without;

_terms.Span.Sort();

return this;
}

Expand Down
117 changes: 111 additions & 6 deletions src/World.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ public sealed partial class World : IDisposable
internal readonly Archetype _archRoot;
private readonly EntitySparseSet<EcsRecord> _entities = new();
private readonly DictionarySlim<int, Archetype> _typeIndex = new();
private Archetype[] _archetypes = new Archetype[16];
private int _archetypeCount;
private readonly ComponentComparer _comparer;
private readonly Commands _commands;
private int _frame;
Expand All @@ -29,18 +31,23 @@ public World()
//_entities.MaxID = ECS_START_USER_ENTITY_DEFINE;
}

public int EntityCount => _entities.Length;

public ReadOnlySpan<Archetype> Archetypes => _archetypes.AsSpan(0, _archetypeCount);

public CommandEntityView DeferredEntity() => _commands.Entity();

public void Merge() => _commands.Merge();

public int EntityCount => _entities.Length;

public void Dispose()
{
_entities.Clear();
_archRoot.Clear();
_typeIndex.Clear();
_commands.Clear();

Array.Clear(_archetypes, 0, _archetypeCount);
_archetypeCount = 0;
}

public void Optimize()
Expand Down Expand Up @@ -229,7 +236,14 @@ bool add
}

ref var arch = ref GetArchetype(span, true);
arch ??= _archRoot.InsertVertex(root, span, in cmp);
if (arch == null)
{
arch = _archRoot.InsertVertex(root, span, in cmp);

if (_archetypeCount >= _archetypes.Length)
Array.Resize(ref _archetypes, _archetypes.Length * 2);
_archetypes[_archetypeCount++] = arch;
}

if (buffer != null)
ArrayPool<EcsComponent>.Shared.Return(buffer);
Expand Down Expand Up @@ -259,12 +273,14 @@ static int getHash(Span<Term> terms, bool checkSize)
internal ref Archetype? GetArchetype(Span<EcsComponent> components, bool create)
{
var hash = getHash(components, false);
var exists = false;

ref var arch = ref Unsafe.NullRef<Archetype>();
if (create)
{
arch = ref _typeIndex.GetOrAddValueRef(hash);
arch = ref _typeIndex.GetOrAddValueRef(hash, out var exists);

Check warning on line 279 in src/World.cs

View workflow job for this annotation

GitHub Actions / build

Nullability of reference types in value of type 'TinyEcs.Archetype' doesn't match target type 'TinyEcs.Archetype?'.

Check warning on line 279 in src/World.cs

View workflow job for this annotation

GitHub Actions / build

Nullability of reference types in value of type 'TinyEcs.Archetype' doesn't match target type 'TinyEcs.Archetype?'.
if (!exists)
{

}
}
else if (_typeIndex.TryGetValue(hash, out arch))
{
Expand Down Expand Up @@ -444,6 +460,71 @@ static void QueryRec(Archetype root, Span<Term> sortedTerms, List<Archetype> lis
}
}
}

// public IEnumerable<Archetype> Query<T>() where T : ITuple
// {
// var terms = QueryLookup<T>.Terms.AsMemory();
//
// for (var i = 0; i < _archetypeCount; ++i)
// {
// var arch = _archetypes[i];
// var result = arch.FindMatch(terms.Span);
// if (result == 0 && arch.Count > 0)
// {
// yield return arch;
// }
// }
// }

public QueryInternal<T> Query<T>() where T : ITuple
{
var it = new QueryInternal<T>(Archetypes);
return it;
//return new QueryIterator(Archetypes, QueryLookup<T>.Terms);
}
}

public readonly ref struct QueryInternal<T> where T : ITuple
{
private readonly ReadOnlySpan<Archetype> _archetypes;
public QueryInternal(ReadOnlySpan<Archetype> archetypes)
{
_archetypes = archetypes;
}

public QueryIterator GetEnumerator()
{
return new QueryIterator(_archetypes, QueryLookup<T>.Terms);
}
}

public ref struct QueryIterator
{
private readonly Span<Term> _terms;
private readonly ReadOnlySpan<Archetype> _archetypes;
private int _index;

internal QueryIterator(ReadOnlySpan<Archetype> archetypes, Span<Term> terms)
{
_archetypes = archetypes;
_terms = terms;
_index = -1;
}

public Archetype Current => _archetypes[_index];

public bool MoveNext()
{
while (++_index < _archetypes.Length)
{
var arch = _archetypes[_index];
var result = arch.FindMatch(_terms);
if (result == 0 && arch.Count > 0)
return true;
}

return false;
}
}

internal static class Lookup
Expand Down Expand Up @@ -480,6 +561,7 @@ static Entity()
{
_arrayCreator.Add(Component.ID, count => Size > 0 ? new T[count] : Array.Empty<T>());
_typesConvertion.Add(typeof(T), Component.ID);
_typesConvertion.Add(typeof(Not<T>), -Component.ID);
}

private static int GetSize()
Expand All @@ -500,6 +582,29 @@ private static int GetSize()
}
}

internal static class QueryLookup<T> where T : ITuple
{
public static readonly T Value = Activator.CreateInstance<T>();
public static readonly Term[] Terms;

static QueryLookup()
{
var tuple = Value;
Terms = new Term[tuple.Length];

for (var i = 0; i < tuple.Length; ++i)
{
var type = tuple[i]!.GetType();
var id = Lookup.GetID(type);

Terms[i].ID = Math.Abs(id);
Terms[i].Op = id >= 0 ? TermOp.With : TermOp.Without;
}

Array.Sort(Terms);
}
}

struct EcsRecord
{
public Archetype Archetype;
Expand Down

0 comments on commit 661fa0c

Please sign in to comment.