implement internal BitSet1024

pull/5/head
Evan Hemsley 2019-12-22 16:21:07 -08:00
parent 570b6832ad
commit 9a1a210039
6 changed files with 322 additions and 123 deletions

View File

@ -0,0 +1,178 @@
using System.Numerics;
namespace Encompass.Collections
{
public static class BitSet1024Builder
{
public static BitSet1024 Zeroes()
{
return new BitSet1024(Vector256Builder.Zeroes(), Vector256Builder.Zeroes(), Vector256Builder.Zeroes(), Vector256Builder.Zeroes());
}
public static BitSet1024 Ones()
{
return new BitSet1024(Vector256Builder.Ones(), Vector256Builder.Ones(), Vector256Builder.Ones(), Vector256Builder.Ones());
}
}
public static class Vector256Builder
{
static readonly uint[] zeroes = new uint[8]; // max size of a Vector<T> is 8 uints
static readonly uint[] ones = new uint[8] { uint.MaxValue, uint.MaxValue, uint.MaxValue, uint.MaxValue, uint.MaxValue, uint.MaxValue, uint.MaxValue, uint.MaxValue };
static uint[] builderInts = new uint[8];
private static void ResetBuilder()
{
for (var i = 0; i < 8; i++)
{
builderInts[i] = 0;
}
}
public static Vector<uint> Zeroes()
{
return new Vector<uint>(zeroes);
}
public static Vector<uint> Ones()
{
return new Vector<uint>(ones);
}
public static Vector<uint> Build(int index)
{
if (index > 255) { throw new System.ArgumentOutOfRangeException(nameof(index)); }
ResetBuilder();
builderInts[index / 32] |= (uint)(1 << (index % 32));
return new Vector<uint>(builderInts);
}
}
public struct BitSet1024
{
public Vector<uint> A { get; }
public Vector<uint> B { get; }
public Vector<uint> C { get; }
public Vector<uint> D { get; }
internal BitSet1024(Vector<uint> a, Vector<uint> b, Vector<uint> c, Vector<uint> d)
{
A = a;
B = b;
C = c;
D = d;
}
public BitSet1024 And(BitSet1024 other)
{
return new BitSet1024(A & other.A, B & other.B, C & other.C, D & other.D);
}
public BitSet1024 Or(BitSet1024 other)
{
return new BitSet1024(A | other.A, B | other.B, C | other.C, D | other.D);
}
public BitSet1024 Not()
{
return new BitSet1024(~A, ~B, ~C, ~D);
}
public BitSet1024 Set(int index)
{
if (index < 256)
{
return new BitSet1024(A | Vector256Builder.Build(index % 256), B, C, D);
}
else if (index < 512)
{
return new BitSet1024(A, B | Vector256Builder.Build(index % 256), C, D);
}
else if (index < 768)
{
return new BitSet1024(A, B, C | Vector256Builder.Build(index % 256), D);
}
else if (index < 1024)
{
return new BitSet1024(A, B, C, D | Vector256Builder.Build(index % 256));
}
else
{
throw new System.ArgumentOutOfRangeException(nameof(index));
}
}
public BitSet1024 UnSet(int index)
{
if (index < 256)
{
return new BitSet1024(A & ~Vector256Builder.Build(index % 256), B, C, D);
}
else if (index < 512)
{
return new BitSet1024(A, B & ~Vector256Builder.Build(index % 256), C, D);
}
else if (index < 768)
{
return new BitSet1024(A, B, C & ~Vector256Builder.Build(index % 256), D);
}
else if (index < 1024)
{
return new BitSet1024(A, B, C, D & ~Vector256Builder.Build(index % 256));
}
else
{
throw new System.ArgumentOutOfRangeException(nameof(index));
}
}
public bool Get(int index)
{
var vectorIndex = index % 256;
if (index < 256)
{
return (A[vectorIndex / 32] & (uint)(1 << vectorIndex % 32)) != 0;
}
else if (index < 512)
{
return (B[vectorIndex / 32] & (uint)(1 << vectorIndex % 32)) != 0;
}
else if (index < 768)
{
return (C[vectorIndex / 32] & (uint)(1 << vectorIndex % 32)) != 0;
}
else if (index < 1024)
{
return (D[vectorIndex / 32] & (uint)(1 << vectorIndex % 32)) != 0;
}
else
{
throw new System.ArgumentOutOfRangeException(nameof(index));
}
}
public bool AllTrue()
{
for (var i = 0; i < 8; i++)
{
if (A[i] != uint.MaxValue) { return false; }
if (B[i] != uint.MaxValue) { return false; }
if (C[i] != uint.MaxValue) { return false; }
if (D[i] != uint.MaxValue) { return false; }
}
return true;
}
public bool AllFalse()
{
for (var i = 0; i < 8; i++)
{
if (A[i] != 0) { return false; }
if (B[i] != 0) { return false; }
if (C[i] != 0) { return false; }
if (D[i] != 0) { return false; }
}
return true;
}
}
}

View File

@ -0,0 +1,60 @@
using Encompass.Collections;
using System;
using System.Collections;
using System.Collections.Generic;
namespace Encompass
{
internal class ComponentBitSet
{
Dictionary<Entity, BitSet1024> entities = new Dictionary<Entity, BitSet1024>();
Dictionary<Type, int> TypeToIndex { get; }
public ComponentBitSet(Dictionary<Type, int> typeToIndex)
{
TypeToIndex = typeToIndex;
}
public void FinishRegistering()
{
}
public void Clear()
{
entities.Clear();
}
public void AddEntity(Entity entity)
{
entities.Add(entity, BitSet1024Builder.Zeroes());
}
public void Set<TComponent>(Entity entity) where TComponent : struct, IComponent
{
if (!entities.ContainsKey(entity)) { AddEntity(entity); }
entities[entity] = entities[entity].Set(TypeToIndex[typeof(TComponent)]);
}
public void RemoveComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{
if (entities.ContainsKey(entity))
{
entities[entity] = entities[entity].UnSet(TypeToIndex[typeof(TComponent)]);
}
}
public void RemoveEntity(Entity entity)
{
if (entities.ContainsKey(entity))
{
entities.Remove(entity);
}
}
public BitSet1024 EntityBitArray(Entity entity)
{
return entities.ContainsKey(entity) ? entities[entity] : BitSet1024Builder.Zeroes();
}
}
}

View File

@ -1,5 +1,5 @@
using System;
using System.Collections;
using Encompass.Collections;
using System;
using System.Collections.Generic;
namespace Encompass
@ -52,7 +52,7 @@ namespace Encompass
return Stores.ContainsKey(type) && Stores[type].Has(entity);
}
public BitArray EntityBitArray(Entity entity)
public BitSet1024 EntityBitArray(Entity entity)
{
return componentBitSet.EntityBitArray(entity);
}

View File

@ -1,75 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace Encompass
{
internal class ComponentBitSet
{
BitArrayPool bitArrayPool = new BitArrayPool(32768); // todo: set entity cap
Dictionary<Entity, BitArray> entities = new Dictionary<Entity, BitArray>();
Dictionary<Type, int> TypeToIndex { get; set; }
BitArray withQueryArray;
BitArray withoutQueryArray;
BitArray emptyArray;
public ComponentBitSet(Dictionary<Type, int> typeToIndex)
{
TypeToIndex = typeToIndex;
}
public void FinishRegistering()
{
withQueryArray = new BitArray(TypeToIndex.Count);
withoutQueryArray = new BitArray(TypeToIndex.Count);
emptyArray = new BitArray(TypeToIndex.Count);
foreach (var kvp in entities)
{
kvp.Value.Length = TypeToIndex.Count;
}
}
public void Clear()
{
foreach (var kvp in entities)
{
bitArrayPool.Free(kvp.Value);
}
entities.Clear();
}
public void AddEntity(Entity entity)
{
var bitArray = bitArrayPool.Obtain(TypeToIndex.Count);
entities.Add(entity, bitArray);
}
public void Set<TComponent>(Entity entity) where TComponent : struct, IComponent
{
if (!entities.ContainsKey(entity)) { AddEntity(entity); }
entities[entity].Set(TypeToIndex[typeof(TComponent)], true);
}
public void RemoveComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{
if (entities.ContainsKey(entity))
{
entities[entity].Set(TypeToIndex[typeof(TComponent)], false);
}
}
public void RemoveEntity(Entity entity)
{
if (entities.ContainsKey(entity))
{
bitArrayPool.Free(entities[entity]);
entities.Remove(entity);
}
}
public BitArray EntityBitArray(Entity entity)
{
return entities.ContainsKey(entity) ? entities[entity] : emptyArray;
}
}
}

View File

@ -1,4 +1,5 @@
using System;
using Encompass.Collections;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
@ -14,16 +15,6 @@ namespace Encompass
private Dictionary<Type, int> typeToIndex;
public ComponentStore UpToDateComponentStore { get; private set; }
// bitarray references to avoid garbage collection
BitArray pendingMask;
BitArray readMask;
BitArray withMask;
BitArray withoutMask;
BitArray notWithMask;
BitArray solverArrayA;
BitArray solverArrayB;
BitArray solverArrayC;
public ComponentUpdateManager(Dictionary<Type, int> typeToIndex)
{
existingAndPendingComponentStore = new ComponentStore(typeToIndex);
@ -47,15 +38,6 @@ namespace Encompass
existingComponentStore.FinishRegistering();
pendingComponentStore.FinishRegistering();
UpToDateComponentStore.FinishRegistering();
pendingMask = new BitArray(typeToIndex.Count);
readMask = new BitArray(typeToIndex.Count);
withMask = new BitArray(typeToIndex.Count);
withoutMask = new BitArray(typeToIndex.Count);
notWithMask = new BitArray(typeToIndex.Count);
solverArrayA = new BitArray(typeToIndex.Count);
solverArrayB = new BitArray(typeToIndex.Count);
solverArrayC = new BitArray(typeToIndex.Count);
}
internal void Clear()
@ -216,32 +198,31 @@ namespace Encompass
internal IEnumerable<Entity> QueryEntities(IEnumerable<Entity> entities, HashSet<Type> readTypes, HashSet<Type> readPendingTypes, IEnumerable<Type> withTypes, IEnumerable<Type> withoutTypes)
{
pendingMask.SetAll(false);
var pendingMask = BitSet1024Builder.Zeroes();
foreach (var type in readPendingTypes)
{
pendingMask.Set(typeToIndex[type], true);
pendingMask = pendingMask.Set(typeToIndex[type]);
}
readMask.SetAll(false);
var readMask = BitSet1024Builder.Zeroes();
foreach (var type in readTypes)
{
readMask.Set(typeToIndex[type], true);
readMask = readMask.Set(typeToIndex[type]);
}
withMask.SetAll(false);
var withMask = BitSet1024Builder.Zeroes();
foreach (var type in withTypes)
{
withMask.Set(typeToIndex[type], true);
withMask = withMask.Set(typeToIndex[type]);
}
withoutMask.SetAll(false);
var withoutMask = BitSet1024Builder.Zeroes();
foreach (var type in withoutTypes)
{
withoutMask.Set(typeToIndex[type], true);
withoutMask = withoutMask.Set(typeToIndex[type]);
}
notWithMask.SetAll(false);
notWithMask.Or(withMask).Not();
var notWithMask = withMask.Not();
foreach (var entity in entities)
{
@ -252,24 +233,17 @@ namespace Encompass
}
}
internal bool VerifyTypes(BitArray pendingEntity, BitArray existingEntity, BitArray readMask, BitArray pendingMask, BitArray withMask, BitArray withoutMask, BitArray notWithMask)
internal bool VerifyTypes(BitSet1024 pendingEntity, BitSet1024 existingEntity, BitSet1024 readMask, BitSet1024 pendingMask, BitSet1024 withMask, BitSet1024 withoutMask, BitSet1024 notWithMask)
{
solverArrayA.SetAll(false);
solverArrayB.SetAll(false);
solverArrayC.SetAll(false);
var pending = pendingMask.And(withMask).And(pendingEntity);
var existing = readMask.And(withMask).And(existingEntity);
var withCheck = pending.Or(existing).Or(notWithMask);
solverArrayA.Or(pendingMask).And(withMask).And(pendingEntity);
solverArrayB.Or(readMask).And(withMask).And(existingEntity);
solverArrayA.Or(solverArrayB).Or(notWithMask);
var pendingForbidden = pendingMask.And(withoutMask).And(pendingEntity).Not();
var existingForbidden = readMask.And(withoutMask).And(existingEntity).Not();
var withoutCheck = pendingForbidden.And(existingForbidden);
solverArrayB.SetAll(false);
solverArrayB.Or(pendingMask).And(withoutMask).And(pendingEntity).Not();
solverArrayC.Or(readMask).And(withoutMask).And(existingEntity).Not();
solverArrayB.And(solverArrayC);
solverArrayA.And(solverArrayB);
return !solverArrayA.Cast<bool>().Contains(false);
return withCheck.And(withoutCheck).AllTrue();
}
}
}

62
test/BitSet1024Test.cs Normal file
View File

@ -0,0 +1,62 @@
using Encompass.Collections;
using FluentAssertions;
using NUnit.Framework;
namespace Tests
{
public class BitSet1024Test
{
[Test]
public void Zeroes()
{
var bitSet = BitSet1024Builder.Zeroes();
bitSet.AllFalse().Should().BeTrue();
}
[Test]
public void Ones()
{
var bitSet = BitSet1024Builder.Ones();
bitSet.AllTrue().Should().BeTrue();
}
[Test]
public void Set()
{
var bitSet = BitSet1024Builder.Zeroes().Set(5);
bitSet.AllFalse().Should().BeFalse();
bitSet = BitSet1024Builder.Zeroes().Set(278);
bitSet.AllFalse().Should().BeFalse();
bitSet = BitSet1024Builder.Zeroes().Set(569);
bitSet.AllFalse().Should().BeFalse();
bitSet = BitSet1024Builder.Zeroes().Set(1023);
bitSet.AllFalse().Should().BeFalse();
}
[Test]
public void UnSet()
{
var bitSet = BitSet1024Builder.Ones().UnSet(562);
bitSet.Get(562).Should().BeFalse();
bitSet.Set(562).AllTrue().Should().BeTrue();
}
[Test]
public void Get()
{
var bitSet = BitSet1024Builder.Zeroes().Set(359);
bitSet.Get(359).Should().BeTrue();
bitSet.UnSet(359).AllFalse().Should().BeTrue();
}
[Test]
public void Not()
{
var bitSet = BitSet1024Builder.Ones().Not();
bitSet.AllFalse().Should().BeTrue();
}
}
}