@@ -1 +1 @@ | |||
Subproject commit 6053063786f5c0972cfd3bc9067a50e02e3bd1de | |||
Subproject commit 49d3591924c8c45e0ec32655f992480f9419e327 |
@@ -1,11 +1,10 @@ | |||
#if DEBUG && !PROFILE_SVELTO | |||
#if !DEBUG || PROFILE_SVELTO | |||
#define DONT_USE | |||
#endif | |||
using System; | |||
using System.Collections.Generic; | |||
using Svelto.DataStructures; | |||
#else | |||
using System; | |||
using System.Diagnostics; | |||
#endif | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
@@ -15,65 +14,72 @@ namespace Svelto.ECS | |||
/// </summary> | |||
public partial class EnginesRoot | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
void CheckRemoveEntityID(EGID egid, Type entityComponent, string caller = "") | |||
#if DONT_USE | |||
[Conditional("CHECK_ALL")] | |||
#endif | |||
void CheckRemoveEntityID(EGID egid, Type entityDescriptorType, string caller = "") | |||
{ | |||
if (_idCheckers.TryGetValue(egid.groupID, out var hash)) | |||
{ | |||
if (hash.Contains(egid.entityID) == false) | |||
throw new ECSException("Entity with not found ID is about to be removed: id: " | |||
.FastConcat(" caller: ", caller, " ") | |||
.FastConcat(egid.entityID).FastConcat(" groupid: ").FastConcat(egid.groupID.ToName()) | |||
.FastConcat(" type: ").FastConcat(entityComponent != null ? entityComponent.Name : "not available")); | |||
if (_multipleOperationOnSameEGIDChecker.ContainsKey(egid) == true) | |||
throw new ECSException( | |||
"Executing multiple structural changes in one submission on the same entity is not supported " | |||
.FastConcat(" caller: ", caller, " ").FastConcat(egid.entityID).FastConcat(" groupid: ") | |||
.FastConcat(egid.groupID.ToName()).FastConcat(" type: ") | |||
.FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available") | |||
.FastConcat(" operation was: ") | |||
.FastConcat(_multipleOperationOnSameEGIDChecker[egid] == 1 ? "add" : "remove")); | |||
hash.Remove(egid.entityID); | |||
if (_idChecker.TryGetValue(egid.groupID, out var hash)) | |||
if (hash.Contains(egid.entityID) == false) | |||
throw new ECSException("Trying to remove an Entity never submitted in the database " | |||
.FastConcat(" caller: ", caller, " ").FastConcat(egid.entityID) | |||
.FastConcat(" groupid: ").FastConcat(egid.groupID.ToName()) | |||
.FastConcat(" type: ") | |||
.FastConcat(entityDescriptorType != null | |||
? entityDescriptorType.Name | |||
: "not available")); | |||
else | |||
hash.Remove(egid.entityID); | |||
if (hash.Count == 0) | |||
_idCheckers.Remove(egid.groupID); | |||
} | |||
else | |||
{ | |||
throw new ECSException("Entity with not found ID is about to be removed: id: " | |||
.FastConcat(" caller: ", caller, " ") | |||
.FastConcat(egid.entityID).FastConcat(" groupid: ").FastConcat(egid.groupID.ToName()) | |||
.FastConcat(" type: ").FastConcat(entityComponent != null ? entityComponent.Name : "not available")); | |||
} | |||
_multipleOperationOnSameEGIDChecker.Add(egid, 0); | |||
} | |||
void CheckAddEntityID(EGID egid, Type entityComponent, string caller = "") | |||
#if DONT_USE | |||
[Conditional("CHECK_ALL")] | |||
#endif | |||
void CheckAddEntityID(EGID egid, Type entityDescriptorType, string caller = "") | |||
{ | |||
// Console.LogError("<color=orange>added</color> ".FastConcat(egid.ToString())); | |||
if (_idCheckers.TryGetValue(egid.groupID, out var hash) == false) | |||
hash = _idCheckers[egid.groupID] = new HashSet<uint>(); | |||
else | |||
if (hash.Contains(egid.entityID)) | |||
throw new ECSException("Entity with used ID is about to be built: '" | |||
.FastConcat("' id: '").FastConcat(egid.entityID).FastConcat("' groupid: '") | |||
.FastConcat(egid.groupID.ToName()).FastConcat(" ", entityComponent != null ? entityComponent.Name : "not available") | |||
.FastConcat("'")); | |||
if (_multipleOperationOnSameEGIDChecker.ContainsKey(egid) == true) | |||
throw new ECSException( | |||
"Executing multiple structural changes in one submission on the same entity is not supported " | |||
.FastConcat(" caller: ", caller, " ").FastConcat(egid.entityID).FastConcat(" groupid: ") | |||
.FastConcat(egid.groupID.ToName()).FastConcat(" type: ") | |||
.FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available") | |||
.FastConcat(" operation was: ") | |||
.FastConcat(_multipleOperationOnSameEGIDChecker[egid] == 1 ? "add" : "remove")); | |||
var hash = _idChecker.GetOrCreate(egid.groupID, () => new HashSet<uint>()); | |||
if (hash.Contains(egid.entityID) == true) | |||
throw new ECSException("Trying to add an Entity already submitted to the database " | |||
.FastConcat(" caller: ", caller, " ").FastConcat(egid.entityID) | |||
.FastConcat(" groupid: ").FastConcat(egid.groupID.ToName()).FastConcat(" type: ") | |||
.FastConcat(entityDescriptorType != null | |||
? entityDescriptorType.Name | |||
: "not available")); | |||
hash.Add(egid.entityID); | |||
_multipleOperationOnSameEGIDChecker.Add(egid, 1); | |||
} | |||
void RemoveGroupID(ExclusiveGroupStruct groupID) { _idCheckers.Remove(groupID); } | |||
readonly FasterDictionary<uint, HashSet<uint>> _idCheckers = new FasterDictionary<uint, HashSet<uint>>(); | |||
#else | |||
[Conditional("_CHECKS_DISABLED")] | |||
void CheckRemoveEntityID(EGID egid, Type entityComponent, string caller = "") | |||
{ | |||
} | |||
#if DONT_USE | |||
[Conditional("CHECK_ALL")] | |||
#endif | |||
void RemoveGroupID(BuildGroup groupID) { _idChecker.Remove(groupID); } | |||
[Conditional("_CHECKS_DISABLED")] | |||
void CheckAddEntityID(EGID egid, Type entityComponen, string caller = "") | |||
{ | |||
} | |||
[Conditional("_CHECKS_DISABLED")] | |||
void RemoveGroupID(ExclusiveGroupStruct groupID) | |||
{ | |||
} | |||
#if DONT_USE | |||
[Conditional("CHECK_ALL")] | |||
#endif | |||
void ClearChecks() { _multipleOperationOnSameEGIDChecker.FastClear(); } | |||
readonly FasterDictionary<EGID, uint> _multipleOperationOnSameEGIDChecker = new FasterDictionary<EGID, uint>(); | |||
readonly FasterDictionary<uint, HashSet<uint>> _idChecker = new FasterDictionary<uint, HashSet<uint>>(); | |||
} | |||
} |
@@ -4,12 +4,11 @@ using System.Diagnostics; | |||
#endif | |||
using System; | |||
using System.Reflection; | |||
using System.Text; | |||
using Svelto.Common; | |||
namespace Svelto.ECS | |||
{ | |||
internal static class ComponentBuilderUtilities | |||
static class ComponentBuilderUtilities | |||
{ | |||
const string MSG = "Entity Components and Entity View Components fields cannot hold managed fields outside the Svelto rules."; | |||
@@ -18,7 +17,7 @@ namespace Svelto.ECS | |||
#endif | |||
public static void CheckFields(Type entityComponentType, bool needsReflection, bool isStringAllowed = false) | |||
{ | |||
if (entityComponentType == ENTITY_STRUCT_INFO_VIEW || entityComponentType == EGIDType || | |||
if (entityComponentType == ENTITY_INFO_COMPONENT || entityComponentType == EGIDType || | |||
entityComponentType == EXCLUSIVEGROUPSTRUCTTYPE || entityComponentType == SERIALIZABLE_ENTITY_STRUCT) | |||
{ | |||
return; | |||
@@ -80,11 +79,7 @@ namespace Svelto.ECS | |||
} | |||
} | |||
else | |||
if (fieldInfo.FieldType.IsUnmanagedEx() == true) | |||
{ | |||
SubCheckFields(fieldInfo.FieldType, entityComponentType, isStringAllowed); | |||
} | |||
else | |||
if (fieldInfo.FieldType.IsUnmanagedEx() == false) | |||
{ | |||
ProcessError("Entity View Components must hold only public interfaces, strings or unmanaged type fields.", | |||
entityComponentType); | |||
@@ -110,10 +105,10 @@ namespace Svelto.ECS | |||
{ | |||
//pass if it's Primitive or C# 8 unmanaged, or it's a string and string are allowed | |||
//this check must allow pointers are they are unmanaged types | |||
if ((isStringAllowed == true && IsString(fieldType) == true) || fieldType.IsUnmanagedEx() == true) | |||
if ((isStringAllowed == true && IsString(fieldType) == true) || fieldType.IsValueTypeEx() == true) | |||
{ | |||
//if it's a struct we have to check the fields recursively | |||
if (IsString(fieldType) == false && !fieldType.IsEnum && fieldType.IsPrimitive == false) | |||
if (IsString(fieldType) == false) | |||
{ | |||
CheckFields(fieldType, false, isStringAllowed); | |||
} | |||
@@ -142,6 +137,6 @@ namespace Svelto.ECS | |||
static readonly Type STRINGTYPE = typeof(string); | |||
static readonly Type STRINGBUILDERTYPE = typeof(System.Text.StringBuilder); | |||
internal static readonly Type ENTITY_STRUCT_INFO_VIEW = typeof(EntityInfoViewComponent); | |||
internal static readonly Type ENTITY_INFO_COMPONENT = typeof(EntityInfoComponent); | |||
} | |||
} |
@@ -30,6 +30,7 @@ namespace Svelto.ECS | |||
var castedDic = dictionary as ITypeSafeDictionary<T>; | |||
T entityComponent = default; | |||
if (IS_ENTITY_VIEW_COMPONENT) | |||
{ | |||
DBC.ECS.Check.Require(castedDic.ContainsKey(egid.entityID) == false, | |||
@@ -89,7 +90,7 @@ namespace Svelto.ECS | |||
EntityViewComponentCache.InitCache(); | |||
else | |||
{ | |||
if (ENTITY_COMPONENT_TYPE != ComponentBuilderUtilities.ENTITY_STRUCT_INFO_VIEW && ENTITY_COMPONENT_TYPE.IsUnmanagedEx() == false) | |||
if (ENTITY_COMPONENT_TYPE != ComponentBuilderUtilities.ENTITY_INFO_COMPONENT && ENTITY_COMPONENT_TYPE.IsUnmanagedEx() == false) | |||
throw new Exception($"Entity Component check failed, unexpected struct type (must be unmanaged) {ENTITY_COMPONENT_TYPE}"); | |||
} | |||
} | |||
@@ -104,7 +105,12 @@ namespace Svelto.ECS | |||
static readonly T DEFAULT_IT; | |||
static readonly string ENTITY_COMPONENT_NAME; | |||
/// <summary> | |||
/// Note: this static class will hold forever the references of the entities implementors. These references | |||
/// are not even cleared when the engines root is destroyed, as they are static references. | |||
/// It must be seen as an application-wide cache system. Honestly, I am not sure if this may cause leaking | |||
/// issues in some kind of applications. To remember. | |||
/// </summary> | |||
static class EntityViewComponentCache | |||
{ | |||
internal static readonly FasterList<KeyValuePair<Type, FastInvokeActionCast<T>>> cachedFields; | |||
@@ -0,0 +1,7 @@ | |||
namespace Svelto.ECS | |||
{ | |||
public struct LinkedEntityComponent : IEntityComponent | |||
{ | |||
public EGID linkedEntity; | |||
} | |||
} |
@@ -61,7 +61,7 @@ namespace Svelto.ECS.Internal | |||
} | |||
} | |||
public void AddEntitiesToEngines(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityComponentEnginesDB, | |||
public void AddEntitiesToEngines(FasterDictionary<RefWrapperType, FasterList<IEngine>> entityComponentEnginesDB, | |||
ITypeSafeDictionary realDic, | |||
ExclusiveGroupStruct @group, | |||
in PlatformProfiler profiler) | |||
@@ -75,7 +75,7 @@ namespace Svelto.ECS.Internal | |||
} | |||
public void RemoveEntitiesFromEngines( | |||
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityComponentEnginesDB, in PlatformProfiler profiler, | |||
FasterDictionary<RefWrapperType, FasterList<IEngine>> entityComponentEnginesDB, in PlatformProfiler profiler, | |||
ExclusiveGroupStruct @group) | |||
{ | |||
foreach (var value in _implementation) | |||
@@ -105,7 +105,7 @@ namespace Svelto.ECS.Internal | |||
public void Clear() { _implementation.Clear(); } | |||
public void MoveEntityFromEngines(EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup, | |||
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines, | |||
FasterDictionary<RefWrapperType, FasterList<IEngine>> engines, | |||
in PlatformProfiler profiler) | |||
{ | |||
var valueIndex = _implementation.GetIndex(fromEntityGid.entityID); | |||
@@ -139,14 +139,14 @@ namespace Svelto.ECS.Internal | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ITypeSafeDictionary Create() { return new FastTypeSafeDictionary<TValue>(); } | |||
void AddEntityComponentToEngines(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityComponentEnginesDB, | |||
void AddEntityComponentToEngines(FasterDictionary<RefWrapperType, FasterList<IEngine>> entityComponentEnginesDB, | |||
ref TValue entity, | |||
ExclusiveGroupStruct? previousGroup, | |||
in PlatformProfiler profiler, | |||
EGID egid) | |||
{ | |||
//get all the engines linked to TValue | |||
if (!entityComponentEnginesDB.TryGetValue(new RefWrapper<Type>(_type), out var entityComponentsEngines)) return; | |||
if (!entityComponentEnginesDB.TryGetValue(new RefWrapperType(_type), out var entityComponentsEngines)) return; | |||
if (previousGroup == null) | |||
{ | |||
@@ -184,13 +184,13 @@ namespace Svelto.ECS.Internal | |||
} | |||
} | |||
static void RemoveEntityComponentFromEngines(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> @group, | |||
static void RemoveEntityComponentFromEngines(FasterDictionary<RefWrapperType, FasterList<IEngine>> @group, | |||
ref TValue entity, | |||
ExclusiveGroupStruct? previousGroup, | |||
in PlatformProfiler profiler, | |||
EGID egid) | |||
{ | |||
if (!@group.TryGetValue(new RefWrapper<Type>(_type), out var entityComponentsEngines)) return; | |||
if (!@group.TryGetValue(new RefWrapperType(_type), out var entityComponentsEngines)) return; | |||
if (previousGroup == null) | |||
{ | |||
@@ -17,16 +17,19 @@ namespace Svelto.ECS.Internal | |||
public interface ITypeSafeDictionary:IDisposable | |||
{ | |||
uint count { get; } | |||
uint count { get; } | |||
ITypeSafeDictionary Create(); | |||
void AddEntitiesToEngines(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityComponentEnginesDb, | |||
ITypeSafeDictionary realDic, ExclusiveGroupStruct @group, in PlatformProfiler profiler); | |||
void RemoveEntitiesFromEngines(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityComponentEnginesDB, | |||
//todo: there is something wrong in the design of the execute callback methods. Something to cleanup | |||
void ExecuteEnginesAddOrSwapCallbacks(FasterDictionary<RefWrapperType, FasterList<IReactEngine>> entityComponentEnginesDb, | |||
ITypeSafeDictionary realDic, ExclusiveGroupStruct? fromGroup, ExclusiveGroupStruct toGroup, in PlatformProfiler profiler); | |||
void ExecuteEnginesSwapOrRemoveCallbacks(EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup, | |||
FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines, in PlatformProfiler profiler); | |||
void ExecuteEnginesRemoveCallbacks(FasterDictionary<RefWrapperType, FasterList<IReactEngine>> entityComponentEnginesDB, | |||
in PlatformProfiler profiler, ExclusiveGroupStruct @group); | |||
void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId); | |||
void MoveEntityFromEngines(EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup, | |||
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines, in PlatformProfiler profiler); | |||
void AddEntityToDictionary(EGID fromEntityGid, EGID toEntityID, ITypeSafeDictionary toGroup); | |||
void RemoveEntityFromDictionary(EGID fromEntityGid); | |||
@@ -38,5 +41,7 @@ namespace Svelto.ECS.Internal | |||
bool ContainsKey(uint egidEntityId); | |||
uint GetIndex(uint valueEntityId); | |||
bool TryFindIndex(uint entityGidEntityId, out uint index); | |||
void KeysEvaluator(System.Action<uint> action); | |||
} | |||
} |
@@ -0,0 +1,74 @@ | |||
// using System.Threading.Tasks; | |||
// using NUnit.Framework; | |||
// using Svelto.Common; | |||
// using Svelto.ECS.DataStructures; | |||
// | |||
// namespace Svelto.ECS.Tests.Common.DataStructures | |||
// { | |||
// [TestFixture] | |||
// public class ThreadSafeNativeBagTest | |||
// { | |||
// [Test] | |||
// public void TestByteReallocWorks() | |||
// { | |||
// var threadNativeBag = new ThreadSafeNativeBag(Allocator.Persistent); | |||
// | |||
// Parallel.Invoke(() => | |||
// { | |||
// for (int i = 0; i < 100; i++) | |||
// { | |||
// threadNativeBag.Enqueue((int)1); | |||
// } | |||
// } | |||
// , // close first Action | |||
// () => | |||
// { | |||
// for (int i = 0; i < 100; i++) | |||
// { | |||
// threadNativeBag.Enqueue((int)2); | |||
// } | |||
// } | |||
// , //close second Action | |||
// | |||
// () => | |||
// { | |||
// for (int i = 0; i < 100; i++) | |||
// { | |||
// threadNativeBag.Enqueue(3); | |||
// } | |||
// } //close third Action | |||
// ); //close parallel.invoke | |||
// | |||
// // for (int i = 0; i < 100; i++) | |||
// // { | |||
// // threadNativeBag.Enqueue(1); | |||
// // } | |||
// | |||
// int oneCount = 0, twoCount = 0, threeCount = 0; | |||
// | |||
// while (threadNativeBag.count > 0) | |||
// { | |||
// var value = threadNativeBag.Dequeue<int>(); | |||
// | |||
// switch (value) | |||
// { | |||
// case 1: | |||
// oneCount++; | |||
// break; | |||
// case 2: | |||
// twoCount++; | |||
// break; | |||
// case 3: | |||
// threeCount++; | |||
// break; | |||
// } | |||
// } | |||
// | |||
// Assert.That(oneCount, Is.EqualTo(100)); | |||
// Assert.That(twoCount, Is.EqualTo(100)); | |||
// Assert.That(threeCount, Is.EqualTo(100)); | |||
// | |||
// threadNativeBag.Dispose(); | |||
// } | |||
// } | |||
// } |
@@ -8,30 +8,36 @@ namespace Svelto.ECS.Internal | |||
{ | |||
sealed class TypeSafeDictionary<TValue> : ITypeSafeDictionary<TValue> where TValue : struct, IEntityComponent | |||
{ | |||
static readonly Type _type = typeof(TValue); | |||
static readonly string _typeName = _type.Name; | |||
static readonly bool _hasEgid = typeof(INeedEGID).IsAssignableFrom(_type); | |||
static readonly Type _type = typeof(TValue); | |||
static readonly string _typeName = _type.Name; | |||
static readonly bool _hasEgid = typeof(INeedEGID).IsAssignableFrom(_type); | |||
internal static readonly bool _isUmanaged = | |||
internal static readonly bool IsUnmanaged = | |||
_type.IsUnmanagedEx() && (typeof(IEntityViewComponent).IsAssignableFrom(_type) == false); | |||
internal SveltoDictionary<uint, TValue, NativeStrategy<FasterDictionaryNode<uint>>, ManagedStrategy<TValue>> implMgd; | |||
internal SveltoDictionary<uint, TValue, NativeStrategy<FasterDictionaryNode<uint>>, NativeStrategy<TValue>> implUnmgd; | |||
SveltoDictionary<uint, TValue, NativeStrategy<FasterDictionaryNode<uint>>, ManagedStrategy<TValue>, | |||
ManagedStrategy<int>> implMgd; | |||
//used directly by native methods | |||
internal SveltoDictionary<uint, TValue, NativeStrategy<FasterDictionaryNode<uint>>, NativeStrategy<TValue>, | |||
NativeStrategy<int>> implUnmgd; | |||
public TypeSafeDictionary(uint size) | |||
{ | |||
if (_isUmanaged) | |||
implUnmgd = new SveltoDictionary<uint, TValue, NativeStrategy<FasterDictionaryNode<uint>>, NativeStrategy<TValue>>(size); | |||
if (IsUnmanaged) | |||
implUnmgd = new SveltoDictionary<uint, TValue, NativeStrategy<FasterDictionaryNode<uint>>, | |||
NativeStrategy<TValue>, NativeStrategy<int>>(size); | |||
else | |||
{ | |||
implMgd = new SveltoDictionary<uint, TValue, NativeStrategy<FasterDictionaryNode<uint>>, ManagedStrategy<TValue>>(size); | |||
implMgd = new SveltoDictionary<uint, TValue, NativeStrategy<FasterDictionaryNode<uint>>, | |||
ManagedStrategy<TValue>, ManagedStrategy<int>>(size); | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Add(uint egidEntityId, in TValue entityComponent) | |||
{ | |||
if (_isUmanaged) | |||
if (IsUnmanaged) | |||
implUnmgd.Add(egidEntityId, entityComponent); | |||
else | |||
implMgd.Add(egidEntityId, entityComponent); | |||
@@ -45,7 +51,7 @@ namespace Svelto.ECS.Internal | |||
/// <exception cref="TypeSafeDictionaryException"></exception> | |||
public void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId) | |||
{ | |||
if (_isUmanaged) | |||
if (IsUnmanaged) | |||
{ | |||
var typeSafeDictionary = (entitiesToSubmit as TypeSafeDictionary<TValue>).implUnmgd; | |||
@@ -89,18 +95,19 @@ namespace Svelto.ECS.Internal | |||
} | |||
} | |||
public void AddEntitiesToEngines | |||
(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityComponentEnginesDB | |||
, ITypeSafeDictionary realDic, ExclusiveGroupStruct group, in PlatformProfiler profiler) | |||
public void ExecuteEnginesAddOrSwapCallbacks | |||
(FasterDictionary<RefWrapperType, FasterList<IReactEngine>> entityComponentEnginesDB | |||
, ITypeSafeDictionary realDic, ExclusiveGroupStruct? fromGroup, ExclusiveGroupStruct toGroup | |||
, in PlatformProfiler profiler) | |||
{ | |||
if (_isUmanaged) | |||
if (IsUnmanaged) | |||
{ | |||
var typeSafeDictionary = realDic as ITypeSafeDictionary<TValue>; | |||
//this can be optimized, should pass all the entities and not restart the process for each one | |||
foreach (var value in implUnmgd) | |||
AddEntityComponentToEngines(entityComponentEnginesDB, ref typeSafeDictionary[value.Key] | |||
, null, in profiler, new EGID(value.Key, group)); | |||
ExecuteEnginesAddOrSwapCallbacksOnSingleEntity(entityComponentEnginesDB, ref typeSafeDictionary[value.Key] | |||
, fromGroup, in profiler, new EGID(value.Key, toGroup)); | |||
} | |||
else | |||
{ | |||
@@ -108,14 +115,14 @@ namespace Svelto.ECS.Internal | |||
//this can be optimized, should pass all the entities and not restart the process for each one | |||
foreach (var value in implMgd) | |||
AddEntityComponentToEngines(entityComponentEnginesDB, ref typeSafeDictionary[value.Key] | |||
, null, in profiler, new EGID(value.Key, group)); | |||
ExecuteEnginesAddOrSwapCallbacksOnSingleEntity(entityComponentEnginesDB, ref typeSafeDictionary[value.Key] | |||
, fromGroup, in profiler, new EGID(value.Key, toGroup)); | |||
} | |||
} | |||
public void AddEntityToDictionary(EGID fromEntityGid, EGID toEntityID, ITypeSafeDictionary toGroup) | |||
{ | |||
if (_isUmanaged) | |||
if (IsUnmanaged) | |||
{ | |||
var valueIndex = implUnmgd.GetIndex(fromEntityGid.entityID); | |||
@@ -152,7 +159,7 @@ namespace Svelto.ECS.Internal | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Clear() | |||
{ | |||
if (_isUmanaged) | |||
if (IsUnmanaged) | |||
{ | |||
implUnmgd.Clear(); | |||
} | |||
@@ -165,7 +172,7 @@ namespace Svelto.ECS.Internal | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void FastClear() | |||
{ | |||
if (_isUmanaged) | |||
if (IsUnmanaged) | |||
{ | |||
implUnmgd.FastClear(); | |||
} | |||
@@ -178,7 +185,7 @@ namespace Svelto.ECS.Internal | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool ContainsKey(uint egidEntityId) | |||
{ | |||
if (_isUmanaged) | |||
if (IsUnmanaged) | |||
{ | |||
return implUnmgd.ContainsKey(egidEntityId); | |||
} | |||
@@ -189,15 +196,12 @@ namespace Svelto.ECS.Internal | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ITypeSafeDictionary Create() | |||
{ | |||
return TypeSafeDictionaryFactory<TValue>.Create(1); | |||
} | |||
public ITypeSafeDictionary Create() { return TypeSafeDictionaryFactory<TValue>.Create(1); } | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public uint GetIndex(uint valueEntityId) | |||
{ | |||
if (_isUmanaged) | |||
if (IsUnmanaged) | |||
{ | |||
return this.implUnmgd.GetIndex(valueEntityId); | |||
} | |||
@@ -210,7 +214,7 @@ namespace Svelto.ECS.Internal | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref TValue GetOrCreate(uint idEntityId) | |||
{ | |||
if (_isUmanaged) | |||
if (IsUnmanaged) | |||
{ | |||
return ref this.implUnmgd.GetOrCreate(idEntityId); | |||
} | |||
@@ -223,7 +227,7 @@ namespace Svelto.ECS.Internal | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public IBuffer<TValue> GetValues(out uint count) | |||
{ | |||
if (_isUmanaged) | |||
if (IsUnmanaged) | |||
{ | |||
return this.implUnmgd.GetValues(out count); | |||
} | |||
@@ -236,7 +240,7 @@ namespace Svelto.ECS.Internal | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref TValue GetDirectValueByRef(uint key) | |||
{ | |||
if (_isUmanaged) | |||
if (IsUnmanaged) | |||
{ | |||
return ref this.implUnmgd.GetDirectValueByRef(key); | |||
} | |||
@@ -249,7 +253,7 @@ namespace Svelto.ECS.Internal | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool Has(uint key) | |||
{ | |||
if (_isUmanaged) | |||
if (IsUnmanaged) | |||
{ | |||
return this.implUnmgd.ContainsKey(key); | |||
} | |||
@@ -259,21 +263,19 @@ namespace Svelto.ECS.Internal | |||
} | |||
} | |||
public void MoveEntityFromEngines | |||
public void ExecuteEnginesSwapOrRemoveCallbacks | |||
(EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup | |||
, FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines, in PlatformProfiler profiler) | |||
, FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines, in PlatformProfiler profiler) | |||
{ | |||
if (_isUmanaged) | |||
if (IsUnmanaged) | |||
{ | |||
var valueIndex = this.implUnmgd.GetIndex(fromEntityGid.entityID); | |||
ref var entity = ref this.implUnmgd.GetDirectValueByRef(valueIndex); | |||
//move | |||
if (toGroup != null) | |||
{ | |||
RemoveEntityComponentFromEngines(engines, ref entity, fromEntityGid.groupID, in profiler | |||
, fromEntityGid); | |||
var toGroupCasted = toGroup as ITypeSafeDictionary<TValue>; | |||
var previousGroup = fromEntityGid.groupID; | |||
@@ -282,12 +284,13 @@ namespace Svelto.ECS.Internal | |||
var index = toGroupCasted.GetIndex(toEntityID.Value.entityID); | |||
AddEntityComponentToEngines(engines, ref toGroupCasted.GetDirectValueByRef(index), previousGroup | |||
, in profiler, toEntityID.Value); | |||
ExecuteEnginesAddOrSwapCallbacksOnSingleEntity(engines, ref toGroupCasted.GetDirectValueByRef(index) | |||
, previousGroup, in profiler, toEntityID.Value); | |||
} | |||
//remove | |||
else | |||
{ | |||
RemoveEntityComponentFromEngines(engines, ref entity, null, in profiler, fromEntityGid); | |||
ExecuteEnginesRemoveCallbackOnSingleEntity(engines, ref entity, in profiler, fromEntityGid); | |||
} | |||
} | |||
else | |||
@@ -298,9 +301,6 @@ namespace Svelto.ECS.Internal | |||
if (toGroup != null) | |||
{ | |||
RemoveEntityComponentFromEngines(engines, ref entity, fromEntityGid.groupID, in profiler | |||
, fromEntityGid); | |||
var toGroupCasted = toGroup as ITypeSafeDictionary<TValue>; | |||
var previousGroup = fromEntityGid.groupID; | |||
@@ -309,37 +309,38 @@ namespace Svelto.ECS.Internal | |||
var index = toGroupCasted.GetIndex(toEntityID.Value.entityID); | |||
AddEntityComponentToEngines(engines, ref toGroupCasted.GetDirectValueByRef(index), previousGroup | |||
, in profiler, toEntityID.Value); | |||
ExecuteEnginesAddOrSwapCallbacksOnSingleEntity(engines, ref toGroupCasted.GetDirectValueByRef(index) | |||
, previousGroup, in profiler, toEntityID.Value); | |||
} | |||
else | |||
{ | |||
RemoveEntityComponentFromEngines(engines, ref entity, null, in profiler, fromEntityGid); | |||
ExecuteEnginesRemoveCallbackOnSingleEntity(engines, ref entity, in profiler, fromEntityGid); | |||
} | |||
} | |||
} | |||
public void RemoveEntitiesFromEngines(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines | |||
, in PlatformProfiler profiler, ExclusiveGroupStruct group) | |||
public void ExecuteEnginesRemoveCallbacks | |||
(FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines, in PlatformProfiler profiler | |||
, ExclusiveGroupStruct group) | |||
{ | |||
if (_isUmanaged) | |||
if (IsUnmanaged) | |||
{ | |||
foreach (var value in implUnmgd) | |||
RemoveEntityComponentFromEngines(engines, ref implUnmgd.GetValueByRef(value.Key), null | |||
, in profiler, new EGID(value.Key, group)); | |||
ExecuteEnginesRemoveCallbackOnSingleEntity(engines, ref implUnmgd.GetValueByRef(value.Key) | |||
, in profiler, new EGID(value.Key, group)); | |||
} | |||
else | |||
{ | |||
foreach (var value in implMgd) | |||
RemoveEntityComponentFromEngines(engines, ref implMgd.GetValueByRef(value.Key), null | |||
, in profiler, new EGID(value.Key, group)); | |||
ExecuteEnginesRemoveCallbackOnSingleEntity(engines, ref implMgd.GetValueByRef(value.Key) | |||
, in profiler, new EGID(value.Key, group)); | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void RemoveEntityFromDictionary(EGID fromEntityGid) | |||
{ | |||
if (_isUmanaged) | |||
if (IsUnmanaged) | |||
{ | |||
this.implUnmgd.Remove(fromEntityGid.entityID); | |||
} | |||
@@ -352,7 +353,7 @@ namespace Svelto.ECS.Internal | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void SetCapacity(uint size) | |||
{ | |||
if (_isUmanaged) | |||
if (IsUnmanaged) | |||
{ | |||
this.implUnmgd.SetCapacity(size); | |||
} | |||
@@ -365,7 +366,7 @@ namespace Svelto.ECS.Internal | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Trim() | |||
{ | |||
if (_isUmanaged) | |||
if (IsUnmanaged) | |||
{ | |||
this.implUnmgd.Trim(); | |||
} | |||
@@ -378,7 +379,7 @@ namespace Svelto.ECS.Internal | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool TryFindIndex(uint entityId, out uint index) | |||
{ | |||
if (_isUmanaged) | |||
if (IsUnmanaged) | |||
{ | |||
return implUnmgd.TryFindIndex(entityId, out index); | |||
} | |||
@@ -388,10 +389,28 @@ namespace Svelto.ECS.Internal | |||
} | |||
} | |||
public void KeysEvaluator(Action<uint> action) | |||
{ | |||
if (IsUnmanaged) | |||
{ | |||
foreach (var key in implUnmgd.keys) | |||
{ | |||
action(key); | |||
} | |||
} | |||
else | |||
{ | |||
foreach (var key in implMgd.keys) | |||
{ | |||
action(key); | |||
} | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool TryGetValue(uint entityId, out TValue item) | |||
{ | |||
if (_isUmanaged) | |||
if (IsUnmanaged) | |||
{ | |||
return this.implUnmgd.TryGetValue(entityId, out item); | |||
} | |||
@@ -406,13 +425,13 @@ namespace Svelto.ECS.Internal | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get | |||
{ | |||
if (_isUmanaged) | |||
if (IsUnmanaged) | |||
{ | |||
return this.implUnmgd.count; | |||
return (uint) this.implUnmgd.count; | |||
} | |||
else | |||
{ | |||
return this.implMgd.count; | |||
return (uint) this.implMgd.count; | |||
} | |||
} | |||
} | |||
@@ -422,7 +441,7 @@ namespace Svelto.ECS.Internal | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get | |||
{ | |||
if (_isUmanaged) | |||
if (IsUnmanaged) | |||
{ | |||
return ref this.implUnmgd.GetValueByRef(idEntityId); | |||
} | |||
@@ -433,36 +452,36 @@ namespace Svelto.ECS.Internal | |||
} | |||
} | |||
static void RemoveEntityComponentFromEngines | |||
(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines, ref TValue entity, uint? previousGroup | |||
static void ExecuteEnginesRemoveCallbackOnSingleEntity | |||
(FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines, ref TValue entity | |||
, in PlatformProfiler profiler, EGID egid) | |||
{ | |||
if (!engines.TryGetValue(new RefWrapper<Type>(_type), out var entityComponentsEngines)) | |||
if (!engines.TryGetValue(new RefWrapperType(_type), out var entityComponentsEngines)) | |||
return; | |||
if (previousGroup == null) | |||
for (var i = 0; i < entityComponentsEngines.count; i++) | |||
try | |||
for (var i = 0; i < entityComponentsEngines.count; i++) | |||
try | |||
{ | |||
using (profiler.Sample(entityComponentsEngines[i], _typeName)) | |||
{ | |||
using (profiler.Sample(entityComponentsEngines[i], _typeName)) | |||
{ | |||
(entityComponentsEngines[i] as IReactOnAddAndRemove<TValue>).Remove(ref entity, egid); | |||
} | |||
(entityComponentsEngines[i] as IReactOnAddAndRemove<TValue>).Remove(ref entity, egid); | |||
} | |||
catch | |||
{ | |||
Svelto.Console.LogError("Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString())); | |||
} | |||
catch | |||
{ | |||
Svelto.Console.LogError( | |||
"Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString())); | |||
throw; | |||
} | |||
throw; | |||
} | |||
} | |||
void AddEntityComponentToEngines | |||
(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines, ref TValue entity | |||
void ExecuteEnginesAddOrSwapCallbacksOnSingleEntity | |||
(FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines, ref TValue entity | |||
, ExclusiveGroupStruct? previousGroup, in PlatformProfiler profiler, EGID egid) | |||
{ | |||
//get all the engines linked to TValue | |||
if (!engines.TryGetValue(new RefWrapper<Type>(_type), out var entityComponentsEngines)) | |||
if (!engines.TryGetValue(new RefWrapperType(_type), out var entityComponentsEngines)) | |||
return; | |||
if (previousGroup == null) | |||
@@ -477,7 +496,8 @@ namespace Svelto.ECS.Internal | |||
} | |||
catch | |||
{ | |||
Svelto.Console.LogError("Code crashed inside Add callback ".FastConcat(typeof(TValue).ToString())); | |||
Svelto.Console.LogError( | |||
"Code crashed inside Add callback ".FastConcat(typeof(TValue).ToString())); | |||
throw; | |||
} | |||
@@ -495,7 +515,8 @@ namespace Svelto.ECS.Internal | |||
} | |||
catch (Exception) | |||
{ | |||
Svelto.Console.LogError("Code crashed inside MoveTo callback ".FastConcat(typeof(TValue).ToString())); | |||
Svelto.Console.LogError( | |||
"Code crashed inside MoveTo callback ".FastConcat(typeof(TValue).ToString())); | |||
throw; | |||
} | |||
@@ -504,11 +525,11 @@ namespace Svelto.ECS.Internal | |||
public void Dispose() | |||
{ | |||
if (_isUmanaged) | |||
if (IsUnmanaged) | |||
implUnmgd.Dispose(); | |||
else | |||
implMgd.Dispose(); | |||
GC.SuppressFinalize(this); | |||
} | |||
} |
@@ -1,40 +1,33 @@ | |||
#if UNITY_NATIVE //because of the thread count, ATM this is only for unity | |||
using System; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.Common; | |||
using Unity.Jobs.LowLevel.Unsafe; | |||
using Allocator = Svelto.Common.Allocator; | |||
namespace Svelto.ECS.DataStructures.Unity | |||
namespace Svelto.ECS.DataStructures | |||
{ | |||
/// <summary> | |||
/// A collection of <see cref="NativeBag"/> intended to allow one buffer per thread. | |||
/// from: https://github.com/jeffvella/UnityEcsEvents/blob/develop/Runtime/MultiAppendBuffer.cs | |||
/// </summary> | |||
public unsafe struct AtomicNativeBags:IDisposable | |||
{ | |||
public const int DefaultThreadIndex = -1; | |||
const int MinThreadIndex = DefaultThreadIndex; | |||
#if UNITY_COLLECTIONS | |||
[global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction] | |||
#endif | |||
readonly NativeBag* _data; | |||
NativeBag* _data; | |||
readonly Allocator _allocator; | |||
readonly uint _threadsCount; | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool IsInvalidThreadIndex(int index) => index < MinThreadIndex || index > _threadsCount; | |||
public uint count => _threadsCount; | |||
public AtomicNativeBags(Common.Allocator allocator, uint threadsCount) | |||
public AtomicNativeBags(Allocator allocator) | |||
{ | |||
_allocator = allocator; | |||
_threadsCount = threadsCount; | |||
_allocator = allocator; | |||
_threadsCount = JobsUtility.MaxJobThreadCount + 1; | |||
var bufferSize = MemoryUtilities.SizeOf<NativeBag>(); | |||
var bufferCount = _threadsCount; | |||
var allocationSize = bufferSize * bufferCount; | |||
var ptr = (byte*)MemoryUtilities.Alloc((uint) allocationSize, allocator); | |||
MemoryUtilities.MemClear((IntPtr) ptr, (uint) allocationSize); | |||
// MemoryUtilities.MemClear((IntPtr) ptr, (uint) allocationSize); | |||
for (int i = 0; i < bufferCount; i++) | |||
{ | |||
@@ -49,22 +42,30 @@ namespace Svelto.ECS.DataStructures.Unity | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref NativeBag GetBuffer(int index) | |||
{ | |||
if (_data == null) | |||
throw new Exception("using invalid AtomicNativeBags"); | |||
return ref MemoryUtilities.ArrayElementAsRef<NativeBag>((IntPtr) _data, index); | |||
} | |||
public uint count => _threadsCount; | |||
public void Dispose() | |||
{ | |||
if (_data == null) | |||
throw new Exception("using invalid AtomicNativeBags"); | |||
for (int i = 0; i < _threadsCount; i++) | |||
{ | |||
GetBuffer(i).Dispose(); | |||
} | |||
MemoryUtilities.Free((IntPtr) _data, _allocator); | |||
_data = null; | |||
} | |||
public void Clear() | |||
{ | |||
if (_data == null) | |||
throw new Exception("using invalid AtomicNativeBags"); | |||
for (int i = 0; i < _threadsCount; i++) | |||
{ | |||
GetBuffer(i).Clear(); | |||
@@ -72,3 +73,4 @@ namespace Svelto.ECS.DataStructures.Unity | |||
} | |||
} | |||
} | |||
#endif |
@@ -0,0 +1,281 @@ | |||
#if DEBUG && !PROFILE_SVELTO | |||
#define ENABLE_DEBUG_CHECKS | |||
#endif | |||
#if DEBUG && !PROFILE_SVELTO | |||
//#define ENABLE_THREAD_SAFE_CHECKS | |||
#endif | |||
using System; | |||
using System.Diagnostics; | |||
using System.Runtime.CompilerServices; | |||
using System.Threading; | |||
using Svelto.Common; | |||
namespace Svelto.ECS.DataStructures | |||
{ | |||
/// <summary> | |||
/// Burst friendly RingBuffer on steroid: | |||
/// it can: Enqueue/Dequeue, it wraps if there is enough space after dequeuing | |||
/// It resizes if there isn't enough space left. | |||
/// It's a "bag", you can queue and dequeue any T. Just be sure that you dequeue what you queue! No check on type | |||
/// is done. | |||
/// You can reserve a position in the queue to update it later. | |||
/// The datastructure is a struct and it's "copyable" | |||
/// I eventually decided to call it NativeBag and not NativeBag because it can also be used as | |||
/// a preallocated memory pool where any kind of T can be stored as long as T is unmanaged | |||
/// </summary> | |||
public struct NativeBag : IDisposable | |||
{ | |||
public uint count | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get | |||
{ | |||
unsafe | |||
{ | |||
BasicTests(); | |||
#if ENABLE_THREAD_SAFE_CHECKS | |||
try | |||
{ | |||
#endif | |||
return _queue->size; | |||
#if ENABLE_THREAD_SAFE_CHECKS | |||
} | |||
finally | |||
{ | |||
Volatile.Write(ref _threadSentinel, 0); | |||
} | |||
#endif | |||
} | |||
} | |||
} | |||
public uint capacity | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get | |||
{ | |||
unsafe | |||
{ | |||
BasicTests(); | |||
#if ENABLE_THREAD_SAFE_CHECKS | |||
try | |||
{ | |||
#endif | |||
return _queue->capacity; | |||
#if ENABLE_THREAD_SAFE_CHECKS | |||
} | |||
finally | |||
{ | |||
Volatile.Write(ref _threadSentinel, 0); | |||
} | |||
#endif | |||
} | |||
} | |||
} | |||
public NativeBag(Allocator allocator) | |||
{ | |||
unsafe | |||
{ | |||
var sizeOf = MemoryUtilities.SizeOf<UnsafeBlob>(); | |||
var listData = (UnsafeBlob*) MemoryUtilities.Alloc((uint) sizeOf, allocator); | |||
//clear to nullify the pointers | |||
//MemoryUtilities.MemClear((IntPtr) listData, (uint) sizeOf); | |||
listData->allocator = allocator; | |||
_queue = listData; | |||
#if ENABLE_THREAD_SAFE_CHECKS | |||
_threadSentinel = 0; | |||
#endif | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool IsEmpty() | |||
{ | |||
unsafe | |||
{ | |||
BasicTests(); | |||
#if ENABLE_THREAD_SAFE_CHECKS | |||
try | |||
{ | |||
#endif | |||
if (_queue == null || _queue->ptr == null) | |||
return true; | |||
#if ENABLE_THREAD_SAFE_CHECKS | |||
} | |||
finally | |||
{ | |||
Volatile.Write(ref _threadSentinel, 0); | |||
} | |||
#endif | |||
} | |||
return count == 0; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public unsafe void Dispose() | |||
{ | |||
if (_queue != null) | |||
{ | |||
#if ENABLE_THREAD_SAFE_CHECKS | |||
//todo: this must be unit tested | |||
if (Interlocked.CompareExchange(ref _threadSentinel, 1, 0) != 0) | |||
throw new Exception("NativeBag is not thread safe, reading and writing operations can happen" + | |||
"on different threads, but not simultaneously"); | |||
try | |||
{ | |||
#endif | |||
_queue->Dispose(); | |||
MemoryUtilities.Free((IntPtr) _queue, _queue->allocator); | |||
_queue = null; | |||
#if ENABLE_THREAD_SAFE_CHECKS | |||
} | |||
finally | |||
{ | |||
Volatile.Write(ref _threadSentinel, 0); | |||
} | |||
#endif | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref T ReserveEnqueue<T>(out UnsafeArrayIndex index) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
BasicTests(); | |||
var sizeOf = MemoryUtilities.SizeOf<T>(); | |||
if (_queue->space - sizeOf < 0) | |||
_queue->Realloc((uint) ((_queue->capacity + MemoryUtilities.Align4((uint) sizeOf)) * 2.0f)); | |||
#if ENABLE_THREAD_SAFE_CHECKS | |||
try | |||
{ | |||
#endif | |||
return ref _queue->Reserve<T>(out index); | |||
#if ENABLE_THREAD_SAFE_CHECKS | |||
} | |||
finally | |||
{ | |||
Volatile.Write(ref _threadSentinel, 0); | |||
} | |||
#endif | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Enqueue<T>(in T item) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
BasicTests(); | |||
#if ENABLE_THREAD_SAFE_CHECKS | |||
try | |||
{ | |||
#endif | |||
var sizeOf = MemoryUtilities.SizeOf<T>(); | |||
if (_queue->space - sizeOf < 0) | |||
_queue->Realloc((uint) ((_queue->capacity + MemoryUtilities.Align4((uint) sizeOf)) * 2.0f)); | |||
_queue->Write(item); | |||
#if ENABLE_THREAD_SAFE_CHECKS | |||
} | |||
finally | |||
{ | |||
Volatile.Write(ref _threadSentinel, 0); | |||
} | |||
#endif | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Clear() | |||
{ | |||
unsafe | |||
{ | |||
BasicTests(); | |||
#if ENABLE_THREAD_SAFE_CHECKS | |||
try | |||
{ | |||
#endif | |||
_queue->Clear(); | |||
#if ENABLE_THREAD_SAFE_CHECKS | |||
} | |||
finally | |||
{ | |||
Volatile.Write(ref _threadSentinel, 0); | |||
} | |||
#endif | |||
} | |||
} | |||
public T Dequeue<T>() where T : struct | |||
{ | |||
unsafe | |||
{ | |||
BasicTests(); | |||
#if ENABLE_THREAD_SAFE_CHECKS | |||
try | |||
{ | |||
#endif | |||
return _queue->Read<T>(); | |||
#if ENABLE_THREAD_SAFE_CHECKS | |||
} | |||
finally | |||
{ | |||
Volatile.Write(ref _threadSentinel, 0); | |||
} | |||
#endif | |||
} | |||
} | |||
internal ref T AccessReserved<T>(UnsafeArrayIndex reserverIndex) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
BasicTests(); | |||
#if ENABLE_THREAD_SAFE_CHECKS | |||
try | |||
{ | |||
#endif | |||
return ref _queue->AccessReserved<T>(reserverIndex); | |||
#if ENABLE_THREAD_SAFE_CHECKS | |||
} | |||
finally | |||
{ | |||
Volatile.Write(ref _threadSentinel, 0); | |||
} | |||
#endif | |||
} | |||
} | |||
[Conditional("ENABLE_DEBUG_CHECKS")] | |||
unsafe void BasicTests() | |||
{ | |||
if (_queue == null) | |||
throw new Exception("SimpleNativeArray: null-access"); | |||
#if ENABLE_THREAD_SAFE_CHECKS | |||
todo: this must be unit tested | |||
if (Interlocked.CompareExchange(ref _threadSentinel, 1, 0) != 0) | |||
throw new Exception("NativeBag is not thread safe, reading and writing operations can happen" | |||
+ "on different threads, but not simultaneously"); | |||
#endif | |||
} | |||
#if ENABLE_THREAD_SAFE_CHECKS | |||
int _threadSentinel; | |||
#endif | |||
#if UNITY_NATIVE | |||
[global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction] | |||
#endif | |||
unsafe UnsafeBlob* _queue; | |||
} | |||
} |
@@ -7,13 +7,17 @@ namespace Svelto.ECS.DataStructures | |||
{ | |||
public struct NativeDynamicArray : IDisposable | |||
{ | |||
#if UNITY_COLLECTIONS | |||
[global::Unity.Burst.NoAlias] [global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction] | |||
#endif | |||
unsafe UnsafeArray* _list; | |||
#if DEBUG && !PROFILE_SVELTO | |||
int hashType; | |||
#endif | |||
public bool isValid | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get | |||
{ | |||
unsafe | |||
{ | |||
return _list != null; | |||
} | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public int Count<T>() where T : struct | |||
@@ -23,8 +27,8 @@ namespace Svelto.ECS.DataStructures | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
if (hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not excepted type used"); | |||
if (_hashType != TypeHash<T>.hash) | |||
throw new Exception($"NativeDynamicArray: not expected type used"); | |||
#endif | |||
return (_list->count / MemoryUtilities.SizeOf<T>()); | |||
@@ -39,8 +43,8 @@ namespace Svelto.ECS.DataStructures | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
if (hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not excepted type used"); | |||
if (_hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not expected type used"); | |||
#endif | |||
return (_list->capacity / MemoryUtilities.SizeOf<T>()); | |||
@@ -51,20 +55,21 @@ namespace Svelto.ECS.DataStructures | |||
{ | |||
unsafe | |||
{ | |||
var rtnStruc = new NativeDynamicArray(); | |||
#if DEBUG && !PROFILE_SVELTO | |||
rtnStruc.hashType = TypeHash<T>.hash; | |||
var rtnStruc = new NativeDynamicArray {_hashType = TypeHash<T>.hash}; | |||
#else | |||
NativeDynamicArray rtnStruc = default; | |||
#endif | |||
var sizeOf = MemoryUtilities.SizeOf<T>(); | |||
uint pointerSize = (uint) MemoryUtilities.SizeOf<UnsafeArray>(); | |||
UnsafeArray* listData = (UnsafeArray*) MemoryUtilities.Alloc(pointerSize, allocator); | |||
uint structSize = (uint) MemoryUtilities.SizeOf<UnsafeArray>(); | |||
UnsafeArray* listData = (UnsafeArray*) MemoryUtilities.Alloc(structSize, allocator); | |||
//clear to nullify the pointers | |||
MemoryUtilities.MemClear((IntPtr) listData, pointerSize); | |||
//MemoryUtilities.MemClear((IntPtr) listData, structSize); | |||
listData->allocator = allocator; | |||
listData->Realloc((uint) (newLength * sizeOf)); | |||
rtnStruc._allocator = allocator; | |||
listData->Realloc((uint) (newLength * sizeOf), allocator); | |||
rtnStruc._list = listData; | |||
@@ -80,8 +85,8 @@ namespace Svelto.ECS.DataStructures | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
if (hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not excepted type used"); | |||
if (_hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not expected type used"); | |||
if (index >= Count<T>()) | |||
throw new Exception($"NativeDynamicArray: out of bound access, index {index} count {Count<T>()}"); | |||
#endif | |||
@@ -97,8 +102,8 @@ namespace Svelto.ECS.DataStructures | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
if (hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not excepted type used"); | |||
if (_hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not expected type used"); | |||
if (index >= Capacity<T>()) | |||
throw new Exception($"NativeDynamicArray: out of bound access, index {index} capacity {Capacity<T>()}"); | |||
#endif | |||
@@ -112,7 +117,7 @@ namespace Svelto.ECS.DataStructures | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
#endif | |||
_list->Dispose(); | |||
_list->Dispose(_allocator); | |||
_list = null; | |||
} | |||
@@ -124,18 +129,42 @@ namespace Svelto.ECS.DataStructures | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
if (hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not excepted type used"); | |||
if (_hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not expected type used"); | |||
#endif | |||
var structSize = (uint) MemoryUtilities.SizeOf<T>(); | |||
if (_list->space - (int) structSize < 0) | |||
_list->Realloc((uint) (((uint) ((Count<T>() + 1) * 1.5f) * (float) structSize))); | |||
_list->Realloc((uint) (((uint) ((Count<T>() + 1) * 1.5f) * (float) structSize)), _allocator); | |||
_list->Add(item); | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref T AddAt<T>(uint index) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
if (_hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not expected type used"); | |||
#endif | |||
var structSize = (uint) MemoryUtilities.SizeOf<T>(); | |||
if (index >= Capacity<T>()) | |||
_list->Realloc((uint) (((index + 1) * 1.5f) * structSize), _allocator); | |||
var writeIndex = (index + 1) * structSize; | |||
if (_list->count < writeIndex) | |||
_list->SetCountTo(writeIndex); | |||
return ref _list->Get<T>(index); | |||
} | |||
} | |||
public void Grow<T>(uint newCapacity) where T : struct | |||
{ | |||
unsafe | |||
@@ -143,15 +172,15 @@ namespace Svelto.ECS.DataStructures | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
if (hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not excepted type used"); | |||
if (_hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not expected type used"); | |||
if (newCapacity <= Capacity<T>()) | |||
throw new Exception("New capacity must be greater than current one"); | |||
#endif | |||
uint structSize = (uint) MemoryUtilities.SizeOf<T>(); | |||
uint size = (uint) (newCapacity * structSize); | |||
_list->Realloc((uint) size); | |||
_list->Realloc((uint) size, _allocator); | |||
} | |||
} | |||
@@ -162,8 +191,8 @@ namespace Svelto.ECS.DataStructures | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
if (hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not excepted type used"); | |||
if (_hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not expected type used"); | |||
#endif | |||
uint structSize = (uint) MemoryUtilities.SizeOf<T>(); | |||
uint size = (uint) (count * structSize); | |||
@@ -180,8 +209,8 @@ namespace Svelto.ECS.DataStructures | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
if (hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not excepted type used"); | |||
if (_hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not expected type used"); | |||
var structSize = (uint) MemoryUtilities.SizeOf<T>(); | |||
@@ -200,15 +229,15 @@ namespace Svelto.ECS.DataStructures | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
if (hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not excepted type used"); | |||
if (_hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not expected type used"); | |||
if (Count<T>() == 0) | |||
throw new Exception("NativeDynamicArray: empty array invalid operation"); | |||
#endif | |||
var count = Count<T>() - 1; | |||
if (index < count) | |||
var indexToMove = Count<T>() - 1; | |||
if (index < indexToMove) | |||
{ | |||
Set<T>(index, Get<T>((uint) count)); | |||
Set<T>(index, Get<T>((uint) indexToMove)); | |||
} | |||
_list->Pop<T>(); | |||
@@ -216,7 +245,7 @@ namespace Svelto.ECS.DataStructures | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Clear() | |||
public void FastClear() | |||
{ | |||
unsafe | |||
{ | |||
@@ -233,8 +262,8 @@ namespace Svelto.ECS.DataStructures | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
if (hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not excepted type used"); | |||
if (_hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not expected type used"); | |||
#endif | |||
return (T*) _list->ptr; | |||
@@ -247,8 +276,8 @@ namespace Svelto.ECS.DataStructures | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
if (hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not excepted type used"); | |||
if (_hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not expected type used"); | |||
#endif | |||
return (IntPtr) _list->ptr; | |||
@@ -262,8 +291,8 @@ namespace Svelto.ECS.DataStructures | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
if (hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not excepted type used"); | |||
if (_hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not expected type used"); | |||
#endif | |||
var count = Count<T>(); | |||
@@ -286,8 +315,8 @@ namespace Svelto.ECS.DataStructures | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
if (hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not excepted type used"); | |||
if (_hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not expected type used"); | |||
#endif | |||
var capacity = Capacity<T>(); | |||
var lengthToCopyInBytes = capacity * MemoryUtilities.SizeOf<T>(); | |||
@@ -309,8 +338,8 @@ namespace Svelto.ECS.DataStructures | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
if (hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not excepted type used"); | |||
if (_hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not expected type used"); | |||
#endif | |||
var sizeOf = MemoryUtilities.SizeOf<T>(); | |||
@@ -328,42 +357,14 @@ namespace Svelto.ECS.DataStructures | |||
MemoryUtilities.MemClear((IntPtr) _list->ptr, (uint) _list->capacity); | |||
} | |||
} | |||
} | |||
public ref struct NativeDynamicArrayCast<T> where T : struct | |||
{ | |||
NativeDynamicArray _array; | |||
public NativeDynamicArrayCast(NativeDynamicArray array) : this() { _array = array; } | |||
public int count | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => _array.Count<T>(); | |||
} | |||
public ref T this[int index] | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => ref _array.Get<T>((uint) index); | |||
} | |||
public ref T this[uint index] | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => ref _array.Get<T>(index); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Add(in T id) { _array.Add(id); } | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void UnorderedRemoveAt(uint index) { _array.UnorderedRemoveAt<T>(index); } | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void RemoveAt(uint index) { _array.RemoveAt<T>(index); } | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Clear() { _array.Clear(); } | |||
#if UNITY_NATIVE | |||
[global::Unity.Burst.NoAlias] [global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction] | |||
#endif | |||
unsafe UnsafeArray* _list; | |||
#if DEBUG && !PROFILE_SVELTO | |||
int _hashType; | |||
#endif | |||
Allocator _allocator; | |||
} | |||
} |
@@ -0,0 +1,48 @@ | |||
using System.Runtime.CompilerServices; | |||
namespace Svelto.ECS.DataStructures | |||
{ | |||
public struct NativeDynamicArrayCast<T> where T : struct | |||
{ | |||
public NativeDynamicArrayCast(NativeDynamicArray array) : this() { _array = array; } | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public int Count() => _array.Count<T>(); | |||
public int count => _array.Count<T>(); | |||
public ref T this[int index] | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => ref _array.Get<T>((uint) index); | |||
} | |||
public ref T this[uint index] | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => ref _array.Get<T>(index); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Add(in T id) { _array.Add(id); } | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void UnorderedRemoveAt(uint index) { _array.UnorderedRemoveAt<T>(index); } | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void RemoveAt(uint index) { _array.RemoveAt<T>(index); } | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Clear() { _array.FastClear(); } | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Dispose() { _array.Dispose(); } | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref T AddAt(uint lastIndex) { return ref _array.AddAt<T>(lastIndex); } | |||
public bool isValid => _array.isValid; | |||
NativeDynamicArray _array; | |||
} | |||
} |
@@ -1,4 +1,4 @@ | |||
#if UNITY_COLLECTIONS | |||
#if UNITY_NATIVE | |||
using Unity.Collections; | |||
using Unity.Collections.LowLevel.Unsafe; | |||
@@ -1,22 +1,35 @@ | |||
using System; | |||
using System.Runtime.InteropServices; | |||
using System.Threading; | |||
using Svelto.Common; | |||
namespace Svelto.ECS.DataStructures | |||
{ | |||
public struct SharedNativeInt: IDisposable | |||
{ | |||
#if UNITY_COLLECTIONS | |||
#if UNITY_NATIVE | |||
[global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction] | |||
#endif | |||
unsafe int* data; | |||
public static SharedNativeInt Create(int t) | |||
Allocator _allocator; | |||
public SharedNativeInt(Allocator allocator) | |||
{ | |||
unsafe | |||
{ | |||
_allocator = allocator; | |||
data = (int*) MemoryUtilities.Alloc(sizeof(int), allocator); | |||
} | |||
} | |||
public static SharedNativeInt Create(int t, Allocator allocator) | |||
{ | |||
unsafe | |||
{ | |||
var current = new SharedNativeInt(); | |||
current.data = (int*) Marshal.AllocHGlobal(sizeof(int)); | |||
current._allocator = allocator; | |||
current.data = (int*) MemoryUtilities.Alloc(sizeof(int), allocator); | |||
*current.data = t; | |||
return current; | |||
@@ -31,7 +44,6 @@ namespace Svelto.ECS.DataStructures | |||
if (t.data == null) | |||
throw new Exception("using disposed SharedInt"); | |||
#endif | |||
return *t.data; | |||
} | |||
} | |||
@@ -40,12 +52,11 @@ namespace Svelto.ECS.DataStructures | |||
{ | |||
unsafe | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (data == null) | |||
throw new Exception("disposing already disposed data"); | |||
#endif | |||
Marshal.FreeHGlobal((IntPtr) data); | |||
data = null; | |||
if (data != null) | |||
{ | |||
MemoryUtilities.Free((IntPtr) data, _allocator); | |||
data = null; | |||
} | |||
} | |||
} | |||
@@ -74,5 +85,31 @@ namespace Svelto.ECS.DataStructures | |||
return Interlocked.Increment(ref *data); | |||
} | |||
} | |||
public int Add(int val) | |||
{ | |||
unsafe | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (data == null) | |||
throw new Exception("null-access"); | |||
#endif | |||
return Interlocked.Add(ref *data, val); | |||
} | |||
} | |||
public void Set(int val) | |||
{ | |||
unsafe | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (data == null) | |||
throw new Exception("null-access"); | |||
#endif | |||
Volatile.Write(ref *data, val); | |||
} | |||
} | |||
} | |||
} |
@@ -1,11 +1,14 @@ | |||
#if later | |||
using System; | |||
using System.Runtime.CompilerServices; | |||
using System.Threading; | |||
using Svelto.Common; | |||
using Svelto.Utilities; | |||
namespace Svelto.ECS.DataStructures | |||
{ | |||
/// <summary> | |||
/// Burst friendly RingBuffer on steroid: | |||
/// Burst friendly Ring Buffer on steroid: | |||
/// it can: Enqueue/Dequeue, it wraps if there is enough space after dequeuing | |||
/// It resizes if there isn't enough space left. | |||
/// It's a "bag", you can queue and dequeue any T. Just be sure that you dequeue what you queue! No check on type | |||
@@ -15,25 +18,8 @@ namespace Svelto.ECS.DataStructures | |||
/// I eventually decided to call it NativeBag and not NativeBag because it can also be used as | |||
/// a preallocated memory pool where any kind of T can be stored as long as T is unmanaged | |||
/// </summary> | |||
public struct NativeBag : IDisposable | |||
public struct ThreadSafeNativeBag : IDisposable | |||
{ | |||
#if UNITY_COLLECTIONS | |||
[global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction] | |||
#endif | |||
unsafe UnsafeBlob* _queue; | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool IsEmpty() | |||
{ | |||
unsafe | |||
{ | |||
if (_queue == null || _queue->ptr == null) | |||
return true; | |||
} | |||
return count == 0; | |||
} | |||
public uint count | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
@@ -68,7 +54,7 @@ namespace Svelto.ECS.DataStructures | |||
} | |||
} | |||
public NativeBag(Allocator allocator) | |||
public ThreadSafeNativeBag(Allocator allocator) | |||
{ | |||
unsafe | |||
{ | |||
@@ -76,36 +62,51 @@ namespace Svelto.ECS.DataStructures | |||
var listData = (UnsafeBlob*) MemoryUtilities.Alloc((uint) sizeOf, allocator); | |||
//clear to nullify the pointers | |||
MemoryUtilities.MemClear((IntPtr) listData, (uint) sizeOf); | |||
//MemoryUtilities.MemClear((IntPtr) listData, (uint) sizeOf); | |||
listData->allocator = allocator; | |||
_queue = listData; | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public unsafe void Dispose() | |||
_writingGuard = 0; | |||
} | |||
public ThreadSafeNativeBag(Allocator allocator, uint capacity) | |||
{ | |||
if (_queue != null) | |||
unsafe | |||
{ | |||
_queue->Dispose(); | |||
_queue = null; | |||
var sizeOf = MemoryUtilities.SizeOf<UnsafeBlob>(); | |||
var listData = (UnsafeBlob*) MemoryUtilities.Alloc((uint) sizeOf, allocator); | |||
//clear to nullify the pointers | |||
//MemoryUtilities.MemClear((IntPtr) listData, (uint) sizeOf); | |||
listData->allocator = allocator; | |||
_queue = listData; | |||
_queue->Realloc(capacity); | |||
} | |||
_writingGuard = 0; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref T ReserveEnqueue<T>(out UnsafeArrayIndex index) where T : struct | |||
public bool IsEmpty() | |||
{ | |||
unsafe | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_queue == null) | |||
throw new Exception("SimpleNativeArray: null-access"); | |||
#endif | |||
var sizeOf = MemoryUtilities.SizeOf<T>(); | |||
if (_queue->space - sizeOf < 0) | |||
_queue->Realloc((uint) ((_queue->capacity + sizeOf) * 2.0f)); | |||
if (_queue == null || _queue->ptr == null) | |||
return true; | |||
} | |||
return ref _queue->Reserve<T>(out index); | |||
return count == 0; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public unsafe void Dispose() | |||
{ | |||
if (_queue != null) | |||
{ | |||
_queue->Dispose(); | |||
MemoryUtilities.Free((IntPtr) _queue, _queue->allocator); | |||
_queue = null; | |||
} | |||
} | |||
@@ -118,11 +119,46 @@ namespace Svelto.ECS.DataStructures | |||
if (_queue == null) | |||
throw new Exception("SimpleNativeArray: null-access"); | |||
#endif | |||
var sizeOf = MemoryUtilities.SizeOf<T>(); | |||
if (_queue->space - sizeOf < 0) | |||
_queue->Realloc((uint) ((_queue->capacity + MemoryUtilities.Align4((uint) sizeOf)) * 2.0f)); | |||
var sizeOf = MemoryUtilities.SizeOf<T>(); | |||
var alignedSize = (uint) MemoryUtilities.SizeOfAligned<T>(); | |||
Interlocked.MemoryBarrier(); | |||
Reset: | |||
var oldCapacity = _queue->capacity; | |||
var spaceleft = oldCapacity - (_queue->_writeIndex - _queue->_readIndex) - sizeOf; | |||
while (spaceleft < 0) | |||
{ | |||
//if _writingGuard is not equal to 0, it means that another thread increased the | |||
//value so it's possible the reallocing is already happening OR it means that | |||
//writing are still in progress and we must be sure that are all flushed first | |||
if (Interlocked.CompareExchange(ref _writingGuard, 1, 0) != 0) | |||
{ | |||
ThreadUtility.Yield(); | |||
goto Reset; | |||
} | |||
var newCapacity = (uint) ((oldCapacity + alignedSize) * 2.0f); | |||
Svelto.Console.Log($"realloc {newCapacity}"); | |||
_queue->Realloc(newCapacity); | |||
Volatile.Write(ref _writingGuard, 0); | |||
} | |||
int writeIndex; | |||
//look for the first available slot to write in | |||
writeIndex = _queue->_writeIndex; | |||
if (Interlocked.CompareExchange(ref _queue->_writeIndex, (int) (writeIndex + alignedSize) | |||
, writeIndex) != writeIndex) | |||
{ | |||
ThreadUtility.Yield(); | |||
goto Reset; | |||
} | |||
_queue->Write(item); | |||
Interlocked.Increment(ref _writingGuard); | |||
_queue->Write(item, (uint) writeIndex); | |||
Interlocked.Decrement(ref _writingGuard); | |||
} | |||
} | |||
@@ -146,17 +182,13 @@ namespace Svelto.ECS.DataStructures | |||
return _queue->Read<T>(); | |||
} | |||
} | |||
public ref T AccessReserved<T>(UnsafeArrayIndex reserverIndex) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_queue == null) | |||
throw new Exception("SimpleNativeArray: null-access"); | |||
#if UNITY_NATIVE | |||
[global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction] | |||
#endif | |||
return ref _queue->AccessReserved<T>(reserverIndex); | |||
} | |||
} | |||
unsafe UnsafeBlob* _queue; | |||
int _writingGuard; | |||
} | |||
} | |||
} | |||
#endif |
@@ -4,7 +4,7 @@ using Svelto.Common; | |||
namespace Svelto.ECS.DataStructures | |||
{ | |||
struct UnsafeArray : IDisposable | |||
struct UnsafeArray | |||
{ | |||
internal unsafe byte* ptr => _ptr; | |||
@@ -17,9 +17,6 @@ namespace Svelto.ECS.DataStructures | |||
//expressed in bytes | |||
internal int space => capacity - count; | |||
/// <summary> | |||
/// </summary> | |||
internal Allocator allocator; | |||
#if DEBUG && !PROFILE_SVELTO | |||
#pragma warning disable 649 | |||
internal uint id; | |||
@@ -31,6 +28,11 @@ namespace Svelto.ECS.DataStructures | |||
{ | |||
unsafe | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
uint sizeOf = (uint) MemoryUtilities.SizeOf<T>(); | |||
if (index + sizeOf > _writeIndex) | |||
throw new Exception("no reading authorized"); | |||
#endif | |||
return ref Unsafe.AsRef<T>(Unsafe.Add<T>(ptr, (int) index)); | |||
} | |||
} | |||
@@ -73,40 +75,34 @@ namespace Svelto.ECS.DataStructures | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Pop<T>() where T : struct | |||
public ref T Pop<T>() where T : struct | |||
{ | |||
var structSize = MemoryUtilities.SizeOf<T>(); | |||
unsafe | |||
{ | |||
var structSize = MemoryUtilities.SizeOf<T>(); | |||
_writeIndex -= (uint)structSize; | |||
_writeIndex -= (uint)structSize; | |||
return ref Unsafe.AsRef<T>(ptr + _writeIndex); | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal void Realloc(uint newCapacity) | |||
internal void Realloc(uint newCapacity, Allocator allocator) | |||
{ | |||
unsafe | |||
{ | |||
byte* newPointer = null; | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_capacity > 0 && newCapacity <= _capacity) | |||
throw new Exception("new capacity must be bigger than current"); | |||
#endif | |||
if (newCapacity >= 0) | |||
{ | |||
newPointer = (byte*) MemoryUtilities.Alloc(newCapacity, allocator); | |||
if (count > 0) | |||
Unsafe.CopyBlock(newPointer, ptr, (uint) count); | |||
} | |||
if (ptr != null) | |||
MemoryUtilities.Free((IntPtr) ptr, allocator); | |||
if (_ptr == null) | |||
_ptr = (byte*) MemoryUtilities.Alloc(newCapacity, allocator); | |||
else | |||
_ptr = (byte*) MemoryUtilities.Realloc((IntPtr) _ptr, (uint) count, newCapacity, allocator); | |||
_ptr = newPointer; | |||
_capacity = newCapacity; | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Dispose() | |||
public void Dispose(Allocator allocator) | |||
{ | |||
unsafe | |||
{ | |||
@@ -131,7 +127,7 @@ namespace Svelto.ECS.DataStructures | |||
_writeIndex = count; | |||
} | |||
#if UNITY_COLLECTIONS | |||
#if UNITY_NATIVE | |||
[global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction] | |||
#endif | |||
unsafe byte* _ptr; |
@@ -23,7 +23,7 @@ namespace Svelto.ECS.DataStructures | |||
internal uint capacity { get; private set; } | |||
//expressed in bytes | |||
internal uint size => _writeIndex - _readIndex; | |||
internal uint size => (uint)_writeIndex - _readIndex; | |||
//expressed in bytes | |||
internal uint space => capacity - size; | |||
@@ -31,7 +31,7 @@ namespace Svelto.ECS.DataStructures | |||
/// <summary> | |||
/// </summary> | |||
internal Allocator allocator; | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal void Write<T>(in T item) where T : struct | |||
{ | |||
@@ -57,22 +57,53 @@ namespace Svelto.ECS.DataStructures | |||
var localCopyToAvoidGcIssues = item; | |||
//read and copy the first portion of Item until the end of the stream | |||
Unsafe.CopyBlock(ptr + writeHead, Unsafe.AsPointer(ref localCopyToAvoidGcIssues), byteCountToEnd); | |||
Unsafe.CopyBlock(ptr + writeHead, Unsafe.AsPointer(ref localCopyToAvoidGcIssues), (uint)byteCountToEnd); | |||
var restCount = structSize - byteCountToEnd; | |||
//read and copy the remainder | |||
Unsafe.CopyBlock(ptr, (byte*) Unsafe.AsPointer(ref localCopyToAvoidGcIssues) + byteCountToEnd | |||
, restCount); | |||
, (uint)restCount); | |||
} | |||
//this is may seems a waste if you are going to use an unsafeBlob just for bytes, but it's necessary for mixed types. | |||
//it's still possible to use WriteUnaligned though | |||
var paddedStructSize = MemoryUtilities.Align4(structSize); | |||
int paddedStructSize = (int) MemoryUtilities.Align4(structSize); | |||
_writeIndex += paddedStructSize; | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal void Write<T>(in T item, uint writeIndex) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
var structSize = (uint) MemoryUtilities.SizeOf<T>(); | |||
//the idea is, considering the wrap, a read pointer must always be behind a writer pointer | |||
var writeHead = writeIndex % capacity; | |||
if (writeHead + structSize <= capacity) | |||
{ | |||
Unsafe.Write(ptr + writeHead, item); | |||
} | |||
else //copy with wrap, will start to copy and wrap for the reminder | |||
{ | |||
var byteCountToEnd = capacity - writeHead; | |||
var localCopyToAvoidGcIssues = item; | |||
//read and copy the first portion of Item until the end of the stream | |||
Unsafe.CopyBlock(ptr + writeHead, Unsafe.AsPointer(ref localCopyToAvoidGcIssues), byteCountToEnd); | |||
var restCount = structSize - byteCountToEnd; | |||
//read and copy the remainder | |||
Unsafe.CopyBlock(ptr, (byte*) Unsafe.AsPointer(ref localCopyToAvoidGcIssues) + byteCountToEnd | |||
, restCount); | |||
} | |||
} | |||
} | |||
// [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
// //ToDo: remove this and create an UnsafeBlobUnaligned, used on NativeRingBuffer where T cannot change | |||
@@ -169,10 +200,10 @@ namespace Svelto.ECS.DataStructures | |||
index = new UnsafeArrayIndex | |||
{ | |||
capacity = capacity | |||
, index = _writeIndex | |||
, index = (uint)_writeIndex | |||
}; | |||
var align4 = MemoryUtilities.Align4(sizeOf); | |||
int align4 = (int) MemoryUtilities.Align4(sizeOf); | |||
_writeIndex += align4; | |||
return ref buffer; | |||
@@ -218,7 +249,7 @@ namespace Svelto.ECS.DataStructures | |||
{ | |||
//copy to the new pointer, from th reader position | |||
var currentSize = _writeIndex - _readIndex; | |||
Unsafe.CopyBlock(newPointer, ptr + readerHead, currentSize); | |||
Unsafe.CopyBlock(newPointer, ptr + readerHead, (uint)currentSize); | |||
} | |||
//the assumption is that if size > 0 (so readerPointer and writerPointer are not the same) | |||
//writerHead wrapped and reached readerHead. so I have to copy from readerHead to the end | |||
@@ -228,7 +259,7 @@ namespace Svelto.ECS.DataStructures | |||
var byteCountToEnd = capacity - readerHead; | |||
Unsafe.CopyBlock(newPointer, ptr + readerHead, byteCountToEnd); | |||
Unsafe.CopyBlock(newPointer + byteCountToEnd, ptr, writerHead); | |||
Unsafe.CopyBlock(newPointer + byteCountToEnd, ptr, (uint)writerHead); | |||
} | |||
} | |||
} | |||
@@ -236,11 +267,11 @@ namespace Svelto.ECS.DataStructures | |||
if (ptr != null) | |||
MemoryUtilities.Free((IntPtr) ptr, allocator); | |||
_writeIndex = size; | |||
_readIndex = 0; | |||
ptr = newPointer; | |||
capacity = newCapacity; | |||
_readIndex = 0; | |||
_writeIndex = (int)size; | |||
} | |||
} | |||
@@ -265,6 +296,7 @@ namespace Svelto.ECS.DataStructures | |||
_readIndex = 0; | |||
} | |||
uint _writeIndex, _readIndex; | |||
internal int _writeIndex; | |||
internal uint _readIndex; | |||
} | |||
} |
@@ -23,15 +23,16 @@ public static class ExclusiveGroupDebugger | |||
{ | |||
if (field.IsStatic && typeof(ExclusiveGroup).IsAssignableFrom(field.FieldType)) | |||
{ | |||
string name = $"{type.FullName}.{field.Name}"; | |||
var group = (ExclusiveGroup) field.GetValue(null); | |||
var group = (ExclusiveGroup) field.GetValue(null); | |||
string name = $"{type.FullName}.{field.Name} ({(uint)group})"; | |||
GroupMap.idToName[(ExclusiveGroupStruct) group] = name; | |||
} | |||
if (field.IsStatic && typeof(ExclusiveGroupStruct).IsAssignableFrom(field.FieldType)) | |||
{ | |||
string name = $"{type.FullName}.{field.Name}"; | |||
var group = (ExclusiveGroupStruct) field.GetValue(null); | |||
var group = (ExclusiveGroupStruct) field.GetValue(null); | |||
string name = $"{type.FullName}.{field.Name} ({(uint)group})"; | |||
GroupMap.idToName[@group] = name; | |||
} | |||
} | |||
@@ -9,7 +9,7 @@ namespace Svelto.ECS | |||
/// This method allocates, so it shouldn't be abused | |||
/// </summary> | |||
/// <typeparam name="TType"></typeparam> | |||
public struct DynamicEntityDescriptor<TType> : IEntityDescriptor where TType : IEntityDescriptor, new() | |||
public struct DynamicEntityDescriptor<TType> : IDynamicEntityDescriptor where TType : IEntityDescriptor, new() | |||
{ | |||
internal DynamicEntityDescriptor(bool isExtendible) : this() | |||
{ | |||
@@ -22,9 +22,9 @@ namespace Svelto.ECS | |||
//assign it after otherwise the previous copy will overwrite the value in case the item | |||
//is already present | |||
ComponentsToBuild[length] = new ComponentBuilder<EntityInfoViewComponent> | |||
ComponentsToBuild[length] = new ComponentBuilder<EntityInfoComponent> | |||
( | |||
new EntityInfoViewComponent | |||
new EntityInfoComponent | |||
{ | |||
componentsToBuild = ComponentsToBuild | |||
} | |||
@@ -72,17 +72,16 @@ namespace Svelto.ECS | |||
} | |||
var defaultEntities = startingEntities; | |||
var length = defaultEntities.Length; | |||
var index = SetupSpecialEntityComponent(defaultEntities, out localEntitiesToBuild, extraEntitiesLength); | |||
var index = SetupEntityInfoComponent(defaultEntities, out localEntitiesToBuild, extraEntitiesLength); | |||
Array.Copy(extraEntities, 0, localEntitiesToBuild, length, extraEntitiesLength); | |||
Array.Copy(extraEntities, 0, localEntitiesToBuild, defaultEntities.Length, extraEntitiesLength); | |||
//assign it after otherwise the previous copy will overwrite the value in case the item | |||
//is already present | |||
localEntitiesToBuild[index] = new ComponentBuilder<EntityInfoViewComponent> | |||
localEntitiesToBuild[index] = new ComponentBuilder<EntityInfoComponent> | |||
( | |||
new EntityInfoViewComponent | |||
new EntityInfoComponent | |||
{ | |||
componentsToBuild = localEntitiesToBuild | |||
} | |||
@@ -91,7 +90,7 @@ namespace Svelto.ECS | |||
return localEntitiesToBuild; | |||
} | |||
static int SetupSpecialEntityComponent(IComponentBuilder[] defaultEntities, out IComponentBuilder[] componentsToBuild, | |||
static int SetupEntityInfoComponent(IComponentBuilder[] defaultEntities, out IComponentBuilder[] componentsToBuild, | |||
int extraLenght) | |||
{ | |||
int length = defaultEntities.Length; | |||
@@ -100,7 +99,7 @@ namespace Svelto.ECS | |||
for (var i = 0; i < length; i++) | |||
{ | |||
//the special entity already exists | |||
if (defaultEntities[i].GetEntityComponentType() == ComponentBuilderUtilities.ENTITY_STRUCT_INFO_VIEW) | |||
if (defaultEntities[i].GetEntityComponentType() == ComponentBuilderUtilities.ENTITY_INFO_COMPONENT) | |||
{ | |||
index = i; | |||
break; | |||
@@ -120,7 +119,6 @@ namespace Svelto.ECS | |||
return index; | |||
} | |||
public IComponentBuilder[] componentsToBuild => ComponentsToBuild; | |||
IComponentBuilder[] ComponentsToBuild; | |||
@@ -8,6 +8,10 @@ namespace Svelto.ECS.Experimental | |||
/// | |||
/// Note: I should extend this to reuse unused id | |||
/// | |||
//todo ResourcesECSDB<T> must be used only inside entity components. Same for ECSString. | |||
//what I could do is that if the component is removed from the database, a reference counter to the object | |||
//will be modified. If 0 is reached, the ID should be recycled. | |||
public struct ECSString:IEquatable<ECSString> | |||
{ | |||
[FieldOffset(0)] uint _id; | |||
@@ -13,6 +13,8 @@ namespace Svelto.ECS | |||
[FieldOffset(0)] public readonly uint entityID; | |||
[FieldOffset(4)] public readonly ExclusiveGroupStruct groupID; | |||
[FieldOffset(0)] readonly ulong _GID; | |||
public static readonly EGID Empty = new EGID(); | |||
public static bool operator ==(EGID obj1, EGID obj2) | |||
{ | |||
@@ -28,6 +30,11 @@ namespace Svelto.ECS | |||
{ | |||
_GID = MAKE_GLOBAL_ID(entityID, groupID); | |||
} | |||
public EGID(uint entityID, BuildGroup groupID) : this() | |||
{ | |||
_GID = MAKE_GLOBAL_ID(entityID, groupID.group); | |||
} | |||
static ulong MAKE_GLOBAL_ID(uint entityId, uint groupId) | |||
{ | |||
@@ -74,7 +81,8 @@ namespace Svelto.ECS | |||
public override string ToString() | |||
{ | |||
return "id ".FastConcat(entityID).FastConcat(" group ").FastConcat(groupID.ToName()); | |||
var value = groupID.ToName(); | |||
return "id ".FastConcat(entityID).FastConcat(" group ").FastConcat(value); | |||
} | |||
} | |||
} |
@@ -1,15 +1,23 @@ | |||
using System; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public readonly struct EGIDMapper<T> where T : struct, IEntityComponent | |||
/// <summary> | |||
/// Note: does mono devirtualize sealed classes? If so it could be worth to use TypeSafeDictionary instead of | |||
/// the interface | |||
/// </summary> | |||
/// <typeparam name="T"></typeparam> | |||
public readonly struct EGIDMapper<T>: IEGIDMapper where T : struct, IEntityComponent | |||
{ | |||
public uint length => _map.count; | |||
public ExclusiveGroupStruct groupID { get; } | |||
public uint length => _map.count; | |||
public ExclusiveGroupStruct groupID { get; } | |||
public Type entityType => TypeCache<T>.type; | |||
public EGIDMapper(ExclusiveGroupStruct groupStructId, ITypeSafeDictionary<T> dic) : this() | |||
internal EGIDMapper(ExclusiveGroupStruct groupStructId, ITypeSafeDictionary<T> dic) : this() | |||
{ | |||
groupID = groupStructId; | |||
_map = dic; | |||
@@ -19,6 +27,8 @@ namespace Svelto.ECS | |||
public ref T Entity(uint entityID) | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_map == null) | |||
throw new System.Exception("Not initialized EGIDMapper in this group ".FastConcat(typeof(T).ToString())); | |||
if (_map.TryFindIndex(entityID, out var findIndex) == false) | |||
throw new System.Exception("Entity not found in this group ".FastConcat(typeof(T).ToString())); | |||
#else | |||
@@ -39,29 +49,34 @@ namespace Svelto.ECS | |||
return false; | |||
} | |||
public IBuffer<T> GetArrayAndEntityIndex(uint entityID, out uint index) | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool Exists(uint idEntityId) | |||
{ | |||
if (_map.TryFindIndex(entityID, out index)) | |||
{ | |||
return _map.GetValues(out _); | |||
} | |||
throw new ECSException("Entity not found"); | |||
return _map.count > 0 && _map.TryFindIndex(idEntityId, out _); | |||
} | |||
public bool TryGetArrayAndEntityIndex(uint entityID, out uint index, out IBuffer<T> array) | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public uint GetIndex(uint entityID) | |||
{ | |||
index = default; | |||
if (_map != null && _map.TryFindIndex(entityID, out index)) | |||
{ | |||
array = _map.GetValues(out _); | |||
return true; | |||
} | |||
return _map.GetIndex(entityID); | |||
} | |||
array = default; | |||
return false; | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool FindIndex(uint valueKey, out uint index) | |||
{ | |||
return _map.TryFindIndex(valueKey, out index); | |||
} | |||
internal readonly ITypeSafeDictionary<T> _map; | |||
} | |||
public interface IEGIDMapper | |||
{ | |||
bool FindIndex(uint valueKey, out uint index); | |||
uint GetIndex(uint entityID); | |||
bool Exists(uint idEntityId); | |||
readonly ITypeSafeDictionary<T> _map; | |||
ExclusiveGroupStruct groupID { get; } | |||
Type entityType { get; } | |||
} | |||
} |
@@ -29,7 +29,7 @@ namespace Svelto.ECS | |||
var otherCount = other.count; | |||
if (otherCount > MaximumNumberOfItemsPerFrameBeforeToClear) | |||
{ | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>[] otherValuesArray = other.unsafeValues; | |||
FasterDictionary<RefWrapperType, ITypeSafeDictionary>[] otherValuesArray = other.unsafeValues; | |||
for (int i = 0; i < otherCount; ++i) | |||
{ | |||
var safeDictionariesCount = otherValuesArray[i].count; | |||
@@ -49,7 +49,7 @@ namespace Svelto.ECS | |||
} | |||
{ | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>[] otherValuesArray = other.unsafeValues; | |||
FasterDictionary<RefWrapperType, ITypeSafeDictionary>[] otherValuesArray = other.unsafeValues; | |||
for (int i = 0; i < otherCount; ++i) | |||
{ | |||
var safeDictionariesCount = otherValuesArray[i].count; | |||
@@ -83,25 +83,25 @@ namespace Svelto.ECS | |||
/// To avoid extra allocation, I don't clear the dictionaries, so I need an extra data structure | |||
/// to keep count of the number of entities submitted this frame | |||
/// </summary> | |||
internal FasterDictionary<uint, uint> currentEntitiesCreatedPerGroup; | |||
internal FasterDictionary<uint, uint> otherEntitiesCreatedPerGroup; | |||
internal FasterDictionary<ExclusiveGroupStruct, uint> currentEntitiesCreatedPerGroup; | |||
internal FasterDictionary<ExclusiveGroupStruct, uint> otherEntitiesCreatedPerGroup; | |||
//Before I tried for the third time to use a SparseSet instead of FasterDictionary, remember that | |||
//while group indices are sequential, they may not be used in a sequential order. Sparaset needs | |||
//entities to be created sequentially (the index cannot be managed externally) | |||
internal FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> current; | |||
internal FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> other; | |||
internal FasterDictionary<uint, FasterDictionary<RefWrapperType, ITypeSafeDictionary>> current; | |||
internal FasterDictionary<uint, FasterDictionary<RefWrapperType, ITypeSafeDictionary>> other; | |||
readonly FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> | |||
readonly FasterDictionary<uint, FasterDictionary<RefWrapperType, ITypeSafeDictionary>> | |||
_entityComponentsToAddBufferA = | |||
new FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>(); | |||
new FasterDictionary<uint, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>(); | |||
readonly FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> | |||
readonly FasterDictionary<uint, FasterDictionary<RefWrapperType, ITypeSafeDictionary>> | |||
_entityComponentsToAddBufferB = | |||
new FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>(); | |||
new FasterDictionary<uint, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>(); | |||
readonly FasterDictionary<uint, uint> _entitiesCreatedPerGroupA = new FasterDictionary<uint, uint>(); | |||
readonly FasterDictionary<uint, uint> _entitiesCreatedPerGroupB = new FasterDictionary<uint, uint>(); | |||
readonly FasterDictionary<ExclusiveGroupStruct, uint> _entitiesCreatedPerGroupA = new FasterDictionary<ExclusiveGroupStruct, uint>(); | |||
readonly FasterDictionary<ExclusiveGroupStruct, uint> _entitiesCreatedPerGroupB = new FasterDictionary<ExclusiveGroupStruct, uint>(); | |||
public DoubleBufferedEntitiesToAdd() | |||
{ | |||
@@ -9,13 +9,15 @@ namespace Svelto.ECS | |||
{ | |||
public sealed partial class EnginesRoot | |||
{ | |||
public struct EntitiesSubmitter | |||
public readonly struct EntitiesSubmitter | |||
{ | |||
public EntitiesSubmitter(EnginesRoot enginesRoot) | |||
{ | |||
_weakReference = new Svelto.DataStructures.WeakReference<EnginesRoot>(enginesRoot); | |||
} | |||
public bool IsUnused => _weakReference.IsValid == false; | |||
public void Invoke() | |||
{ | |||
if (_weakReference.IsValid) | |||
@@ -25,7 +27,8 @@ namespace Svelto.ECS | |||
readonly Svelto.DataStructures.WeakReference<EnginesRoot> _weakReference; | |||
} | |||
public IEntitiesSubmissionScheduler scheduler { get; } | |||
readonly EntitiesSubmissionScheduler _scheduler; | |||
public IEntitiesSubmissionScheduler scheduler => _scheduler; | |||
/// <summary> | |||
/// Engines root contextualize your engines and entities. You don't need to limit yourself to one EngineRoot | |||
@@ -35,34 +38,38 @@ namespace Svelto.ECS | |||
/// The EntitySubmissionScheduler cannot hold an EnginesRoot reference, that's why | |||
/// it must receive a weak reference of the EnginesRoot callback. | |||
/// </summary> | |||
public EnginesRoot(IEntitiesSubmissionScheduler entitiesComponentScheduler) | |||
public EnginesRoot(EntitiesSubmissionScheduler entitiesComponentScheduler) | |||
{ | |||
_entitiesOperations = new ThreadSafeDictionary<ulong, EntitySubmitOperation>(); | |||
serializationDescriptorMap = new SerializationDescriptorMap(); | |||
_reactiveEnginesAddRemove = new FasterDictionary<RefWrapper<Type>, FasterList<IEngine>>(); | |||
_reactiveEnginesSwap = new FasterDictionary<RefWrapper<Type>, FasterList<IEngine>>(); | |||
_reactiveEnginesAddRemove = new FasterDictionary<RefWrapperType, FasterList<IReactEngine>>(); | |||
_reactiveEnginesSwap = new FasterDictionary<RefWrapperType, FasterList<IReactEngine>>(); | |||
_reactiveEnginesSubmission = new FasterList<IReactOnSubmission>(); | |||
_enginesSet = new FasterList<IEngine>(); | |||
_enginesTypeSet = new HashSet<Type>(); | |||
_disposableEngines = new FasterList<IDisposable>(); | |||
_transientEntitiesOperations = new FasterList<EntitySubmitOperation>(); | |||
_groupEntityComponentsDB = | |||
new FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>(); | |||
_groupsPerEntity = new FasterDictionary<RefWrapper<Type>, FasterDictionary<uint, ITypeSafeDictionary>>(); | |||
new FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>(); | |||
_groupsPerEntity = | |||
new FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>>(); | |||
_groupedEntityToAdd = new DoubleBufferedEntitiesToAdd(); | |||
_entitiesStream = new EntitiesStream(); | |||
_entitiesDB = new EntitiesDB(_groupEntityComponentsDB, _groupsPerEntity, _entitiesStream); | |||
_entityStreams = EntitiesStreams.Create(); | |||
_groupFilters = | |||
new FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, GroupFilters>>(); | |||
_entitiesDB = new EntitiesDB(this); | |||
scheduler = entitiesComponentScheduler; | |||
scheduler.onTick = new EntitiesSubmitter(this); | |||
#if UNITY_BURST | |||
_scheduler = entitiesComponentScheduler; | |||
_scheduler.onTick = new EntitiesSubmitter(this); | |||
#if UNITY_NATIVE | |||
AllocateNativeOperations(); | |||
#endif | |||
} | |||
public EnginesRoot | |||
(IEntitiesSubmissionScheduler entitiesComponentScheduler, bool isDeserializationOnly) : | |||
(EntitiesSubmissionScheduler entitiesComponentScheduler, bool isDeserializationOnly) : | |||
this(entitiesComponentScheduler) | |||
{ | |||
_isDeserializationOnly = isDeserializationOnly; | |||
@@ -75,12 +82,20 @@ namespace Svelto.ECS | |||
/// </summary> | |||
public void Dispose() | |||
{ | |||
_isDisposing = true; | |||
using (var profiler = new PlatformProfiler("Final Dispose")) | |||
{ | |||
//Note: The engines are disposed before the the remove callback to give the chance to behave | |||
//differently if a remove happens as a consequence of a dispose | |||
//The pattern is to implement the IDisposable interface and set a flag in the engine. The | |||
//remove callback will then behave differently according the flag. | |||
foreach (var engine in _disposableEngines) | |||
{ | |||
try | |||
{ | |||
if (engine is IDisposingEngine dengine) | |||
dengine.isDisposing = true; | |||
engine.Dispose(); | |||
} | |||
catch (Exception e) | |||
@@ -88,15 +103,15 @@ namespace Svelto.ECS | |||
Svelto.Console.LogException(e); | |||
} | |||
} | |||
foreach (FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>. | |||
foreach (FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>. | |||
KeyValuePairFast groups in _groupEntityComponentsDB) | |||
{ | |||
foreach (FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>.KeyValuePairFast entityList in | |||
groups.Value) | |||
foreach (FasterDictionary<RefWrapperType, ITypeSafeDictionary>.KeyValuePairFast entityList in groups | |||
.Value) | |||
try | |||
{ | |||
entityList.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, profiler | |||
entityList.Value.ExecuteEnginesRemoveCallbacks(_reactiveEnginesAddRemove, profiler | |||
, new ExclusiveGroupStruct(groups.Key)); | |||
} | |||
catch (Exception e) | |||
@@ -104,15 +119,27 @@ namespace Svelto.ECS | |||
Svelto.Console.LogException(e); | |||
} | |||
} | |||
foreach (FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>. | |||
foreach (FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>. | |||
KeyValuePairFast groups in _groupEntityComponentsDB) | |||
{ | |||
foreach (FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>.KeyValuePairFast entityList in | |||
groups.Value) | |||
foreach (FasterDictionary<RefWrapperType, ITypeSafeDictionary>.KeyValuePairFast entityList in groups | |||
.Value) | |||
entityList.Value.Dispose(); | |||
} | |||
foreach (FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, GroupFilters>>. | |||
KeyValuePairFast type in _groupFilters) | |||
foreach (FasterDictionary<ExclusiveGroupStruct, GroupFilters>.KeyValuePairFast group in type.Value) | |||
group.Value.Dispose(); | |||
_groupFilters.Clear(); | |||
#if UNITY_NATIVE | |||
_addOperationQueue.Dispose(); | |||
_removeOperationQueue.Dispose(); | |||
_swapOperationQueue.Dispose(); | |||
#endif | |||
_groupEntityComponentsDB.Clear(); | |||
_groupsPerEntity.Clear(); | |||
@@ -121,15 +148,13 @@ namespace Svelto.ECS | |||
_enginesTypeSet.Clear(); | |||
_reactiveEnginesSwap.Clear(); | |||
_reactiveEnginesAddRemove.Clear(); | |||
_reactiveEnginesSubmission.Clear(); | |||
_entitiesOperations.Clear(); | |||
_transientEntitiesOperations.Clear(); | |||
#if DEBUG && !PROFILE_SVELTO | |||
_idCheckers.Clear(); | |||
#endif | |||
_groupedEntityToAdd.Dispose(); | |||
_entitiesStream.Dispose(); | |||
_entityStreams.Dispose(); | |||
scheduler.Dispose(); | |||
} | |||
@@ -146,7 +171,7 @@ namespace Svelto.ECS | |||
public void AddEngine(IEngine engine) | |||
{ | |||
var type = engine.GetType(); | |||
var refWrapper = new RefWrapper<Type>(type); | |||
var refWrapper = new RefWrapperType(type); | |||
DBC.ECS.Check.Require(engine != null, "Engine to add is invalid or null"); | |||
DBC.ECS.Check.Require( | |||
_enginesTypeSet.Contains(refWrapper) == false | |||
@@ -156,10 +181,13 @@ namespace Svelto.ECS | |||
try | |||
{ | |||
if (engine is IReactOnAddAndRemove viewEngine) | |||
CheckEntityComponentsEngine(viewEngine, _reactiveEnginesAddRemove); | |||
CheckReactEngineComponents(viewEngine, _reactiveEnginesAddRemove); | |||
if (engine is IReactOnSwap viewEngineSwap) | |||
CheckEntityComponentsEngine(viewEngineSwap, _reactiveEnginesSwap); | |||
CheckReactEngineComponents(viewEngineSwap, _reactiveEnginesSwap); | |||
if (engine is IReactOnSubmission submissionEngine) | |||
_reactiveEnginesSubmission.Add(submissionEngine); | |||
_enginesTypeSet.Add(refWrapper); | |||
_enginesSet.Add(engine); | |||
@@ -180,8 +208,8 @@ namespace Svelto.ECS | |||
} | |||
} | |||
void CheckEntityComponentsEngine<T>(T engine, FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines) | |||
where T : class, IEngine | |||
void CheckReactEngineComponents<T>(T engine, FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines) | |||
where T : class, IReactEngine | |||
{ | |||
var interfaces = engine.GetType().GetInterfaces(); | |||
@@ -197,8 +225,8 @@ namespace Svelto.ECS | |||
} | |||
static void AddEngine<T> | |||
(T engine, Type[] entityComponentTypes, FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines) | |||
where T : class, IEngine | |||
(T engine, Type[] entityComponentTypes, FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines) | |||
where T : class, IReactEngine | |||
{ | |||
for (var i = 0; i < entityComponentTypes.Length; i++) | |||
{ | |||
@@ -208,23 +236,25 @@ namespace Svelto.ECS | |||
} | |||
} | |||
static void AddEngine<T>(T engine, FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines, Type type) | |||
where T : class, IEngine | |||
static void AddEngine<T>(T engine, FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines, Type type) | |||
where T : class, IReactEngine | |||
{ | |||
if (engines.TryGetValue(new RefWrapper<Type>(type), out var list) == false) | |||
if (engines.TryGetValue(new RefWrapperType(type), out var list) == false) | |||
{ | |||
list = new FasterList<IEngine>(); | |||
list = new FasterList<IReactEngine>(); | |||
engines.Add(new RefWrapper<Type>(type), list); | |||
engines.Add(new RefWrapperType(type), list); | |||
} | |||
list.Add(engine); | |||
} | |||
readonly FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> _reactiveEnginesAddRemove; | |||
readonly FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> _reactiveEnginesSwap; | |||
readonly FasterList<IDisposable> _disposableEngines; | |||
readonly FasterList<IEngine> _enginesSet; | |||
readonly HashSet<Type> _enginesTypeSet; | |||
readonly FasterDictionary<RefWrapperType, FasterList<IReactEngine>> _reactiveEnginesAddRemove; | |||
readonly FasterDictionary<RefWrapperType, FasterList<IReactEngine>> _reactiveEnginesSwap; | |||
readonly FasterList<IReactOnSubmission> _reactiveEnginesSubmission; | |||
readonly FasterList<IDisposable> _disposableEngines; | |||
readonly FasterList<IEngine> _enginesSet; | |||
readonly HashSet<Type> _enginesTypeSet; | |||
internal bool _isDisposing; | |||
} | |||
} |
@@ -16,19 +16,28 @@ namespace Svelto.ECS | |||
{ | |||
return new GenericEntityStreamConsumerFactory(this); | |||
} | |||
public IEntityFactory GenerateEntityFactory() { return new GenericEntityFactory(this); } | |||
public IEntityFunctions GenerateEntityFunctions() { return new GenericEntityFunctions(this); } | |||
public IEntityFactory GenerateEntityFactory() | |||
{ | |||
return new GenericEntityFactory(this); | |||
} | |||
public IEntityFunctions GenerateEntityFunctions() | |||
{ | |||
return new GenericEntityFunctions(this); | |||
} | |||
///-------------------------------------------- | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
EntityComponentInitializer BuildEntity | |||
(EGID entityID, IComponentBuilder[] componentsToBuild, Type implementorType, IEnumerable<object> implementors = null) | |||
(EGID entityID, IComponentBuilder[] componentsToBuild, Type descriptorType, | |||
IEnumerable<object> implementors = null) | |||
{ | |||
CheckAddEntityID(entityID, implementorType); | |||
Check.Require(entityID.groupID != 0, "invalid group detected"); | |||
CheckAddEntityID(entityID, descriptorType); | |||
Check.Require(entityID.groupID != 0, "invalid group detected, are you using new ExclusiveGroupStruct() instead of new ExclusiveGroup()?"); | |||
var dic = EntityFactory.BuildGroupedEntities(entityID, _groupedEntityToAdd, componentsToBuild | |||
, implementors); | |||
, implementors, descriptorType); | |||
return new EntityComponentInitializer(entityID, dic); | |||
} | |||
@@ -41,21 +50,21 @@ namespace Svelto.ECS | |||
var entityComponentsToBuild = EntityDescriptorTemplate<T>.descriptor.componentsToBuild; | |||
var numberOfEntityComponents = entityComponentsToBuild.Length; | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> group = GetOrCreateGroup(groupID, profiler); | |||
FasterDictionary<RefWrapperType, ITypeSafeDictionary> group = GetOrCreateGroup(groupID, profiler); | |||
for (var index = 0; index < numberOfEntityComponents; index++) | |||
{ | |||
var entityComponentBuilder = entityComponentsToBuild[index]; | |||
var entityComponentType = entityComponentBuilder.GetEntityComponentType(); | |||
var refWrapper = new RefWrapper<Type>(entityComponentType); | |||
var refWrapper = new RefWrapperType(entityComponentType); | |||
if (group.TryGetValue(refWrapper, out var dbList) == false) | |||
group[refWrapper] = entityComponentBuilder.Preallocate(ref dbList, size); | |||
else | |||
dbList.SetCapacity(size); | |||
if (_groupsPerEntity.TryGetValue(refWrapper, out var groupedGroup) == false) | |||
groupedGroup = _groupsPerEntity[refWrapper] = new FasterDictionary<uint, ITypeSafeDictionary>(); | |||
groupedGroup = _groupsPerEntity[refWrapper] = new FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>(); | |||
groupedGroup[groupID] = dbList; | |||
} | |||
@@ -70,28 +79,30 @@ namespace Svelto.ECS | |||
{ | |||
var fromGroup = GetGroup(fromEntityGID.groupID); | |||
//Check if there is an EntityInfoView linked to this entity, if so it's a DynamicEntityDescriptor! | |||
if (fromGroup.TryGetValue(new RefWrapper<Type>(ComponentBuilderUtilities.ENTITY_STRUCT_INFO_VIEW) | |||
, out var entityInfoViewDic) | |||
&& (entityInfoViewDic as ITypeSafeDictionary<EntityInfoViewComponent>).TryGetValue( | |||
fromEntityGID.entityID, out var entityInfoView)) | |||
MoveEntityComponents(fromEntityGID, toEntityGID, entityInfoView.componentsToBuild, fromGroup | |||
, sampler); | |||
//Check if there is an EntityInfo linked to this entity, if so it's a DynamicEntityDescriptor! | |||
if (fromGroup.TryGetValue(new RefWrapperType(ComponentBuilderUtilities.ENTITY_INFO_COMPONENT) | |||
, out var entityInfoDic) | |||
&& (entityInfoDic as ITypeSafeDictionary<EntityInfoComponent>).TryGetValue( | |||
fromEntityGID.entityID, out var entityInfo)) | |||
SwapOrRemoveEntityComponents(fromEntityGID, toEntityGID, entityInfo.componentsToBuild, fromGroup | |||
, sampler); | |||
//otherwise it's a normal static entity descriptor | |||
else | |||
MoveEntityComponents(fromEntityGID, toEntityGID, componentBuilders, fromGroup, sampler); | |||
SwapOrRemoveEntityComponents(fromEntityGID, toEntityGID, componentBuilders, fromGroup, sampler); | |||
} | |||
} | |||
void MoveEntityComponents(EGID fromEntityGID, EGID? toEntityGID, IComponentBuilder[] entitiesToMove | |||
, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup, in PlatformProfiler sampler) | |||
void SwapOrRemoveEntityComponents(EGID fromEntityGID, EGID? toEntityGID, IComponentBuilder[] entitiesToMove | |||
, FasterDictionary<RefWrapperType, ITypeSafeDictionary> fromGroup, in PlatformProfiler sampler) | |||
{ | |||
using (sampler.Sample("MoveEntityComponents")) | |||
{ | |||
var length = entitiesToMove.Length; | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup = null; | |||
FasterDictionary<RefWrapperType, ITypeSafeDictionary> toGroup = null; | |||
//Swap is not like adding a new entity. While adding new entities happen at the end of submission | |||
//Adding an entity to a group due to a swap of groups happens now. | |||
if (toEntityGID != null) | |||
{ | |||
var toGroupID = toEntityGID.Value.groupID; | |||
@@ -101,36 +112,38 @@ namespace Svelto.ECS | |||
//Add all the entities to the dictionary | |||
for (var i = 0; i < length; i++) | |||
CopyEntityToDictionary(fromEntityGID, toEntityGID.Value, fromGroup, toGroup | |||
, entitiesToMove[i].GetEntityComponentType(), sampler); | |||
, entitiesToMove[i].GetEntityComponentType(), sampler); | |||
} | |||
//call all the callbacks | |||
for (var i = 0; i < length; i++) | |||
MoveEntityComponentFromAndToEngines(fromEntityGID, toEntityGID, fromGroup, toGroup | |||
, entitiesToMove[i].GetEntityComponentType(), sampler); | |||
ExecuteEnginesSwapOrRemoveCallbacks(fromEntityGID, toEntityGID, fromGroup, toGroup | |||
, entitiesToMove[i].GetEntityComponentType(), sampler); | |||
//then remove all the entities from the dictionary | |||
for (var i = 0; i < length; i++) | |||
RemoveEntityFromDictionary(fromEntityGID, fromGroup, entitiesToMove[i].GetEntityComponentType(), sampler); | |||
RemoveEntityFromDictionary(fromEntityGID, fromGroup, entitiesToMove[i].GetEntityComponentType(), | |||
sampler); | |||
} | |||
} | |||
void CopyEntityToDictionary | |||
(EGID entityGID, EGID toEntityGID, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup | |||
, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup, Type entityComponentType, in PlatformProfiler sampler) | |||
(EGID entityGID, EGID toEntityGID, FasterDictionary<RefWrapperType, ITypeSafeDictionary> fromGroup | |||
, FasterDictionary<RefWrapperType, ITypeSafeDictionary> toGroup, Type entityComponentType, | |||
in PlatformProfiler sampler) | |||
{ | |||
using (sampler.Sample("CopyEntityToDictionary")) | |||
{ | |||
var wrapper = new RefWrapper<Type>(entityComponentType); | |||
var wrapper = new RefWrapperType(entityComponentType); | |||
ITypeSafeDictionary fromTypeSafeDictionary = | |||
GetTypeSafeDictionary(entityGID.groupID, fromGroup, wrapper); | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (fromTypeSafeDictionary.Has(entityGID.entityID) == false) | |||
{ | |||
throw new EntityNotFoundException(entityGID, entityComponentType); | |||
} | |||
if (fromTypeSafeDictionary.Has(entityGID.entityID) == false) | |||
{ | |||
throw new EntityNotFoundException(entityGID, entityComponentType); | |||
} | |||
#endif | |||
ITypeSafeDictionary toEntitiesDictionary = | |||
GetOrCreateTypeSafeDictionary(toEntityGID.groupID, toGroup, wrapper, fromTypeSafeDictionary); | |||
@@ -139,15 +152,15 @@ namespace Svelto.ECS | |||
} | |||
} | |||
void MoveEntityComponentFromAndToEngines | |||
(EGID entityGID, EGID? toEntityGID, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup | |||
, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup, Type entityComponentType | |||
, in PlatformProfiler profiler) | |||
void ExecuteEnginesSwapOrRemoveCallbacks | |||
(EGID entityGID, EGID? toEntityGID, FasterDictionary<RefWrapperType, ITypeSafeDictionary> fromGroup | |||
, FasterDictionary<RefWrapperType, ITypeSafeDictionary> toGroup, Type entityComponentType | |||
, in PlatformProfiler profiler) | |||
{ | |||
using (profiler.Sample("MoveEntityComponentFromAndToEngines")) | |||
{ | |||
//add all the entities | |||
var refWrapper = new RefWrapper<Type>(entityComponentType); | |||
var refWrapper = new RefWrapperType(entityComponentType); | |||
var fromTypeSafeDictionary = GetTypeSafeDictionary(entityGID.groupID, fromGroup, refWrapper); | |||
ITypeSafeDictionary toEntitiesDictionary = null; | |||
@@ -155,24 +168,22 @@ namespace Svelto.ECS | |||
toEntitiesDictionary = toGroup[refWrapper]; //this is guaranteed to exist by AddEntityToDictionary | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (fromTypeSafeDictionary.Has(entityGID.entityID) == false) | |||
throw new EntityNotFoundException(entityGID, entityComponentType); | |||
if (fromTypeSafeDictionary.Has(entityGID.entityID) == false) | |||
throw new EntityNotFoundException(entityGID, entityComponentType); | |||
#endif | |||
fromTypeSafeDictionary.MoveEntityFromEngines(entityGID, toEntityGID, toEntitiesDictionary | |||
, toEntityGID == null | |||
? _reactiveEnginesAddRemove | |||
: _reactiveEnginesSwap, in profiler); | |||
fromTypeSafeDictionary.ExecuteEnginesSwapOrRemoveCallbacks(entityGID, toEntityGID, toEntitiesDictionary | |||
, toEntityGID == null ? _reactiveEnginesAddRemove : _reactiveEnginesSwap, in profiler); | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
void RemoveEntityFromDictionary | |||
(EGID entityGID, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup, Type entityComponentType | |||
, in PlatformProfiler sampler) | |||
(EGID entityGID, FasterDictionary<RefWrapperType, ITypeSafeDictionary> fromGroup, Type entityComponentType | |||
, in PlatformProfiler sampler) | |||
{ | |||
using (sampler.Sample("RemoveEntityFromDictionary")) | |||
{ | |||
var refWrapper = new RefWrapper<Type>(entityComponentType); | |||
var refWrapper = new RefWrapperType(entityComponentType); | |||
var fromTypeSafeDictionary = GetTypeSafeDictionary(entityGID.groupID, fromGroup, refWrapper); | |||
fromTypeSafeDictionary.RemoveEntityFromDictionary(entityGID); | |||
@@ -181,65 +192,67 @@ namespace Svelto.ECS | |||
/// <summary> | |||
/// Swap all the entities from one group to another | |||
/// | |||
/// TODO: write unit test that also tests that this calls MoveTo callbacks and not Add or Remove. | |||
/// also that the passing EGID is the same of a component with EGID | |||
/// </summary> | |||
/// <param name="fromIdGroupId"></param> | |||
/// <param name="toGroupId"></param> | |||
/// <param name="profiler"></param> | |||
void SwapEntitiesBetweenGroups(uint fromIdGroupId, uint toGroupId, in PlatformProfiler profiler) | |||
void SwapEntitiesBetweenGroups(ExclusiveGroupStruct fromIdGroupId, ExclusiveGroupStruct toGroupId, in PlatformProfiler profiler) | |||
{ | |||
using (profiler.Sample("SwapEntitiesBetweenGroups")) | |||
{ | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup = GetGroup(fromIdGroupId); | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup = GetOrCreateGroup(toGroupId, profiler); | |||
FasterDictionary<RefWrapperType, ITypeSafeDictionary> fromGroup = GetGroup(fromIdGroupId); | |||
FasterDictionary<RefWrapperType, ITypeSafeDictionary> toGroup = GetOrCreateGroup(toGroupId, profiler); | |||
foreach (FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>.KeyValuePairFast dictionaryOfEntities | |||
in fromGroup) | |||
foreach (var dictionaryOfEntities in fromGroup) | |||
{ | |||
//call all the MoveTo callbacks | |||
dictionaryOfEntities.Value.AddEntitiesToEngines(_reactiveEnginesAddRemove | |||
, dictionaryOfEntities.Value | |||
, new ExclusiveGroupStruct(toGroupId), profiler); | |||
ITypeSafeDictionary toEntitiesDictionary = | |||
GetOrCreateTypeSafeDictionary(toGroupId, toGroup, dictionaryOfEntities.Key | |||
, dictionaryOfEntities.Value); | |||
, dictionaryOfEntities.Value); | |||
FasterDictionary<uint, ITypeSafeDictionary> groupsOfEntityType = | |||
_groupsPerEntity[dictionaryOfEntities.Key]; | |||
var groupsOfEntityType = _groupsPerEntity[dictionaryOfEntities.Key]; | |||
ITypeSafeDictionary typeSafeDictionary = groupsOfEntityType[fromIdGroupId]; | |||
toEntitiesDictionary.AddEntitiesFromDictionary(typeSafeDictionary, toGroupId); | |||
var groupOfEntitiesToCopyAndClear = groupsOfEntityType[fromIdGroupId]; | |||
toEntitiesDictionary.AddEntitiesFromDictionary(groupOfEntitiesToCopyAndClear, toGroupId); | |||
//call all the MoveTo callbacks | |||
dictionaryOfEntities.Value.ExecuteEnginesAddOrSwapCallbacks(_reactiveEnginesSwap | |||
, dictionaryOfEntities.Value, new ExclusiveGroupStruct(fromIdGroupId), new ExclusiveGroupStruct(toGroupId), profiler); | |||
typeSafeDictionary.FastClear(); | |||
//todo: if it's unmanaged, I can use fastclear | |||
groupOfEntitiesToCopyAndClear.Clear(); | |||
} | |||
} | |||
} | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> GetGroup(uint fromIdGroupId) | |||
FasterDictionary<RefWrapperType, ITypeSafeDictionary> GetGroup(ExclusiveGroupStruct fromIdGroupId) | |||
{ | |||
if (_groupEntityComponentsDB.TryGetValue(fromIdGroupId | |||
, out FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> | |||
fromGroup) == false) | |||
, out FasterDictionary<RefWrapperType, ITypeSafeDictionary> | |||
fromGroup) == false) | |||
throw new ECSException("Group doesn't exist: ".FastConcat(fromIdGroupId)); | |||
return fromGroup; | |||
} | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> GetOrCreateGroup(uint toGroupId, in PlatformProfiler profiler) | |||
FasterDictionary<RefWrapperType, ITypeSafeDictionary> GetOrCreateGroup(ExclusiveGroupStruct toGroupId, | |||
in PlatformProfiler profiler) | |||
{ | |||
using (profiler.Sample("GetOrCreateGroup")) | |||
{ | |||
if (_groupEntityComponentsDB.TryGetValue( | |||
toGroupId, out FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup) == false) | |||
toGroupId, out FasterDictionary<RefWrapperType, ITypeSafeDictionary> toGroup) == false) | |||
toGroup = _groupEntityComponentsDB[toGroupId] = | |||
new FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>(); | |||
new FasterDictionary<RefWrapperType, ITypeSafeDictionary>(); | |||
return toGroup; | |||
} | |||
} | |||
ITypeSafeDictionary GetOrCreateTypeSafeDictionary | |||
(uint groupId, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup, RefWrapper<Type> type | |||
(ExclusiveGroupStruct groupId, FasterDictionary<RefWrapperType, ITypeSafeDictionary> toGroup, RefWrapperType type | |||
, ITypeSafeDictionary fromTypeSafeDictionary) | |||
{ | |||
//be sure that the TypeSafeDictionary for the entity Type exists | |||
@@ -251,14 +264,14 @@ namespace Svelto.ECS | |||
//update GroupsPerEntity | |||
if (_groupsPerEntity.TryGetValue(type, out var groupedGroup) == false) | |||
groupedGroup = _groupsPerEntity[type] = new FasterDictionary<uint, ITypeSafeDictionary>(); | |||
groupedGroup = _groupsPerEntity[type] = new FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>(); | |||
groupedGroup[groupId] = toEntitiesDictionary; | |||
return toEntitiesDictionary; | |||
} | |||
static ITypeSafeDictionary GetTypeSafeDictionary | |||
(uint groupID, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> @group, RefWrapper<Type> refWrapper) | |||
(uint groupID, FasterDictionary<RefWrapperType, ITypeSafeDictionary> @group, RefWrapperType refWrapper) | |||
{ | |||
if (@group.TryGetValue(refWrapper, out ITypeSafeDictionary fromTypeSafeDictionary) == false) | |||
{ | |||
@@ -268,34 +281,24 @@ namespace Svelto.ECS | |||
return fromTypeSafeDictionary; | |||
} | |||
void RemoveGroupAndEntities(uint groupID, in PlatformProfiler profiler) | |||
void RemoveEntitiesFromGroup(ExclusiveGroupStruct groupID, in PlatformProfiler profiler) | |||
{ | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> dictionariesOfEntities = | |||
_groupEntityComponentsDB[groupID]; | |||
foreach (FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>.KeyValuePairFast dictionaryOfEntities in dictionariesOfEntities) | |||
if (_groupEntityComponentsDB.TryGetValue(groupID, out var dictionariesOfEntities)) | |||
{ | |||
dictionaryOfEntities.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, profiler | |||
, new ExclusiveGroupStruct(groupID)); | |||
dictionaryOfEntities.Value.FastClear(); | |||
foreach (FasterDictionary<RefWrapperType, ITypeSafeDictionary>.KeyValuePairFast dictionaryOfEntities | |||
in dictionariesOfEntities) | |||
{ | |||
dictionaryOfEntities.Value.ExecuteEnginesRemoveCallbacks(_reactiveEnginesAddRemove, profiler | |||
, new ExclusiveGroupStruct(groupID)); | |||
dictionaryOfEntities.Value.FastClear(); | |||
FasterDictionary<uint, ITypeSafeDictionary> groupsOfEntityType = | |||
_groupsPerEntity[dictionaryOfEntities.Key]; | |||
groupsOfEntityType[groupID].FastClear(); | |||
var groupsOfEntityType = | |||
_groupsPerEntity[dictionaryOfEntities.Key]; | |||
groupsOfEntityType[groupID].FastClear(); | |||
} | |||
} | |||
} | |||
internal Consumer<T> GenerateConsumer<T>(string name, uint capacity) where T : unmanaged, IEntityComponent | |||
{ | |||
return _entitiesStream.GenerateConsumer<T>(name, capacity); | |||
} | |||
internal Consumer<T> GenerateConsumer<T>(ExclusiveGroupStruct group, string name, uint capacity) | |||
where T : unmanaged, IEntityComponent | |||
{ | |||
return _entitiesStream.GenerateConsumer<T>(group, name, capacity); | |||
} | |||
//one datastructure rule them all: | |||
//split by group | |||
//split by type per group. It's possible to get all the entities of a give type T per group thanks | |||
@@ -303,17 +306,21 @@ namespace Svelto.ECS | |||
//ID. This ID doesn't need to be the EGID, it can be just the entityID | |||
//for each group id, save a dictionary indexed by entity type of entities indexed by id | |||
// group EntityComponentType entityID, EntityComponent | |||
readonly FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> | |||
internal readonly FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, ITypeSafeDictionary>> | |||
_groupEntityComponentsDB; | |||
//for each entity view type, return the groups (dictionary of entities indexed by entity id) where they are | |||
//found indexed by group id. TypeSafeDictionary are never created, they instead point to the ones hold | |||
//by _groupEntityComponentsDB | |||
// EntityComponentType groupID entityID, EntityComponent | |||
readonly FasterDictionary<RefWrapper<Type>, FasterDictionary<uint, ITypeSafeDictionary>> _groupsPerEntity; | |||
// <EntityComponentType <groupID <entityID, EntityComponent>>> | |||
internal readonly FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>> | |||
_groupsPerEntity; | |||
//The filters stored for each component and group | |||
internal readonly FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, GroupFilters>> | |||
_groupFilters; | |||
readonly EntitiesDB _entitiesDB; | |||
readonly EntitiesStream _entitiesStream; | |||
readonly EntitiesDB _entitiesDB; | |||
EntitiesDB IUnitTestingInterface.entitiesForTesting => _entitiesDB; | |||
} | |||
@@ -1,4 +1,4 @@ | |||
using System; | |||
using System; | |||
using System.Collections.Generic; | |||
using Svelto.Common; | |||
@@ -14,7 +14,7 @@ namespace Svelto.ECS | |||
} | |||
public EntityComponentInitializer BuildEntity<T> | |||
(uint entityID, ExclusiveGroupStruct groupStructId, IEnumerable<object> implementors = null) | |||
(uint entityID, BuildGroup groupStructId, IEnumerable<object> implementors = null) | |||
where T : IEntityDescriptor, new() | |||
{ | |||
return _enginesRoot.Target.BuildEntity(new EGID(entityID, groupStructId) | |||
@@ -34,14 +34,14 @@ namespace Svelto.ECS | |||
{ | |||
return _enginesRoot.Target.BuildEntity(egid, entityDescriptor.componentsToBuild, TypeCache<T>.type, implementors); | |||
} | |||
#if UNITY_BURST | |||
#if UNITY_NATIVE | |||
public NativeEntityFactory ToNative<T>(string memberName) where T : IEntityDescriptor, new() | |||
{ | |||
return _enginesRoot.Target.ProvideNativeEntityFactoryQueue<T>(memberName); | |||
} | |||
#endif | |||
public EntityComponentInitializer BuildEntity<T> | |||
(uint entityID, ExclusiveGroupStruct groupStructId, T descriptorEntity, IEnumerable<object> implementors) | |||
(uint entityID, BuildGroup groupStructId, T descriptorEntity, IEnumerable<object> implementors) | |||
where T : IEntityDescriptor | |||
{ | |||
return _enginesRoot.Target.BuildEntity(new EGID(entityID, groupStructId) | |||
@@ -1,6 +1,8 @@ | |||
using System; | |||
using System.Runtime.CompilerServices; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
@@ -8,7 +10,7 @@ namespace Svelto.ECS | |||
{ | |||
/// <summary> | |||
/// todo: EnginesRoot was a weakreference to give the change to inject | |||
/// entityfunctions from other engines root. It probably should be reverted | |||
/// entity functions from other engines root. It probably should be reverted | |||
/// </summary> | |||
class GenericEntityFunctions : IEntityFunctions | |||
{ | |||
@@ -18,7 +20,7 @@ namespace Svelto.ECS | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void RemoveEntity<T>(uint entityID, ExclusiveGroupStruct groupID) where T : | |||
public void RemoveEntity<T>(uint entityID, BuildGroup groupID) where T : | |||
IEntityDescriptor, new() | |||
{ | |||
RemoveEntity<T>(new EGID(entityID, groupID)); | |||
@@ -28,64 +30,91 @@ namespace Svelto.ECS | |||
public void RemoveEntity<T>(EGID entityEGID) where T : IEntityDescriptor, new() | |||
{ | |||
DBC.ECS.Check.Require(entityEGID.groupID != 0, "invalid group detected"); | |||
var descriptorComponentsToBuild = EntityDescriptorTemplate<T>.descriptor.componentsToBuild; | |||
_enginesRoot.Target.CheckRemoveEntityID(entityEGID, TypeCache<T>.type); | |||
_enginesRoot.Target.QueueEntitySubmitOperation<T>( | |||
new EntitySubmitOperation(EntitySubmitOperationType.Remove, entityEGID, entityEGID, | |||
EntityDescriptorTemplate<T>.descriptor.componentsToBuild)); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void RemoveAllEntities<T>(ExclusiveGroupStruct group) where T : IEntityDescriptor, new() | |||
{ | |||
throw new NotImplementedException(); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void RemoveAllEntities<T>() where T : IEntityDescriptor, new() | |||
{ | |||
throw new NotImplementedException(); | |||
descriptorComponentsToBuild)); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void RemoveGroupAndEntities(ExclusiveGroupStruct groupID) | |||
public void RemoveEntitiesFromGroup(BuildGroup groupID) | |||
{ | |||
_enginesRoot.Target.RemoveGroupID(groupID); | |||
DBC.ECS.Check.Require(groupID != 0, "invalid group detected"); | |||
_enginesRoot.Target.RemoveGroupID(groupID); | |||
_enginesRoot.Target.QueueEntitySubmitOperation( | |||
new EntitySubmitOperation(EntitySubmitOperationType.RemoveGroup, new EGID(0, groupID), new EGID())); | |||
} | |||
// [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
// void RemoveAllEntities<D, S>(ExclusiveGroup group) | |||
// where D : IEntityDescriptor, new() where S : unmanaged, IEntityComponent | |||
// { | |||
// var targetEntitiesDB = _enginesRoot.Target._entitiesDB; | |||
// var (buffer, count) = targetEntitiesDB.QueryEntities<S>(@group); | |||
// for (uint i = 0; i < count; ++i) | |||
// { | |||
// RemoveEntity<D>(new EGID(i, group)); | |||
// } | |||
// } | |||
// | |||
// [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
// void RemoveAllEntities<D, S>() | |||
// where D : IEntityDescriptor, new() where S : unmanaged, IEntityComponent | |||
// { | |||
// var targetEntitiesDB = _enginesRoot.Target._entitiesDB; | |||
// foreach (var ((buffer, count), exclusiveGroupStruct) in targetEntitiesDB.QueryEntities<S>()) | |||
// for (uint i = 0; i < count; ++i) | |||
// { | |||
// RemoveEntity<D>(new EGID(i, exclusiveGroupStruct)); | |||
// } | |||
// } | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void SwapEntitiesInGroup<T>(ExclusiveGroupStruct fromGroupID, ExclusiveGroupStruct toGroupID) | |||
public void SwapEntitiesInGroup<T>(BuildGroup fromGroupID, BuildGroup toGroupID) | |||
where T : IEntityDescriptor, new() | |||
{ | |||
throw new NotImplementedException("can't run this until I add the checks!"); | |||
#pragma warning disable 162 | |||
_enginesRoot.Target.QueueEntitySubmitOperation( | |||
new EntitySubmitOperation(EntitySubmitOperationType.SwapGroup, new EGID(0, fromGroupID), | |||
new EGID(0, toGroupID))); | |||
#pragma warning restore 162 | |||
if (_enginesRoot.Target._groupEntityComponentsDB.TryGetValue( | |||
fromGroupID.group, out FasterDictionary<RefWrapperType, ITypeSafeDictionary> entitiesInGroupPerType) | |||
== true) | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
IComponentBuilder[] components = EntityDescriptorTemplate<T>.descriptor.componentsToBuild; | |||
var dictionary = entitiesInGroupPerType[new RefWrapperType(components[0].GetEntityComponentType())]; | |||
dictionary.KeysEvaluator((key) => | |||
{ | |||
_enginesRoot.Target.CheckRemoveEntityID(new EGID(key, fromGroupID), TypeCache<T>.type); | |||
_enginesRoot.Target.CheckAddEntityID(new EGID(key, toGroupID), TypeCache<T>.type); | |||
}); | |||
#endif | |||
_enginesRoot.Target.QueueEntitySubmitOperation( | |||
new EntitySubmitOperation(EntitySubmitOperationType.SwapGroup, new EGID(0, fromGroupID) | |||
, new EGID(0, toGroupID))); | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void SwapEntityGroup<T>(uint entityID, ExclusiveGroupStruct fromGroupID, ExclusiveGroupStruct toGroupID) | |||
public void SwapEntityGroup<T>(uint entityID, BuildGroup fromGroupID, | |||
BuildGroup toGroupID) | |||
where T : IEntityDescriptor, new() | |||
{ | |||
SwapEntityGroup<T>(new EGID(entityID, fromGroupID), toGroupID); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void SwapEntityGroup<T>(EGID fromID, ExclusiveGroupStruct toGroupID) | |||
public void SwapEntityGroup<T>(EGID fromID, BuildGroup toGroupID) | |||
where T : IEntityDescriptor, new() | |||
{ | |||
SwapEntityGroup<T>(fromID, new EGID(fromID.entityID, (uint) toGroupID)); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void SwapEntityGroup<T>(EGID fromID, ExclusiveGroupStruct toGroupID | |||
, ExclusiveGroupStruct mustBeFromGroup) | |||
public void SwapEntityGroup<T>(EGID fromID, BuildGroup toGroupID | |||
, BuildGroup mustBeFromGroup) | |||
where T : IEntityDescriptor, new() | |||
{ | |||
if (fromID.groupID != mustBeFromGroup) | |||
@@ -96,7 +125,7 @@ namespace Svelto.ECS | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void SwapEntityGroup<T>(EGID fromID, EGID toID | |||
, ExclusiveGroupStruct mustBeFromGroup) | |||
, BuildGroup mustBeFromGroup) | |||
where T : IEntityDescriptor, new() | |||
{ | |||
if (fromID.groupID != mustBeFromGroup) | |||
@@ -105,17 +134,17 @@ namespace Svelto.ECS | |||
SwapEntityGroup<T>(fromID, toID); | |||
} | |||
#if UNITY_BURST | |||
#if UNITY_NATIVE | |||
public NativeEntityRemove ToNativeRemove<T>(string memberName) where T : IEntityDescriptor, new() | |||
{ | |||
return _enginesRoot.Target.ProvideNativeEntityRemoveQueue<T>(memberName); | |||
} | |||
public NativeEntitySwap ToNativeSwap<T>(string memberName) where T : IEntityDescriptor, new() | |||
{ | |||
return _enginesRoot.Target.ProvideNativeEntitySwapQueue<T>(memberName); | |||
} | |||
#endif | |||
#endif | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void SwapEntityGroup<T>(EGID fromID, EGID toID) | |||
@@ -123,17 +152,20 @@ namespace Svelto.ECS | |||
{ | |||
DBC.ECS.Check.Require(fromID.groupID != 0, "invalid group detected"); | |||
DBC.ECS.Check.Require(toID.groupID != 0, "invalid group detected"); | |||
var enginesRootTarget = _enginesRoot.Target; | |||
var descriptorComponentsToBuild = EntityDescriptorTemplate<T>.descriptor.componentsToBuild; | |||
_enginesRoot.Target.CheckRemoveEntityID(fromID, TypeCache<T>.type); | |||
_enginesRoot.Target.CheckAddEntityID(toID, TypeCache<T>.type); | |||
enginesRootTarget.CheckRemoveEntityID(fromID, TypeCache<T>.type); | |||
enginesRootTarget.CheckAddEntityID(toID, TypeCache<T>.type); | |||
_enginesRoot.Target.QueueEntitySubmitOperation<T>( | |||
enginesRootTarget.QueueEntitySubmitOperation<T>( | |||
new EntitySubmitOperation(EntitySubmitOperationType.Swap, | |||
fromID, toID, EntityDescriptorTemplate<T>.descriptor.componentsToBuild)); | |||
fromID, toID, descriptorComponentsToBuild)); | |||
} | |||
//enginesRoot is a weakreference because GenericEntityStreamConsumerFactory can be injected inside | |||
//engines of other enginesRoot | |||
//engines of other enginesRoot | |||
readonly Svelto.DataStructures.WeakReference<EnginesRoot> _enginesRoot; | |||
} | |||
@@ -154,13 +186,13 @@ namespace Svelto.ECS | |||
{ | |||
if (entitySubmitedOperation != entitySubmitOperation) | |||
throw new ECSException("Only one entity operation per submission is allowed" | |||
.FastConcat(" entityComponentType: ") | |||
.FastConcat(typeof(T).Name) | |||
.FastConcat(" submission type ", entitySubmitOperation.type.ToString(), | |||
.FastConcat(" entityComponentType: ") | |||
.FastConcat(typeof(T).Name) | |||
.FastConcat(" submission type ", entitySubmitOperation.type.ToString(), | |||
" from ID: ", entitySubmitOperation.fromID.entityID.ToString()) | |||
.FastConcat(" previous operation type: ", | |||
.FastConcat(" previous operation type: ", | |||
_entitiesOperations[(ulong) entitySubmitOperation.fromID].type | |||
.ToString())); | |||
.ToString())); | |||
} | |||
else | |||
#endif | |||
@@ -27,11 +27,20 @@ namespace Svelto.ECS | |||
} | |||
} | |||
/// <summary> | |||
/// Todo: it would be probably better to split even further the logic between submission and callbacks | |||
/// Something to do when I will optimize the callbacks | |||
/// </summary> | |||
/// <param name="profiler"></param> | |||
void SingleSubmission(in PlatformProfiler profiler) | |||
{ | |||
#if UNITY_BURST | |||
#if UNITY_NATIVE | |||
NativeOperationSubmission(profiler); | |||
#endif | |||
ClearChecks(); | |||
bool entitiesAreSubmitted = false; | |||
if (_entitiesOperations.Count > 0) | |||
{ | |||
using (profiler.Sample("Remove and Swap operations")) | |||
@@ -56,7 +65,7 @@ namespace Svelto.ECS | |||
entitiesOperations[i].fromID, null); | |||
break; | |||
case EntitySubmitOperationType.RemoveGroup: | |||
RemoveGroupAndEntities( | |||
RemoveEntitiesFromGroup( | |||
entitiesOperations[i].fromID.groupID, profiler); | |||
break; | |||
case EntitySubmitOperationType.SwapGroup: | |||
@@ -81,6 +90,8 @@ namespace Svelto.ECS | |||
} | |||
} | |||
} | |||
entitiesAreSubmitted = true; | |||
} | |||
_groupedEntityToAdd.Swap(); | |||
@@ -102,6 +113,15 @@ namespace Svelto.ECS | |||
} | |||
} | |||
} | |||
entitiesAreSubmitted = true; | |||
} | |||
if (entitiesAreSubmitted) | |||
{ | |||
var enginesCount = _reactiveEnginesSubmission.count; | |||
for (int i = 0; i < enginesCount; i++) | |||
_reactiveEnginesSubmission[i].EntitiesSubmitted(); | |||
} | |||
} | |||
@@ -114,14 +134,14 @@ namespace Svelto.ECS | |||
{ | |||
var groupID = groupOfEntitiesToSubmit.Key; | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> groupDB = GetOrCreateGroup(groupID, profiler); | |||
var groupDB = GetOrCreateGroup(groupID, profiler); | |||
//add the entityComponents in the group | |||
foreach (var entityComponentsToSubmit in _groupedEntityToAdd.other[groupID]) | |||
{ | |||
var type = entityComponentsToSubmit.Key; | |||
var targetTypeSafeDictionary = entityComponentsToSubmit.Value; | |||
var wrapper = new RefWrapper<Type>(type); | |||
var wrapper = new RefWrapperType(type); | |||
ITypeSafeDictionary dbDic = GetOrCreateTypeSafeDictionary(groupID, groupDB, wrapper, | |||
targetTypeSafeDictionary); | |||
@@ -143,16 +163,16 @@ namespace Svelto.ECS | |||
foreach (var entityComponentsToSubmit in _groupedEntityToAdd.other[groupID]) | |||
{ | |||
var realDic = groupDB[new RefWrapper<Type>(entityComponentsToSubmit.Key)]; | |||
var realDic = groupDB[new RefWrapperType(entityComponentsToSubmit.Key)]; | |||
entityComponentsToSubmit.Value.AddEntitiesToEngines(_reactiveEnginesAddRemove, realDic, | |||
new ExclusiveGroupStruct(groupToSubmit.Key), in profiler); | |||
entityComponentsToSubmit.Value.ExecuteEnginesAddOrSwapCallbacks(_reactiveEnginesAddRemove, realDic, | |||
null, new ExclusiveGroupStruct(groupToSubmit.Key), in profiler); | |||
} | |||
} | |||
} | |||
} | |||
DoubleBufferedEntitiesToAdd _groupedEntityToAdd; | |||
readonly DoubleBufferedEntitiesToAdd _groupedEntityToAdd; | |||
readonly ThreadSafeDictionary<ulong, EntitySubmitOperation> _entitiesOperations; | |||
} | |||
} |
@@ -7,20 +7,12 @@ namespace Svelto.ECS | |||
{ | |||
public partial class EntitiesDB | |||
{ | |||
internal FasterDictionary<uint, ITypeSafeDictionary> FindGroups_INTERNAL<T1>() where T1 : IEntityComponent | |||
{ | |||
if (_groupsPerEntity.ContainsKey(TypeRefWrapper<T1>.wrapper) == false) | |||
return _emptyDictionary; | |||
return _groupsPerEntity[TypeRefWrapper<T1>.wrapper]; | |||
} | |||
public LocalFasterReadOnlyList<ExclusiveGroupStruct> FindGroups<T1>() where T1 : IEntityComponent | |||
{ | |||
FasterList<ExclusiveGroupStruct> result = groups.Value; | |||
result.FastClear(); | |||
if (_groupsPerEntity.TryGetValue(TypeRefWrapper<T1>.wrapper | |||
, out FasterDictionary<uint, ITypeSafeDictionary> result1) == false) | |||
if (groupsPerEntity.TryGetValue(TypeRefWrapper<T1>.wrapper | |||
, out FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary> result1) == false) | |||
return result; | |||
var result1Count = result1.count; | |||
@@ -38,11 +30,11 @@ namespace Svelto.ECS | |||
{ | |||
FasterList<ExclusiveGroupStruct> result = groups.Value; | |||
result.FastClear(); | |||
if (_groupsPerEntity.TryGetValue(TypeRefWrapper<T1>.wrapper | |||
, out FasterDictionary<uint, ITypeSafeDictionary> result1) == false) | |||
if (groupsPerEntity.TryGetValue(TypeRefWrapper<T1>.wrapper | |||
, out FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary> result1) == false) | |||
return result; | |||
if (_groupsPerEntity.TryGetValue(TypeRefWrapper<T2>.wrapper | |||
, out FasterDictionary<uint, ITypeSafeDictionary> result2) == false) | |||
if (groupsPerEntity.TryGetValue(TypeRefWrapper<T2>.wrapper | |||
, out FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary> result2) == false) | |||
return result; | |||
var result1Count = result1.count; | |||
@@ -52,12 +44,13 @@ namespace Svelto.ECS | |||
for (int i = 0; i < result1Count; i++) | |||
{ | |||
var groupID = fasterDictionaryNodes1[i].key; | |||
for (int j = 0; j < result2Count; j++) | |||
{ | |||
//if the same group is found used with both T1 and T2 | |||
if (fasterDictionaryNodes1[i].key == fasterDictionaryNodes2[j].key) | |||
if (groupID == fasterDictionaryNodes2[j].key) | |||
{ | |||
result.Add(new ExclusiveGroupStruct(fasterDictionaryNodes1[i].key)); | |||
result.Add(new ExclusiveGroupStruct(groupID)); | |||
break; | |||
} | |||
} | |||
@@ -77,33 +70,68 @@ namespace Svelto.ECS | |||
public LocalFasterReadOnlyList<ExclusiveGroupStruct> FindGroups<T1, T2, T3>() | |||
where T1 : IEntityComponent where T2 : IEntityComponent where T3 : IEntityComponent | |||
{ | |||
FindGroups<T1, T2>(); | |||
FasterList<ExclusiveGroupStruct> result = groups.Value; | |||
if (result.count == 0) | |||
result.FastClear(); | |||
if (groupsPerEntity.TryGetValue(TypeRefWrapper<T1>.wrapper | |||
, out FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary> groupOfEntities1) == false) | |||
return result; | |||
if (_groupsPerEntity.TryGetValue(TypeRefWrapper<T3>.wrapper | |||
, out FasterDictionary<uint, ITypeSafeDictionary> result3) == false) | |||
if (groupsPerEntity.TryGetValue(TypeRefWrapper<T2>.wrapper | |||
, out FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary> groupOfEntities2) == false) | |||
return result; | |||
var result3Count = result3.count; | |||
var fasterDictionaryNodes3 = result3.unsafeKeys; | |||
for (int j = 0; j < result3Count; j++) | |||
for (int i = (int) 0; i < result.count; i++) | |||
if (groupsPerEntity.TryGetValue(TypeRefWrapper<T3>.wrapper | |||
, out FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary> groupOfEntities3) == false) | |||
return result; | |||
var result1Count = groupOfEntities1.count; | |||
var result2Count = groupOfEntities2.count; | |||
var result3Count = groupOfEntities3.count; | |||
var fasterDictionaryNodes1 = groupOfEntities1.unsafeKeys; | |||
var fasterDictionaryNodes2 = groupOfEntities2.unsafeKeys; | |||
var fasterDictionaryNodes3 = groupOfEntities3.unsafeKeys; | |||
// | |||
//TODO: I have to find once for ever a solution to be sure that the entities in the groups match | |||
//Currently this returns group where the entities are found, but the entities may not match in these | |||
//groups. | |||
//Checking the size of the entities is an early check, needed, but not sufficient, as entities components may | |||
//coincidentally match in number but not from which entities they are generated | |||
//foreach group where T1 is found | |||
for (int i = 0; i < result1Count; i++) | |||
{ | |||
if (fasterDictionaryNodes3[j].key == result[i]) | |||
break; | |||
result.UnorderedRemoveAt(i); | |||
i--; | |||
var groupT1 = fasterDictionaryNodes1[i].key; | |||
//foreach group where T2 is found | |||
for (int j = 0; j < result2Count; ++j) | |||
{ | |||
if (groupT1 == fasterDictionaryNodes2[j].key) | |||
{ | |||
//foreach group where T3 is found | |||
for (int k = 0; k < result3Count; ++k) | |||
{ | |||
if (groupT1 == fasterDictionaryNodes3[k].key) | |||
{ | |||
result.Add(new ExclusiveGroupStruct(groupT1)); | |||
break; | |||
} | |||
} | |||
break; | |||
} | |||
} | |||
} | |||
return result; | |||
} | |||
internal FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary> FindGroups_INTERNAL(Type type) | |||
{ | |||
if (groupsPerEntity.ContainsKey(new RefWrapperType(type)) == false) | |||
return _emptyDictionary; | |||
return groupsPerEntity[new RefWrapperType(type)]; | |||
} | |||
struct GroupsList | |||
{ | |||
static GroupsList() | |||
@@ -6,36 +6,31 @@ using System; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Hybrid; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public partial class EntitiesDB | |||
{ | |||
internal EntitiesDB | |||
(FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> groupEntityComponentsDB | |||
, FasterDictionary<RefWrapper<Type>, FasterDictionary<uint, ITypeSafeDictionary>> groupsPerEntity | |||
, EntitiesStream entityStream) | |||
internal EntitiesDB(EnginesRoot enginesRoot) | |||
{ | |||
_groupEntityComponentsDB = groupEntityComponentsDB; | |||
_groupsPerEntity = groupsPerEntity; | |||
_entityStream = entityStream; | |||
_enginesRoot = enginesRoot; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref T QueryUniqueEntity<T>(ExclusiveGroupStruct group) where T : struct, IEntityComponent | |||
EntityCollection<T> InternalQueryEntities<T>(FasterDictionary<RefWrapperType, ITypeSafeDictionary> entitiesInGroupPerType) | |||
where T : struct, IEntityComponent | |||
{ | |||
var entities = QueryEntities<T>(group); | |||
uint count = 0; | |||
IBuffer<T> buffer; | |||
if (SafeQueryEntityDictionary<T>(out var typeSafeDictionary, entitiesInGroupPerType) == false) | |||
buffer = RetrieveEmptyEntityComponentArray<T>(); | |||
else | |||
{ | |||
var safeDictionary = (typeSafeDictionary as ITypeSafeDictionary<T>); | |||
buffer = safeDictionary.GetValues(out count); | |||
} | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (entities.count == 0) | |||
throw new ECSException("Unique entity not found '".FastConcat(typeof(T).ToString()).FastConcat("'")); | |||
if (entities.count != 1) | |||
throw new ECSException("Unique entities must be unique! '".FastConcat(typeof(T).ToString()) | |||
.FastConcat("'")); | |||
#endif | |||
return ref entities[0]; | |||
return new EntityCollection<T>(buffer, count); | |||
} | |||
/// <summary> | |||
@@ -49,33 +44,34 @@ namespace Svelto.ECS | |||
public EntityCollection<T> QueryEntities<T>(ExclusiveGroupStruct groupStructId) | |||
where T : struct, IEntityComponent | |||
{ | |||
IBuffer<T> buffer; | |||
uint count = 0; | |||
if (SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false) | |||
buffer = RetrieveEmptyEntityComponentArray<T>(); | |||
else | |||
if (groupEntityComponentsDB.TryGetValue(groupStructId, out var entitiesInGroupPerType) == false) | |||
{ | |||
var safeDictionary = (typeSafeDictionary as ITypeSafeDictionary<T>); | |||
buffer = safeDictionary.GetValues(out count); | |||
var buffer = RetrieveEmptyEntityComponentArray<T>(); | |||
return new EntityCollection<T>(buffer, 0); | |||
} | |||
return new EntityCollection<T>(buffer, count); | |||
return InternalQueryEntities<T>(entitiesInGroupPerType); | |||
} | |||
public EntityCollection<T1, T2> QueryEntities<T1, T2>(ExclusiveGroupStruct groupStruct) | |||
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent | |||
{ | |||
var T1entities = QueryEntities<T1>(groupStruct); | |||
var T2entities = QueryEntities<T2>(groupStruct); | |||
if (groupEntityComponentsDB.TryGetValue(groupStruct, out var entitiesInGroupPerType) == false) | |||
{ | |||
return new EntityCollection<T1, T2>(new EntityCollection<T1>(RetrieveEmptyEntityComponentArray<T1>(), 0), | |||
new EntityCollection<T2>(RetrieveEmptyEntityComponentArray<T2>(), 0)); | |||
} | |||
var T1entities = InternalQueryEntities<T1>(entitiesInGroupPerType); | |||
var T2entities = InternalQueryEntities<T2>(entitiesInGroupPerType); | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (T1entities.count != T2entities.count) | |||
throw new ECSException("Entity components count do not match in group. Entity 1: ' count: " | |||
.FastConcat(T1entities.count).FastConcat(" ", typeof(T1).ToString()) | |||
.FastConcat("'. Entity 2: ' count: ".FastConcat(T2entities.count) | |||
.FastConcat(" ", typeof(T2).ToString()) | |||
.FastConcat("' group: ", groupStruct.ToName()))); | |||
#endif | |||
.FastConcat(T1entities.count).FastConcat(" ", typeof(T1).ToString()) | |||
.FastConcat("'. Entity 2: ' count: ".FastConcat(T2entities.count) | |||
.FastConcat(" ", typeof(T2).ToString()) | |||
.FastConcat("' group: ", groupStruct.ToName()))); | |||
#endif | |||
return new EntityCollection<T1, T2>(T1entities, T2entities); | |||
} | |||
@@ -83,66 +79,87 @@ namespace Svelto.ECS | |||
public EntityCollection<T1, T2, T3> QueryEntities<T1, T2, T3>(ExclusiveGroupStruct groupStruct) | |||
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent where T3 : struct, IEntityComponent | |||
{ | |||
var T1entities = QueryEntities<T1>(groupStruct); | |||
var T2entities = QueryEntities<T2>(groupStruct); | |||
var T3entities = QueryEntities<T3>(groupStruct); | |||
if (groupEntityComponentsDB.TryGetValue(groupStruct, out var entitiesInGroupPerType) == false) | |||
{ | |||
return new EntityCollection<T1, T2, T3>( | |||
new EntityCollection<T1>(RetrieveEmptyEntityComponentArray<T1>(), 0), | |||
new EntityCollection<T2>(RetrieveEmptyEntityComponentArray<T2>(), 0), | |||
new EntityCollection<T3>(RetrieveEmptyEntityComponentArray<T3>(), 0)); | |||
} | |||
var T1entities = InternalQueryEntities<T1>(entitiesInGroupPerType); | |||
var T2entities = InternalQueryEntities<T2>(entitiesInGroupPerType); | |||
var T3entities = InternalQueryEntities<T3>(entitiesInGroupPerType); | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (T1entities.count != T2entities.count || T2entities.count != T3entities.count) | |||
throw new ECSException("Entity components count do not match in group. Entity 1: " | |||
.FastConcat(typeof(T1).ToString()).FastConcat(" count: ") | |||
.FastConcat(T1entities.count).FastConcat( | |||
" Entity 2: " | |||
.FastConcat(typeof(T2).ToString()).FastConcat(" count: ") | |||
.FastConcat(T2entities.count) | |||
.FastConcat(" Entity 3: ".FastConcat(typeof(T3).ToString())) | |||
.FastConcat(" count: ").FastConcat(T3entities.count))); | |||
#endif | |||
.FastConcat(typeof(T1).ToString()).FastConcat(" count: ") | |||
.FastConcat(T1entities.count).FastConcat( | |||
" Entity 2: " | |||
.FastConcat(typeof(T2).ToString()).FastConcat(" count: ") | |||
.FastConcat(T2entities.count) | |||
.FastConcat(" Entity 3: ".FastConcat(typeof(T3).ToString())) | |||
.FastConcat(" count: ").FastConcat(T3entities.count))); | |||
#endif | |||
return new EntityCollection<T1, T2, T3>(T1entities, T2entities, T3entities); | |||
} | |||
public int IterateOverGroupsAndCount<T> | |||
(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) where T : struct, IEntityComponent | |||
public EntityCollection<T1, T2, T3, T4> QueryEntities<T1, T2, T3, T4>(ExclusiveGroupStruct groupStruct) | |||
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent where T3 : struct, IEntityComponent where T4 : struct, IEntityComponent | |||
{ | |||
int count = 0; | |||
for (int i = 0; i < groups.count; i++) | |||
if (groupEntityComponentsDB.TryGetValue(groupStruct, out var entitiesInGroupPerType) == false) | |||
{ | |||
count += Count<T>(groups[i]); | |||
return new EntityCollection<T1, T2, T3, T4>( | |||
new EntityCollection<T1>(RetrieveEmptyEntityComponentArray<T1>(), 0), | |||
new EntityCollection<T2>(RetrieveEmptyEntityComponentArray<T2>(), 0), | |||
new EntityCollection<T3>(RetrieveEmptyEntityComponentArray<T3>(), 0), | |||
new EntityCollection<T4>(RetrieveEmptyEntityComponentArray<T4>(), 0)); | |||
} | |||
var T1entities = InternalQueryEntities<T1>(entitiesInGroupPerType); | |||
var T2entities = InternalQueryEntities<T2>(entitiesInGroupPerType); | |||
var T3entities = InternalQueryEntities<T3>(entitiesInGroupPerType); | |||
var T4entities = InternalQueryEntities<T4>(entitiesInGroupPerType); | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (T1entities.count != T2entities.count || T2entities.count != T3entities.count) | |||
throw new ECSException("Entity components count do not match in group. Entity 1: " | |||
.FastConcat(typeof(T1).ToString()).FastConcat(" count: ") | |||
.FastConcat(T1entities.count).FastConcat( | |||
" Entity 2: " | |||
.FastConcat(typeof(T2).ToString()).FastConcat(" count: ") | |||
.FastConcat(T2entities.count) | |||
.FastConcat(" Entity 3: ".FastConcat(typeof(T3).ToString())) | |||
.FastConcat(" count: ").FastConcat(T3entities.count))); | |||
#endif | |||
return 0; | |||
return new EntityCollection<T1, T2, T3, T4>(T1entities, T2entities, T3entities, T4entities); | |||
} | |||
public TupleRef<T> QueryEntities<T> | |||
public GroupsEnumerable<T> QueryEntities<T> | |||
(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) where T : struct, IEntityComponent | |||
{ | |||
return new TupleRef<T>(new EntityCollections<T>(this, groups), new GroupsEnumerable<T>(this, groups)); | |||
return new GroupsEnumerable<T>(this, groups); | |||
} | |||
public TupleRef<T1, T2> QueryEntities<T1, T2>(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) | |||
public GroupsEnumerable<T1, T2> QueryEntities<T1, T2>(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) | |||
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent | |||
{ | |||
return new TupleRef<T1, T2>(new EntityCollections<T1, T2>(this, groups) | |||
, new GroupsEnumerable<T1, T2>(this, groups)); | |||
return new GroupsEnumerable<T1, T2>(this, groups); | |||
} | |||
public TupleRef<T1, T2, T3> QueryEntities<T1, T2, T3>(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) | |||
public GroupsEnumerable<T1, T2, T3> QueryEntities<T1, T2, T3>(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) | |||
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent where T3 : struct, IEntityComponent | |||
{ | |||
return new TupleRef<T1, T2, T3>(new EntityCollections<T1, T2, T3>(this, groups) | |||
, new GroupsEnumerable<T1, T2, T3>(this, groups)); | |||
return new GroupsEnumerable<T1, T2, T3>(this, groups); | |||
} | |||
public TupleRef<T1, T2, T3, T4> QueryEntities<T1, T2, T3, T4> | |||
public GroupsEnumerable<T1, T2, T3, T4> QueryEntities<T1, T2, T3, T4> | |||
(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) | |||
where T1 : struct, IEntityComponent | |||
where T2 : struct, IEntityComponent | |||
where T3 : struct, IEntityComponent | |||
where T4 : struct, IEntityComponent | |||
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent | |||
where T3 : struct, IEntityComponent where T4 : struct, IEntityComponent | |||
{ | |||
return new TupleRef<T1, T2, T3, T4>(new EntityCollections<T1, T2, T3, T4>(this, groups) | |||
, new GroupsEnumerable<T1, T2, T3, T4>(this, groups)); | |||
return new GroupsEnumerable<T1, T2, T3, T4>(this, groups); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
@@ -150,11 +167,11 @@ namespace Svelto.ECS | |||
where T : struct, IEntityComponent | |||
{ | |||
if (SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false) | |||
throw new EntityGroupNotFoundException(typeof(T)); | |||
throw new EntityGroupNotFoundException(typeof(T) , groupStructId.ToName()); | |||
return (typeSafeDictionary as ITypeSafeDictionary<T>).ToEGIDMapper(groupStructId); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool TryQueryMappedEntities<T> | |||
(ExclusiveGroupStruct groupStructId, out EGIDMapper<T> mapper) where T : struct, IEntityComponent | |||
@@ -190,8 +207,8 @@ namespace Svelto.ECS | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool ExistsAndIsNotEmpty(ExclusiveGroupStruct gid) | |||
{ | |||
if (_groupEntityComponentsDB.TryGetValue( | |||
gid, out FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> group) == true) | |||
if (groupEntityComponentsDB.TryGetValue( | |||
gid, out FasterDictionary<RefWrapperType, ITypeSafeDictionary> group) == true) | |||
{ | |||
return group.count > 0; | |||
} | |||
@@ -210,50 +227,36 @@ namespace Svelto.ECS | |||
{ | |||
if (SafeQueryEntityDictionary<T>(groupStruct, out var typeSafeDictionary) == false) | |||
return 0; | |||
return (int) typeSafeDictionary.count; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void PublishEntityChange<T>(EGID egid) where T : unmanaged, IEntityComponent | |||
public bool FoundInGroups<T1>() where T1 : IEntityComponent | |||
{ | |||
_entityStream.PublishEntity(ref this.QueryEntity<T>(egid), egid); | |||
return groupsPerEntity.ContainsKey(TypeRefWrapper<T1>.wrapper); | |||
} | |||
[Obsolete("<color=orange>This Method will be removed soon. please use QueryEntities instead</color>")] | |||
public void ExecuteOnAllEntities<T>(ExecuteOnAllEntitiesAction<T> action) where T : struct, IEntityComponent | |||
{ | |||
if (_groupsPerEntity.TryGetValue(TypeRefWrapper<T>.wrapper, out var dictionary)) | |||
foreach (var pair in dictionary) | |||
{ | |||
IBuffer<T> entities = (pair.Value as ITypeSafeDictionary<T>).GetValues(out var count); | |||
if (count > 0) | |||
action(entities, new ExclusiveGroupStruct(pair.Key), count, this); | |||
} | |||
} | |||
public bool IsDisposing => _enginesRoot._isDisposing; | |||
[Obsolete("<color=orange>This Method will be removed soon. please use QueryEntities instead</color>")] | |||
public void ExecuteOnAllEntities<T, W>(ref W value, ExecuteOnAllEntitiesAction<T, W> action) | |||
where T : struct, IEntityComponent | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal bool SafeQueryEntityDictionary<T>(out ITypeSafeDictionary typeSafeDictionary, | |||
FasterDictionary<RefWrapperType, ITypeSafeDictionary> entitiesInGroupPerType) | |||
where T : IEntityComponent | |||
{ | |||
if (_groupsPerEntity.TryGetValue(TypeRefWrapper<T>.wrapper, out var dic)) | |||
foreach (var pair in dic) | |||
{ | |||
IBuffer<T> entities = (pair.Value as ITypeSafeDictionary<T>).GetValues(out var innerCount); | |||
if (entitiesInGroupPerType.TryGetValue(new RefWrapperType(TypeCache<T>.type), out var safeDictionary) == false) | |||
{ | |||
typeSafeDictionary = default; | |||
return false; | |||
} | |||
if (innerCount > 0) | |||
action(entities, new ExclusiveGroupStruct(pair.Key), innerCount, this, ref value); | |||
} | |||
} | |||
//return the indexes entities if they exist | |||
typeSafeDictionary = safeDictionary; | |||
public bool FoundInGroups<T1>() where T1 : IEntityComponent | |||
{ | |||
return _groupsPerEntity.ContainsKey(TypeRefWrapper<T1>.wrapper); | |||
return true; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal bool SafeQueryEntityDictionary<T>(uint group, out ITypeSafeDictionary typeSafeDictionary) | |||
internal bool SafeQueryEntityDictionary<T>(ExclusiveGroupStruct group, out ITypeSafeDictionary typeSafeDictionary) | |||
where T : IEntityComponent | |||
{ | |||
if (UnsafeQueryEntityDictionary(group, TypeCache<T>.type, out var safeDictionary) == false) | |||
@@ -269,17 +272,47 @@ namespace Svelto.ECS | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal bool UnsafeQueryEntityDictionary(uint group, Type type, out ITypeSafeDictionary typeSafeDictionary) | |||
internal bool UnsafeQueryEntityDictionary(ExclusiveGroupStruct group, Type type, out ITypeSafeDictionary typeSafeDictionary) | |||
{ | |||
//search for the group | |||
if (_groupEntityComponentsDB.TryGetValue(group, out var entitiesInGroupPerType) == false) | |||
if (groupEntityComponentsDB.TryGetValue(group, out var entitiesInGroupPerType) == false) | |||
{ | |||
typeSafeDictionary = null; | |||
return false; | |||
} | |||
//search for the indexed entities in the group | |||
return entitiesInGroupPerType.TryGetValue(new RefWrapper<Type>(type), out typeSafeDictionary); | |||
return entitiesInGroupPerType.TryGetValue(new RefWrapperType(type), out typeSafeDictionary); | |||
} | |||
internal bool FindIndex(uint entityID, ExclusiveGroupStruct @group, Type type, out uint index) | |||
{ | |||
EGID entityGID = new EGID(entityID, @group); | |||
index = default; | |||
if (UnsafeQueryEntityDictionary(@group, type, out var safeDictionary) == false) | |||
return false; | |||
if (safeDictionary.TryFindIndex(entityGID.entityID, out index) == false) | |||
return false; | |||
return true; | |||
} | |||
internal uint GetIndex(uint entityID, ExclusiveGroupStruct @group, Type type) | |||
{ | |||
EGID entityGID = new EGID(entityID, @group); | |||
if (UnsafeQueryEntityDictionary(@group, type, out var safeDictionary) == false) | |||
{ | |||
throw new EntityNotFoundException(entityGID, type); | |||
} | |||
if (safeDictionary.TryFindIndex(entityGID.entityID, out var index) == false) | |||
throw new EntityNotFoundException(entityGID, type); | |||
return index; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
@@ -309,20 +342,25 @@ namespace Svelto.ECS | |||
} | |||
} | |||
readonly FasterDictionary<uint, ITypeSafeDictionary> _emptyDictionary = | |||
new FasterDictionary<uint, ITypeSafeDictionary>(); | |||
static readonly FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary> _emptyDictionary = | |||
new FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>(); | |||
readonly EntitiesStream _entityStream; | |||
readonly EnginesRoot _enginesRoot; | |||
EntitiesStreams _entityStream => _enginesRoot._entityStreams; | |||
//grouped set of entity components, this is the standard way to handle entity components are grouped per | |||
//group, then indexable per type, then indexable per EGID. however the TypeSafeDictionary can return an array of | |||
//values directly, that can be iterated over, so that is possible to iterate over all the entity components of | |||
//a specific type inside a specific group. | |||
readonly FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> | |||
_groupEntityComponentsDB; | |||
//needed to be able to track in which groups a specific entity type can be found. | |||
//may change in future as it could be expanded to support queries | |||
readonly FasterDictionary<RefWrapper<Type>, FasterDictionary<uint, ITypeSafeDictionary>> _groupsPerEntity; | |||
FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, ITypeSafeDictionary>> | |||
groupEntityComponentsDB => _enginesRoot._groupEntityComponentsDB; | |||
//for each entity view type, return the groups (dictionary of entities indexed by entity id) where they are | |||
//found indexed by group id. TypeSafeDictionary are never created, they instead point to the ones hold | |||
//by _groupEntityComponentsDB | |||
// <EntityComponentType <groupID <entityID, EntityComponent>>> | |||
FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>> groupsPerEntity => | |||
_enginesRoot._groupsPerEntity; | |||
} | |||
} |
@@ -1,5 +1,4 @@ | |||
using System.Runtime.CompilerServices; | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
@@ -7,7 +6,7 @@ namespace Svelto.ECS | |||
{ | |||
public readonly ref struct EntityCollection<T> where T : struct, IEntityComponent | |||
{ | |||
static readonly bool IsUnmanaged = TypeSafeDictionary<T>._isUmanaged; | |||
static readonly bool IsUnmanaged = TypeSafeDictionary<T>.IsUnmanaged; | |||
public EntityCollection(IBuffer<T> buffer, uint count):this() | |||
{ | |||
@@ -17,74 +16,19 @@ namespace Svelto.ECS | |||
_managedBuffer = (MB<T>) buffer; | |||
_count = count; | |||
_buffer = buffer; | |||
} | |||
public uint count => _count; | |||
internal readonly MB<T> _managedBuffer; | |||
internal readonly NB<T> _nativedBuffer; | |||
readonly uint _count; | |||
//todo very likely remove this | |||
public ref T this[uint i] | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get | |||
{ | |||
if (IsUnmanaged) | |||
return ref _nativedBuffer[i]; | |||
else | |||
return ref _managedBuffer[i]; | |||
} | |||
} | |||
public ref T this[int i] | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get | |||
{ | |||
if (IsUnmanaged) | |||
return ref _nativedBuffer[i]; | |||
else | |||
return ref _managedBuffer[i]; | |||
} | |||
} | |||
//TODO SOON: ALL THIS STUFF BELOW MUST DISAPPEAR | |||
readonly IBuffer<T> _buffer; | |||
//todo to remove | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public EntityIterator GetEnumerator() { return new EntityIterator(_buffer, _count); } | |||
//todo to remove | |||
public ref struct EntityIterator | |||
{ | |||
public EntityIterator(IBuffer<T> array, uint count) : this() | |||
{ | |||
_array = array; | |||
_count = count; | |||
_index = -1; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool MoveNext() { return ++_index < _count; } | |||
public ref T Current | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => ref _array[_index]; | |||
} | |||
readonly IBuffer<T> _array; | |||
readonly uint _count; | |||
int _index; | |||
} | |||
readonly uint _count; | |||
} | |||
public readonly ref struct EntityCollection<T1, T2> where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent | |||
{ | |||
public EntityCollection(in EntityCollection<T1> array1, in EntityCollection<T2> array2) | |||
internal EntityCollection(in EntityCollection<T1> array1, in EntityCollection<T2> array2) | |||
{ | |||
_array1 = array1; | |||
_array2 = array2; | |||
@@ -92,14 +36,13 @@ namespace Svelto.ECS | |||
public uint count => _array1.count; | |||
//todo to remove | |||
public EntityCollection<T2> Item2 | |||
internal EntityCollection<T2> buffer2 | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => _array2; | |||
} | |||
//todo to remove | |||
public EntityCollection<T1> Item1 | |||
internal EntityCollection<T1> buffer1 | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => _array1; | |||
@@ -107,81 +50,52 @@ namespace Svelto.ECS | |||
readonly EntityCollection<T1> _array1; | |||
readonly EntityCollection<T2> _array2; | |||
//todo to remove | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public EntityIterator GetEnumerator() { return new EntityIterator(this); } | |||
//todo to remove | |||
public ref struct EntityIterator | |||
{ | |||
public EntityIterator(in EntityCollection<T1, T2> array1) : this() | |||
{ | |||
_array1 = array1; | |||
_count = array1.count; | |||
_index = -1; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool MoveNext() { return ++_index < _count; } | |||
public void Reset() { _index = -1; } | |||
public ValueRef<T1, T2> Current | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => new ValueRef<T1, T2>(_array1, (uint) _index); | |||
} | |||
readonly EntityCollection<T1, T2> _array1; | |||
readonly uint _count; | |||
int _index; | |||
} | |||
} | |||
public readonly ref struct EntityCollection<T1, T2, T3> where T3 : struct, IEntityComponent | |||
where T2 : struct, IEntityComponent | |||
where T1 : struct, IEntityComponent | |||
where T2 : struct, IEntityComponent | |||
where T1 : struct, IEntityComponent | |||
{ | |||
public EntityCollection | |||
internal EntityCollection | |||
(in EntityCollection<T1> array1, in EntityCollection<T2> array2, in EntityCollection<T3> array3) | |||
{ | |||
_array1 = array1; | |||
_array2 = array2; | |||
_array3 = array3; | |||
} | |||
//todo to remove | |||
public EntityCollection<T1> Item1 | |||
internal EntityCollection<T1> buffer1 | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => _array1; | |||
} | |||
//todo to remove | |||
public EntityCollection<T2> Item2 | |||
internal EntityCollection<T2> buffer2 | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => _array2; | |||
} | |||
//todo to remove | |||
public EntityCollection<T3> Item3 | |||
internal EntityCollection<T3> buffer3 | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => _array3; | |||
} | |||
public uint count => Item1.count; | |||
internal uint count => buffer1.count; | |||
readonly EntityCollection<T1> _array1; | |||
readonly EntityCollection<T2> _array2; | |||
readonly EntityCollection<T3> _array3; | |||
} | |||
public readonly ref struct EntityCollection<T1, T2, T3, T4> | |||
where T1 : struct, IEntityComponent | |||
where T2 : struct, IEntityComponent | |||
where T3 : struct, IEntityComponent | |||
where T4 : struct, IEntityComponent | |||
{ | |||
public EntityCollection | |||
internal EntityCollection | |||
(in EntityCollection<T1> array1, in EntityCollection<T2> array2, in EntityCollection<T3> array3, in EntityCollection<T4> array4) | |||
{ | |||
_array1 = array1; | |||
@@ -190,35 +104,31 @@ namespace Svelto.ECS | |||
_array4 = array4; | |||
} | |||
//todo to remove | |||
public EntityCollection<T1> Item1 | |||
internal EntityCollection<T1> Item1 | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => _array1; | |||
} | |||
//todo to remove | |||
public EntityCollection<T2> Item2 | |||
internal EntityCollection<T2> Item2 | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => _array2; | |||
} | |||
//todo to remove | |||
public EntityCollection<T3> Item3 | |||
internal EntityCollection<T3> Item3 | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => _array3; | |||
} | |||
//todo to remove | |||
public EntityCollection<T4> Item4 | |||
internal EntityCollection<T4> Item4 | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => _array4; | |||
} | |||
public uint count => _array1.count; | |||
internal uint count => _array1.count; | |||
readonly EntityCollection<T1> _array1; | |||
readonly EntityCollection<T2> _array2; | |||
@@ -308,62 +218,4 @@ namespace Svelto.ECS | |||
count = this.count; | |||
} | |||
} | |||
public readonly ref struct ValueRef<T1, T2> where T2 : struct, IEntityComponent where T1 : struct, IEntityComponent | |||
{ | |||
readonly EntityCollection<T1, T2> array1; | |||
readonly uint index; | |||
public ValueRef(in EntityCollection<T1, T2> entity2, uint i) | |||
{ | |||
array1 = entity2; | |||
index = i; | |||
} | |||
public ref T1 entityComponentA | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => ref array1.Item1[index]; | |||
} | |||
public ref T2 entityComponentB | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => ref array1.Item2[index]; | |||
} | |||
} | |||
public readonly ref struct ValueRef<T1, T2, T3> where T2 : struct, IEntityComponent | |||
where T1 : struct, IEntityComponent | |||
where T3 : struct, IEntityComponent | |||
{ | |||
readonly EntityCollection<T1, T2, T3> array1; | |||
readonly uint index; | |||
public ValueRef(in EntityCollection<T1, T2, T3> entity, uint i) | |||
{ | |||
array1 = entity; | |||
index = i; | |||
} | |||
public ref T1 entityComponentA | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => ref array1.Item1[index]; | |||
} | |||
public ref T2 entityComponentB | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => ref array1.Item2[index]; | |||
} | |||
public ref T3 entityComponentC | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => ref array1.Item3[index]; | |||
} | |||
} | |||
} |
@@ -1,266 +0,0 @@ | |||
using System; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
public readonly ref struct EntityCollections<T1, T2, T3, T4> where T1 : struct, IEntityComponent | |||
where T2 : struct, IEntityComponent | |||
where T3 : struct, IEntityComponent | |||
where T4 : struct, IEntityComponent | |||
{ | |||
public EntityCollections(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) : this() | |||
{ | |||
_db = db; | |||
_groups = groups; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public EntityGroupsIterator GetEnumerator() | |||
{ | |||
throw new NotImplementedException("tell seb to finish this one"); | |||
#pragma warning disable 162 | |||
return new EntityGroupsIterator(_db, _groups); | |||
#pragma warning restore 162 | |||
} | |||
readonly EntitiesDB _db; | |||
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups; | |||
public ref struct EntityGroupsIterator | |||
{ | |||
public EntityGroupsIterator(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) : this() | |||
{ | |||
_db = db; | |||
_groups = groups; | |||
_indexGroup = -1; | |||
_index = -1; | |||
} | |||
public bool MoveNext() | |||
{ | |||
//attention, the while is necessary to skip empty groups | |||
while (_index + 1 >= _count && ++_indexGroup < _groups.count) | |||
{ | |||
_index = -1; | |||
_array1 = _db.QueryEntities<T1, T2, T3>(_groups[_indexGroup]); | |||
_count = _array1.count; | |||
} | |||
return ++_index < _count; | |||
} | |||
public void Reset() | |||
{ | |||
_index = -1; | |||
_indexGroup = -1; | |||
_array1 = _db.QueryEntities<T1, T2, T3>(_groups[0]); | |||
_count = _array1.count; | |||
} | |||
public ValueRef<T1, T2, T3> Current | |||
{ | |||
get | |||
{ | |||
var valueRef = new ValueRef<T1, T2, T3>(_array1, (uint) _index); | |||
return valueRef; | |||
} | |||
} | |||
readonly EntitiesDB _db; | |||
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups; | |||
uint _count; | |||
int _index; | |||
int _indexGroup; | |||
EntityCollection<T1, T2, T3> _array1; | |||
} | |||
} | |||
public readonly ref struct EntityCollections<T1, T2, T3> where T1 : struct, IEntityComponent | |||
where T2 : struct, IEntityComponent | |||
where T3 : struct, IEntityComponent | |||
{ | |||
public EntityCollections(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) : this() | |||
{ | |||
_db = db; | |||
_groups = groups; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public EntityGroupsIterator GetEnumerator() { return new EntityGroupsIterator(_db, _groups); } | |||
readonly EntitiesDB _db; | |||
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups; | |||
public ref struct EntityGroupsIterator | |||
{ | |||
public EntityGroupsIterator(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) : this() | |||
{ | |||
_db = db; | |||
_groups = groups; | |||
_indexGroup = -1; | |||
_index = -1; | |||
} | |||
public bool MoveNext() | |||
{ | |||
//attention, the while is necessary to skip empty groups | |||
while (_index + 1 >= _count && ++_indexGroup < _groups.count) | |||
{ | |||
_index = -1; | |||
_array1 = _db.QueryEntities<T1, T2, T3>(_groups[_indexGroup]); | |||
_count = _array1.count; | |||
} | |||
return ++_index < _count; | |||
} | |||
public void Reset() | |||
{ | |||
_index = -1; | |||
_indexGroup = -1; | |||
_array1 = _db.QueryEntities<T1, T2, T3>(_groups[0]); | |||
_count = _array1.count; | |||
} | |||
public ValueRef<T1, T2, T3> Current | |||
{ | |||
get | |||
{ | |||
var valueRef = new ValueRef<T1, T2, T3>(_array1, (uint) _index); | |||
return valueRef; | |||
} | |||
} | |||
readonly EntitiesDB _db; | |||
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups; | |||
uint _count; | |||
int _index; | |||
int _indexGroup; | |||
EntityCollection<T1, T2, T3> _array1; | |||
} | |||
} | |||
public readonly ref struct EntityCollections<T1, T2> | |||
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent | |||
{ | |||
public EntityCollections(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) : this() | |||
{ | |||
_db = db; | |||
_groups = groups; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public EntityGroupsIterator GetEnumerator() { return new EntityGroupsIterator(_db, _groups); } | |||
readonly EntitiesDB _db; | |||
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups; | |||
public ref struct EntityGroupsIterator | |||
{ | |||
public EntityGroupsIterator(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) : this() | |||
{ | |||
_db = db; | |||
_groups = groups; | |||
_indexGroup = -1; | |||
_index = -1; | |||
} | |||
public bool MoveNext() | |||
{ | |||
//attention, the while is necessary to skip empty groups | |||
while (_index + 1 >= _array1.count && ++_indexGroup < _groups.count) | |||
{ | |||
_index = -1; | |||
_array1 = _db.QueryEntities<T1, T2>(_groups[_indexGroup]); | |||
} | |||
return ++_index < _array1.count; | |||
} | |||
public void Reset() | |||
{ | |||
_index = -1; | |||
_indexGroup = -1; | |||
_array1 = _db.QueryEntities<T1, T2>(_groups[0]); | |||
} | |||
public ValueRef<T1, T2> Current | |||
{ | |||
get | |||
{ | |||
var valueRef = new ValueRef<T1, T2>(_array1, (uint) _index); | |||
return valueRef; | |||
} | |||
} | |||
readonly EntitiesDB _db; | |||
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups; | |||
int _index; | |||
int _indexGroup; | |||
EntityCollection<T1, T2> _array1; | |||
} | |||
} | |||
public readonly ref struct EntityCollections<T> where T : struct, IEntityComponent | |||
{ | |||
public EntityCollections(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) : this() | |||
{ | |||
_db = db; | |||
_groups = groups; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public EntityGroupsIterator GetEnumerator() { return new EntityGroupsIterator(_db, _groups); } | |||
readonly EntitiesDB _db; | |||
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups; | |||
public ref struct EntityGroupsIterator | |||
{ | |||
public EntityGroupsIterator(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) : this() | |||
{ | |||
_db = db; | |||
_groups = groups; | |||
_indexGroup = -1; | |||
_index = -1; | |||
} | |||
public bool MoveNext() | |||
{ | |||
//attention, the while is necessary to skip empty groups | |||
while (_index + 1 >= _count && ++_indexGroup < _groups.count) | |||
{ | |||
_index = -1; | |||
_array = _db.QueryEntities<T>(_groups[_indexGroup]); | |||
_count = _array.count; | |||
} | |||
return ++_index < _count; | |||
} | |||
public void Reset() | |||
{ | |||
_index = -1; | |||
_indexGroup = -1; | |||
_count = 0; | |||
} | |||
public ref T Current => ref _array[(uint) _index]; | |||
readonly EntitiesDB _db; | |||
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups; | |||
EntityCollection<T> _array; | |||
uint _count; | |||
int _index; | |||
int _indexGroup; | |||
} | |||
} | |||
} |
@@ -6,7 +6,7 @@ namespace Svelto.ECS | |||
{ | |||
public readonly ref struct EntityComponentInitializer | |||
{ | |||
public EntityComponentInitializer(EGID id, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> group) | |||
public EntityComponentInitializer(EGID id, FasterDictionary<RefWrapperType, ITypeSafeDictionary> group) | |||
{ | |||
_group = group; | |||
_ID = id; | |||
@@ -16,7 +16,7 @@ namespace Svelto.ECS | |||
public void Init<T>(T initializer) where T : struct, IEntityComponent | |||
{ | |||
if (_group.TryGetValue(new RefWrapper<Type>(ComponentBuilder<T>.ENTITY_COMPONENT_TYPE), | |||
if (_group.TryGetValue(new RefWrapperType(ComponentBuilder<T>.ENTITY_COMPONENT_TYPE), | |||
out var typeSafeDictionary) == false) return; | |||
var dictionary = (ITypeSafeDictionary<T>) typeSafeDictionary; | |||
@@ -30,7 +30,7 @@ namespace Svelto.ECS | |||
public ref T GetOrCreate<T>() where T : struct, IEntityComponent | |||
{ | |||
ref var entityDictionary = ref _group.GetOrCreate(new RefWrapper<Type>(ComponentBuilder<T>.ENTITY_COMPONENT_TYPE) | |||
ref var entityDictionary = ref _group.GetOrCreate(new RefWrapperType(ComponentBuilder<T>.ENTITY_COMPONENT_TYPE) | |||
, TypeSafeDictionaryFactory<T>.Create); | |||
var dictionary = (ITypeSafeDictionary<T>) entityDictionary; | |||
@@ -39,13 +39,13 @@ namespace Svelto.ECS | |||
public ref T Get<T>() where T : struct, IEntityComponent | |||
{ | |||
return ref (_group[new RefWrapper<Type>(ComponentBuilder<T>.ENTITY_COMPONENT_TYPE)] as ITypeSafeDictionary<T>)[ | |||
return ref (_group[new RefWrapperType(ComponentBuilder<T>.ENTITY_COMPONENT_TYPE)] as ITypeSafeDictionary<T>)[ | |||
_ID.entityID]; | |||
} | |||
public bool Has<T>() where T : struct, IEntityComponent | |||
{ | |||
if (_group.TryGetValue(new RefWrapper<Type>(ComponentBuilder<T>.ENTITY_COMPONENT_TYPE), | |||
if (_group.TryGetValue(new RefWrapperType(ComponentBuilder<T>.ENTITY_COMPONENT_TYPE), | |||
out var typeSafeDictionary)) | |||
{ | |||
var dictionary = (ITypeSafeDictionary<T>) typeSafeDictionary; | |||
@@ -58,6 +58,6 @@ namespace Svelto.ECS | |||
} | |||
readonly EGID _ID; | |||
readonly FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> _group; | |||
readonly FasterDictionary<RefWrapperType, ITypeSafeDictionary> _group; | |||
} | |||
} |
@@ -1,17 +1,26 @@ | |||
using System; | |||
namespace Svelto.ECS | |||
{ | |||
public interface IEntityDescriptor | |||
{ | |||
IComponentBuilder[] componentsToBuild { get; } | |||
} | |||
public interface IDynamicEntityDescriptor: IEntityDescriptor | |||
{ | |||
} | |||
static class EntityDescriptorTemplate<TType> where TType : IEntityDescriptor, new() | |||
{ | |||
static EntityDescriptorTemplate() | |||
{ | |||
descriptor = new TType(); | |||
realDescriptor = new TType(); | |||
descriptor = realDescriptor; | |||
} | |||
public static IEntityDescriptor descriptor { get; } | |||
public static TType realDescriptor { get; } | |||
public static Type type => typeof(TType); | |||
public static IEntityDescriptor descriptor { get; } | |||
} | |||
} |
@@ -6,23 +6,23 @@ namespace Svelto.ECS.Internal | |||
{ | |||
static class EntityFactory | |||
{ | |||
public static FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> BuildGroupedEntities(EGID egid, | |||
EnginesRoot.DoubleBufferedEntitiesToAdd groupEntitiesToAdd, IComponentBuilder[] componentsToBuild, | |||
IEnumerable<object> implementors) | |||
public static FasterDictionary<RefWrapperType, ITypeSafeDictionary> BuildGroupedEntities | |||
(EGID egid, EnginesRoot.DoubleBufferedEntitiesToAdd groupEntitiesToAdd | |||
, IComponentBuilder[] componentsToBuild, IEnumerable<object> implementors, Type implementorType) | |||
{ | |||
var group = FetchEntityGroup(egid.groupID, groupEntitiesToAdd); | |||
BuildEntitiesAndAddToGroup(egid, group, componentsToBuild, implementors); | |||
BuildEntitiesAndAddToGroup(egid, group, componentsToBuild, implementors, implementorType); | |||
return group; | |||
} | |||
static FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> FetchEntityGroup(uint groupID, | |||
static FasterDictionary<RefWrapperType, ITypeSafeDictionary> FetchEntityGroup(ExclusiveGroupStruct groupID, | |||
EnginesRoot.DoubleBufferedEntitiesToAdd groupEntityComponentsByType) | |||
{ | |||
if (groupEntityComponentsByType.current.TryGetValue(groupID, out var group) == false) | |||
{ | |||
group = new FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>(); | |||
group = new FasterDictionary<RefWrapperType, ITypeSafeDictionary>(); | |||
groupEntityComponentsByType.current.Add(groupID, group); | |||
} | |||
@@ -35,9 +35,9 @@ namespace Svelto.ECS.Internal | |||
return group; | |||
} | |||
static void BuildEntitiesAndAddToGroup(EGID entityID, | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> group, | |||
IComponentBuilder[] componentBuilders, IEnumerable<object> implementors) | |||
static void BuildEntitiesAndAddToGroup | |||
(EGID entityID, FasterDictionary<RefWrapperType, ITypeSafeDictionary> @group | |||
, IComponentBuilder[] componentBuilders, IEnumerable<object> implementors, Type implementorType) | |||
{ | |||
var count = componentBuilders.Length; | |||
@@ -49,7 +49,7 @@ namespace Svelto.ECS.Internal | |||
var entityComponentType = componentBuilders[index].GetEntityComponentType(); | |||
if (types.Contains(entityComponentType)) | |||
{ | |||
throw new ECSException("EntityBuilders must be unique inside an EntityDescriptor"); | |||
throw new ECSException($"EntityBuilders must be unique inside an EntityDescriptor. Descriptor Type {implementorType} Component Type: {entityComponentType}"); | |||
} | |||
types.Add(entityComponentType); | |||
@@ -58,17 +58,17 @@ namespace Svelto.ECS.Internal | |||
for (var index = 0; index < count; ++index) | |||
{ | |||
var entityComponentBuilder = componentBuilders[index]; | |||
var entityComponentType = entityComponentBuilder.GetEntityComponentType(); | |||
var entityComponentType = entityComponentBuilder.GetEntityComponentType(); | |||
BuildEntity(entityID, @group, entityComponentType, entityComponentBuilder, implementors); | |||
} | |||
} | |||
static void BuildEntity(EGID entityID, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> group, | |||
static void BuildEntity(EGID entityID, FasterDictionary<RefWrapperType, ITypeSafeDictionary> group, | |||
Type entityComponentType, IComponentBuilder componentBuilder, IEnumerable<object> implementors) | |||
{ | |||
var entityComponentsPoolWillBeCreated = | |||
group.TryGetValue(new RefWrapper<Type>(entityComponentType), out var safeDictionary) == false; | |||
group.TryGetValue(new RefWrapperType(entityComponentType), out var safeDictionary) == false; | |||
//passing the undefined entityComponentsByType inside the entityComponentBuilder will allow it to be created with the | |||
//correct type and casted back to the undefined list. that's how the list will be eventually of the target | |||
@@ -76,7 +76,7 @@ namespace Svelto.ECS.Internal | |||
componentBuilder.BuildEntityAndAddToList(ref safeDictionary, entityID, implementors); | |||
if (entityComponentsPoolWillBeCreated) | |||
group.Add(new RefWrapper<Type>(entityComponentType), safeDictionary); | |||
group.Add(new RefWrapperType(entityComponentType), safeDictionary); | |||
} | |||
} | |||
} |
@@ -4,8 +4,8 @@ namespace Svelto.ECS.Internal | |||
{ | |||
class EntityGroupNotFoundException : Exception | |||
{ | |||
public EntityGroupNotFoundException(Type type) | |||
: base("entity group not found ".FastConcat(type.ToString())) | |||
public EntityGroupNotFoundException(Type type, string toName) | |||
: base($"entity group {toName} not used for component type ".FastConcat(type.ToString())) | |||
{ | |||
} | |||
} |
@@ -1,11 +0,0 @@ | |||
namespace Svelto.ECS | |||
{ | |||
public struct EntityHierarchyStruct: IEntityComponent, INeedEGID | |||
{ | |||
public readonly ExclusiveGroupStruct parentGroup; | |||
public EntityHierarchyStruct(ExclusiveGroup @group): this() { parentGroup = group; } | |||
public EGID ID { get; set; } | |||
} | |||
} |
@@ -1,6 +1,6 @@ | |||
namespace Svelto.ECS | |||
{ | |||
struct EntityInfoViewComponent: IEntityComponent | |||
struct EntityInfoComponent: IEntityComponent | |||
{ | |||
public IComponentBuilder[] componentsToBuild; | |||
} |
@@ -1,205 +0,0 @@ | |||
using System; | |||
using System.Runtime.InteropServices; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
/// <summary> | |||
/// I eventually realised that, with the ECS design, no form of communication other than polling entity components can exist. | |||
/// Using groups, you can have always an optimal set of entity components to poll. However EntityStreams | |||
/// can be useful if: | |||
/// - you need to react on seldom entity changes, usually due to user events | |||
/// - you want engines to be able to track entity changes | |||
/// - you want a thread-safe way to read entity states, which includes all the state changes and not the last | |||
/// one only | |||
/// - you want to communicate between EnginesRoots | |||
/// </summary> | |||
class EntitiesStream : IDisposable | |||
{ | |||
internal Consumer<T> GenerateConsumer<T>(string name, uint capacity) | |||
where T : unmanaged, IEntityComponent | |||
{ | |||
if (_streams.ContainsKey(TypeRefWrapper<T>.wrapper) == false) | |||
_streams[TypeRefWrapper<T>.wrapper] = new EntityStream<T>(); | |||
return (_streams[TypeRefWrapper<T>.wrapper] as EntityStream<T>).GenerateConsumer(name, capacity); | |||
} | |||
public Consumer<T> GenerateConsumer<T>(ExclusiveGroupStruct group, string name, uint capacity) | |||
where T : unmanaged, IEntityComponent | |||
{ | |||
if (_streams.ContainsKey(TypeRefWrapper<T>.wrapper) == false) | |||
_streams[TypeRefWrapper<T>.wrapper] = new EntityStream<T>(); | |||
EntityStream<T> typeSafeStream = (EntityStream<T>) _streams[TypeRefWrapper<T>.wrapper]; | |||
return typeSafeStream.GenerateConsumer(group, name, capacity); | |||
} | |||
internal void PublishEntity<T>(ref T entity, EGID egid) where T : unmanaged, IEntityComponent | |||
{ | |||
if (_streams.TryGetValue(TypeRefWrapper<T>.wrapper, out var typeSafeStream)) | |||
(typeSafeStream as EntityStream<T>).PublishEntity(ref entity, egid); | |||
else | |||
Console.LogDebug("No Consumers are waiting for this entity to change ", typeof(T)); | |||
} | |||
readonly ThreadSafeDictionary<RefWrapper<Type>, ITypeSafeStream> _streams = | |||
new ThreadSafeDictionary<RefWrapper<Type>, ITypeSafeStream>(); | |||
public void Dispose() | |||
{ | |||
_streams.Clear(); | |||
} | |||
} | |||
interface ITypeSafeStream | |||
{ } | |||
public class EntityStream<T> : ITypeSafeStream where T : unmanaged, IEntityComponent | |||
{ | |||
~EntityStream() | |||
{ | |||
for (int i = 0; i < _consumers.Count; i++) | |||
_consumers[i].Free(); | |||
} | |||
internal EntityStream() | |||
{ | |||
_consumers = new ThreadSafeFasterList<Consumer<T>>(); | |||
} | |||
internal void PublishEntity(ref T entity, EGID egid) | |||
{ | |||
for (int i = 0; i < _consumers.Count; i++) | |||
{ | |||
unsafe | |||
{ | |||
if (*(bool *)_consumers[i].mustBeDisposed) | |||
{ | |||
_consumers[i].Free(); | |||
_consumers.UnorderedRemoveAt(i); | |||
--i; | |||
continue; | |||
} | |||
if (_consumers[i].hasGroup) | |||
{ | |||
if (egid.groupID == _consumers[i].@group) | |||
{ | |||
_consumers[i].Enqueue(entity, egid); | |||
} | |||
} | |||
else | |||
{ | |||
_consumers[i].Enqueue(entity, egid); | |||
} | |||
} | |||
} | |||
} | |||
internal Consumer<T> GenerateConsumer(string name, uint capacity) | |||
{ | |||
var consumer = new Consumer<T>(name, capacity); | |||
_consumers.Add(consumer); | |||
return consumer; | |||
} | |||
internal Consumer<T> GenerateConsumer(ExclusiveGroupStruct group, string name, uint capacity) | |||
{ | |||
var consumer = new Consumer<T>(group, name, capacity); | |||
_consumers.Add(consumer); | |||
return consumer; | |||
} | |||
readonly ThreadSafeFasterList<Consumer<T>> _consumers; | |||
} | |||
public struct Consumer<T> :IDisposable where T : unmanaged, IEntityComponent | |||
{ | |||
internal Consumer(string name, uint capacity) : this() | |||
{ | |||
unsafe | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
_name = name; | |||
#endif | |||
_ringBuffer = new RingBuffer<ValueTuple<T, EGID>>((int) capacity, | |||
#if DEBUG && !PROFILE_SVELTO | |||
_name | |||
#else | |||
string.Empty | |||
#endif | |||
); | |||
mustBeDisposed = Marshal.AllocHGlobal(sizeof(bool)); | |||
*((bool*) mustBeDisposed) = false; | |||
} | |||
} | |||
internal Consumer(ExclusiveGroupStruct group, string name, uint capacity) : this(name, capacity) | |||
{ | |||
this.@group = @group; | |||
hasGroup = true; | |||
} | |||
internal void Enqueue(in T entity, in EGID egid) | |||
{ | |||
_ringBuffer.Enqueue((entity, egid)); | |||
} | |||
public bool TryDequeue(out T entity) | |||
{ | |||
var tryDequeue = _ringBuffer.TryDequeue(out var values); | |||
entity = values.Item1; | |||
return tryDequeue; | |||
} | |||
public bool TryDequeue(out T entity, out EGID id) | |||
{ | |||
var tryDequeue = _ringBuffer.TryDequeue(out var values); | |||
entity = values.Item1; | |||
id = values.Item2; | |||
return tryDequeue; | |||
} | |||
public void Flush() | |||
{ | |||
_ringBuffer.Reset(); | |||
} | |||
public void Dispose() | |||
{ | |||
unsafe | |||
{ | |||
*(bool *)mustBeDisposed = true; | |||
} | |||
} | |||
public uint Count() | |||
{ | |||
return (uint) _ringBuffer.Count; | |||
} | |||
public void Free() | |||
{ | |||
Marshal.FreeHGlobal(mustBeDisposed); | |||
} | |||
readonly RingBuffer<ValueTuple<T, EGID>> _ringBuffer; | |||
internal readonly ExclusiveGroupStruct @group; | |||
internal readonly bool hasGroup; | |||
internal IntPtr mustBeDisposed; | |||
#if DEBUG && !PROFILE_SVELTO | |||
readonly string _name; | |||
#endif | |||
} | |||
} |
@@ -4,8 +4,18 @@ namespace Svelto.ECS.Schedulers | |||
{ | |||
public interface IEntitiesSubmissionScheduler: IDisposable | |||
{ | |||
EnginesRoot.EntitiesSubmitter onTick { set; } | |||
bool paused { get; set; } | |||
} | |||
public abstract class EntitiesSubmissionScheduler: IEntitiesSubmissionScheduler | |||
{ | |||
protected internal abstract EnginesRoot.EntitiesSubmitter onTick { set; } | |||
public abstract void Dispose(); | |||
public abstract bool paused { get; set; } | |||
} | |||
public abstract class ISimpleEntitiesSubmissionScheduler: EntitiesSubmissionScheduler | |||
{ | |||
public abstract void SubmitEntities(); | |||
} | |||
} |
@@ -8,7 +8,7 @@ namespace Svelto.ECS | |||
: IEquatable<EntitySubmitOperation> | |||
{ | |||
public readonly EntitySubmitOperationType type; | |||
public readonly IComponentBuilder[] builders; | |||
public readonly IComponentBuilder[] builders; | |||
public readonly EGID fromID; | |||
public readonly EGID toID; | |||
#if DEBUG && !PROFILE_SVELTO | |||
@@ -26,7 +26,19 @@ namespace Svelto.ECS | |||
trace = default; | |||
#endif | |||
} | |||
public EntitySubmitOperation | |||
(EntitySubmitOperationType operation, ExclusiveGroupStruct @group | |||
, IComponentBuilder[] descriptorComponentsToBuild):this() | |||
{ | |||
type = operation; | |||
this.builders = descriptorComponentsToBuild; | |||
fromID = new EGID(0, group); | |||
#if DEBUG && !PROFILE_SVELTO | |||
trace = default; | |||
#endif | |||
} | |||
public static bool operator ==(EntitySubmitOperation obj1, EntitySubmitOperation obj2) | |||
{ | |||
return obj1.Equals(obj2); | |||
@@ -46,7 +46,7 @@ namespace Svelto.ECS | |||
//efficient way to collect the fields of every EntityComponentType | |||
var setters = FasterList<KeyValuePair<Type, FastInvokeActionCast<T>>>.NoVirt.ToArrayFast( | |||
entityComponentBlazingFastReflection, out var count); | |||
//todo this should happen once per T, not once per Build<T> | |||
if (implementors != null) | |||
{ | |||
@@ -65,6 +65,7 @@ namespace Svelto.ECS | |||
return a._group + b; | |||
} | |||
//todo document the use case for this method | |||
public static ExclusiveGroupStruct Search(string holderGroupName) | |||
{ | |||
if (_knownGroups.ContainsKey(holderGroupName) == false) | |||
@@ -1,10 +1,34 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Diagnostics; | |||
using System.Runtime.InteropServices; | |||
namespace Svelto.ECS | |||
{ | |||
public readonly struct BuildGroup | |||
{ | |||
internal BuildGroup(ExclusiveGroupStruct group) | |||
{ | |||
this.group = group; | |||
} | |||
public static implicit operator BuildGroup(ExclusiveGroupStruct group) | |||
{ | |||
return new BuildGroup(group); | |||
} | |||
public static implicit operator BuildGroup(ExclusiveGroup group) | |||
{ | |||
return new BuildGroup(group); | |||
} | |||
public static implicit operator uint(BuildGroup groupStruct) | |||
{ | |||
return groupStruct.group; | |||
} | |||
internal ExclusiveGroupStruct @group { get; } | |||
} | |||
[StructLayout(LayoutKind.Explicit, Size = 4)] | |||
public struct ExclusiveGroupStruct : IEquatable<ExclusiveGroupStruct>, IComparable<ExclusiveGroupStruct>, | |||
IEqualityComparer<ExclusiveGroupStruct> | |||
@@ -65,7 +89,7 @@ namespace Svelto.ECS | |||
return groupStruct; | |||
} | |||
internal ExclusiveGroupStruct(ExclusiveGroupStruct @group):this() { this = group; } | |||
/// <summary> | |||
@@ -107,10 +131,9 @@ namespace Svelto.ECS | |||
return @group; | |||
} | |||
[FieldOffset(0)] uint _id; | |||
[FieldOffset(0)] uint _id; | |||
[FieldOffset(3)] byte _bytemask; | |||
static uint _globalId = 1; //it starts from 1 because default EGID is considered not initalized value | |||
static uint _globalId = 1; //it starts from 1 because default EGID is considered not initalized value | |||
} | |||
} |
@@ -8,7 +8,7 @@ namespace Svelto.ECS | |||
/// to swap and remove specialized entities from abstract engines | |||
/// </summary> | |||
/// <typeparam name="TType"></typeparam> | |||
public class ExtendibleEntityDescriptor<TType> : IEntityDescriptor where TType : IEntityDescriptor, new() | |||
public class ExtendibleEntityDescriptor<TType> : IDynamicEntityDescriptor where TType : IEntityDescriptor, new() | |||
{ | |||
static ExtendibleEntityDescriptor() | |||
{ | |||
@@ -4,8 +4,8 @@ namespace Svelto.ECS | |||
{ | |||
internal static class ProcessorCount | |||
{ | |||
static readonly int processorCount = Environment.ProcessorCount; | |||
public static readonly int processorCount = Environment.ProcessorCount; | |||
public static int BatchSize(uint totalIterations) | |||
{ | |||
var iterationsPerBatch = totalIterations / processorCount; | |||
@@ -1,3 +1,4 @@ | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
@@ -31,7 +32,7 @@ namespace Svelto.ECS | |||
{ | |||
public GroupsIterator(EntitiesDB db) : this() | |||
{ | |||
_db = db.FindGroups_INTERNAL<T1>().GetEnumerator(); | |||
_db = db.FindGroups_INTERNAL(TypeCache<T1>.type).GetEnumerator(); | |||
} | |||
public bool MoveNext() | |||
@@ -39,7 +40,7 @@ namespace Svelto.ECS | |||
//attention, the while is necessary to skip empty groups | |||
while (_db.MoveNext() == true) | |||
{ | |||
FasterDictionary<uint, ITypeSafeDictionary>.KeyValuePairFast group = _db.Current; | |||
FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>.KeyValuePairFast group = _db.Current; | |||
ITypeSafeDictionary<T1> typeSafeDictionary = @group.Value as ITypeSafeDictionary<T1>; | |||
if (typeSafeDictionary.count == 0) continue; | |||
@@ -55,7 +56,7 @@ namespace Svelto.ECS | |||
public GroupCollection Current => _array; | |||
FasterDictionary<uint, ITypeSafeDictionary>.FasterDictionaryKeyValueEnumerator _db; | |||
FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>.FasterDictionaryKeyValueEnumerator _db; | |||
GroupCollection _array; | |||
} | |||
@@ -66,63 +67,4 @@ namespace Svelto.ECS | |||
readonly EntitiesDB _db; | |||
} | |||
#if TO_BE_FINISHED | |||
public struct NativeAllGroupsEnumerable<T1, T2> | |||
where T1 : unmanaged, IEntityComponent where T2 : unmanaged, IEntityComponent | |||
{ | |||
public NativeAllGroupsEnumerable(EntitiesDB db) | |||
{ | |||
_db = db; | |||
} | |||
public struct NativeGroupsIterator | |||
{ | |||
public NativeGroupsIterator(EntitiesDB db) : this() | |||
{ | |||
_db = db.FindGroups<T1, T2>().GetEnumerator(); | |||
} | |||
public bool MoveNext() | |||
{ | |||
//attention, the while is necessary to skip empty groups | |||
while (_db.MoveNext() == true) | |||
{ | |||
FasterDictionary<uint, ITypeSafeDictionary>.KeyValuePairFast group = _db.Current; | |||
ITypeSafeDictionary<T1> typeSafeDictionary1 = @group.Value as ITypeSafeDictionary<T1>; | |||
ITypeSafeDictionary<T2> typeSafeDictionary2 = @group.Value as ITypeSafeDictionary<T2>; | |||
DBC.ECS.Check.Require(typeSafeDictionary1.Count != typeSafeDictionary2.Count | |||
, "entities count do not match"); | |||
if (typeSafeDictionary1.Count == 0) continue; | |||
_array = new BT<NB<T1>, NB<T2>>()(new EntityCollection<T1>(typeSafeDictionary1.GetValuesArray(out var count), count) | |||
.ToBuffer(); | |||
return true; | |||
} | |||
return false; | |||
} | |||
public void Reset() | |||
{ | |||
} | |||
public BT<NB<T1>, NB<T2>> Current => _array; | |||
FasterDictionary<uint, ITypeSafeDictionary>.FasterDictionaryKeyValueEnumerator _db; | |||
BT<NB<T1>, NB<T2>> _array; | |||
} | |||
public NativeGroupsIterator GetEnumerator() | |||
{ | |||
return new NativeGroupsIterator(_db); | |||
} | |||
readonly EntitiesDB _db; | |||
} | |||
#endif | |||
} |
@@ -8,32 +8,51 @@ namespace Svelto.ECS | |||
public static class EntityCollectionExtension | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Deconstruct<T1>(in this EntityCollection<T1> ec, out NB<T1> buffer, out int count) where T1 : unmanaged, IEntityComponent | |||
public static void Deconstruct<T1> | |||
(in this EntityCollection<T1> ec, out NB<T1> buffer, out int count) where T1 : unmanaged, IEntityComponent | |||
{ | |||
buffer = ec._nativedBuffer; | |||
count = (int) ec.count; | |||
count = (int) ec.count; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Deconstruct<T1, T2>(in this EntityCollection<T1, T2> ec, out NB<T1> buffer1, out NB<T2> buffer2, out int count) where T1 : unmanaged, IEntityComponent | |||
where T2 : unmanaged, IEntityComponent | |||
public static void Deconstruct<T1, T2> | |||
(in this EntityCollection<T1, T2> ec, out NB<T1> buffer1, out NB<T2> buffer2, out int count) | |||
where T1 : unmanaged, IEntityComponent where T2 : unmanaged, IEntityComponent | |||
{ | |||
buffer1 = ec.Item1._nativedBuffer; | |||
buffer2 = ec.Item2._nativedBuffer; | |||
count = (int) ec.count; | |||
buffer1 = ec.buffer1._nativedBuffer; | |||
buffer2 = ec.buffer2._nativedBuffer; | |||
count = (int) ec.count; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Deconstruct<T1, T2, T3> | |||
(in this EntityCollection<T1, T2, T3> ec, out NB<T1> buffer1, out NB<T2> buffer2, out NB<T3> buffer3 | |||
, out int count) where T1 : unmanaged, IEntityComponent | |||
where T2 : unmanaged, IEntityComponent | |||
where T3 : unmanaged, IEntityComponent | |||
{ | |||
buffer1 = ec.buffer1._nativedBuffer; | |||
buffer2 = ec.buffer2._nativedBuffer; | |||
buffer3 = ec.buffer3._nativedBuffer; | |||
count = (int) ec.count; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Deconstruct<T1, T2, T3>(in this EntityCollection<T1, T2, T3> ec, out NB<T1> buffer1, out NB<T2> buffer2, out NB<T3> buffer3, out int count) where T1 : unmanaged, IEntityComponent | |||
where T2 : unmanaged, IEntityComponent | |||
where T3 : unmanaged, IEntityComponent | |||
public static void Deconstruct<T1, T2, T3, T4> | |||
(in this EntityCollection<T1, T2, T3, T4> ec, out NB<T1> buffer1, out NB<T2> buffer2, out NB<T3> buffer3 | |||
, out NB<T4> buffer4, out int count) where T1 : unmanaged, IEntityComponent | |||
where T2 : unmanaged, IEntityComponent | |||
where T3 : unmanaged, IEntityComponent | |||
where T4 : unmanaged, IEntityComponent | |||
{ | |||
buffer1 = ec.Item1._nativedBuffer; | |||
buffer2 = ec.Item2._nativedBuffer; | |||
buffer3 = ec.Item3._nativedBuffer; | |||
buffer4 = ec.Item4._nativedBuffer; | |||
count = (int) ec.count; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static BT<NB<T1>> ToBuffer<T1>(in this EntityCollection<T1> ec) where T1 : unmanaged, IEntityComponent | |||
{ | |||
@@ -45,7 +64,7 @@ namespace Svelto.ECS | |||
(in this EntityCollection<T1, T2> ec) | |||
where T2 : unmanaged, IEntityComponent where T1 : unmanaged, IEntityComponent | |||
{ | |||
return new BT<NB<T1>, NB<T2>>(ec.Item1._nativedBuffer, ec.Item2._nativedBuffer, ec.count); | |||
return new BT<NB<T1>, NB<T2>>(ec.buffer1._nativedBuffer, ec.buffer2._nativedBuffer, ec.count); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
@@ -55,15 +74,16 @@ namespace Svelto.ECS | |||
where T1 : unmanaged, IEntityComponent | |||
where T3 : unmanaged, IEntityComponent | |||
{ | |||
return new BT<NB<T1>, NB<T2>, NB<T3>>(ec.Item1._nativedBuffer, ec.Item2._nativedBuffer | |||
, ec.Item3._nativedBuffer, ec.count); | |||
return new BT<NB<T1>, NB<T2>, NB<T3>>(ec.buffer1._nativedBuffer, ec.buffer2._nativedBuffer | |||
, ec.buffer3._nativedBuffer, ec.count); | |||
} | |||
} | |||
public static class EntityCollectionExtensionB | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Deconstruct<T1>(in this EntityCollection<T1> ec, out MB<T1> buffer, out int count) where T1 : struct, IEntityViewComponent | |||
public static void Deconstruct<T1> | |||
(in this EntityCollection<T1> ec, out MB<T1> buffer, out int count) where T1 : struct, IEntityViewComponent | |||
{ | |||
buffer = ec._managedBuffer; | |||
count = (int) ec.count; | |||
@@ -76,12 +96,13 @@ namespace Svelto.ECS | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Deconstruct<T1, T2>(in this EntityCollection<T1, T2> ec, out MB<T1> buffer1, out MB<T2> buffer2, out int count) where T1 : struct, IEntityViewComponent | |||
where T2 : struct, IEntityViewComponent | |||
public static void Deconstruct<T1, T2> | |||
(in this EntityCollection<T1, T2> ec, out MB<T1> buffer1, out MB<T2> buffer2, out int count) | |||
where T1 : struct, IEntityViewComponent where T2 : struct, IEntityViewComponent | |||
{ | |||
buffer1 = ec.Item1._managedBuffer; | |||
buffer2 = ec.Item2._managedBuffer; | |||
count = (int) ec.count; | |||
buffer1 = ec.buffer1._managedBuffer; | |||
buffer2 = ec.buffer2._managedBuffer; | |||
count = (int) ec.count; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
@@ -89,18 +110,20 @@ namespace Svelto.ECS | |||
(in this EntityCollection<T1, T2> ec) | |||
where T2 : struct, IEntityViewComponent where T1 : struct, IEntityViewComponent | |||
{ | |||
return (ec.Item1._managedBuffer, ec.Item2._managedBuffer, ec.count); | |||
return (ec.buffer1._managedBuffer, ec.buffer2._managedBuffer, ec.count); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Deconstruct<T1, T2, T3>(in this EntityCollection<T1, T2, T3> ec, out MB<T1> buffer1, out MB<T2> buffer2, out MB<T3> buffer3, out int count) where T1 : struct, IEntityViewComponent | |||
where T2 : struct, IEntityViewComponent | |||
where T3 : struct, IEntityViewComponent | |||
public static void Deconstruct<T1, T2, T3> | |||
(in this EntityCollection<T1, T2, T3> ec, out MB<T1> buffer1, out MB<T2> buffer2, out MB<T3> buffer3 | |||
, out int count) where T1 : struct, IEntityViewComponent | |||
where T2 : struct, IEntityViewComponent | |||
where T3 : struct, IEntityViewComponent | |||
{ | |||
buffer1 = ec.Item1._managedBuffer; | |||
buffer2 = ec.Item2._managedBuffer; | |||
buffer3 = ec.Item3._managedBuffer; | |||
count = (int) ec.count; | |||
buffer1 = ec.buffer1._managedBuffer; | |||
buffer2 = ec.buffer2._managedBuffer; | |||
buffer3 = ec.buffer3._managedBuffer; | |||
count = (int) ec.count; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
@@ -110,7 +133,7 @@ namespace Svelto.ECS | |||
where T1 : struct, IEntityViewComponent | |||
where T3 : struct, IEntityViewComponent | |||
{ | |||
return (ec.Item1._managedBuffer, ec.Item2._managedBuffer, ec.Item3._managedBuffer, ec.count); | |||
return (ec.buffer1._managedBuffer, ec.buffer2._managedBuffer, ec.buffer3._managedBuffer, ec.count); | |||
} | |||
} | |||
@@ -121,42 +144,73 @@ namespace Svelto.ECS | |||
(in this EntityCollection<T1, T2> ec) | |||
where T1 : unmanaged, IEntityComponent where T2 : struct, IEntityViewComponent | |||
{ | |||
return (ec.Item1._nativedBuffer, ec.Item2._managedBuffer, ec.count); | |||
return (ec.buffer1._nativedBuffer, ec.buffer2._managedBuffer, ec.count); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Deconstruct<T1, T2>(in this EntityCollection<T1, T2> ec, out NB<T1> buffer1, out MB<T2> buffer2, out int count) where T1 : unmanaged, IEntityComponent | |||
where T2 : struct, IEntityViewComponent | |||
public static (NB<T1> buffer1, MB<T2> buffer2, MB<T3> buffer3, uint count) ToBuffers<T1, T2, T3> | |||
(in this EntityCollection<T1, T2, T3> ec) | |||
where T1 : unmanaged, IEntityComponent | |||
where T2 : struct, IEntityViewComponent | |||
where T3 : struct, IEntityViewComponent | |||
{ | |||
buffer1 = ec.Item1._nativedBuffer; | |||
buffer2 = ec.Item2._managedBuffer; | |||
return (ec.buffer1._nativedBuffer, ec.buffer2._managedBuffer, ec.buffer3._managedBuffer, ec.count); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Deconstruct<T1, T2> | |||
(in this EntityCollection<T1, T2> ec, out NB<T1> buffer1, out MB<T2> buffer2, out int count) | |||
where T1 : unmanaged, IEntityComponent where T2 : struct, IEntityViewComponent | |||
{ | |||
buffer1 = ec.buffer1._nativedBuffer; | |||
buffer2 = ec.buffer2._managedBuffer; | |||
count = (int) ec.count; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static (NB<T1> buffer1, MB<T2> buffer2, MB<T3> buffer3, uint count) ToBuffers<T1, T2, T3> | |||
(in this EntityCollection<T1, T2, T3> ec) | |||
public static void Deconstruct<T1, T2, T3> | |||
(in this EntityCollection<T1, T2, T3> ec, out NB<T1> buffer1, out MB<T2> buffer2, out MB<T3> buffer3, out int count) | |||
where T1 : unmanaged, IEntityComponent | |||
where T2 : struct, IEntityViewComponent | |||
where T3 : struct, IEntityViewComponent | |||
{ | |||
return (ec.Item1._nativedBuffer, ec.Item2._managedBuffer, ec.Item3._managedBuffer, ec.count); | |||
buffer1 = ec.buffer1._nativedBuffer; | |||
buffer2 = ec.buffer2._managedBuffer; | |||
buffer3 = ec.buffer3._managedBuffer; | |||
count = (int) ec.count; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Deconstruct<T1, T2, T3, T4> | |||
(in this EntityCollection<T1, T2, T3, T4> ec, out NB<T1> buffer1, out NB<T2> buffer2, out NB<T3> buffer3 | |||
, out MB<T4> buffer4, out int count) where T1 : unmanaged, IEntityComponent | |||
where T2 : unmanaged, IEntityComponent | |||
where T3 : unmanaged, IEntityComponent | |||
where T4 : struct, IEntityViewComponent | |||
{ | |||
buffer1 = ec.Item1._nativedBuffer; | |||
buffer2 = ec.Item2._nativedBuffer; | |||
buffer3 = ec.Item3._nativedBuffer; | |||
buffer4 = ec.Item4._managedBuffer; | |||
count = (int) ec.count; | |||
} | |||
} | |||
public static class EntityCollectionExtensionD | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Deconstruct<T1, T2, T3>(in this EntityCollection<T1, T2, T3> ec, out NB<T1> buffer1, out NB<T2> buffer2, out MB<T3> buffer3, out int count) where T1 : unmanaged, IEntityComponent | |||
where T2 : unmanaged, IEntityComponent | |||
where T3 : struct, IEntityViewComponent | |||
public static void Deconstruct<T1, T2, T3> | |||
(in this EntityCollection<T1, T2, T3> ec, out NB<T1> buffer1, out NB<T2> buffer2, out MB<T3> buffer3 | |||
, out int count) where T1 : unmanaged, IEntityComponent | |||
where T2 : unmanaged, IEntityComponent | |||
where T3 : struct, IEntityViewComponent | |||
{ | |||
buffer1 = ec.Item1._nativedBuffer; | |||
buffer2 = ec.Item2._nativedBuffer; | |||
buffer3 = ec.Item3._managedBuffer; | |||
buffer1 = ec.buffer1._nativedBuffer; | |||
buffer2 = ec.buffer2._nativedBuffer; | |||
buffer3 = ec.buffer3._managedBuffer; | |||
count = (int) ec.count; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static (NB<T1> buffer1, NB<T2> buffer2, MB<T3> buffer3, uint count) ToBuffers<T1, T2, T3> | |||
(in this EntityCollection<T1, T2, T3> ec) | |||
@@ -164,18 +218,34 @@ namespace Svelto.ECS | |||
where T2 : unmanaged, IEntityComponent | |||
where T3 : struct, IEntityViewComponent | |||
{ | |||
return (ec.Item1._nativedBuffer, ec.Item2._nativedBuffer, ec.Item3._managedBuffer, ec.count); | |||
return (ec.buffer1._nativedBuffer, ec.buffer2._nativedBuffer, ec.buffer3._managedBuffer, ec.count); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static BT<NB<T1>, NB<T2>, NB<T3>, NB<T4> > ToBuffers<T1, T2, T3, T4> | |||
public static BT<NB<T1>, NB<T2>, NB<T3>, NB<T4>> ToBuffers<T1, T2, T3, T4> | |||
(in this EntityCollection<T1, T2, T3, T4> ec) | |||
where T2 : unmanaged, IEntityComponent | |||
where T1 : unmanaged, IEntityComponent | |||
where T3 : unmanaged, IEntityComponent | |||
where T4 : unmanaged, IEntityComponent | |||
{ | |||
return new BT<NB<T1>, NB<T2>, NB<T3>, NB<T4>>(ec.Item1._nativedBuffer, ec.Item2._nativedBuffer, ec.Item3._nativedBuffer, ec.Item4._nativedBuffer, ec.count); | |||
return new BT<NB<T1>, NB<T2>, NB<T3>, NB<T4>>(ec.Item1._nativedBuffer, ec.Item2._nativedBuffer | |||
, ec.Item3._nativedBuffer, ec.Item4._nativedBuffer, ec.count); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Deconstruct<T1, T2, T3, T4> | |||
(in this EntityCollection<T1, T2, T3, T4> ec, out NB<T1> buffer1, out NB<T2> buffer2, out MB<T3> buffer3 | |||
, out MB<T4> buffer4, out int count) where T1 : unmanaged, IEntityComponent | |||
where T2 : unmanaged, IEntityComponent | |||
where T3 : struct, IEntityViewComponent | |||
where T4 : struct, IEntityViewComponent | |||
{ | |||
buffer1 = ec.Item1._nativedBuffer; | |||
buffer2 = ec.Item2._nativedBuffer; | |||
buffer3 = ec.Item3._managedBuffer; | |||
buffer4 = ec.Item4._managedBuffer; | |||
count = (int) ec.count; | |||
} | |||
} | |||
} |
@@ -5,7 +5,7 @@ using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public static class EntityDBExtensionsB | |||
public static class EntityManagedDBExtensions | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static MB<T> QueryEntitiesAndIndex<T>(this EntitiesDB entitiesDb, EGID entityGID, out uint index) where T : struct, IEntityViewComponent | |||
@@ -65,5 +65,45 @@ namespace Svelto.ECS | |||
{ | |||
return ref entitiesDb.QueryEntity<T>(new EGID(id, group)); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static ref T QueryUniqueEntity<T>(this EntitiesDB entitiesDb, ExclusiveGroupStruct group) where T : struct, IEntityViewComponent | |||
{ | |||
var (entities, entitiescount) = entitiesDb.QueryEntities<T>(@group); | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (entitiescount == 0) | |||
throw new ECSException("Unique entity not found '".FastConcat(typeof(T).ToString()).FastConcat("'")); | |||
if (entitiescount != 1) | |||
throw new ECSException("Unique entities must be unique! '".FastConcat(typeof(T).ToString()) | |||
.FastConcat("'")); | |||
#endif | |||
return ref entities[0]; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static MB<T> GetArrayAndEntityIndex<T>(this EGIDMapper<T> mapper, uint entityID, out uint index) where T : struct, IEntityViewComponent | |||
{ | |||
if (mapper._map.TryFindIndex(entityID, out index)) | |||
{ | |||
return (MB<T>) mapper._map.GetValues(out _); | |||
} | |||
throw new ECSException("Entity not found"); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static bool TryGetArrayAndEntityIndex<T>(this EGIDMapper<T> mapper, uint entityID, out uint index, out MB<T> array) where T : struct, IEntityViewComponent | |||
{ | |||
index = default; | |||
if (mapper._map != null && mapper._map.TryFindIndex(entityID, out index)) | |||
{ | |||
array = (MB<T>) mapper._map.GetValues(out _); | |||
return true; | |||
} | |||
array = default; | |||
return false; | |||
} | |||
} | |||
} |
@@ -4,7 +4,7 @@ using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public static class EntityDBExtensions | |||
public static class EntityNativeDBExtensions | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static AllGroupsEnumerable<T1> QueryEntities<T1>(this EntitiesDB db) | |||
@@ -16,7 +16,7 @@ namespace Svelto.ECS | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static NB<T> QueryEntitiesAndIndex<T>(this EntitiesDB entitiesDb, EGID entityGID, out uint index) where T : unmanaged, IEntityComponent | |||
{ | |||
if (entitiesDb.QueryEntitiesAndIndexInternal<T>(entityGID, out index, out NB<T> array) == true) | |||
if (entitiesDb.QueryEntitiesAndIndexInternal(entityGID, out index, out NB<T> array) == true) | |||
return array; | |||
throw new EntityNotFoundException(entityGID, typeof(T)); | |||
@@ -26,7 +26,7 @@ namespace Svelto.ECS | |||
public static NB<T> QueryEntitiesAndIndex<T>(this EntitiesDB entitiesDb, uint id, ExclusiveGroupStruct group, out uint index) where T : unmanaged, IEntityComponent | |||
{ | |||
EGID entityGID = new EGID(id, group); | |||
if (entitiesDb.QueryEntitiesAndIndexInternal<T>(entityGID, out index, out NB<T> array) == true) | |||
if (entitiesDb.QueryEntitiesAndIndexInternal(entityGID, out index, out NB<T> array) == true) | |||
return array; | |||
throw new EntityNotFoundException(entityGID, typeof(T)); | |||
@@ -36,7 +36,7 @@ namespace Svelto.ECS | |||
public static bool TryQueryEntitiesAndIndex<T>(this EntitiesDB entitiesDb, EGID entityGID, out uint index, out NB<T> array) | |||
where T : unmanaged, IEntityComponent | |||
{ | |||
if (entitiesDb.QueryEntitiesAndIndexInternal<T>(entityGID, out index, out array) == true) | |||
if (entitiesDb.QueryEntitiesAndIndexInternal(entityGID, out index, out array) == true) | |||
return true; | |||
return false; | |||
@@ -46,7 +46,7 @@ namespace Svelto.ECS | |||
public static bool TryQueryEntitiesAndIndex<T>(this EntitiesDB entitiesDb, uint id, ExclusiveGroupStruct group, out uint index, out NB<T> array) | |||
where T : unmanaged, IEntityComponent | |||
{ | |||
if (entitiesDb.QueryEntitiesAndIndexInternal<T>(new EGID(id, group), out index, out array) == true) | |||
if (entitiesDb.QueryEntitiesAndIndexInternal(new EGID(id, group), out index, out array) == true) | |||
return true; | |||
return false; | |||
@@ -81,5 +81,45 @@ namespace Svelto.ECS | |||
{ | |||
return ref entitiesDb.QueryEntity<T>(new EGID(id, group)); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static ref T QueryUniqueEntity<T>(this EntitiesDB entitiesDb, ExclusiveGroupStruct group) where T : unmanaged, IEntityComponent | |||
{ | |||
var (entities, entitiescount) = entitiesDb.QueryEntities<T>(@group); | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (entitiescount == 0) | |||
throw new ECSException("Unique entity not found '".FastConcat(typeof(T).ToString()).FastConcat("'")); | |||
if (entitiescount != 1) | |||
throw new ECSException("Unique entities must be unique! '".FastConcat(typeof(T).ToString()) | |||
.FastConcat("'")); | |||
#endif | |||
return ref entities[0]; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static NB<T> GetArrayAndEntityIndex<T>(this EGIDMapper<T> mapper, uint entityID, out uint index) where T : unmanaged, IEntityComponent | |||
{ | |||
if (mapper._map.TryFindIndex(entityID, out index)) | |||
{ | |||
return (NB<T>) mapper._map.GetValues(out _); | |||
} | |||
throw new ECSException("Entity not found"); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static bool TryGetArrayAndEntityIndex<T>(this EGIDMapper<T> mapper, uint entityID, out uint index, out NB<T> array) where T : unmanaged, IEntityComponent | |||
{ | |||
index = default; | |||
if (mapper._map != null && mapper._map.TryFindIndex(entityID, out index)) | |||
{ | |||
array = (NB<T>) mapper._map.GetValues(out _); | |||
return true; | |||
} | |||
array = default; | |||
return false; | |||
} | |||
} | |||
} |
@@ -0,0 +1,38 @@ | |||
using System.Runtime.CompilerServices; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
public static class ExclusiveGroupExtensions | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static bool FoundIn(this in ExclusiveGroupStruct group, ExclusiveGroupStruct[] groups) | |||
{ | |||
for (int i = 0; i < groups.Length; ++i) | |||
if (groups[i] == group) | |||
return true; | |||
return false; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static bool FoundIn(this in ExclusiveGroupStruct group, FasterList<ExclusiveGroupStruct> groups) | |||
{ | |||
for (int i = 0; i < groups.count; ++i) | |||
if (groups[i] == group) | |||
return true; | |||
return false; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static bool FoundIn(this in ExclusiveGroupStruct group, LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) | |||
{ | |||
for (int i = 0; i < groups.count; ++i) | |||
if (groups[i] == group) | |||
return true; | |||
return false; | |||
} | |||
} | |||
} |
@@ -4,20 +4,20 @@ using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
/// <summary> | |||
/// NOTE THESE ENUMERABLES EXIST TO AVOID BOILERPLATE CODE AS THEY SKIP 0 SIZED GROUPS | |||
/// However if the normal pattern with the double foreach is used, this is not necessary | |||
/// Note: atm cannot be ref structs because they are returned in a valuetuple | |||
/// NOTE THESE ENUMERABLES EXIST TO AVOID BOILERPLATE CODE AS THEY SKIP 0 SIZED GROUPS | |||
/// However if the normal pattern with the double foreach is used, this is not necessary | |||
/// Note: atm cannot be ref structs because they are returned in a valuetuple | |||
/// </summary> | |||
/// <typeparam name="T1"></typeparam> | |||
/// <typeparam name="T2"></typeparam> | |||
/// <typeparam name="T3"></typeparam> | |||
/// <typeparam name="T4"></typeparam> | |||
public readonly ref struct GroupsEnumerable<T1, T2, T3, T4> where T1 : struct, IEntityComponent | |||
where T2 : struct, IEntityComponent | |||
where T3 : struct, IEntityComponent | |||
where T4 : struct, IEntityComponent | |||
where T2 : struct, IEntityComponent | |||
where T3 : struct, IEntityComponent | |||
where T4 : struct, IEntityComponent | |||
{ | |||
readonly EntitiesDB _db; | |||
readonly EntitiesDB _db; | |||
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups; | |||
public GroupsEnumerable(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) | |||
@@ -50,15 +50,14 @@ namespace Svelto.ECS | |||
Check.Assert(entityCollection1.count == entityCollection2.count | |||
, "congratulation, you found a bug in Svelto, please report it"); | |||
EntityCollection<T1, T2, T3> array = entityCollection1; | |||
var array = entityCollection1; | |||
var array2 = entityCollection2; | |||
_buffers = new EntityCollection<T1, T2, T3, T4>( | |||
array.Item1, array.Item2, array.Item3, array2); | |||
_buffers = new EntityCollection<T1, T2, T3, T4>(array.buffer1, array.buffer2, array.buffer3, array2); | |||
break; | |||
} | |||
var moveNext = _indexGroup < _groups.count; | |||
if (moveNext == false) | |||
Reset(); | |||
@@ -67,7 +66,7 @@ namespace Svelto.ECS | |||
public void Reset() { _indexGroup = -1; } | |||
public EntityCollection<T1, T2, T3, T4> Current => _buffers; | |||
public RefCurrent<T1, T2, T3, T4> Current => new RefCurrent<T1, T2, T3, T4>(_buffers, _groups[_indexGroup]); | |||
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups; | |||
@@ -79,17 +78,38 @@ namespace Svelto.ECS | |||
public GroupsIterator GetEnumerator() { return new GroupsIterator(_db, _groups); } | |||
} | |||
public ref struct RefCurrent<T1, T2, T3, T4> where T1 : struct, IEntityComponent | |||
where T2 : struct, IEntityComponent | |||
where T3 : struct, IEntityComponent | |||
where T4 : struct, IEntityComponent | |||
{ | |||
public RefCurrent(in EntityCollection<T1, T2, T3, T4> buffers, ExclusiveGroupStruct group) | |||
{ | |||
_buffers = buffers; | |||
_group = group; | |||
} | |||
public void Deconstruct(out EntityCollection<T1, T2, T3, T4> buffers, out ExclusiveGroupStruct group) | |||
{ | |||
buffers = _buffers; | |||
group = _group; | |||
} | |||
public readonly EntityCollection<T1, T2, T3, T4> _buffers; | |||
public readonly ExclusiveGroupStruct _group; | |||
} | |||
/// <summary> | |||
/// ToDo source gen could return the implementation of IBuffer directly, but cannot be done manually | |||
/// ToDo source gen could return the implementation of IBuffer directly, but cannot be done manually | |||
/// </summary> | |||
/// <typeparam name="T1"></typeparam> | |||
/// <typeparam name="T2"></typeparam> | |||
/// <typeparam name="T3"></typeparam> | |||
public readonly ref struct GroupsEnumerable<T1, T2, T3> where T1 : struct, IEntityComponent | |||
where T2 : struct, IEntityComponent | |||
where T3 : struct, IEntityComponent | |||
where T2 : struct, IEntityComponent | |||
where T3 : struct, IEntityComponent | |||
{ | |||
readonly EntitiesDB _db; | |||
readonly EntitiesDB _db; | |||
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups; | |||
public GroupsEnumerable(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) | |||
@@ -112,16 +132,16 @@ namespace Svelto.ECS | |||
//attention, the while is necessary to skip empty groups | |||
while (++_indexGroup < _groups.count) | |||
{ | |||
EntityCollection<T1, T2, T3> entityCollection = _entitiesDB.QueryEntities<T1, T2, T3>(_groups[_indexGroup]); | |||
var entityCollection = _entitiesDB.QueryEntities<T1, T2, T3>(_groups[_indexGroup]); | |||
if (entityCollection.count == 0) | |||
continue; | |||
_buffers = entityCollection; | |||
break; | |||
} | |||
var moveNext = _indexGroup < _groups.count; | |||
if (moveNext == false) | |||
Reset(); | |||
@@ -130,19 +150,40 @@ namespace Svelto.ECS | |||
public void Reset() { _indexGroup = -1; } | |||
public EntityCollection<T1, T2, T3> Current => _buffers; | |||
public RefCurrent<T1, T2, T3> Current => new RefCurrent<T1, T2, T3>(_buffers, _groups[_indexGroup]); | |||
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups; | |||
int _indexGroup; | |||
int _indexGroup; | |||
EntityCollection<T1, T2, T3> _buffers; | |||
readonly EntitiesDB _entitiesDB; | |||
readonly EntitiesDB _entitiesDB; | |||
} | |||
public GroupsIterator GetEnumerator() { return new GroupsIterator(_db, _groups); } | |||
} | |||
public readonly ref struct GroupsEnumerable<T1, T2> where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent | |||
public ref struct RefCurrent<T1, T2, T3> where T1 : struct, IEntityComponent | |||
where T2 : struct, IEntityComponent | |||
where T3 : struct, IEntityComponent | |||
{ | |||
public RefCurrent(in EntityCollection<T1, T2, T3> buffers, ExclusiveGroupStruct group) | |||
{ | |||
_buffers = buffers; | |||
_group = group; | |||
} | |||
public void Deconstruct(out EntityCollection<T1, T2, T3> buffers, out ExclusiveGroupStruct group) | |||
{ | |||
buffers = _buffers; | |||
group = _group; | |||
} | |||
public readonly EntityCollection<T1, T2, T3> _buffers; | |||
public readonly ExclusiveGroupStruct _group; | |||
} | |||
public readonly ref struct GroupsEnumerable<T1, T2> | |||
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent | |||
{ | |||
public GroupsEnumerable(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) | |||
{ | |||
@@ -173,30 +214,48 @@ namespace Svelto.ECS | |||
} | |||
var moveNext = _indexGroup < _groups.count; | |||
if (moveNext == false) | |||
Reset(); | |||
return moveNext; | |||
} | |||
public void Reset() { _indexGroup = -1; } | |||
public EntityCollection<T1, T2> Current => _buffers; | |||
public RefCurrent<T1, T2> Current => new RefCurrent<T1, T2>(_buffers, _groups[_indexGroup]); | |||
readonly EntitiesDB _db; | |||
readonly EntitiesDB _db; | |||
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups; | |||
int _indexGroup; | |||
int _indexGroup; | |||
EntityCollection<T1, T2> _buffers; | |||
} | |||
public GroupsIterator GetEnumerator() { return new GroupsIterator(_db, _groups); } | |||
readonly EntitiesDB _db; | |||
readonly EntitiesDB _db; | |||
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups; | |||
} | |||
public ref struct RefCurrent<T1, T2> where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent | |||
{ | |||
public RefCurrent(in EntityCollection<T1, T2> buffers, ExclusiveGroupStruct group) | |||
{ | |||
_buffers = buffers; | |||
_group = group; | |||
} | |||
public void Deconstruct(out EntityCollection<T1, T2> buffers, out ExclusiveGroupStruct group) | |||
{ | |||
buffers = _buffers; | |||
group = _group; | |||
} | |||
public readonly EntityCollection<T1, T2> _buffers; | |||
public readonly ExclusiveGroupStruct _group; | |||
} | |||
public readonly ref struct GroupsEnumerable<T1> where T1 : struct, IEntityComponent | |||
{ | |||
public GroupsEnumerable(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) | |||
@@ -232,18 +291,36 @@ namespace Svelto.ECS | |||
public void Reset() { _indexGroup = -1; } | |||
public EntityCollection<T1> Current => _buffer; | |||
public RefCurrent<T1> Current => new RefCurrent<T1>(_buffer, _groups[_indexGroup]); | |||
readonly EntitiesDB _db; | |||
readonly EntitiesDB _db; | |||
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups; | |||
int _indexGroup; | |||
int _indexGroup; | |||
EntityCollection<T1> _buffer; | |||
} | |||
public GroupsIterator GetEnumerator() { return new GroupsIterator(_db, _groups); } | |||
readonly EntitiesDB _db; | |||
readonly EntitiesDB _db; | |||
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups; | |||
} | |||
public ref struct RefCurrent<T1> where T1 : struct, IEntityComponent | |||
{ | |||
public RefCurrent(in EntityCollection<T1> buffers, ExclusiveGroupStruct group) | |||
{ | |||
_buffers = buffers; | |||
_group = group; | |||
} | |||
public void Deconstruct(out EntityCollection<T1> buffers, out ExclusiveGroupStruct group) | |||
{ | |||
buffers = _buffers; | |||
group = _group; | |||
} | |||
public readonly EntityCollection<T1> _buffers; | |||
public readonly ExclusiveGroupStruct _group; | |||
} | |||
} |
@@ -7,35 +7,37 @@ namespace Svelto.ECS.Extensions.Unity | |||
{ | |||
public interface IJobifiedEngine : IEngine | |||
{ | |||
JobHandle Execute(JobHandle _jobHandle); | |||
JobHandle Execute(JobHandle inputDeps); | |||
string name { get; } | |||
} | |||
public interface IJobifiedGroupEngine : IJobifiedEngine | |||
{ } | |||
public interface IJobifiedEngine<T> : IEngine | |||
{ | |||
JobHandle Execute(JobHandle _jobHandle, ref T _param); | |||
JobHandle Execute(JobHandle inputDeps, ref T _param); | |||
string name { get; } | |||
} | |||
public interface IJobifiedGroupEngine<T> : IJobifiedEngine<T> | |||
{ | |||
} | |||
{ } | |||
/// <summary> | |||
/// Note unsorted jobs run in parallel | |||
/// </summary> | |||
/// <typeparam name="Interface"></typeparam> | |||
public abstract class JobifedEnginesGroup<Interface> : IJobifiedGroupEngine where Interface : class, IJobifiedEngine | |||
public abstract class JobifiedEnginesGroup<Interface>:IJobifiedEngine where Interface : class, IJobifiedEngine | |||
{ | |||
protected JobifedEnginesGroup(FasterList<Interface> engines) | |||
protected JobifiedEnginesGroup(FasterList<Interface> engines) | |||
{ | |||
_name = "JobifiedEnginesGroup - "+this.GetType().Name; | |||
_engines = engines; | |||
} | |||
protected JobifiedEnginesGroup() | |||
{ | |||
_name = "JobifiedEnginesGroup - "+this.GetType().Name; | |||
_engines = new FasterList<Interface>(); | |||
} | |||
public JobHandle Execute(JobHandle inputHandles) | |||
{ | |||
@@ -48,7 +50,7 @@ namespace Svelto.ECS.Extensions.Unity | |||
ref var engine = ref engines[index]; | |||
using (profiler.Sample(engine.name)) | |||
{ | |||
combinedHandles = JobHandle.CombineDependencies(combinedHandles, engine.Execute(inputHandles)); | |||
combinedHandles = engine.Execute(inputHandles); | |||
} | |||
} | |||
} | |||
@@ -56,16 +58,20 @@ namespace Svelto.ECS.Extensions.Unity | |||
return combinedHandles; | |||
} | |||
protected internal void Add(Interface engine) | |||
{ | |||
_engines.Add(engine); | |||
} | |||
public string name => _name; | |||
readonly FasterReadOnlyList<Interface> _engines; | |||
readonly bool _completeEachJob; | |||
readonly string _name; | |||
protected readonly FasterList<Interface> _engines; | |||
readonly string _name; | |||
} | |||
public abstract class JobifedEnginesGroup<Interface, Param>: IJobifiedGroupEngine<Param> where Interface : class, IJobifiedEngine<Param> | |||
public abstract class JobifiedEnginesGroup<Interface, Param>: IJobifiedGroupEngine<Param> where Interface : class, IJobifiedEngine<Param> | |||
{ | |||
protected JobifedEnginesGroup(FasterList<Interface> engines) | |||
protected JobifiedEnginesGroup(FasterList<Interface> engines) | |||
{ | |||
_name = "JobifiedEnginesGroup - "+this.GetType().Name; | |||
_engines = engines; | |||
@@ -79,8 +85,7 @@ namespace Svelto.ECS.Extensions.Unity | |||
for (var index = 0; index < engines.count; index++) | |||
{ | |||
var engine = engines[index]; | |||
using (profiler.Sample(engine.name)) combinedHandles = | |||
JobHandle.CombineDependencies(combinedHandles, engine.Execute(combinedHandles, ref _param)); | |||
using (profiler.Sample(engine.name)) combinedHandles = engine.Execute(combinedHandles, ref _param); | |||
} | |||
} | |||
@@ -7,15 +7,15 @@ namespace Svelto.ECS.Extensions.Unity | |||
{ | |||
/// <summary> | |||
/// Note sorted jobs run in serial | |||
/// </summary> | |||
/// </summary> | |||
/// <typeparam name="Interface"></typeparam> | |||
/// <typeparam name="SequenceOrder"></typeparam> | |||
public abstract class SortedJobifedEnginesGroup<Interface, SequenceOrder> : IJobifiedGroupEngine | |||
public abstract class SortedJobifiedEnginesGroup<Interface, SequenceOrder> | |||
where SequenceOrder : struct, ISequenceOrder where Interface : class, IJobifiedEngine | |||
{ | |||
protected SortedJobifedEnginesGroup(FasterList<Interface> engines) | |||
protected SortedJobifiedEnginesGroup(FasterList<Interface> engines) | |||
{ | |||
_name = "SortedJobifedEnginesGroup - "+this.GetType().Name; | |||
_name = "SortedJobifiedEnginesGroup - "+this.GetType().Name; | |||
_instancedSequence = new Sequence<Interface, SequenceOrder>(engines); | |||
} | |||
@@ -28,7 +28,7 @@ namespace Svelto.ECS.Extensions.Unity | |||
for (var index = 0; index < sequenceItems.count; index++) | |||
{ | |||
var engine = sequenceItems[index]; | |||
using (profiler.Sample(engine.name)) combinedHandles = JobHandle.CombineDependencies(combinedHandles, engine.Execute(combinedHandles)); | |||
using (profiler.Sample(engine.name)) combinedHandles = engine.Execute(combinedHandles); | |||
} | |||
} | |||
@@ -41,12 +41,12 @@ namespace Svelto.ECS.Extensions.Unity | |||
readonly Sequence<Interface, SequenceOrder> _instancedSequence; | |||
} | |||
public abstract class SortedJobifedEnginesGroup<Interface, Parameter, SequenceOrder>: IJobifiedGroupEngine<Parameter> | |||
public abstract class SortedJobifiedEnginesGroup<Interface, Parameter, SequenceOrder>: IJobifiedGroupEngine<Parameter> | |||
where SequenceOrder : struct, ISequenceOrder where Interface : class, IJobifiedEngine<Parameter> | |||
{ | |||
protected SortedJobifedEnginesGroup(FasterList<Interface> engines) | |||
protected SortedJobifiedEnginesGroup(FasterList<Interface> engines) | |||
{ | |||
_name = "SortedJobifedEnginesGroup - "+this.GetType().Name; | |||
_name = "SortedJobifiedEnginesGroup - "+this.GetType().Name; | |||
_instancedSequence = new Sequence<Interface, SequenceOrder>(engines); | |||
} | |||
@@ -58,8 +58,7 @@ namespace Svelto.ECS.Extensions.Unity | |||
for (var index = 0; index < sequenceItems.count; index++) | |||
{ | |||
var engine = sequenceItems[index]; | |||
using (profiler.Sample(engine.name)) combinedHandles = | |||
JobHandle.CombineDependencies(combinedHandles, engine.Execute(combinedHandles, ref param)); | |||
using (profiler.Sample(engine.name)) combinedHandles = engine.Execute(combinedHandles, ref param); | |||
} | |||
} | |||
@@ -0,0 +1,68 @@ | |||
#if UNITY_NATIVE | |||
using System; | |||
using Svelto.ECS.Extensions.Unity; | |||
using Unity.Jobs; | |||
namespace Svelto.ECS | |||
{ | |||
#if UNITY_JOBS | |||
public static class UnityJobExtensions | |||
{ | |||
public static JobHandle ScheduleParallel | |||
<JOB>(this JOB job, uint iterations, JobHandle inputDeps) where JOB: struct, IJobParallelForBatch | |||
{ | |||
if (iterations == 0) | |||
return inputDeps; | |||
var innerloopBatchCount = ProcessorCount.BatchSize((uint) iterations); | |||
return job.ScheduleBatch((int)iterations, innerloopBatchCount, inputDeps); | |||
} | |||
public static JobHandle ScheduleParallel | |||
<JOB>(this JOB job, int iterations, JobHandle inputDeps) where JOB: struct, IJobParallelForBatch | |||
{ | |||
if (iterations <= 0) | |||
return inputDeps; | |||
var innerloopBatchCount = ProcessorCount.BatchSize((uint) iterations); | |||
return job.ScheduleBatch((int)iterations, innerloopBatchCount, inputDeps); | |||
} | |||
} | |||
#endif | |||
public static class UnityJobExtensions2 | |||
{ | |||
public static JobHandle ScheduleDispose | |||
<T1>(this T1 disposable, JobHandle inputDeps) where T1 : struct, IDisposable | |||
{ | |||
return new DisposeJob<T1>(disposable).Schedule(inputDeps); | |||
} | |||
public static JobHandle ScheduleDispose | |||
<T1, T2>(this T1 disposable1, T2 disposable2, JobHandle inputDeps) | |||
where T1 : struct, IDisposable where T2 : struct, IDisposable | |||
{ | |||
return new DisposeJob<T1, T2>(disposable1, disposable2).Schedule(inputDeps); | |||
} | |||
public static JobHandle ScheduleParallel | |||
<JOB>(this JOB job, int iterations, JobHandle inputDeps) where JOB: struct, IJobParallelFor | |||
{ | |||
if (iterations <= 0) | |||
return inputDeps; | |||
var innerloopBatchCount = ProcessorCount.BatchSize((uint) iterations); | |||
return job.Schedule((int)iterations, innerloopBatchCount, inputDeps); | |||
} | |||
public static JobHandle ScheduleParallel | |||
<JOB>(this JOB job, uint iterations, JobHandle inputDeps) where JOB: struct, IJobParallelFor | |||
{ | |||
if (iterations == 0) | |||
return inputDeps; | |||
var innerloopBatchCount = ProcessorCount.BatchSize(iterations); | |||
return job.Schedule((int)iterations, innerloopBatchCount, inputDeps); | |||
} | |||
} | |||
} | |||
#endif |
@@ -1,9 +1,8 @@ | |||
#if UNITY_BURST | |||
#if UNITY_NATIVE | |||
using System; | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.DataStructures.Unity; | |||
using Unity.Jobs.LowLevel.Unsafe; | |||
using Svelto.ECS.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
@@ -11,13 +10,13 @@ namespace Svelto.ECS | |||
{ | |||
//todo: I very likely don't need to create one for each native entity factory, the same can be reused | |||
readonly AtomicNativeBags _addOperationQueue = | |||
new AtomicNativeBags(Common.Allocator.Persistent, JobsUtility.MaxJobThreadCount + 1); | |||
new AtomicNativeBags(Common.Allocator.Persistent); | |||
readonly AtomicNativeBags _removeOperationQueue = | |||
new AtomicNativeBags(Common.Allocator.Persistent, JobsUtility.MaxJobThreadCount + 1); | |||
new AtomicNativeBags(Common.Allocator.Persistent); | |||
readonly AtomicNativeBags _swapOperationQueue = | |||
new AtomicNativeBags(Common.Allocator.Persistent, JobsUtility.MaxJobThreadCount + 1); | |||
new AtomicNativeBags(Common.Allocator.Persistent); | |||
NativeEntityRemove ProvideNativeEntityRemoveQueue<T>(string memberName) where T : IEntityDescriptor, new() | |||
{ | |||
@@ -57,12 +56,13 @@ namespace Svelto.ECS | |||
while (buffer.IsEmpty() == false) | |||
{ | |||
var componentsIndex = buffer.Dequeue<uint>(); | |||
var entityEGID = buffer.Dequeue<EGID>(); | |||
CheckRemoveEntityID(entityEGID, _nativeRemoveOperations[componentsIndex].type); | |||
var componentsIndex = buffer.Dequeue<uint>(); | |||
var entityEGID = buffer.Dequeue<EGID>(); | |||
var nativeRemoveOperation = _nativeRemoveOperations[componentsIndex]; | |||
CheckRemoveEntityID(entityEGID, nativeRemoveOperation.entityDescriptorType); | |||
QueueEntitySubmitOperation(new EntitySubmitOperation( | |||
EntitySubmitOperationType.Remove, entityEGID, entityEGID | |||
, _nativeRemoveOperations[componentsIndex].components)); | |||
, nativeRemoveOperation.components)); | |||
} | |||
} | |||
@@ -75,12 +75,14 @@ namespace Svelto.ECS | |||
var componentsIndex = buffer.Dequeue<uint>(); | |||
var entityEGID = buffer.Dequeue<DoubleEGID>(); | |||
CheckRemoveEntityID(entityEGID.@from, _nativeSwapOperations[componentsIndex].type, _nativeSwapOperations[componentsIndex].caller ); | |||
CheckAddEntityID(entityEGID.to, _nativeSwapOperations[componentsIndex].type, _nativeSwapOperations[componentsIndex].caller); | |||
var componentBuilders = _nativeSwapOperations[componentsIndex].components; | |||
CheckRemoveEntityID(entityEGID.@from, _nativeSwapOperations[componentsIndex].entityDescriptorType, _nativeSwapOperations[componentsIndex].caller ); | |||
CheckAddEntityID(entityEGID.to, _nativeSwapOperations[componentsIndex].entityDescriptorType, _nativeSwapOperations[componentsIndex].caller); | |||
QueueEntitySubmitOperation(new EntitySubmitOperation( | |||
EntitySubmitOperationType.Swap, entityEGID.@from, entityEGID.to | |||
, _nativeSwapOperations[componentsIndex].components)); | |||
, componentBuilders)); | |||
} | |||
} | |||
} | |||
@@ -98,7 +100,7 @@ namespace Svelto.ECS | |||
var componentCounts = buffer.Dequeue<uint>(); | |||
EntityComponentInitializer init = | |||
BuildEntity(egid, _nativeAddOperations[componentsIndex].components, _nativeAddOperations[componentsIndex].type); | |||
BuildEntity(egid, _nativeAddOperations[componentsIndex].components, _nativeAddOperations[componentsIndex].entityDescriptorType); | |||
//only called if Init is called on the initialized (there is something to init) | |||
while (componentCounts > 0) | |||
@@ -144,11 +146,11 @@ namespace Svelto.ECS | |||
readonly struct NativeOperationBuild | |||
{ | |||
internal readonly IComponentBuilder[] components; | |||
internal readonly Type type; | |||
internal readonly Type entityDescriptorType; | |||
public NativeOperationBuild(IComponentBuilder[] descriptorComponentsToBuild, Type entityType) | |||
public NativeOperationBuild(IComponentBuilder[] descriptorComponentsToBuild, Type entityDescriptorType) | |||
{ | |||
type = entityType; | |||
this.entityDescriptorType = entityDescriptorType; | |||
components = descriptorComponentsToBuild; | |||
} | |||
} | |||
@@ -156,28 +158,28 @@ namespace Svelto.ECS | |||
readonly struct NativeOperationRemove | |||
{ | |||
internal readonly IComponentBuilder[] components; | |||
internal readonly Type type; | |||
internal readonly Type entityDescriptorType; | |||
internal readonly string caller; | |||
public NativeOperationRemove(IComponentBuilder[] descriptorComponentsToRemove, Type entityType, string caller) | |||
public NativeOperationRemove(IComponentBuilder[] descriptorComponentsToRemove, Type entityDescriptorType, string caller) | |||
{ | |||
this.caller = caller; | |||
components = descriptorComponentsToRemove; | |||
type = entityType; | |||
this.caller = caller; | |||
components = descriptorComponentsToRemove; | |||
this.entityDescriptorType = entityDescriptorType; | |||
} | |||
} | |||
readonly struct NativeOperationSwap | |||
{ | |||
internal readonly IComponentBuilder[] components; | |||
internal readonly Type type; | |||
internal readonly Type entityDescriptorType; | |||
internal readonly string caller; | |||
public NativeOperationSwap(IComponentBuilder[] descriptorComponentsToSwap, Type entityType, string caller) | |||
public NativeOperationSwap(IComponentBuilder[] descriptorComponentsToSwap, Type entityDescriptorType, string caller) | |||
{ | |||
this.caller = caller; | |||
components = descriptorComponentsToSwap; | |||
type = entityType; | |||
this.caller = caller; | |||
components = descriptorComponentsToSwap; | |||
this.entityDescriptorType = entityDescriptorType; | |||
} | |||
} | |||
} |
@@ -0,0 +1,97 @@ | |||
#if UNITY_NATIVE | |||
using System; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
public readonly struct NativeEGIDMapper<T>:IEGIDMapper where T : unmanaged, IEntityComponent | |||
{ | |||
public NativeEGIDMapper | |||
(ExclusiveGroupStruct groupStructId, SveltoDictionaryNative<uint, T> toNative) : this() | |||
{ | |||
groupID = groupStructId; | |||
_map = toNative; | |||
} | |||
public int count => _map.count; | |||
public Type entityType => TypeCache<T>.type; | |||
public ExclusiveGroupStruct groupID { get; } | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref T Entity(uint entityID) | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_map.TryFindIndex(entityID, out var findIndex) == false) | |||
throw new Exception("Entity not found in this group ".FastConcat(typeof(T).ToString())); | |||
#else | |||
_map.TryFindIndex(entityID, out var findIndex); | |||
#endif | |||
return ref _map.GetDirectValueByRef(findIndex); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool TryGetEntity(uint entityID, out T value) | |||
{ | |||
if (_map.count > 0 && _map.TryFindIndex(entityID, out var index)) | |||
{ | |||
unsafe | |||
{ | |||
value = Unsafe.AsRef<T>(Unsafe.Add<T>((void*) _map.GetValues(out _).ToNativeArray(out _) | |||
, (int) index)); | |||
return true; | |||
} | |||
} | |||
value = default; | |||
return false; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public NB<T> GetArrayAndEntityIndex(uint entityID, out uint index) | |||
{ | |||
if (_map.TryFindIndex(entityID, out index)) | |||
{ | |||
return new NB<T>(_map.GetValues(out var count).ToNativeArray(out _), count); | |||
} | |||
throw new ECSException("Entity not found"); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool TryGetArrayAndEntityIndex(uint entityID, out uint index, out NB<T> array) | |||
{ | |||
index = 0; | |||
if (_map.count > 0 && _map.TryFindIndex(entityID, out index)) | |||
{ | |||
array = new NB<T>(_map.GetValues(out var count).ToNativeArray(out _), count); | |||
return true; | |||
} | |||
array = default; | |||
return false; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool Exists(uint idEntityId) | |||
{ | |||
return _map.count > 0 && _map.TryFindIndex(idEntityId, out _); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public uint GetIndex(uint entityID) | |||
{ | |||
return _map.GetIndex(entityID); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool FindIndex(uint valueKey, out uint index) | |||
{ | |||
return _map.TryFindIndex(valueKey, out index); | |||
} | |||
readonly ReadonlySveltoDictionaryNative<uint, T> _map; | |||
} | |||
} | |||
#endif |
@@ -0,0 +1,55 @@ | |||
#if UNITY_NATIVE | |||
using System; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
public struct NativeEGIDMultiMapper<T>:IDisposable where T : unmanaged, IEntityComponent | |||
{ | |||
SveltoDictionary<ExclusiveGroupStruct, SveltoDictionary<uint, T, | |||
NativeStrategy<FasterDictionaryNode<uint>>, | |||
NativeStrategy<T>, | |||
NativeStrategy<int>>, | |||
NativeStrategy<FasterDictionaryNode<ExclusiveGroupStruct>>, | |||
NativeStrategy<SveltoDictionary<uint, T, | |||
NativeStrategy<FasterDictionaryNode<uint>>, | |||
NativeStrategy<T>, | |||
NativeStrategy<int>>>, | |||
NativeStrategy<int>> _dic; | |||
public NativeEGIDMultiMapper | |||
(SveltoDictionary<ExclusiveGroupStruct, SveltoDictionary<uint, T, | |||
NativeStrategy<FasterDictionaryNode<uint>>, | |||
NativeStrategy<T>, | |||
NativeStrategy<int>>, | |||
NativeStrategy<FasterDictionaryNode<ExclusiveGroupStruct>>, | |||
NativeStrategy<SveltoDictionary<uint, T, | |||
NativeStrategy<FasterDictionaryNode<uint>>, | |||
NativeStrategy<T>, | |||
NativeStrategy<int>>>, | |||
NativeStrategy<int>> dictionary) | |||
{ | |||
_dic = dictionary; | |||
} | |||
public int count => (int) _dic.count; | |||
public void Dispose() | |||
{ | |||
_dic.Dispose(); | |||
} | |||
public ref T Entity(EGID entity) | |||
{ | |||
ref var sveltoDictionary = ref _dic.GetValueByRef(entity.groupID); | |||
return ref sveltoDictionary.GetValueByRef(entity.entityID); | |||
} | |||
public bool Exists(EGID entity) | |||
{ | |||
return _dic.TryFindIndex(entity.groupID, out var index) | |||
&& _dic.GetDirectValueByRef(index).ContainsKey(entity.entityID); | |||
} | |||
} | |||
} | |||
#endif |
@@ -1,27 +1,26 @@ | |||
#if UNITY_BURST | |||
#if UNITY_NATIVE | |||
using Svelto.ECS.DataStructures; | |||
using Svelto.ECS.DataStructures.Unity; | |||
namespace Svelto.ECS | |||
{ | |||
public readonly struct NativeEntityFactory | |||
{ | |||
readonly AtomicNativeBags _addOperationQueue; | |||
readonly uint _index; | |||
readonly int _index; | |||
internal NativeEntityFactory(AtomicNativeBags addOperationQueue, uint index) | |||
internal NativeEntityFactory(AtomicNativeBags addOperationQueue, int index) | |||
{ | |||
_index = index; | |||
_addOperationQueue = addOperationQueue; | |||
} | |||
public NativeEntityComponentInitializer BuildEntity | |||
(uint eindex, ExclusiveGroupStruct buildGroup, int threadIndex) | |||
(uint eindex, BuildGroup BuildGroup, int threadIndex) | |||
{ | |||
NativeBag unsafeBuffer = _addOperationQueue.GetBuffer(threadIndex + 1); | |||
unsafeBuffer.Enqueue(_index); | |||
unsafeBuffer.Enqueue(new EGID(eindex, buildGroup)); | |||
unsafeBuffer.Enqueue(new EGID(eindex, BuildGroup)); | |||
unsafeBuffer.ReserveEnqueue<uint>(out var index) = 0; | |||
return new NativeEntityComponentInitializer(unsafeBuffer, index); |
@@ -1,14 +1,14 @@ | |||
#if UNITY_BURST | |||
using Svelto.ECS.DataStructures.Unity; | |||
#if UNITY_NATIVE | |||
using Svelto.ECS.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
public readonly struct NativeEntityRemove | |||
{ | |||
readonly AtomicNativeBags _removeQueue; | |||
readonly uint _indexRemove; | |||
readonly int _indexRemove; | |||
internal NativeEntityRemove(AtomicNativeBags EGIDsToRemove, uint indexRemove) | |||
internal NativeEntityRemove(AtomicNativeBags EGIDsToRemove, int indexRemove) | |||
{ | |||
_removeQueue = EGIDsToRemove; | |||
_indexRemove = indexRemove; |
@@ -1,14 +1,14 @@ | |||
#if UNITY_BURST | |||
using Svelto.ECS.DataStructures.Unity; | |||
#if UNITY_NATIVE | |||
using Svelto.ECS.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
public readonly struct NativeEntitySwap | |||
{ | |||
readonly AtomicNativeBags _swapQueue; | |||
readonly uint _indexSwap; | |||
readonly int _indexSwap; | |||
internal NativeEntitySwap(AtomicNativeBags EGIDsToSwap, uint indexSwap) | |||
internal NativeEntitySwap(AtomicNativeBags EGIDsToSwap, int indexSwap) | |||
{ | |||
_swapQueue = EGIDsToSwap; | |||
_indexSwap = indexSwap; | |||
@@ -22,7 +22,7 @@ namespace Svelto.ECS | |||
} | |||
public void SwapEntity(EGID from, ExclusiveGroupStruct to, int threadIndex) | |||
public void SwapEntity(EGID from, BuildGroup to, int threadIndex) | |||
{ | |||
var simpleNativeBag = _swapQueue.GetBuffer(threadIndex); | |||
simpleNativeBag.Enqueue(_indexSwap); |
@@ -0,0 +1,73 @@ | |||
#if UNITY_NATIVE | |||
using System.Runtime.CompilerServices; | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public static class UnityEntityDBExtensions | |||
{ | |||
internal static NativeEGIDMapper<T> ToNativeEGIDMapper<T>(this TypeSafeDictionary<T> dic, | |||
ExclusiveGroupStruct groupStructId) where T : unmanaged, IEntityComponent | |||
{ | |||
var mapper = new NativeEGIDMapper<T>(groupStructId, dic.implUnmgd); | |||
return mapper; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static NativeEGIDMapper<T> QueryNativeMappedEntities<T>(this EntitiesDB entitiesDb, ExclusiveGroupStruct groupStructId) | |||
where T : unmanaged, IEntityComponent | |||
{ | |||
if (entitiesDb.SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false) | |||
throw new EntityGroupNotFoundException(typeof(T), groupStructId.ToName()); | |||
return (typeSafeDictionary as TypeSafeDictionary<T>).ToNativeEGIDMapper(groupStructId); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static bool TryQueryNativeMappedEntities<T>(this EntitiesDB entitiesDb, ExclusiveGroupStruct groupStructId, | |||
out NativeEGIDMapper<T> mapper) | |||
where T : unmanaged, IEntityComponent | |||
{ | |||
mapper = default; | |||
if (entitiesDb.SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false || | |||
typeSafeDictionary.count == 0) | |||
return false; | |||
mapper = (typeSafeDictionary as TypeSafeDictionary<T>).ToNativeEGIDMapper(groupStructId); | |||
return true; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static NativeEGIDMultiMapper<T> QueryNativeMappedEntities<T>(this EntitiesDB entitiesDb, | |||
LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) | |||
where T : unmanaged, IEntityComponent | |||
{ | |||
var dictionary = | |||
new SveltoDictionary<ExclusiveGroupStruct, SveltoDictionary<uint, T, | |||
NativeStrategy<FasterDictionaryNode<uint>>, | |||
NativeStrategy<T>, | |||
NativeStrategy<int>>, | |||
NativeStrategy<FasterDictionaryNode<ExclusiveGroupStruct>>, | |||
NativeStrategy<SveltoDictionary<uint, T, | |||
NativeStrategy<FasterDictionaryNode<uint>>, | |||
NativeStrategy<T>, | |||
NativeStrategy<int>>>, | |||
NativeStrategy<int>> | |||
((uint) groups.count, Allocator.TempJob); | |||
foreach (var group in groups) | |||
{ | |||
if (entitiesDb.SafeQueryEntityDictionary<T>(group, out var typeSafeDictionary) == true) | |||
if (typeSafeDictionary.count > 0) | |||
dictionary.Add(group, ((TypeSafeDictionary<T>)typeSafeDictionary).implUnmgd); | |||
} | |||
return new NativeEGIDMultiMapper<T>(dictionary); | |||
} | |||
} | |||
} | |||
#endif |
@@ -1,83 +0,0 @@ | |||
#if UNITY_BURST | |||
using System; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
public readonly struct NativeEGIDMapper<T> where T : unmanaged, IEntityComponent | |||
{ | |||
readonly SveltoDictionaryNative<uint, T> map; | |||
public ExclusiveGroupStruct groupID { get; } | |||
public NativeEGIDMapper | |||
(ExclusiveGroupStruct groupStructId | |||
, SveltoDictionary<uint, T, NativeStrategy<FasterDictionaryNode<uint>>, NativeStrategy<T>> toNative) : this() | |||
{ | |||
groupID = groupStructId; | |||
map = toNative; | |||
} | |||
public uint Count => map.count; | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref T Entity(uint entityID) | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (map.TryFindIndex(entityID, out var findIndex) == false) | |||
throw new Exception("Entity not found in this group ".FastConcat(typeof(T).ToString())); | |||
#else | |||
map.TryFindIndex(entityID, out var findIndex); | |||
#endif | |||
return ref map.GetDirectValueByRef(findIndex); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool TryGetEntity(uint entityID, out T value) | |||
{ | |||
if (map.count > 0 && map.TryFindIndex(entityID, out var index)) | |||
{ | |||
unsafe | |||
{ | |||
value = Unsafe.AsRef<T>(Unsafe.Add<T>((void*) map.GetValues(out _).ToNativeArray(out _) | |||
, (int) index)); | |||
return true; | |||
} | |||
} | |||
value = default; | |||
return false; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public NB<T> GetArrayAndEntityIndex(uint entityID, out uint index) | |||
{ | |||
if (map.TryFindIndex(entityID, out index)) | |||
{ | |||
return new NB<T>((IntPtr) map.GetValues(out var count).ToNativeArray(out _), count); | |||
} | |||
throw new ECSException("Entity not found"); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool TryGetArrayAndEntityIndex(uint entityID, out uint index, out NB<T> array) | |||
{ | |||
index = 0; | |||
if (map.count > 0 && map.TryFindIndex(entityID, out index)) | |||
{ | |||
array = new NB<T>((IntPtr) map.GetValues(out var count).ToNativeArray(out _), count); | |||
return true; | |||
} | |||
array = default; | |||
return false; | |||
} | |||
public bool Exists(uint idEntityId) | |||
{ | |||
return map.count > 0 && map.TryFindIndex(idEntityId, out _); | |||
} | |||
} | |||
} | |||
#endif |
@@ -1,43 +0,0 @@ | |||
#if UNITY_BURST | |||
using System; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
public struct NativeEGIDMultiMapper<T>:IDisposable where T : unmanaged, IEntityComponent | |||
{ | |||
SveltoDictionary<ExclusiveGroupStruct, | |||
SveltoDictionary<uint, T, NativeStrategy<FasterDictionaryNode<uint>>, NativeStrategy<T>>, | |||
NativeStrategy<FasterDictionaryNode<ExclusiveGroupStruct>>, NativeStrategy< | |||
SveltoDictionary<uint, T, NativeStrategy<FasterDictionaryNode<uint>>, NativeStrategy<T>>>> _dic; | |||
public NativeEGIDMultiMapper | |||
(SveltoDictionary<ExclusiveGroupStruct, | |||
SveltoDictionary<uint, T, NativeStrategy<FasterDictionaryNode<uint>>, NativeStrategy<T>>, | |||
NativeStrategy<FasterDictionaryNode<ExclusiveGroupStruct>>, NativeStrategy< | |||
SveltoDictionary<uint, T, NativeStrategy<FasterDictionaryNode<uint>>, NativeStrategy<T>>>> dictionary) | |||
{ | |||
_dic = dictionary; | |||
} | |||
public int count => (int) _dic.count; | |||
public void Dispose() | |||
{ | |||
_dic.Dispose(); | |||
} | |||
public ref T Entity(EGID entity) | |||
{ | |||
ref var sveltoDictionary = ref _dic.GetValueByRef(entity.groupID); | |||
return ref sveltoDictionary.GetValueByRef(entity.entityID); | |||
} | |||
public bool Exists(EGID entity) | |||
{ | |||
return _dic.TryFindIndex(entity.groupID, out var index) | |||
&& _dic.GetDirectValueByRef(index).ContainsKey(entity.entityID); | |||
} | |||
} | |||
} | |||
#endif |
@@ -1,29 +0,0 @@ | |||
#if UNITY_ECS | |||
using Svelto.Common; | |||
using Unity.Entities; | |||
using Unity.Jobs; | |||
namespace Svelto.ECS.Extensions.Unity | |||
{ | |||
[Sequenced(nameof(JobifiedSveltoEngines.PureUECSSystemsGroup))] | |||
[DisableAutoCreation] | |||
public class PureUECSSystemsGroup : IJobifiedEngine | |||
{ | |||
public PureUECSSystemsGroup(World world) | |||
{ | |||
_world = world; | |||
} | |||
public JobHandle Execute(JobHandle _jobHandle) | |||
{ | |||
_world.Update(); | |||
return _jobHandle; | |||
} | |||
public string name => nameof(PureUECSSystemsGroup); | |||
readonly World _world; | |||
} | |||
} | |||
#endif |
@@ -1,39 +0,0 @@ | |||
#if UNITY_ECS | |||
using Svelto.Common; | |||
using Unity.Entities; | |||
using Unity.Jobs; | |||
namespace Svelto.ECS.Extensions.Unity | |||
{ | |||
[Sequenced(nameof(JobifiedSveltoEngines.CopySveltoToUECSEnginesGroup))] | |||
[DisableAutoCreation] | |||
public class SyncSveltoToUECSGroup : ComponentSystemGroup, IJobifiedEngine | |||
{ | |||
public JobHandle Execute(JobHandle _jobHandle) | |||
{ | |||
foreach (var engine in Systems) | |||
(engine as SyncSveltoToUECSEngine).externalHandle = _jobHandle; | |||
Update(); | |||
return _jobHandle; | |||
} | |||
public string name => nameof(SyncSveltoToUECSGroup); | |||
readonly SimulationSystemGroup _simulationSystemGroup; | |||
} | |||
public abstract class SyncSveltoToUECSEngine : SystemBase, IEngine | |||
{ | |||
internal JobHandle externalHandle; | |||
protected abstract void Execute(); | |||
protected sealed override void OnUpdate() | |||
{ | |||
Dependency = JobHandle.CombineDependencies(Dependency, externalHandle); | |||
Execute(); | |||
} | |||
} | |||
} | |||
#endif |
@@ -0,0 +1,12 @@ | |||
#if UNITY_ECS | |||
using Unity.Entities; | |||
namespace Svelto.ECS.Extensions.Unity | |||
{ | |||
public interface IUECSSubmissionEngine : IJobifiedEngine | |||
{ | |||
EntityCommandBuffer ECB { get; set;} | |||
EntityManager EM { get; set;} | |||
} | |||
} | |||
#endif |
@@ -1,10 +1,9 @@ | |||
#if UNITY_BURST | |||
#if UNITY_ECS | |||
namespace Svelto.ECS.Extensions.Unity | |||
{ | |||
public enum JobifiedSveltoEngines | |||
{ | |||
CopySveltoToUECSEnginesGroup, | |||
PureUECSSystemsGroup | |||
SveltoOverUECS | |||
} | |||
} | |||
#endif |
@@ -0,0 +1,100 @@ | |||
#if UNITY_ECS | |||
using Svelto.Common; | |||
using Svelto.ECS.Schedulers; | |||
using Unity.Entities; | |||
using Unity.Jobs; | |||
namespace Svelto.ECS.Extensions.Unity | |||
{ | |||
[Sequenced(nameof(JobifiedSveltoEngines.SveltoOverUECS))] | |||
public class SveltoOverUECSEnginesGroup: IJobifiedEngine | |||
{ | |||
public SveltoOverUECSEnginesGroup(EnginesRoot enginesRoot) | |||
{ | |||
DBC.ECS.Check.Require(enginesRoot.scheduler is ISimpleEntitiesSubmissionScheduler, "The Engines root must use a EntitiesSubmissionScheduler scheduler implementation"); | |||
CreateUnityECSWorldForSvelto(enginesRoot.scheduler as ISimpleEntitiesSubmissionScheduler, enginesRoot); | |||
} | |||
public World world { get; private set; } | |||
void CreateUnityECSWorldForSvelto(ISimpleEntitiesSubmissionScheduler scheduler, EnginesRoot enginesRoot) | |||
{ | |||
world = new World("Svelto<>UECS world"); | |||
var systems = DefaultWorldInitialization.GetAllSystems(WorldSystemFilterFlags.Default); | |||
DefaultWorldInitialization.AddSystemsToRootLevelSystemGroups(world, systems); | |||
World.DefaultGameObjectInjectionWorld = world; | |||
//This is the UECS group that takes care of all the UECS systems that creates entities | |||
//it also submits Svelto entities | |||
_sveltoUecsEntitiesSubmissionGroup = new SveltoUECSEntitiesSubmissionGroup(scheduler, world); | |||
enginesRoot.AddEngine(_sveltoUecsEntitiesSubmissionGroup); | |||
//This is the group that handles the UECS sync systems that copy the svelto entities values to UECS entities | |||
_syncSveltoToUecsGroup = new SyncSveltoToUECSGroup(); | |||
enginesRoot.AddEngine(_syncSveltoToUecsGroup); | |||
_syncUecsToSveltoGroup = new SyncUECSToSveltoGroup(); | |||
enginesRoot.AddEngine(_syncUecsToSveltoGroup); | |||
//This is the group that handles the UECS sync systems that copy the UECS entities values to svelto entities | |||
//enginesRoot.AddEngine(new SveltoUECSEntitiesSubmissionGroup(scheduler, world)); | |||
enginesRoot.AddEngine(this); | |||
_enginesRoot = enginesRoot; | |||
} | |||
public JobHandle Execute(JobHandle inputDeps) | |||
{ | |||
//this is a sync point, there won't be pending jobs after this | |||
_sveltoUecsEntitiesSubmissionGroup.Execute(inputDeps); | |||
//Mixed explicit job dependency and internal automatic ECS dependency system | |||
//Write in to UECS entities so the UECS dependencies react on the components touched | |||
var handle = _syncSveltoToUecsGroup.Execute(default); | |||
//As long as pure UECS systems do not use external containers (like native arrays and so) the Unity | |||
//automatic dependencies system will guarantee that there won't be race conditions | |||
world.Update(); | |||
//this svelto group of UECS SystemBase systems | |||
return _syncUecsToSveltoGroup.Execute(handle); | |||
} | |||
public void AddUECSSubmissionEngine(IUECSSubmissionEngine spawnUnityEntityOnSveltoEntityEngine) | |||
{ | |||
_sveltoUecsEntitiesSubmissionGroup.Add(spawnUnityEntityOnSveltoEntityEngine); | |||
_enginesRoot.AddEngine(spawnUnityEntityOnSveltoEntityEngine); | |||
} | |||
public void AddSveltoToUECSEngine(SyncSveltoToUECSEngine engine) | |||
{ | |||
//it's a Svelto Engine/UECS SystemBase so it must be added in the UECS world AND svelto enginesRoot | |||
world.AddSystem(engine); | |||
_enginesRoot.AddEngine(engine); | |||
_syncSveltoToUecsGroup.Add(engine); | |||
} | |||
public void AddUECSToSveltoEngine(SyncUECSToSveltoEngine engine) | |||
{ | |||
//it's a Svelto Engine/UECS SystemBase so it must be added in the UECS world AND svelto enginesRoot | |||
world.AddSystem(engine); | |||
_enginesRoot.AddEngine(engine); | |||
_syncUecsToSveltoGroup.Add(engine); | |||
} | |||
public void Dispose() | |||
{ | |||
world.Dispose(); | |||
} | |||
public string name => nameof(SveltoOverUECSEnginesGroup); | |||
SveltoUECSEntitiesSubmissionGroup _sveltoUecsEntitiesSubmissionGroup; | |||
SyncSveltoToUECSGroup _syncSveltoToUecsGroup; | |||
SyncUECSToSveltoGroup _syncUecsToSveltoGroup; | |||
EnginesRoot _enginesRoot; | |||
} | |||
} | |||
#endif |
@@ -0,0 +1,61 @@ | |||
#if UNITY_ECS | |||
using Svelto.ECS.Schedulers; | |||
using Unity.Entities; | |||
using Unity.Jobs; | |||
namespace Svelto.ECS.Extensions.Unity | |||
{ | |||
/// <summary> | |||
/// Group of UECS/Svelto SystemBase engines that creates UECS entities. | |||
/// Svelto entities are submitted | |||
/// Svelto Add and remove callback are called | |||
/// OnUpdate of the systems are called | |||
/// finally the UECS command buffer is flushed | |||
/// Note: I cannot use Unity ComponentSystemGroups nor I can rely on the SystemBase Dependency field to | |||
/// solve external dependencies. External dependencies are tracked, but only linked to the UECS components operations | |||
/// With Dependency I cannot guarantee that an external container is used before previous jobs working on it are completed | |||
/// </summary> | |||
public class SveltoUECSEntitiesSubmissionGroup : JobifiedEnginesGroup<IUECSSubmissionEngine> | |||
{ | |||
public SveltoUECSEntitiesSubmissionGroup | |||
(ISimpleEntitiesSubmissionScheduler submissionScheduler, World UECSWorld) | |||
{ | |||
_submissionScheduler = submissionScheduler; | |||
_ECBSystem = UECSWorld.CreateSystem<SubmissionEntitiesCommandBufferSystem>(); | |||
} | |||
public new void Execute(JobHandle jobHandle) | |||
{ | |||
//Sync Point as we must be sure that jobs that create/swap/remove entities are done | |||
jobHandle.Complete(); | |||
if (_submissionScheduler.paused) | |||
return; | |||
//prepare the entity command buffer to be used by the registered engines | |||
var entityCommandBuffer = _ECBSystem.CreateCommandBuffer(); | |||
foreach (var system in _engines) | |||
{ | |||
system.ECB = entityCommandBuffer; | |||
system.EM = _ECBSystem.EntityManager; | |||
} | |||
//Submit Svelto Entities, calls Add/Remove/MoveTo that can be used by the IUECSSubmissionEngines | |||
_submissionScheduler.SubmitEntities(); | |||
//execute submission engines and complete jobs | |||
base.Execute(default).Complete(); | |||
//flush command buffer | |||
_ECBSystem.Update(); | |||
} | |||
readonly ISimpleEntitiesSubmissionScheduler _submissionScheduler; | |||
readonly SubmissionEntitiesCommandBufferSystem _ECBSystem; | |||
[DisableAutoCreation] | |||
class SubmissionEntitiesCommandBufferSystem : EntityCommandBufferSystem { } | |||
} | |||
} | |||
#endif |
@@ -0,0 +1,25 @@ | |||
#if UNITY_ECS | |||
using Unity.Entities; | |||
using Unity.Jobs; | |||
namespace Svelto.ECS.Extensions.Unity | |||
{ | |||
public class SyncSveltoToUECSGroup : JobifiedEnginesGroup<SyncSveltoToUECSEngine> | |||
{ | |||
} | |||
public abstract class SyncSveltoToUECSEngine : SystemBase, IJobifiedEngine | |||
{ | |||
public JobHandle Execute(JobHandle inputDeps) | |||
{ | |||
Dependency = JobHandle.CombineDependencies(Dependency, inputDeps); | |||
Update(); | |||
return Dependency; | |||
} | |||
public abstract string name { get; } | |||
} | |||
} | |||
#endif |
@@ -0,0 +1,26 @@ | |||
#if UNITY_ECS | |||
using Unity.Entities; | |||
using Unity.Jobs; | |||
namespace Svelto.ECS.Extensions.Unity | |||
{ | |||
public class SyncUECSToSveltoGroup : JobifiedEnginesGroup<SyncUECSToSveltoEngine> | |||
{ | |||
} | |||
public abstract class SyncUECSToSveltoEngine : SystemBase, IJobifiedEngine | |||
{ | |||
public JobHandle Execute(JobHandle inputDeps) | |||
{ | |||
Dependency = JobHandle.CombineDependencies(Dependency, inputDeps); | |||
Update(); | |||
return Dependency; | |||
} | |||
public abstract string name { get; } | |||
} | |||
} | |||
#endif |
@@ -1,119 +0,0 @@ | |||
#if UNITY_JOBS | |||
using System; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Extensions.Unity; | |||
using Svelto.ECS.Internal; | |||
using Unity.Jobs; | |||
namespace Svelto.ECS | |||
{ | |||
public static class UnityEntityDBExtensions2 | |||
{ | |||
public static JobHandle ScheduleParallel | |||
<JOB>(this JOB job, uint iterations, JobHandle inputDeps) where JOB: struct, IJobParallelForBatch | |||
{ | |||
if (iterations == 0) | |||
return inputDeps; | |||
var innerloopBatchCount = ProcessorCount.BatchSize(iterations); | |||
return job.ScheduleBatch((int)iterations, innerloopBatchCount, inputDeps); | |||
} | |||
public static JobHandle ScheduleParallel | |||
<JOB>(this JOB job, int iterations, JobHandle inputDeps) where JOB: struct, IJobParallelForBatch | |||
{ | |||
if (iterations <= 0) | |||
return inputDeps; | |||
var innerloopBatchCount = ProcessorCount.BatchSize((uint) iterations); | |||
return job.ScheduleBatch((int)iterations, innerloopBatchCount, inputDeps); | |||
} | |||
} | |||
public static class UnityEntityDBExtensions | |||
{ | |||
internal static NativeEGIDMapper<T> ToNativeEGIDMapper<T>(this TypeSafeDictionary<T> dic, | |||
ExclusiveGroupStruct groupStructId) where T : unmanaged, IEntityComponent | |||
{ | |||
var mapper = new NativeEGIDMapper<T>(groupStructId, dic.implUnmgd); | |||
return mapper; | |||
} | |||
public static JobHandle ScheduleDispose | |||
<T1>(this T1 disposable, JobHandle inputDeps) where T1 : struct, IDisposable | |||
{ | |||
return new DisposeJob<T1>(disposable).Schedule(inputDeps); | |||
} | |||
public static JobHandle ScheduleDispose | |||
<T1, T2>(this T1 disposable1, T2 disposable2, JobHandle inputDeps) | |||
where T1 : struct, IDisposable where T2 : struct, IDisposable | |||
{ | |||
return new DisposeJob<T1, T2>(disposable1, disposable2).Schedule(inputDeps); | |||
} | |||
public static JobHandle ScheduleParallel | |||
<JOB>(this JOB job, uint iterations, JobHandle inputDeps) where JOB: struct, IJobParallelFor | |||
{ | |||
if (iterations == 0) | |||
return inputDeps; | |||
var innerloopBatchCount = ProcessorCount.BatchSize(iterations); | |||
return job.Schedule((int)iterations, innerloopBatchCount, inputDeps); | |||
} | |||
public static JobHandle ScheduleParallel | |||
<JOB>(this JOB job, int iterations, JobHandle inputDeps) where JOB: struct, IJobParallelFor | |||
{ | |||
if (iterations <= 0) | |||
return inputDeps; | |||
var innerloopBatchCount = ProcessorCount.BatchSize((uint) iterations); | |||
return job.Schedule((int)iterations, innerloopBatchCount, inputDeps); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static NativeEGIDMapper<T> QueryNativeMappedEntities<T>(this EntitiesDB entitiesDb, ExclusiveGroupStruct groupStructId) | |||
where T : unmanaged, IEntityComponent | |||
{ | |||
if (entitiesDb.SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false) | |||
throw new EntityGroupNotFoundException(typeof(T)); | |||
return (typeSafeDictionary as TypeSafeDictionary<T>).ToNativeEGIDMapper<T>(groupStructId); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static bool TryQueryNativeMappedEntities<T>(this EntitiesDB entitiesDb, ExclusiveGroupStruct groupStructId, | |||
out NativeEGIDMapper<T> mapper) | |||
where T : unmanaged, IEntityComponent | |||
{ | |||
mapper = default; | |||
if (entitiesDb.SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false || | |||
typeSafeDictionary.count == 0) | |||
return false; | |||
mapper = (typeSafeDictionary as TypeSafeDictionary<T>).ToNativeEGIDMapper(groupStructId); | |||
return true; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static NativeEGIDMultiMapper<T> QueryNativeMappedEntities<T>(this EntitiesDB entitiesDb, LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) | |||
where T : unmanaged, IEntityComponent | |||
{ | |||
var dictionary = | |||
new SveltoDictionary<ExclusiveGroupStruct, SveltoDictionary<uint, T, NativeStrategy<FasterDictionaryNode<uint>>, NativeStrategy<T>>, | |||
NativeStrategy<FasterDictionaryNode<ExclusiveGroupStruct>>, NativeStrategy<SveltoDictionary<uint, T, NativeStrategy<FasterDictionaryNode<uint>>, NativeStrategy<T>>>> | |||
(groups.count, Allocator.TempJob); | |||
foreach (var group in groups) | |||
{ | |||
if (entitiesDb.SafeQueryEntityDictionary<T>(group, out var typeSafeDictionary) == true) | |||
if (typeSafeDictionary.count > 0) | |||
dictionary.Add(group, ((TypeSafeDictionary<T>)typeSafeDictionary).implUnmgd); | |||
} | |||
return new NativeEGIDMultiMapper<T>(dictionary); | |||
} | |||
} | |||
} | |||
#endif |
@@ -0,0 +1,30 @@ | |||
#if UNITY_ECS | |||
using Svelto.ECS.Hybrid; | |||
using UnityEngine; | |||
namespace Svelto.ECS.Extensions.Unity | |||
{ | |||
public static class EntityDescriptorHolderHelper | |||
{ | |||
public static EntityComponentInitializer CreateEntity<T>(this Transform contextHolder, EGID ID, | |||
IEntityFactory factory, out T holder) | |||
where T : MonoBehaviour, IEntityDescriptorHolder | |||
{ | |||
holder = contextHolder.GetComponentInChildren<T>(true); | |||
var implementors = holder.GetComponents<IImplementor>(); | |||
return factory.BuildEntity(ID, holder.GetDescriptor(), implementors); | |||
} | |||
public static EntityComponentInitializer Create<T>(this Transform contextHolder, EGID ID, | |||
IEntityFactory factory) | |||
where T : MonoBehaviour, IEntityDescriptorHolder | |||
{ | |||
var holder = contextHolder.GetComponentInChildren<T>(true); | |||
var implementors = holder.GetComponents<IImplementor>(); | |||
return factory.BuildEntity(ID, holder.GetDescriptor(), implementors); | |||
} | |||
} | |||
} | |||
#endif |
@@ -3,8 +3,7 @@ using UnityEngine; | |||
namespace Svelto.ECS.Extensions.Unity | |||
{ | |||
public abstract class GenericEntityDescriptorHolder<T>: | |||
MonoBehaviour , IEntityDescriptorHolder | |||
public abstract class GenericEntityDescriptorHolder<T>: MonoBehaviour , IEntityDescriptorHolder | |||
where T: IEntityDescriptor, new() | |||
{ | |||
public IEntityDescriptor GetDescriptor() | |||
@@ -12,6 +11,11 @@ namespace Svelto.ECS.Extensions.Unity | |||
return EntityDescriptorTemplate<T>.descriptor; | |||
} | |||
public T GetRealDescriptor() | |||
{ | |||
return EntityDescriptorTemplate<T>.realDescriptor; | |||
} | |||
public string groupName => _groupName; | |||
public ushort id => _id; | |||
@@ -1,34 +1,23 @@ | |||
#if UNITY_5 || UNITY_5_3_OR_NEWER | |||
using System; | |||
using Svelto.ECS.Hybrid; | |||
using UnityEngine; | |||
namespace Svelto.ECS.Extensions.Unity | |||
{ | |||
public static class EntityDescriptorHolderHelper | |||
{ | |||
public static EntityComponentInitializer CreateEntity<T>(this Transform contextHolder, EGID ID, | |||
IEntityFactory factory, out T holder) | |||
where T : MonoBehaviour, IEntityDescriptorHolder | |||
{ | |||
holder = contextHolder.GetComponentInChildren<T>(true); | |||
var implementors = holder.GetComponents<IImplementor>(); | |||
return factory.BuildEntity(ID, holder.GetDescriptor(), implementors); | |||
} | |||
public static EntityComponentInitializer Create<T>(this Transform contextHolder, EGID ID, | |||
IEntityFactory factory) | |||
where T : MonoBehaviour, IEntityDescriptorHolder | |||
{ | |||
var holder = contextHolder.GetComponentInChildren<T>(true); | |||
var implementors = holder.GetComponents<IImplementor>(); | |||
return factory.BuildEntity(ID, holder.GetDescriptor(), implementors); | |||
} | |||
} | |||
public static class SveltoGUIHelper | |||
{ | |||
/// <summary> | |||
/// This is the suggested way to create GUIs from prefabs now. | |||
/// </summary> | |||
/// <param name="startIndex"></param> | |||
/// <param name="contextHolder"></param> | |||
/// <param name="factory"></param> | |||
/// <param name="group"></param> | |||
/// <param name="searchImplementorsInChildren"></param> | |||
/// <param name="groupNamePostfix"></param> | |||
/// <typeparam name="T"></typeparam> | |||
/// <returns></returns> | |||
public static T CreateFromPrefab<T>(ref uint startIndex, Transform contextHolder, IEntityFactory factory, | |||
ExclusiveGroup group, bool searchImplementorsInChildren = false, string groupNamePostfix = null) where T : MonoBehaviour, IEntityDescriptorHolder | |||
{ | |||
@@ -37,64 +26,96 @@ namespace Svelto.ECS.Extensions.Unity | |||
foreach (var child in children) | |||
{ | |||
IImplementor[] childImplementors; | |||
if (child.GetType() != typeof(T)) | |||
{ | |||
var monoBehaviour = child as MonoBehaviour; | |||
var monoBehaviour = child as MonoBehaviour; | |||
IImplementor[] childImplementors; | |||
if (searchImplementorsInChildren == false) | |||
childImplementors = monoBehaviour.GetComponents<IImplementor>(); | |||
else | |||
childImplementors = monoBehaviour.GetComponentsInChildren<IImplementor>(true); | |||
startIndex = InternalBuildAll( | |||
startIndex, | |||
child, | |||
factory, | |||
group, | |||
childImplementors, | |||
groupNamePostfix); | |||
startIndex = InternalBuildAll(startIndex, child, factory, group, childImplementors, | |||
groupNamePostfix); | |||
} | |||
} | |||
return holder; | |||
} | |||
public static EntityComponentInitializer Create<T>(EGID ID, Transform contextHolder, | |||
IEntityFactory factory, out T holder, bool searchImplementorsInChildren = false) | |||
where T : MonoBehaviour, IEntityDescriptorHolder | |||
/// <summary> | |||
/// Creates all the entities in a hierarchy. This was commonly used to create entities from gameobjects | |||
/// already present in the scene | |||
/// </summary> | |||
/// <param name="startIndex"></param> | |||
/// <param name="group"></param> | |||
/// <param name="contextHolder"></param> | |||
/// <param name="factory"></param> | |||
/// <param name="groupNamePostfix"></param> | |||
/// <typeparam name="T"></typeparam> | |||
/// <returns></returns> | |||
public static uint CreateAll<T>(uint startIndex, ExclusiveGroup group, | |||
Transform contextHolder, IEntityFactory factory, string groupNamePostfix = null) where T : MonoBehaviour, IEntityDescriptorHolder | |||
{ | |||
holder = contextHolder.GetComponentInChildren<T>(true); | |||
var implementors = searchImplementorsInChildren == false ? holder.GetComponents<IImplementor>() : holder.GetComponentsInChildren<IImplementor>(true) ; | |||
var holders = contextHolder.GetComponentsInChildren<T>(true); | |||
return factory.BuildEntity(ID, holder.GetDescriptor(), implementors); | |||
foreach (var holder in holders) | |||
{ | |||
var implementors = holder.GetComponents<IImplementor>(); | |||
try | |||
{ | |||
startIndex = InternalBuildAll(startIndex, holder, factory, group, implementors, groupNamePostfix); | |||
} | |||
catch (Exception ex) | |||
{ | |||
throw new Exception($"When building entity from game object {Path(holder.transform)}", ex); | |||
} | |||
} | |||
return startIndex; | |||
} | |||
static string Path(Transform go) | |||
{ | |||
string s = go.name; | |||
while (go.parent != null) | |||
{ | |||
go = go.parent; | |||
s = go.name + "/" + s; | |||
} | |||
return s; | |||
} | |||
public static EntityComponentInitializer Create<T>(EGID ID, Transform contextHolder, | |||
IEntityFactory factory, bool searchImplementorsInChildren = false) | |||
public static EntityComponentInitializer Create<T>(EGID ID, Transform contextHolder, IEntityFactory factory, | |||
out T holder, bool searchImplementorsInChildren = false) | |||
where T : MonoBehaviour, IEntityDescriptorHolder | |||
{ | |||
var holder = contextHolder.GetComponentInChildren<T>(true); | |||
holder = contextHolder.GetComponentInChildren<T>(true); | |||
if (holder == null) | |||
{ | |||
throw new Exception($"Could not find holder {typeof(T).Name} in {contextHolder.name}"); | |||
} | |||
var implementors = searchImplementorsInChildren == false ? holder.GetComponents<IImplementor>() : holder.GetComponentsInChildren<IImplementor>(true) ; | |||
return factory.BuildEntity(ID, holder.GetDescriptor(), implementors); | |||
} | |||
public static uint CreateAll<T>(uint startIndex, ExclusiveGroup group, | |||
Transform contextHolder, IEntityFactory factory, string groupNamePostfix = null) where T : MonoBehaviour, IEntityDescriptorHolder | |||
public static EntityComponentInitializer Create<T>(EGID ID, Transform contextHolder, | |||
IEntityFactory factory, bool searchImplementorsInChildren = false) | |||
where T : MonoBehaviour, IEntityDescriptorHolder | |||
{ | |||
var holders = contextHolder.GetComponentsInChildren<T>(true); | |||
foreach (var holder in holders) | |||
var holder = contextHolder.GetComponentInChildren<T>(true); | |||
if (holder == null) | |||
{ | |||
var implementors = holder.GetComponents<IImplementor>(); | |||
startIndex = InternalBuildAll(startIndex, holder, factory, group, implementors, groupNamePostfix); | |||
throw new Exception($"Could not find holder {typeof(T).Name} in {contextHolder.name}"); | |||
} | |||
var implementors = searchImplementorsInChildren == false ? holder.GetComponents<IImplementor>() : holder.GetComponentsInChildren<IImplementor>(true) ; | |||
return startIndex; | |||
return factory.BuildEntity(ID, holder.GetDescriptor(), implementors); | |||
} | |||
static uint InternalBuildAll(uint startIndex, IEntityDescriptorHolder descriptorHolder, | |||
IEntityFactory factory, ExclusiveGroup group, IImplementor[] implementors, string groupNamePostfix) | |||
IEntityFactory factory, ExclusiveGroup group, IImplementor[] implementors, string groupNamePostfix) | |||
{ | |||
ExclusiveGroupStruct realGroup = group; | |||
@@ -121,6 +142,8 @@ namespace Svelto.ECS.Extensions.Unity | |||
/// <summary> | |||
/// Works like CreateAll but only builds entities with holders that have the same group specified | |||
/// This is a very specific case, basically used only by ControlsScreenRowFactory. Not sure what the real | |||
/// use case is. | |||
/// </summary> | |||
/// <param name="startId"></param> | |||
/// <param name="group">The group to match</param> | |||
@@ -7,7 +7,7 @@ namespace Svelto.ECS.Schedulers.Unity | |||
{ | |||
//The EntitySubmissionScheduler has been introduced to make the entity components submission logic platform independent | |||
//You can customize the scheduler if you wish | |||
public class UnityEntitiesSubmissionScheduler : IEntitiesSubmissionScheduler | |||
public class UnityEntitiesSubmissionScheduler : EntitiesSubmissionScheduler | |||
{ | |||
class Scheduler : MonoBehaviour | |||
{ | |||
@@ -37,14 +37,14 @@ namespace Svelto.ECS.Schedulers.Unity | |||
public System.Action onTick; | |||
} | |||
public UnityEntitiesSubmissionScheduler(string name = "ECSScheduler") | |||
public UnityEntitiesSubmissionScheduler(string name) | |||
{ | |||
_scheduler = new GameObject(name).AddComponent<Scheduler>(); | |||
GameObject.DontDestroyOnLoad(_scheduler.gameObject); | |||
_scheduler.onTick = SubmitEntities; | |||
} | |||
public void Dispose() | |||
public override void Dispose() | |||
{ | |||
if (_scheduler != null && _scheduler.gameObject != null) | |||
{ | |||
@@ -52,18 +52,18 @@ namespace Svelto.ECS.Schedulers.Unity | |||
} | |||
} | |||
public override bool paused { get; set; } | |||
void SubmitEntities() | |||
{ | |||
if (paused == false) | |||
_onTick.Invoke(); | |||
} | |||
EnginesRoot.EntitiesSubmitter IEntitiesSubmissionScheduler.onTick | |||
protected internal override EnginesRoot.EntitiesSubmitter onTick | |||
{ | |||
set => _onTick = value; | |||
} | |||
public bool paused { get; set; } | |||
readonly Scheduler _scheduler; | |||
EnginesRoot.EntitiesSubmitter _onTick; |
@@ -1,27 +0,0 @@ | |||
#if later | |||
namespace Svelto.ECS | |||
{ | |||
public class FastGroup | |||
{ | |||
internal static uint entitiesCount; | |||
public FastGroup() | |||
{ | |||
_group = ExclusiveGroupStruct.Generate(1); | |||
} | |||
public static implicit operator ExclusiveGroupStruct(FastGroup group) | |||
{ | |||
return group._group; | |||
} | |||
public static explicit operator uint(FastGroup group) | |||
{ | |||
return group._group; | |||
} | |||
readonly ExclusiveGroupStruct _group; | |||
public uint value => _group; | |||
} | |||
} | |||
#endif |
@@ -0,0 +1,213 @@ | |||
using System; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
/// <summary> | |||
/// This feature must be eventually tied to the new ExclusiveGroup that won't allow the use of custom EntitiesID | |||
/// The filters could be updated when entities buffer changes during the submission, while now this process | |||
/// is completely manual. | |||
/// Making this working properly is not in my priorities right now, as I will need to add the new group type | |||
/// AND optimize the submission process to be able to add more overhead | |||
/// </summary> | |||
public partial class EntitiesDB | |||
{ | |||
public readonly struct Filters | |||
{ | |||
public Filters | |||
(FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, GroupFilters>> filters) | |||
{ | |||
_filters = filters; | |||
} | |||
public ref FilterGroup CreateOrGetFilterForGroup<T>(int filterID, ExclusiveGroupStruct groupID) | |||
where T : struct, IEntityComponent | |||
{ | |||
var refWrapper = TypeRefWrapper<T>.wrapper; | |||
return ref CreateOrGetFilterForGroup(filterID, groupID, refWrapper); | |||
} | |||
ref FilterGroup CreateOrGetFilterForGroup(int filterID, ExclusiveGroupStruct groupID, RefWrapperType refWrapper) | |||
{ | |||
var fasterDictionary = | |||
_filters.GetOrCreate(refWrapper, () => new FasterDictionary<ExclusiveGroupStruct, GroupFilters>()); | |||
GroupFilters filters = | |||
fasterDictionary.GetOrCreate( | |||
groupID, () => new GroupFilters(new SharedSveltoDictionaryNative<int, FilterGroup>(0), groupID)); | |||
return ref filters.CreateOrGetFilter(filterID); | |||
} | |||
public bool HasFiltersForGroup<T>(ExclusiveGroupStruct groupID) where T : struct, IEntityComponent | |||
{ | |||
if (_filters.TryGetValue(TypeRefWrapper<T>.wrapper, out var fasterDictionary) == false) | |||
return false; | |||
return fasterDictionary.ContainsKey(groupID); | |||
} | |||
public bool HasFilterForGroup<T>(int filterID, ExclusiveGroupStruct groupID) | |||
where T : struct, IEntityComponent | |||
{ | |||
if (_filters.TryGetValue(TypeRefWrapper<T>.wrapper, out var fasterDictionary) == false) | |||
return false; | |||
if (fasterDictionary.TryGetValue(groupID, out var result)) | |||
return result.HasFilter(filterID); | |||
return false; | |||
} | |||
public ref GroupFilters CreateOrGetFiltersForGroup<T>(ExclusiveGroupStruct groupID) | |||
where T : struct, IEntityComponent | |||
{ | |||
var fasterDictionary = | |||
_filters.GetOrCreate(TypeRefWrapper<T>.wrapper, () => new FasterDictionary<ExclusiveGroupStruct, GroupFilters>()); | |||
return ref | |||
fasterDictionary.GetOrCreate( | |||
groupID, () => new GroupFilters(new SharedSveltoDictionaryNative<int, FilterGroup>(0), groupID)); | |||
} | |||
public ref GroupFilters GetFiltersForGroup<T>(ExclusiveGroupStruct groupID) | |||
where T : struct, IEntityComponent | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_filters.ContainsKey(TypeRefWrapper<T>.wrapper) == false) | |||
throw new ECSException( | |||
$"trying to fetch not existing filters, type {typeof(T)}"); | |||
if (_filters[TypeRefWrapper<T>.wrapper].ContainsKey(groupID) == false) | |||
throw new ECSException( | |||
$"trying to fetch not existing filters, type {typeof(T)} group {groupID.ToName()}"); | |||
#endif | |||
return ref _filters[TypeRefWrapper<T>.wrapper].GetValueByRef(groupID); | |||
} | |||
public ref FilterGroup GetFilterForGroup<T>(int filterId, ExclusiveGroupStruct groupID) | |||
where T : struct, IEntityComponent | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_filters.ContainsKey(TypeRefWrapper<T>.wrapper) == false) | |||
throw new ECSException( | |||
$"trying to fetch not existing filters, type {typeof(T)}"); | |||
if (_filters[TypeRefWrapper<T>.wrapper].ContainsKey(groupID) == false) | |||
throw new ECSException( | |||
$"trying to fetch not existing filters, type {typeof(T)} group {groupID.ToName()}"); | |||
#endif | |||
return ref _filters[TypeRefWrapper<T>.wrapper][groupID].GetFilter(filterId); | |||
} | |||
public bool TryGetFilterForGroup<T>(int filterId, ExclusiveGroupStruct groupID, out FilterGroup groupFilter) | |||
where T : struct, IEntityComponent | |||
{ | |||
groupFilter = default; | |||
if (_filters.TryGetValue(TypeRefWrapper<T>.wrapper, out var fasterDictionary) == false) | |||
return false; | |||
if (fasterDictionary.TryGetValue(groupID, out var groupFilters) == false) | |||
return false; | |||
if (groupFilters.TryGetFilter(filterId, out groupFilter) == false) | |||
return false; | |||
return true; | |||
} | |||
public bool TryGetFiltersForGroup<T>(ExclusiveGroupStruct groupID, out GroupFilters groupFilters) | |||
where T : struct, IEntityComponent | |||
{ | |||
groupFilters = default; | |||
if (_filters.TryGetValue(TypeRefWrapper<T>.wrapper, out var fasterDictionary) == false) | |||
return false; | |||
return fasterDictionary.TryGetValue(groupID, out groupFilters); | |||
} | |||
public void ClearFilter<T>(int filterID, ExclusiveGroupStruct exclusiveGroupStruct) | |||
{ | |||
if (_filters.TryGetValue(TypeRefWrapper<T>.wrapper, out var fasterDictionary) == true) | |||
{ | |||
DBC.ECS.Check.Require(fasterDictionary.ContainsKey(exclusiveGroupStruct), $"trying to clear filter not present in group {exclusiveGroupStruct}"); | |||
fasterDictionary[exclusiveGroupStruct].ClearFilter(filterID); | |||
} | |||
} | |||
public void ClearFilters<T>(int filterID) | |||
{ | |||
if (_filters.TryGetValue(TypeRefWrapper<T>.wrapper, out var fasterDictionary) == true) | |||
{ | |||
foreach (var filtersPerGroup in fasterDictionary) | |||
filtersPerGroup.Value.ClearFilter(filterID); | |||
} | |||
} | |||
public void DisposeFilters<T>(ExclusiveGroupStruct exclusiveGroupStruct) | |||
{ | |||
if (_filters.TryGetValue(TypeRefWrapper<T>.wrapper, out var fasterDictionary) == true) | |||
{ | |||
fasterDictionary[exclusiveGroupStruct].DisposeFilters(); | |||
fasterDictionary.Remove(exclusiveGroupStruct); | |||
} | |||
} | |||
public void DisposeFilters<T>() | |||
{ | |||
if (_filters.TryGetValue(TypeRefWrapper<T>.wrapper, out var fasterDictionary) == true) | |||
{ | |||
foreach (var filtersPerGroup in fasterDictionary) | |||
filtersPerGroup.Value.DisposeFilters(); | |||
} | |||
_filters.Remove(TypeRefWrapper<T>.wrapper); | |||
} | |||
public void DisposeFilterForGroup<T>(int resetFilterID, ExclusiveGroupStruct @group) | |||
{ | |||
if (_filters.TryGetValue(TypeRefWrapper<T>.wrapper, out var fasterDictionary) == true) | |||
{ | |||
fasterDictionary[group].DisposeFilter(resetFilterID); | |||
} | |||
} | |||
public bool TryRemoveEntityFromFilter<T>(int filtersID, EGID egid) where T : unmanaged, IEntityComponent | |||
{ | |||
if (TryGetFilterForGroup<T>(filtersID, egid.groupID, out var filter)) | |||
{ | |||
return filter.TryRemove(egid.entityID); | |||
} | |||
return false; | |||
} | |||
public void RemoveEntityFromFilter<T>(int filtersID, EGID egid) where T : unmanaged, IEntityComponent | |||
{ | |||
ref var filter = ref GetFilterForGroup<T>(filtersID, egid.groupID); | |||
filter.Remove(egid.entityID); | |||
} | |||
public void AddEntityToFilter<N>(int filtersID, EGID egid, N mapper) where N:IEGIDMapper | |||
{ | |||
ref var filter = ref CreateOrGetFilterForGroup(filtersID, egid.groupID, new RefWrapperType(mapper.entityType)); | |||
filter.Add(egid.entityID, mapper); | |||
} | |||
readonly FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, GroupFilters>> _filters; | |||
} | |||
public Filters GetFilters() | |||
{ | |||
return new Filters(_filters); | |||
} | |||
FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, GroupFilters>> _filters | |||
=> _enginesRoot._groupFilters; | |||
} | |||
} |
@@ -0,0 +1,194 @@ | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
/// <summary> | |||
/// In order to complete this feature, I need to be able to detect if the entity pointed by the filter | |||
/// is still present. | |||
/// This feature should work only with groups where entityID cannot be chosen by the user, so that | |||
/// a real sparse set can be used like explained at: https://skypjack.github.io/2020-08-02-ecs-baf-part-9/ | |||
/// For a sparse set to work, the index in the sparse list must coincide with the ID of the entity | |||
/// so that from the dense list (that holds unordered entity index), I can get to the sparse list index | |||
/// sparse[0] = position in the dense list of the entity 0 | |||
/// dense[index] = entity ID but also index in the sparse list of the same entity ID | |||
/// </summary> | |||
public struct FilterGroup | |||
{ | |||
internal FilterGroup(ExclusiveGroupStruct exclusiveGroupStruct, int ID) | |||
{ | |||
_denseListOfIndicesToEntityComponentArray = | |||
new NativeDynamicArrayCast<uint>(NativeDynamicArray.Alloc<uint>(Allocator.Persistent)); | |||
//from the index, find the entityID | |||
_reverseEIDs = new NativeDynamicArrayCast<uint>(NativeDynamicArray.Alloc<uint>(Allocator.Persistent)); | |||
//from the entityID, find the index | |||
_indexOfEntityInDenseList = new SharedSveltoDictionaryNative<uint, uint>(0, Allocator.Persistent); | |||
_exclusiveGroupStruct = exclusiveGroupStruct; | |||
_ID = ID; | |||
} | |||
/// <summary> | |||
/// Todo: how to detect if the indices are still pointing to valid entities? | |||
/// </summary> | |||
public FilteredIndices filteredIndices => new FilteredIndices(_denseListOfIndicesToEntityComponentArray); | |||
public void Add<N>(uint entityID, N mapper) where N:IEGIDMapper | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_denseListOfIndicesToEntityComponentArray.isValid == false) | |||
throw new ECSException($"using an invalid filter"); | |||
if (_indexOfEntityInDenseList.ContainsKey(entityID) == true) | |||
throw new ECSException( | |||
$"trying to add an existing entity {entityID} to filter {mapper.entityType} - {_ID} with group {mapper.groupID}"); | |||
if (mapper.Exists(entityID) == false) | |||
throw new ECSException( | |||
$"trying adding an entity {entityID} to filter {mapper.entityType} - {_ID} with group {mapper.groupID}, but entity is not found! "); | |||
#endif | |||
//Get the index of the Entity in the component array | |||
var indexOfEntityInBufferComponent = mapper.GetIndex(entityID); | |||
//add the index in the list of filtered indices | |||
_denseListOfIndicesToEntityComponentArray.Add(indexOfEntityInBufferComponent); | |||
//inverse map: need to get the entityID from the index. This wouldn't be needed with a real sparseset | |||
var lastIndex = (uint) (_denseListOfIndicesToEntityComponentArray.Count() - 1); | |||
_reverseEIDs.AddAt(lastIndex) = entityID; | |||
//remember the entities indices. This is needed to remove entities from the filter | |||
_indexOfEntityInDenseList.Add(entityID, lastIndex); | |||
} | |||
public void Remove(uint entityID) | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_denseListOfIndicesToEntityComponentArray.isValid == false) | |||
throw new ECSException($"invalid Filter"); | |||
if (_indexOfEntityInDenseList.ContainsKey(entityID) == false) | |||
throw new ECSException( | |||
$"trying to remove a not existing entity {new EGID(entityID, _exclusiveGroupStruct)} from filter"); | |||
#endif | |||
InternalRemove(entityID); | |||
} | |||
public bool TryRemove(uint entityID) | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_denseListOfIndicesToEntityComponentArray.isValid == false) | |||
throw new ECSException($"invalid Filter"); | |||
#endif | |||
if (_indexOfEntityInDenseList.ContainsKey(entityID) == false) | |||
return false; | |||
InternalRemove(entityID); | |||
return true; | |||
} | |||
/// <summary> | |||
/// Filters were initially designed to be used for tagging operations within submissions of entities. | |||
/// They were designed as a fast tagging mechanism to be used within the submission frame. However I then | |||
/// extended it, but the extension includes a drawback: | |||
///If filters are not in sync with the operations of remove and swap, filters may end up pointing to | |||
///invalid indices. I need to put in place a way to be able to recognised an invalid filter. | |||
///This is currently a disadvantage of the filters. The filters are not updated by the framework | |||
///but they must be updated by the user. | |||
///When to use this method: Add and Removed should be used to add and remove entities in the filters. This is | |||
/// valid as long as no structural changes happen in the group of entities involved. | |||
/// IF structural changes happen, the indices stored in the filters won't be valid anymore as they will possibly | |||
/// point to entities that were not the original ones. On structural changes | |||
/// (specifically entities swapped or removed) | |||
/// the filters must then be rebuilt. It would be too slow to add this in the standard flow of Svelto in | |||
/// the current state, so calling this method is a user responsibility. | |||
/// </summary> | |||
public void RebuildIndicesOnStructuralChange<N>(N mapper) where N:IEGIDMapper | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_denseListOfIndicesToEntityComponentArray.isValid == false) | |||
throw new ECSException($"invalid Filter"); | |||
#endif | |||
_denseListOfIndicesToEntityComponentArray.Clear(); | |||
_reverseEIDs.Clear(); | |||
foreach (var value in _indexOfEntityInDenseList) | |||
if (mapper.FindIndex(value.Key, out var indexOfEntityInBufferComponent) == true) | |||
{ | |||
_denseListOfIndicesToEntityComponentArray.Add(indexOfEntityInBufferComponent); | |||
var lastIndex = (uint) (_denseListOfIndicesToEntityComponentArray.Count() - 1); | |||
_reverseEIDs.AddAt(lastIndex) = value.Key; | |||
} | |||
_indexOfEntityInDenseList.Clear(); | |||
for (uint i = 0; i < _reverseEIDs.Count(); i++) | |||
_indexOfEntityInDenseList[_reverseEIDs[i]] = i; | |||
} | |||
public void Clear() | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_denseListOfIndicesToEntityComponentArray.isValid == false) | |||
throw new ECSException($"invalid Filter"); | |||
#endif | |||
_indexOfEntityInDenseList.FastClear(); | |||
_reverseEIDs.Clear(); | |||
_denseListOfIndicesToEntityComponentArray.Clear(); | |||
} | |||
internal void Dispose() | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_denseListOfIndicesToEntityComponentArray.isValid == false) | |||
throw new ECSException($"invalid Filter"); | |||
#endif | |||
_denseListOfIndicesToEntityComponentArray.Dispose(); | |||
_indexOfEntityInDenseList.Dispose(); | |||
_reverseEIDs.Dispose(); | |||
} | |||
void InternalRemove(uint entityID) | |||
{ | |||
var count = (uint) _denseListOfIndicesToEntityComponentArray.Count(); | |||
if (count > 0) | |||
{ | |||
if (count > 1) | |||
{ | |||
//get the index in the filter array of the entity to delete | |||
var indexInDenseListFromEGID = _indexOfEntityInDenseList[entityID]; | |||
//get the entityID of the last entity in the filter array | |||
uint entityIDToMove = _reverseEIDs[count - 1]; | |||
//the last index of the last entity is updated to the slot of the deleted entity | |||
if (entityIDToMove != entityID) | |||
{ | |||
_indexOfEntityInDenseList[entityIDToMove] = indexInDenseListFromEGID; | |||
//the reverseEGID is updated accordingly | |||
_reverseEIDs[indexInDenseListFromEGID] = entityIDToMove; | |||
} | |||
// | |||
_reverseEIDs.UnorderedRemoveAt(count - 1); | |||
//finally remove the deleted entity from the filters array | |||
_denseListOfIndicesToEntityComponentArray.UnorderedRemoveAt(indexInDenseListFromEGID); | |||
//remove the entity to delete from the tracked Entity | |||
_indexOfEntityInDenseList.Remove(entityID); | |||
} | |||
else | |||
{ | |||
_indexOfEntityInDenseList.FastClear(); | |||
_reverseEIDs.Clear(); | |||
_denseListOfIndicesToEntityComponentArray.Clear(); | |||
} | |||
} | |||
} | |||
NativeDynamicArrayCast<uint> _denseListOfIndicesToEntityComponentArray; | |||
NativeDynamicArrayCast<uint> _reverseEIDs; //forced to use this because it's not a real sparse set | |||
SharedSveltoDictionaryNative<uint, uint> _indexOfEntityInDenseList; | |||
readonly ExclusiveGroupStruct _exclusiveGroupStruct; | |||
readonly int _ID; | |||
} | |||
} |
@@ -0,0 +1,21 @@ | |||
using System.Runtime.CompilerServices; | |||
using Svelto.ECS.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
public readonly struct FilteredIndices | |||
{ | |||
public FilteredIndices(NativeDynamicArrayCast<uint> denseListOfIndicesToEntityComponentArray) | |||
{ | |||
_denseListOfIndicesToEntityComponentArray = denseListOfIndicesToEntityComponentArray; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public int Count() => _denseListOfIndicesToEntityComponentArray.Count(); | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public uint Get(uint index) => _denseListOfIndicesToEntityComponentArray[index]; | |||
readonly NativeDynamicArrayCast<uint> _denseListOfIndicesToEntityComponentArray; | |||
} | |||
} |
@@ -0,0 +1,103 @@ | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
public struct GroupFilters | |||
{ | |||
internal GroupFilters(SharedSveltoDictionaryNative<int, FilterGroup> filters, ExclusiveGroupStruct group) | |||
{ | |||
this.filters = filters; | |||
_group = @group; | |||
} | |||
public ref FilterGroup GetFilter(int filterIndex) | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (filters.isValid == false) | |||
throw new ECSException($"trying to fetch not existing filters {filterIndex} group {_group.ToName()}"); | |||
if (filters.ContainsKey(filterIndex) == false) | |||
throw new ECSException($"trying to fetch not existing filters {filterIndex} group {_group.ToName()}"); | |||
#endif | |||
return ref filters.GetValueByRef(filterIndex); | |||
} | |||
public bool HasFilter(int filterIndex) { return filters.ContainsKey(filterIndex); } | |||
public void ClearFilter(int filterIndex) | |||
{ | |||
if (filters.TryFindIndex(filterIndex, out var index)) | |||
filters.GetValues(out _)[index].Clear(); | |||
} | |||
public void ClearFilters() | |||
{ | |||
foreach (var filter in filters) | |||
filter.Value.Clear(); | |||
} | |||
public bool TryGetFilter(int filterIndex, out FilterGroup filter) | |||
{ | |||
return filters.TryGetValue(filterIndex, out filter); | |||
} | |||
public SveltoDictionary<int, FilterGroup, NativeStrategy<FasterDictionaryNode<int>>, NativeStrategy<FilterGroup> | |||
, NativeStrategy<int>>.SveltoDictionaryKeyValueEnumerator GetEnumerator() | |||
{ | |||
return filters.GetEnumerator(); | |||
} | |||
//Note the following methods are internal because I was pondering the idea to be able to return | |||
//the list of GroupFilters linked to a specific filter ID. However this would mean to be able to | |||
//maintain a revers map which at this moment seems too much and also would need the following | |||
//method to be for ever internal (at this point in time I am not sure it's a good idea) | |||
internal void DisposeFilter(int filterIndex) | |||
{ | |||
if (filters.TryFindIndex(filterIndex, out var index)) | |||
{ | |||
ref var filterGroup = ref filters.GetValues(out _)[index]; | |||
filterGroup.Dispose(); | |||
filters.Remove(filterIndex); | |||
} | |||
} | |||
internal void DisposeFilters() | |||
{ | |||
//must release the native buffers! | |||
foreach (var filter in filters) | |||
filter.Value.Dispose(); | |||
filters.FastClear(); | |||
} | |||
internal ref FilterGroup CreateOrGetFilter(int filterID) | |||
{ | |||
if (filters.TryFindIndex(filterID, out var index) == false) | |||
{ | |||
var orGetFilterForGroup = new FilterGroup(_group, filterID); | |||
filters[filterID] = orGetFilterForGroup; | |||
return ref filters.GetValueByRef(filterID); | |||
} | |||
return ref filters.GetValues(out _)[index]; | |||
} | |||
internal void Dispose() | |||
{ | |||
foreach (var filter in filters) | |||
{ | |||
filter.Value.Dispose(); | |||
} | |||
filters.Dispose(); | |||
} | |||
readonly ExclusiveGroupStruct _group; | |||
//filterID, filter | |||
SharedSveltoDictionaryNative<int, FilterGroup> filters; | |||
} | |||
} |
@@ -23,7 +23,7 @@ namespace Svelto.ECS | |||
{ | |||
static Filler() | |||
{ | |||
DBC.ECS.Check.Require(UnmanagedTypeExtensions.IsUnmanagedEx<T>() == true, "invalid type used"); | |||
DBC.ECS.Check.Require(TypeCache<T>.IsUnmanaged == true, "invalid type used"); | |||
} | |||
//it's an internal interface | |||
@@ -37,7 +37,7 @@ namespace Svelto.ECS | |||
static class EntityComponentID<T> | |||
{ | |||
#if UNITY_BURST | |||
#if UNITY_NATIVE | |||
internal static readonly Unity.Burst.SharedStatic<uint> ID = | |||
Unity.Burst.SharedStatic<uint>.GetOrCreate<GlobalTypeID, T>(); | |||
#else | |||
@@ -1,126 +1,225 @@ | |||
using System; | |||
using System.Threading; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
public abstract class GroupCompound<G1, G2, G3> | |||
where G1 : GroupTag<G1> where G2 : GroupTag<G2> where G3 : GroupTag<G3> | |||
/// <summary> | |||
/// Very naive fail safe, but at least it's simple to understand and safe | |||
/// </summary> | |||
static class GroupCompoundInitializer | |||
{ | |||
static readonly FasterList<ExclusiveGroupStruct> _Groups; | |||
public static FasterReadOnlyList<ExclusiveGroupStruct> Groups => new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups); | |||
static GroupCompound() | |||
{ | |||
if ((_Groups = GroupCompound<G3, G1, G2>._Groups) == null) | |||
if ((_Groups = GroupCompound<G2, G3, G1>._Groups) == null) | |||
if ((_Groups = GroupCompound<G3, G2, G1>._Groups) == null) | |||
if ((_Groups = GroupCompound<G1, G3, G2>._Groups) == null) | |||
if ((_Groups = GroupCompound<G2, G1, G3>._Groups) == null) | |||
{ | |||
_Groups = new FasterList<ExclusiveGroupStruct>(1); | |||
var Group = new ExclusiveGroup(); | |||
_Groups.Add(Group); | |||
GroupCompound<G1, G2>.Add(Group); //<G1/G2> and <G2/G1> must share the same array | |||
GroupCompound<G1, G3>.Add(Group); | |||
GroupCompound<G2, G3>.Add(Group); | |||
//This is done here to be sure that the group is added once per group tag | |||
//(if done inside the previous group compound it would be added multiple times) | |||
GroupTag<G1>.Add(Group); | |||
GroupTag<G2>.Add(Group); | |||
GroupTag<G3>.Add(Group); | |||
#if DEBUG | |||
GroupMap.idToName[(uint) Group] = $"Compound: {typeof(G1).Name}-{typeof(G2).Name}-{typeof(G3).Name}"; | |||
#endif | |||
} | |||
} | |||
public static void Add(ExclusiveGroupStruct @group) | |||
{ | |||
for (int i = 0; i < _Groups.count; ++i) | |||
if (_Groups[i] == group) | |||
throw new Exception("temporary must be transformed in unit test"); | |||
_Groups.Add(group); | |||
// GroupCompound<G1, G2, G3>._Groups = _Groups; | |||
} | |||
public static ExclusiveGroupStruct BuildGroup => new ExclusiveGroupStruct(_Groups[0]); | |||
} | |||
public abstract class GroupCompound<G1, G2> where G1 : GroupTag<G1> where G2 : GroupTag<G2> | |||
{ | |||
static FasterList<ExclusiveGroupStruct> _Groups; | |||
public static FasterReadOnlyList<ExclusiveGroupStruct> Groups => new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups); | |||
static GroupCompound() | |||
{ | |||
_Groups = GroupCompound<G2, G1>._Groups; | |||
if (_Groups == null) | |||
{ | |||
_Groups = new FasterList<ExclusiveGroupStruct>(1); | |||
var Group = new ExclusiveGroup(); | |||
_Groups.Add(Group); | |||
//every abstract group preemptively adds this group, it may or may not be empty in future | |||
GroupTag<G1>.Add(Group); | |||
GroupTag<G2>.Add(Group); | |||
#if DEBUG | |||
GroupMap.idToName[(uint) Group] = $"Compound: {typeof(G1).Name}-{typeof(G2).Name}"; | |||
#endif | |||
} | |||
} | |||
public static ExclusiveGroupStruct BuildGroup => new ExclusiveGroupStruct(_Groups[0]); | |||
public static void Add(ExclusiveGroupStruct @group) | |||
{ | |||
for (int i = 0; i < _Groups.count; ++i) | |||
if (_Groups[i] == group) | |||
throw new Exception("temporary must be transformed in unit test"); | |||
_Groups.Add(group); | |||
//unit test this to check if it's necessary | |||
// GroupCompound<G2, G1>._Groups = _Groups; | |||
} | |||
} | |||
//A Group Tag holds initially just a group, itself. However the number of groups can grow with the number of | |||
//combinations of GroupTags including this one. This because a GroupTag is an adjective and different entities | |||
//can use the same adjective together with other ones. However since I need to be able to iterate over all the | |||
//groups with the same adjective, a group tag needs to hold all the groups sharing it. | |||
public abstract class GroupTag<T> where T : GroupTag<T> | |||
{ | |||
static FasterList<ExclusiveGroupStruct> _Groups = new FasterList<ExclusiveGroupStruct>(1); | |||
public static FasterReadOnlyList<ExclusiveGroupStruct> Groups => new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups); | |||
static GroupTag() | |||
{ | |||
_Groups.Add(new ExclusiveGroup()); | |||
} | |||
//Each time a new combination of group tags is found a new group is added. | |||
internal static void Add(ExclusiveGroupStruct @group) | |||
{ | |||
for (int i = 0; i < _Groups.count; ++i) | |||
if (_Groups[i] == group) | |||
throw new Exception("temporary must be transformed in unit test"); | |||
_Groups.Add(group); | |||
#if DEBUG | |||
GroupMap.idToName[(uint) group] = $"Compound: {typeof(T).Name}"; | |||
#endif | |||
} | |||
public static ExclusiveGroupStruct BuildGroup => new ExclusiveGroupStruct(_Groups[0]); | |||
} | |||
} | |||
internal static readonly ThreadLocal<bool> isInitializing4 = new ThreadLocal<bool>(); | |||
internal static readonly ThreadLocal<bool> isInitializing3 = new ThreadLocal<bool>(); | |||
internal static readonly ThreadLocal<bool> isInitializing2 = new ThreadLocal<bool>(); | |||
} | |||
public abstract class GroupCompound<G1, G2, G3, G4> | |||
where G1 : GroupTag<G1> where G2 : GroupTag<G2> where G3 : GroupTag<G3> where G4 : GroupTag<G4> | |||
{ | |||
static readonly FasterList<ExclusiveGroupStruct> _Groups; | |||
public static FasterReadOnlyList<ExclusiveGroupStruct> Groups => new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups); | |||
public static BuildGroup BuildGroup => new BuildGroup(_Groups[0]); | |||
static GroupCompound() | |||
{ | |||
if (GroupCompoundInitializer.isInitializing4.Value == false) | |||
{ | |||
_Groups = new FasterList<ExclusiveGroupStruct>(1); | |||
var Group = new ExclusiveGroup(); | |||
_Groups.Add(Group); | |||
GroupCompound<G1, G2, G3>.Add(Group); | |||
GroupCompound<G1, G2, G4>.Add(Group); | |||
GroupCompound<G1, G3, G4>.Add(Group); | |||
GroupCompound<G2, G3, G4>.Add(Group); | |||
GroupCompound<G1, G2>.Add(Group); //<G1/G2> and <G2/G1> must share the same array | |||
GroupCompound<G1, G3>.Add(Group); | |||
GroupCompound<G1, G4>.Add(Group); | |||
GroupCompound<G2, G3>.Add(Group); | |||
GroupCompound<G2, G4>.Add(Group); | |||
GroupCompound<G3, G4>.Add(Group); | |||
//This is done here to be sure that the group is added once per group tag | |||
//(if done inside the previous group compound it would be added multiple times) | |||
GroupTag<G1>.Add(Group); | |||
GroupTag<G2>.Add(Group); | |||
GroupTag<G3>.Add(Group); | |||
GroupTag<G4>.Add(Group); | |||
#if DEBUG | |||
GroupMap.idToName[(uint) Group] = $"Compound: {typeof(G1).Name}-{typeof(G2).Name}-{typeof(G3).Name}-{typeof(G4).Name} ID {(uint)Group}"; | |||
#endif | |||
GroupCompoundInitializer.isInitializing4.Value = true; | |||
//all the combinations must share the same group | |||
GroupCompound<G1, G2, G4, G3>._Groups = _Groups; | |||
GroupCompound<G1, G3, G2, G4>._Groups = _Groups; | |||
GroupCompound<G1, G3, G4, G2>._Groups = _Groups; | |||
GroupCompound<G1, G4, G2, G3>._Groups = _Groups; | |||
GroupCompound<G2, G1, G3, G4>._Groups = _Groups; | |||
GroupCompound<G2, G3, G4, G1>._Groups = _Groups; | |||
GroupCompound<G3, G1, G2, G4>._Groups = _Groups; | |||
GroupCompound<G4, G1, G2, G3>._Groups = _Groups; | |||
GroupCompound<G1, G4, G3, G2>._Groups = _Groups; | |||
GroupCompound<G2, G1, G4, G3>._Groups = _Groups; | |||
GroupCompound<G2, G4, G3, G1>._Groups = _Groups; | |||
GroupCompound<G3, G1, G4, G2>._Groups = _Groups; | |||
GroupCompound<G4, G1, G3, G2>._Groups = _Groups; | |||
GroupCompound<G2, G3, G1, G4>._Groups = _Groups; | |||
GroupCompound<G3, G4, G1, G2>._Groups = _Groups; | |||
GroupCompound<G2, G4, G1, G3>._Groups = _Groups; | |||
GroupCompound<G3, G2, G1, G4>._Groups = _Groups; | |||
GroupCompound<G3, G2, G4, G1>._Groups = _Groups; | |||
GroupCompound<G3, G4, G2, G1>._Groups = _Groups; | |||
GroupCompound<G4, G2, G1, G3>._Groups = _Groups; | |||
GroupCompound<G4, G2, G3, G1>._Groups = _Groups; | |||
GroupCompound<G4, G3, G1, G2>._Groups = _Groups; | |||
GroupCompound<G4, G3, G2, G1>._Groups = _Groups; | |||
GroupCompoundInitializer.isInitializing4.Value = false; | |||
} | |||
} | |||
public static void Add(ExclusiveGroupStruct @group) | |||
{ | |||
for (int i = 0; i < _Groups.count; ++i) | |||
if (_Groups[i] == group) | |||
throw new Exception("temporary must be transformed in unit test"); | |||
_Groups.Add(group); | |||
} | |||
} | |||
public abstract class GroupCompound<G1, G2, G3> | |||
where G1 : GroupTag<G1> where G2 : GroupTag<G2> where G3 : GroupTag<G3> | |||
{ | |||
static readonly FasterList<ExclusiveGroupStruct> _Groups; | |||
public static FasterReadOnlyList<ExclusiveGroupStruct> Groups => | |||
new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups); | |||
public static BuildGroup BuildGroup => new BuildGroup(_Groups[0]); | |||
public static void Add(ExclusiveGroupStruct group) | |||
{ | |||
for (var i = 0; i < _Groups.count; ++i) | |||
if (_Groups[i] == group) | |||
throw new Exception("temporary must be transformed in unit test"); | |||
_Groups.Add(group); | |||
} | |||
static GroupCompound() | |||
{ | |||
if (GroupCompoundInitializer.isInitializing3.Value == false) | |||
{ | |||
_Groups = new FasterList<ExclusiveGroupStruct>(1); | |||
var Group = new ExclusiveGroup(); | |||
_Groups.Add(Group); | |||
GroupCompound<G1, G2>.Add(Group); //<G1/G2> and <G2/G1> must share the same array | |||
GroupCompound<G1, G3>.Add(Group); | |||
GroupCompound<G2, G3>.Add(Group); | |||
//This is done here to be sure that the group is added once per group tag | |||
//(if done inside the previous group compound it would be added multiple times) | |||
GroupTag<G1>.Add(Group); | |||
GroupTag<G2>.Add(Group); | |||
GroupTag<G3>.Add(Group); | |||
#if DEBUG | |||
GroupMap.idToName[(uint) Group] = $"Compound: {typeof(G1).Name}-{typeof(G2).Name}-{typeof(G3).Name} ID {(uint)Group}"; | |||
#endif | |||
//all the combinations must share the same group | |||
GroupCompoundInitializer.isInitializing3.Value = true; | |||
GroupCompound<G3, G1, G2>._Groups = _Groups; | |||
GroupCompound<G2, G3, G1>._Groups = _Groups; | |||
GroupCompound<G3, G2, G1>._Groups = _Groups; | |||
GroupCompound<G1, G3, G2>._Groups = _Groups; | |||
GroupCompound<G2, G1, G3>._Groups = _Groups; | |||
GroupCompoundInitializer.isInitializing3.Value = false; | |||
} | |||
} | |||
} | |||
public abstract class GroupCompound<G1, G2> where G1 : GroupTag<G1> where G2 : GroupTag<G2> | |||
{ | |||
static readonly FasterList<ExclusiveGroupStruct> _Groups; | |||
public static FasterReadOnlyList<ExclusiveGroupStruct> Groups => | |||
new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups); | |||
public static BuildGroup BuildGroup => new BuildGroup(_Groups[0]); | |||
public static void Add(ExclusiveGroupStruct group) | |||
{ | |||
for (var i = 0; i < _Groups.count; ++i) | |||
if (_Groups[i] == group) | |||
throw new Exception("temporary must be transformed in unit test"); | |||
_Groups.Add(group); | |||
} | |||
static GroupCompound() | |||
{ | |||
if (GroupCompoundInitializer.isInitializing2.Value == false) | |||
{ | |||
var Group = new ExclusiveGroup(); | |||
_Groups = new FasterList<ExclusiveGroupStruct>(1); | |||
_Groups.Add(Group); | |||
//every abstract group preemptively adds this group, it may or may not be empty in future | |||
GroupTag<G1>.Add(Group); | |||
GroupTag<G2>.Add(Group); | |||
#if DEBUG | |||
GroupMap.idToName[(uint) Group] = $"Compound: {typeof(G1).Name}-{typeof(G2).Name} ID {(uint)Group}"; | |||
#endif | |||
GroupCompoundInitializer.isInitializing2.Value = true; | |||
GroupCompound<G2, G1>._Groups = _Groups; | |||
GroupCompoundInitializer.isInitializing2.Value = false; | |||
} | |||
} | |||
} | |||
/// <summary> | |||
///A Group Tag holds initially just a group, itself. However the number of groups can grow with the number of | |||
///combinations of GroupTags including this one. This because a GroupTag is an adjective and different entities | |||
///can use the same adjective together with other ones. However since I need to be able to iterate over all the | |||
///groups with the same adjective, a group tag needs to hold all the groups sharing it. | |||
/// </summary> | |||
/// <typeparam name="T"></typeparam> | |||
public abstract class GroupTag<T> where T : GroupTag<T> | |||
{ | |||
static readonly FasterList<ExclusiveGroupStruct> _Groups = new FasterList<ExclusiveGroupStruct>(1); | |||
public static FasterReadOnlyList<ExclusiveGroupStruct> Groups => | |||
new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups); | |||
public static BuildGroup BuildGroup => new BuildGroup(_Groups[0]); | |||
static GroupTag() | |||
{ | |||
var group = new ExclusiveGroup(); | |||
_Groups.Add(group); | |||
#if DEBUG | |||
GroupMap.idToName[(uint) group] = $"Compound: {typeof(T).Name} ID {(uint)group}"; | |||
#endif | |||
} | |||
//Each time a new combination of group tags is found a new group is added. | |||
internal static void Add(ExclusiveGroupStruct group) | |||
{ | |||
for (var i = 0; i < _Groups.count; ++i) | |||
if (_Groups[i] == group) | |||
throw new Exception("temporary must be transformed in unit test"); | |||
_Groups.Add(group); | |||
} | |||
} | |||
} |
@@ -1,6 +1,9 @@ | |||
namespace Svelto.ECS.Hybrid | |||
{ | |||
public interface IEntityViewComponent:IEntityComponent, INeedEGID | |||
public interface IManagedComponent:IEntityComponent | |||
{} | |||
public interface IEntityViewComponent:IManagedComponent, INeedEGID | |||
{} | |||
} | |||
@@ -0,0 +1,9 @@ | |||
using System; | |||
namespace Svelto.ECS | |||
{ | |||
public interface IDisposingEngine: IDisposable | |||
{ | |||
bool isDisposing { set; } | |||
} | |||
} |
@@ -1,3 +1,5 @@ | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS.Internal | |||
{ | |||
public interface IReactEngine: IEngine | |||
@@ -14,4 +16,20 @@ namespace Svelto.ECS | |||
{ | |||
public interface IEngine | |||
{} | |||
public interface IReactOnAddAndRemove<T> : IReactOnAddAndRemove where T : IEntityComponent | |||
{ | |||
void Add(ref T entityComponent, EGID egid); | |||
void Remove(ref T entityComponent, EGID egid); | |||
} | |||
public interface IReactOnSwap<T> : IReactOnSwap where T : IEntityComponent | |||
{ | |||
void MovedTo(ref T entityComponent, ExclusiveGroupStruct previousGroup, EGID egid); | |||
} | |||
public interface IReactOnSubmission:IReactEngine | |||
{ | |||
void EntitiesSubmitted(); | |||
} | |||
} |
@@ -1,9 +0,0 @@ | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
public delegate void ExecuteOnAllEntitiesAction<T, W>(IBuffer<T> prefabStruct, ExclusiveGroupStruct group, | |||
uint count, EntitiesDB db, ref W instances); | |||
public delegate void ExecuteOnAllEntitiesAction<T>(IBuffer<T> entities, ExclusiveGroupStruct group, | |||
uint count, EntitiesDB db); | |||
} |
@@ -1,12 +1,12 @@ | |||
namespace Svelto.ECS | |||
{ | |||
///<summary>EntityComponent MUST implement IEntityComponent</summary> | |||
///<summary>Entity Components MUST implement IEntityComponent</summary> | |||
public interface IEntityComponent | |||
{ | |||
} | |||
/// <summary> | |||
/// use INeedEGID on an IEntityComponent only if you need the EGID | |||
/// use INeedEGID on an IEntityComponent only if you need the EGID. consider using EGIDComponent instead | |||
/// </summary> | |||
public interface INeedEGID | |||
{ | |||
@@ -43,22 +43,14 @@ namespace Svelto.ECS | |||
/// <param name="groupStructId"></param> | |||
/// <param name="ed"></param> | |||
/// <param name="implementors"></param> | |||
EntityComponentInitializer BuildEntity<T>(uint entityID, ExclusiveGroupStruct groupStructId, | |||
EntityComponentInitializer BuildEntity<T>(uint entityID, BuildGroup groupStructId, | |||
IEnumerable<object> implementors = null) | |||
where T : IEntityDescriptor, new(); | |||
EntityComponentInitializer BuildEntity<T>(EGID egid, IEnumerable<object> implementors = null) | |||
where T : IEntityDescriptor, new(); | |||
/// <summary> | |||
/// When the type of the entity is not known (this is a special case!) an EntityDescriptorInfo | |||
/// can be built in place of the generic parameter T. | |||
/// </summary> | |||
/// <param name="entityID"></param> | |||
/// <param name="entityDescriptor"></param> | |||
/// <param name="implementors"></param> | |||
/// | |||
EntityComponentInitializer BuildEntity<T>(uint entityID, ExclusiveGroupStruct groupStructId, | |||
EntityComponentInitializer BuildEntity<T>(uint entityID, BuildGroup groupStructId, | |||
T descriptorEntity, IEnumerable<object> implementors = null) | |||
where T : IEntityDescriptor; | |||
@@ -68,7 +60,7 @@ namespace Svelto.ECS | |||
EntityComponentInitializer BuildEntity | |||
(EGID egid, IComponentBuilder[] componentsToBuild, Type type, IEnumerable<object> implementors = null); | |||
#if UNITY_BURST | |||
#if UNITY_NATIVE | |||
NativeEntityFactory ToNative<T>(string memberName) where T : IEntityDescriptor, new(); | |||
#endif | |||
} |
@@ -5,29 +5,26 @@ namespace Svelto.ECS | |||
//being entity ID globally not unique, the group must be specified when | |||
//an entity is removed. Not specifying the group will attempt to remove | |||
//the entity from the special standard group. | |||
void RemoveEntity<T>(uint entityID, ExclusiveGroupStruct groupID) where T : IEntityDescriptor, new(); | |||
void RemoveEntity<T>(uint entityID, BuildGroup groupID) where T : IEntityDescriptor, new(); | |||
void RemoveEntity<T>(EGID entityegid) where T : IEntityDescriptor, new(); | |||
void RemoveAllEntities<T>(ExclusiveGroupStruct group) where T : IEntityDescriptor, new(); | |||
void RemoveAllEntities<T>() where T : IEntityDescriptor, new(); | |||
void RemoveEntitiesFromGroup(BuildGroup groupID); | |||
void RemoveGroupAndEntities(ExclusiveGroupStruct groupID); | |||
void SwapEntitiesInGroup<T>(BuildGroup fromGroupID, BuildGroup toGroupID) where T : IEntityDescriptor, new(); | |||
void SwapEntitiesInGroup<T>(ExclusiveGroupStruct fromGroupID, ExclusiveGroupStruct toGroupID); | |||
void SwapEntityGroup<T>(uint entityID, ExclusiveGroupStruct fromGroupID, ExclusiveGroupStruct toGroupID) | |||
void SwapEntityGroup<T>(uint entityID, BuildGroup fromGroupID, BuildGroup toGroupID) | |||
where T : IEntityDescriptor, new(); | |||
void SwapEntityGroup<T>(EGID fromID, ExclusiveGroupStruct toGroupID) where T : IEntityDescriptor, new(); | |||
void SwapEntityGroup<T>(EGID fromID, BuildGroup toGroupID) where T : IEntityDescriptor, new(); | |||
void SwapEntityGroup<T>(EGID fromID, ExclusiveGroupStruct toGroupID, ExclusiveGroupStruct mustBeFromGroup) | |||
void SwapEntityGroup<T>(EGID fromID, BuildGroup toGroupID, BuildGroup mustBeFromGroup) | |||
where T : IEntityDescriptor, new(); | |||
void SwapEntityGroup<T>(EGID fromID, EGID toId) where T : IEntityDescriptor, new(); | |||
void SwapEntityGroup<T>(EGID fromID, EGID toId, ExclusiveGroupStruct mustBeFromGroup) | |||
void SwapEntityGroup<T>(EGID fromID, EGID toId, BuildGroup mustBeFromGroup) | |||
where T : IEntityDescriptor, new(); | |||
#if UNITY_BURST | |||
#if UNITY_NATIVE | |||
NativeEntityRemove ToNativeRemove<T>(string memberName) where T : IEntityDescriptor, new(); | |||
NativeEntitySwap ToNativeSwap<T>(string memberName) where T : IEntityDescriptor, new(); | |||
#endif | |||
@@ -1,10 +0,0 @@ | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public interface IReactOnAddAndRemove<T> : IReactOnAddAndRemove where T : IEntityComponent | |||
{ | |||
void Add(ref T entityComponent, EGID egid); | |||
void Remove(ref T entityComponent, EGID egid); | |||
} | |||
} |
@@ -1,9 +0,0 @@ | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public interface IReactOnSwap<T> : IReactOnSwap where T : IEntityComponent | |||
{ | |||
void MovedTo(ref T entityComponent, ExclusiveGroupStruct previousGroup, EGID egid); | |||
} | |||
} |
@@ -14,7 +14,7 @@ namespace Svelto.ECS | |||
static NamedExclusiveGroup() | |||
{ | |||
#if DEBUG | |||
GroupMap.idToName[(uint) Group] = name; | |||
GroupMap.idToName[(uint) Group] = $"{name} ID {(uint)Group}"; | |||
#endif | |||
} | |||
// protected NamedExclusiveGroup(string recognizeAs) : base(recognizeAs) {} | |||