Skip to content

Commit

Permalink
Engine - ECS: removed TreeNode.arrayHeap
Browse files Browse the repository at this point in the history
  • Loading branch information
friflo committed Jul 18, 2024
1 parent 574d080 commit 0439023
Show file tree
Hide file tree
Showing 10 changed files with 58 additions and 60 deletions.
19 changes: 15 additions & 4 deletions Engine/src/ECS/Archetype/EntityStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ public abstract partial class EntityStoreBase
// --- nodes
[Browse(Never)] internal int entityCount; // 4 - number of all entities
// --- misc
[Browse(Never)] private readonly ArchetypeKey searchKey; // 8 - key buffer to find archetypes by key
[Browse(Never)] private readonly ArchetypeKey searchKey; // 8 - key buffer to find archetypes by key
[Browse(Never)] private readonly int[] singleIds; // 8
[Browse(Never)] private int singleIndex; // 4

private InternBase internBase; // 40
/// <summary>Contains state of <see cref="EntityStoreBase"/> not relevant for application development.</summary>
Expand Down Expand Up @@ -135,13 +137,22 @@ protected EntityStoreBase()
internBase.entityBatches = new StackArray<EntityBatch> (Array.Empty<EntityBatch>());
internBase.createEntityBatches = new StackArray<CreateEntityBatch> (Array.Empty<CreateEntityBatch>());
internBase.entityLists = new StackArray<EntityList> (Array.Empty<EntityList>());
singleIds = new int[SingleMax];
}

protected internal abstract void UpdateEntityCompIndex(int id, int compIndex);

#endregion

private const int SingleMax = 32;

/// safe alternative for unsafe variant using <see cref="System.Runtime.InteropServices.MemoryMarshal.CreateReadOnlySpan{T}"/>.
internal ReadOnlySpan<int> GetSpanId(int id) {
var ids = singleIds;
var index = singleIndex;
singleIndex = (index + 1) % SingleMax;
ids[index] = id;
return new ReadOnlySpan<int>(ids, index, 1);
}

protected internal abstract void UpdateEntityCompIndex(int id, int compIndex);

#region exceptions
/// <summary>
Expand Down
13 changes: 6 additions & 7 deletions Engine/src/ECS/Collections/Entities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using static System.Diagnostics.DebuggerBrowsableState;
using Browse = System.Diagnostics.DebuggerBrowsableAttribute;
Expand All @@ -15,21 +14,21 @@
namespace Friflo.Engine.ECS;

[DebuggerTypeProxy(typeof(EntitiesDebugView))]
public struct Entities : IReadOnlyList<Entity>
public readonly struct Entities : IReadOnlyList<Entity>
{
#region properties
public int Count => count;
public EntityStore EntityStore => store;
public ReadOnlySpan<int> Ids => ids != null ? new ReadOnlySpan<int>(ids, start, count) : MemoryMarshal.CreateReadOnlySpan(ref id, 1);
public ReadOnlySpan<int> Ids => ids != null ? new ReadOnlySpan<int>(ids, start, count) : store.GetSpanId(start);
public override string ToString() => $"Entity[{count}]";
#endregion

#region interal fields
internal readonly int[] ids; // 8
internal readonly EntityStore store; // 8
/// id - if <see cref="ids"/> == null.
internal readonly int start; // 4
internal readonly int count; // 4
internal int id; // 4
#endregion

#region general
Expand All @@ -47,7 +46,7 @@ internal Entities(EntityStore store) {

internal Entities(EntityStore store, int id) {
this.store = store;
this.id = id;
start = id;
count = 1;
}

Expand All @@ -58,7 +57,7 @@ public Entity this[int index] {
}
// case: count == 1
if (index != 0) throw new IndexOutOfRangeException();
return new Entity(store, id);
return new Entity(store, start);
}
}

Expand Down Expand Up @@ -106,7 +105,7 @@ internal EntityEnumerator(in Entities entities) {
store = entities.store;
start = entities.start - 1;
last = start + entities.count;
id = entities.id;
id = entities.start;
index = start;
}

Expand Down
2 changes: 1 addition & 1 deletion Engine/src/ECS/Collections/EntityList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ private void AddEntityTree(Entity entity)
{
AddInternal(entity.Id);
entity.TryGetTreeNode(out var node);
foreach (var id in node.ChildIds) {
foreach (var id in node.GetChildIds(entityStore)) {
var child = new Entity(entityStore, id);
AddEntityTree(child);
}
Expand Down
2 changes: 1 addition & 1 deletion Engine/src/ECS/Collections/IdArray/IdArray.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ private string GetString()

internal static class IdArrayExtensions {

public static ReadOnlySpan<int> GetIdSpan(this ref IdArray array, IdArrayHeap heap)
internal static ReadOnlySpan<int> GetIdSpan(this ref IdArray array, IdArrayHeap heap)
{
var count = array.count;
switch (count) {
Expand Down
18 changes: 5 additions & 13 deletions Engine/src/ECS/Components/TreeNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@
// See LICENSE file in the project root for full license information.

using System;
using System.Runtime.InteropServices;
using Friflo.Engine.ECS.Collections;
using static System.Diagnostics.DebuggerBrowsableState;
using Browse = System.Diagnostics.DebuggerBrowsableAttribute;

// ReSharper disable once CheckNamespace
namespace Friflo.Engine.ECS;
Expand All @@ -14,27 +11,22 @@ namespace Friflo.Engine.ECS;
[ComponentKey(null)]
public struct TreeNode : IComponent // todo should be internal
{
public ReadOnlySpan<int> ChildIds => GetChildIds();
public int ChildCount => childIds.count;
public override string ToString() => $"ChildCount: {childIds.count}";

// [Browse(Never)] internal int parentId; // 4 0 if entity has no parent
internal IdArray childIds; // 8
internal IdArrayHeap arrayHeap; // 8

internal TreeNode(IdArrayHeap arrayHeap) {
this.arrayHeap = arrayHeap;
}
internal int[] dummy; // 8 todo remove

/// same as <see cref="IdArrayExtensions.GetIdSpan"/>
private ReadOnlySpan<int> GetChildIds()
public ReadOnlySpan<int> GetChildIds(EntityStore store)
{
var count = childIds.count;
switch (count) {
case 0: return default;
case 1: return MemoryMarshal.CreateReadOnlySpan(ref childIds.start, 1);
case 0: return default;
case 1: return store.GetSpanId(childIds.start);
}
var curPoolIndex = IdArrayHeap.PoolIndex(count);
return new ReadOnlySpan<int>(arrayHeap.GetPool(curPoolIndex).Ids, childIds.start, count);
return new ReadOnlySpan<int>(store.extension.hierarchyHeap.GetPool(curPoolIndex).Ids, childIds.start, count);
}
}
4 changes: 2 additions & 2 deletions Engine/src/ECS/Entity/ChildEntities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public struct ChildEntities : IEnumerable<Entity>
{
// --- public properties
public int Count => node.childIds.count;
public ReadOnlySpan<int> Ids => node.ChildIds;
public ReadOnlySpan<int> Ids => node.GetChildIds(store);

public Entity this[int index] => new Entity(store, Ids[index]);
public override string ToString() => $"Entity[{Count}]";
Expand Down Expand Up @@ -79,7 +79,7 @@ internal ChildEnumerator(in ChildEntities childEntities) {
}

// --- IEnumerator<>
public readonly Entity Current => new Entity(store, node.childIds.Get(index - 1, node.arrayHeap));
public readonly Entity Current => new Entity(store, node.childIds.Get(index - 1, store.extension.hierarchyHeap));

// --- IEnumerator
public bool MoveNext() {
Expand Down
51 changes: 24 additions & 27 deletions Engine/src/ECS/Entity/Store/NodeTree.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,7 @@ private ref TreeNode GetTreeNodeRef(Entity entity)
entity.AddComponent<TreeNode>(); // set entity.archetype
heap = entity.archetype.heapMap[StructInfo<TreeNode>.Index];
}
ref var node = ref ((StructHeap<TreeNode>)heap).components[entity.compIndex];
node.arrayHeap = extension.hierarchyHeap;
return ref node;
return ref ((StructHeap<TreeNode>)heap).components[entity.compIndex];
}

internal int AddChild (int parentId, int childId)
Expand Down Expand Up @@ -98,7 +96,7 @@ internal int AddChild (int parentId, int childId)
int index = parent.childIds.count;
// childNode.parentId = parentId;
extension.parentMap[childId] = parentId;
parent.childIds.AddId(childId, parent.arrayHeap);
parent.childIds.AddId(childId, extension.hierarchyHeap);
SetTreeFlags(localNodes, childId, nodes[parentId].flags & NodeFlags.TreeNode);

OnChildNodeAdd(parentId, childId, index);
Expand Down Expand Up @@ -187,7 +185,7 @@ internal Entity GetChildEntityByIndex(int parentId, int childIndex) {
internal static int GetChildIndex(Entity parent, int childId)
{
parent.TryGetTreeNode(out var node);
var childIds = node.ChildIds;
var childIds = node.GetChildIds(parent.store);
int count = node.childIds.count;
for (int n = 0; n < count; n++) {
if (childId != childIds[n]) {
Expand All @@ -202,21 +200,21 @@ private int RemoveChildNode (int parentId, int childId)
{
var parent = new Entity(this, parentId);
ref var treeNode = ref GetTreeNodeRef(parent);
var childIds = treeNode.ChildIds;
var childIds = treeNode.GetChildIds(this);
int count = treeNode.childIds.count;
for (int n = 0; n < count; n++) {
if (childId != childIds[n]) {
continue;
}
treeNode.childIds.RemoveAt(n, treeNode.arrayHeap, keepOrder: true);
treeNode.childIds.RemoveAt(n, extension.hierarchyHeap, keepOrder: true);
return n;
}
throw new InvalidOperationException($"unexpected state: child id not found. parent id: {parentId}, child id: {childId}");
}

private static void InsertChildNode (ref TreeNode parent, int childId, int childIndex)
private void InsertChildNode (ref TreeNode parent, int childId, int childIndex)
{
parent.childIds.InsertAt(childIndex, childId, parent.arrayHeap);
parent.childIds.InsertAt(childIndex, childId, extension.hierarchyHeap);
}

/*
Expand Down Expand Up @@ -244,13 +242,13 @@ private void SetChildNodes(Entity parent, ReadOnlySpan<int> newChildIds)
return;
}
// ref var node = ref nodes[parent.Id];
var newCount = newChildIds.Length;

/* if (!parent.HasTreeNode()) { // todo could optimize
parent.AddComponent(new TreeNode(extension.hierarchyHeap));
} */
ref var node = ref GetTreeNodeRef(parent);
node.childIds.SetArray(newChildIds, node.arrayHeap);
SetChildParents(node, newCount, parent.Id);
node.childIds.SetArray(newChildIds, extension.hierarchyHeap);
SetChildParents(node, parent.Id);
}

private void SetChildNodesWithEvents(Entity parent, ReadOnlySpan<int> newIds)
Expand All @@ -259,7 +257,6 @@ private void SetChildNodesWithEvents(Entity parent, ReadOnlySpan<int> newIds)
parent.AddComponent(new TreeNode(extension.hierarchyHeap));
} */
ref var node = ref GetTreeNodeRef(parent);
var newCount = newIds.Length;
// --- 1. Remove missing ids in new child ids. E.g. cur ids [2, 3, 4, 5]
// *newIds [6, 4, 2, 5] => remove: 3
// result [2, 4, 5]
Expand All @@ -283,7 +280,7 @@ private void SetChildNodesWithEvents(Entity parent, ReadOnlySpan<int> newIds)
ChildIds_RemoveRange(ref node, first, last, parent.Id);
ChildIds_InsertRange(ref node, first, last, newIds, parent.Id);

SetChildParents ( node, newCount, parent.Id);
SetChildParents ( node, parent.Id);
}

// --- 1.
Expand All @@ -294,13 +291,13 @@ private void ChildIds_RemoveMissingIds(ReadOnlySpan<int> newIds, ref TreeNode no
foreach (var id in newIds) {
newIdSet.Add(id);
}
var childIds = node.ChildIds;
var childIds = node.GetChildIds(this);
for (int index = node.childIds.count - 1; index >= 0; index--) {
var id = childIds[index];
if (newIdSet.Contains(id)) {
continue;
}
node.childIds.RemoveAt(index, node.arrayHeap, keepOrder: true);
node.childIds.RemoveAt(index, extension.hierarchyHeap, keepOrder: true);
// nodes[id].parentId = Static.NoParentId;
extension.parentMap.Remove(id);
// OnChildNodeRemove(node.id, id, index);
Expand All @@ -313,7 +310,7 @@ private void ChildIds_InsertNewIds(ReadOnlySpan<int> newIds, ref TreeNode node,
{
var curIdSet = idBufferSet;
curIdSet.Clear();
foreach (var id in node.ChildIds) {
foreach (var id in node.GetChildIds(this)) {
curIdSet.Add(id);
}
var newCount = newIds.Length;
Expand All @@ -325,15 +322,15 @@ private void ChildIds_InsertNewIds(ReadOnlySpan<int> newIds, ref TreeNode node,
continue;
}
// case: child ids does not contain id => insert at specified position
node.childIds.InsertAt(index, id, node.arrayHeap);
node.childIds.InsertAt(index, id, extension.hierarchyHeap);
OnChildNodeAdd(parentId, id, index);
}
}

// --- 3.1
private static void ChildIds_GetRange(in TreeNode node, ReadOnlySpan<int> newIds, out int first, out int last)
private void ChildIds_GetRange(in TreeNode node, ReadOnlySpan<int> newIds, out int first, out int last)
{
var childIds = node.ChildIds;
var childIds = node.GetChildIds(this);
var count = newIds.Length;
first = 0;
for (; first < count; first++) {
Expand All @@ -358,10 +355,11 @@ private static void ChildIds_GetRange(in TreeNode node, ReadOnlySpan<int> newIds
// --- 3.2
private void ChildIds_RemoveRange(ref TreeNode node, int first, int last, int parentId)
{
var heap = extension.hierarchyHeap;
for (int index = last; index >= first; index--)
{
int removedId = node.childIds.Get(index, node.arrayHeap);
node.childIds.RemoveAt(index, node.arrayHeap, keepOrder: true);
int removedId = node.childIds.Get(index, heap);
node.childIds.RemoveAt(index, heap, keepOrder: true);
// nodes[removedId].parentId = Static.NoParentId;
extension.parentMap.Remove(removedId);
OnChildNodeRemove(parentId, removedId, index);
Expand All @@ -374,16 +372,15 @@ private void ChildIds_InsertRange(ref TreeNode node, int first, int last, ReadOn
for (int index = first; index <= last; index++)
{
var addedId = newIds[index];
node.childIds.InsertAt(index, addedId, node.arrayHeap);
node.childIds.InsertAt(index, addedId, extension.hierarchyHeap);
OnChildNodeAdd(parentId, addedId, index);
}
}

// todo - remove redundant count
private void SetChildParents(in TreeNode node, int count, int parentId)
private void SetChildParents(in TreeNode node, int parentId)
{
// var localNodes = nodes;
foreach (int childId in node.ChildIds)
foreach (int childId in node.GetChildIds(this))
{
// ref var child = ref localNodes[childId];
extension.parentMap.TryGetValue(childId, out int curParentId);
Expand Down Expand Up @@ -681,6 +678,6 @@ internal static ReadOnlySpan<int> GetChildIds(Entity entity)
// ref var node = ref store.nodes[id];
// return new ReadOnlySpan<int>(node.childIds, 0, node.childCount);
entity.TryGetTreeNode(out var node);
return node.ChildIds;
return node.GetChildIds(entity.store);
}
}
1 change: 0 additions & 1 deletion Engine/src/Tests-internal/ECS/Test_IdArray.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Friflo.Engine.ECS.Collections;
using NUnit.Framework;
using Tests.Utils;
Expand Down
4 changes: 2 additions & 2 deletions Engine/src/Tests/ECS/Entity/Test_Entity_Tree.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public static void Test_AddChild()
AreEqual(1, childEntities.Ids.Length);
var rootNode = root.GetComponent<TreeNode>();
AreEqual(1, rootNode.ChildCount);
AreEqual(1, rootNode.ChildIds.Length);
AreEqual(1, rootNode.GetChildIds(store).Length);
AreEqual("ChildCount: 1", rootNode.ToString());
// AreEqual("id: 1 \"root\" [EntityName] ChildCount: 1 flags: Created", root.ToString()); TREE_NODE
AreEqual("id: 1 \"root\" [EntityName, TreeNode]", root.ToString());
Expand Down Expand Up @@ -140,7 +140,7 @@ public static void Test_InsertChild()
AreEqual(1, childNodes.Ids.Length);
var rootNode = root.GetComponent<TreeNode>();
AreEqual(1, rootNode.ChildCount);
AreEqual(1, rootNode.ChildIds.Length);
AreEqual(1, rootNode.GetChildIds(store).Length);
AreEqual("id: 1 \"root\" [EntityName, TreeNode]", root.ToString());
IsTrue(child4 == childNodes[0]);
events.RemoveHandler();
Expand Down
4 changes: 2 additions & 2 deletions Engine/src/Tests/ECS/Serialize/Test_ComponentReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,7 @@ public static void Test_ComponentReader_Load_DataEntity_UsePidAsId()
var entity20 = store.GetEntityById(20);
AreEqual(10, store.GetInternalParentId(entity20.Id));
AreEqual(20, entity20.Pid);
var childIds = treeNode.ChildIds;
var childIds = treeNode.GetChildIds(store);
for (int n = 0; n < 100; n++) {
AreEqual(n + 20, childIds[n]);
}
Expand Down Expand Up @@ -487,7 +487,7 @@ public static void Test_ComponentReader_Load_DataEntity_RandomPids() {
var entity2 = store.GetEntityById(2);
AreEqual(1, store.GetInternalParentId(entity2.Id));
AreEqual(20, entity2.Pid);
var childIds = treeNode1.ChildIds;
var childIds = treeNode1.GetChildIds(store);
for (int n = 0; n < 100; n++) {
AreEqual(n + 2, childIds[n]);
}
Expand Down

0 comments on commit 0439023

Please sign in to comment.