@@ -1 +1 @@ | |||
Subproject commit 800c1a9abe35986fabb6562178e27d3b17c34b5c | |||
Subproject commit 7d7aaba893e8aa0a24b8c36b46a8ed805a8f514c |
@@ -1,3 +0,0 @@ | |||
# These are supported funding model platforms | |||
custom: https://www.paypal.me/smandala |
@@ -1,3 +0,0 @@ | |||
/obj | |||
/bin/Release/netstandard2.0 | |||
/bin/Debug/netstandard2.0 |
@@ -1,3 +0,0 @@ | |||
[submodule "Svelto.Common"] | |||
path = Svelto.Common | |||
url = https://github.com/sebas77/Svelto.Common |
@@ -1,29 +1,30 @@ | |||
#if DEBUG && !PROFILE_SVELTO | |||
using System; | |||
using System.Collections.Generic; | |||
using Svelto.DataStructures; | |||
#else | |||
using System; | |||
using System.Diagnostics; | |||
#endif | |||
namespace Svelto.ECS | |||
{ | |||
/// <summary> | |||
/// Note: this check doesn't catch the case when an add and remove is done on the same entity before the next | |||
/// submission. Two operations on the same entity are not allowed between submissions. | |||
/// Note: this check doesn't catch the case when an add and remove is done on the same entity before the nextI am | |||
/// submission. Two operations on the same entity are not allowed between submissions. | |||
/// </summary> | |||
public partial class EnginesRoot | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
void CheckRemoveEntityID(EGID egid) | |||
#if DEBUG && !PROFILE_SVELTO | |||
void CheckRemoveEntityID(EGID egid, Type entityComponent, string caller = "") | |||
{ | |||
// Console.LogError("<color=orange>removed</color>".FastConcat(egid.ToString())); | |||
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(egid.entityID) | |||
.FastConcat(" groupid: ") | |||
.FastConcat(egid.groupID)); | |||
.FastConcat(" caller: ", caller, " ") | |||
.FastConcat(egid.entityID).FastConcat(" groupid: ").FastConcat(egid.groupID.ToName()) | |||
.FastConcat(" type: ").FastConcat(entityComponent != null ? entityComponent.Name : "not available")); | |||
hash.Remove(egid.entityID); | |||
@@ -33,46 +34,39 @@ namespace Svelto.ECS | |||
else | |||
{ | |||
throw new ECSException("Entity with not found ID is about to be removed: id: " | |||
.FastConcat(egid.entityID) | |||
.FastConcat(" groupid: ") | |||
.FastConcat(egid.groupID)); | |||
.FastConcat(" caller: ", caller, " ") | |||
.FastConcat(egid.entityID).FastConcat(" groupid: ").FastConcat(egid.groupID.ToName()) | |||
.FastConcat(" type: ").FastConcat(entityComponent != null ? entityComponent.Name : "not available")); | |||
} | |||
} | |||
void CheckAddEntityID(EGID egid) | |||
void CheckAddEntityID(EGID egid, Type entityComponent, 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) | |||
.FastConcat("'")); | |||
} | |||
.FastConcat("' id: '").FastConcat(egid.entityID).FastConcat("' groupid: '") | |||
.FastConcat(egid.groupID.ToName()).FastConcat(" ", entityComponent != null ? entityComponent.Name : "not available") | |||
.FastConcat("'")); | |||
hash.Add(egid.entityID); | |||
} | |||
void RemoveGroupID(ExclusiveGroupStruct groupID) | |||
{ | |||
_idCheckers.Remove(groupID); | |||
} | |||
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) | |||
void CheckRemoveEntityID(EGID egid, Type entityComponent, string caller = "") | |||
{ | |||
} | |||
[Conditional("_CHECKS_DISABLED")] | |||
void CheckAddEntityID(EGID egid) | |||
void CheckAddEntityID(EGID egid, Type entityComponen, string caller = "") | |||
{ | |||
} | |||
@@ -82,4 +76,4 @@ namespace Svelto.ECS | |||
} | |||
#endif | |||
} | |||
} | |||
} |
@@ -7,7 +7,7 @@ using System.Reflection; | |||
namespace Svelto.ECS | |||
{ | |||
internal static class EntityBuilderUtilities | |||
internal static class ComponentBuilderUtilities | |||
{ | |||
const string MSG = "Entity Structs field and Entity View Struct components must hold value types."; | |||
@@ -48,16 +48,16 @@ namespace Svelto.ECS | |||
if (fields.Length < 1) | |||
{ | |||
ProcessError("Entity View Structs must hold only entity components interfaces.", entityComponentType); | |||
ProcessError("No valid fields found in Entity View Struct", entityComponentType); | |||
} | |||
for (int i = fields.Length - 1; i >= 0; --i) | |||
{ | |||
FieldInfo fieldInfo = fields[i]; | |||
if (fieldInfo.FieldType.IsInterfaceEx() == false) | |||
if (fieldInfo.FieldType.IsInterfaceEx() == false && fieldInfo.FieldType.IsValueTypeEx() == false) | |||
{ | |||
ProcessError("Entity View Structs must hold only entity components interfaces.", | |||
ProcessError("Entity View Structs must hold only public interfaces or value type fields.", | |||
entityComponentType); | |||
} | |||
@@ -120,7 +120,7 @@ namespace Svelto.ECS | |||
static readonly Type SERIALIZABLE_ENTITY_STRUCT = typeof(SerializableEntityComponent); | |||
static readonly Type STRINGTYPE = typeof(string); | |||
internal static readonly Type ENTITY_STRUCT_INFO_VIEW = typeof(EntityInfoComponentView); | |||
internal static readonly Type ENTITY_STRUCT_INFO_VIEW = typeof(EntityInfoViewComponent); | |||
} | |||
public class EntityComponentException : Exception |
@@ -1,6 +1,7 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Reflection; | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Hybrid; | |||
using Svelto.ECS.Internal; | |||
@@ -13,11 +14,6 @@ namespace Svelto.ECS | |||
public ComponentBuilder() | |||
{ | |||
_initializer = DEFAULT_IT; | |||
EntityBuilderUtilities.CheckFields(ENTITY_COMPONENT_TYPE, IS_ENTITY_VIEW_COMPONENT); | |||
if (IS_ENTITY_VIEW_COMPONENT) | |||
EntityViewComponentCache.InitCache(); | |||
} | |||
public ComponentBuilder(in T initializer) : this() | |||
@@ -36,8 +32,6 @@ namespace Svelto.ECS | |||
T entityComponent = default; | |||
if (IS_ENTITY_VIEW_COMPONENT) | |||
{ | |||
DBC.ECS.Check.Require(implementors != null, | |||
$"Implementors not found while building an EntityComponent `{typeof(T)}`"); | |||
DBC.ECS.Check.Require(castedDic.ContainsKey(egid.entityID) == false, | |||
$"building an entity with already used entity id! id: '{(ulong) egid}', {ENTITY_COMPONENT_NAME}"); | |||
@@ -82,21 +76,35 @@ namespace Svelto.ECS | |||
IS_ENTITY_VIEW_COMPONENT = typeof(IEntityViewComponent).IsAssignableFrom(ENTITY_COMPONENT_TYPE); | |||
HAS_EGID = typeof(INeedEGID).IsAssignableFrom(ENTITY_COMPONENT_TYPE); | |||
ENTITY_COMPONENT_NAME = ENTITY_COMPONENT_TYPE.ToString(); | |||
#if UNITY_ECS | |||
EntityComponentIDMap.Register<T>(new Filler<T>()); | |||
#endif | |||
var IS_UNMANAGED = ENTITY_COMPONENT_TYPE.IsUnmanaged(); | |||
if (IS_UNMANAGED) | |||
EntityComponentIDMap.Register<T>(new Filler<T>()); | |||
SetEGIDWithoutBoxing<T>.Warmup(); | |||
ComponentBuilderUtilities.CheckFields(ENTITY_COMPONENT_TYPE, IS_ENTITY_VIEW_COMPONENT); | |||
if (IS_ENTITY_VIEW_COMPONENT) | |||
EntityViewComponentCache.InitCache(); | |||
else | |||
{ | |||
if (ENTITY_COMPONENT_TYPE != ComponentBuilderUtilities.ENTITY_STRUCT_INFO_VIEW && ENTITY_COMPONENT_TYPE.IsUnmanaged() == false) | |||
throw new Exception($"Entity Component check failed, unexpected struct type (must be unmanaged) {ENTITY_COMPONENT_TYPE}"); | |||
} | |||
} | |||
readonly T _initializer; | |||
internal static readonly Type ENTITY_COMPONENT_TYPE; | |||
public static readonly bool HAS_EGID; | |||
public static readonly bool HAS_EGID; | |||
internal static readonly bool IS_ENTITY_VIEW_COMPONENT; | |||
static readonly T DEFAULT_IT; | |||
static readonly bool IS_ENTITY_VIEW_COMPONENT; | |||
static readonly string ENTITY_COMPONENT_NAME; | |||
static readonly T DEFAULT_IT; | |||
static readonly string ENTITY_COMPONENT_NAME; | |||
static class EntityViewComponentCache | |||
{ | |||
internal static readonly FasterList<KeyValuePair<Type, FastInvokeActionCast<T>>> cachedFields; | |||
@@ -116,11 +124,13 @@ namespace Svelto.ECS | |||
for (var i = fields.Length - 1; i >= 0; --i) | |||
{ | |||
var field = fields[i]; | |||
DBC.ECS.Check.Require(field.FieldType.IsInterface == true, "Entity View Components must hold only public interfaces"); | |||
var setter = FastInvoke<T>.MakeSetter(field); | |||
if (field.FieldType.IsInterface == true) | |||
{ | |||
var setter = FastInvoke<T>.MakeSetter(field); | |||
//for each interface, cache the setter for this type | |||
cachedFields.Add(new KeyValuePair<Type, FastInvokeActionCast<T>>(field.FieldType, setter)); | |||
//for each interface, cache the setter for this type | |||
cachedFields.Add(new KeyValuePair<Type, FastInvokeActionCast<T>>(field.FieldType, setter)); | |||
} | |||
} | |||
cachedTypes = new Dictionary<Type, Type[]>(); | |||
@@ -12,13 +12,13 @@ namespace Svelto.ECS.DataStructures.Unity | |||
public unsafe struct AtomicNativeBags:IDisposable | |||
{ | |||
public const int DefaultThreadIndex = -1; | |||
public const int MinThreadIndex = DefaultThreadIndex; | |||
const int MinThreadIndex = DefaultThreadIndex; | |||
#if UNITY_ECS | |||
#if UNITY_COLLECTIONS | |||
[global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction] | |||
#endif | |||
NativeBag* _data; | |||
public readonly Allocator Allocator; | |||
readonly NativeBag* _data; | |||
readonly Allocator _allocator; | |||
readonly uint _threadsCount; | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
@@ -26,20 +26,20 @@ namespace Svelto.ECS.DataStructures.Unity | |||
public AtomicNativeBags(Common.Allocator allocator, uint threadsCount) | |||
{ | |||
Allocator = allocator; | |||
_allocator = allocator; | |||
_threadsCount = threadsCount; | |||
var bufferSize = MemoryUtilities.SizeOf<NativeBag>(); | |||
var bufferCount = _threadsCount; | |||
var allocationSize = bufferSize * bufferCount; | |||
var ptr = (byte*)MemoryUtilities.Alloc<int>((uint) allocationSize, allocator); | |||
var ptr = (byte*)MemoryUtilities.Alloc((uint) allocationSize, allocator); | |||
MemoryUtilities.MemClear((IntPtr) ptr, (uint) allocationSize); | |||
for (int i = 0; i < bufferCount; i++) | |||
{ | |||
var bufferPtr = (NativeBag*)(ptr + bufferSize * i); | |||
var buffer = new NativeBag((uint) i, allocator); | |||
var buffer = new NativeBag(allocator); | |||
MemoryUtilities.CopyStructureToPtr(ref buffer, (IntPtr) bufferPtr); | |||
} | |||
@@ -60,7 +60,7 @@ namespace Svelto.ECS.DataStructures.Unity | |||
{ | |||
GetBuffer(i).Dispose(); | |||
} | |||
MemoryUtilities.Free((IntPtr) _data, Allocator); | |||
MemoryUtilities.Free((IntPtr) _data, _allocator); | |||
} | |||
public void Clear() | |||
@@ -264,11 +264,6 @@ namespace Svelto.ECS.Internal | |||
get => _implementation.unsafeValues; | |||
} | |||
public object GenerateSentinel() | |||
{ | |||
throw new NotImplementedException(); | |||
} | |||
public SetDictionary<TValue> implementation => _implementation; | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
@@ -7,34 +7,27 @@ namespace Svelto.ECS.Internal | |||
public interface ITypeSafeDictionary<TValue> : ITypeSafeDictionary where TValue : IEntityComponent | |||
{ | |||
void Add(uint egidEntityId, in TValue entityComponent); | |||
ref TValue GetValueByRef(uint key); | |||
ref TValue this[uint idEntityId] { get; } | |||
bool TryGetValue(uint entityId, out TValue item); | |||
ref TValue GetOrCreate(uint idEntityId); | |||
TValue[] GetValuesArray(out uint count); | |||
TValue[] unsafeValues { get; } | |||
object GenerateSentinel(); | |||
IBuffer<TValue> GetValues(out uint count); | |||
ref TValue GetDirectValueByRef(uint key); | |||
} | |||
public interface ITypeSafeDictionary | |||
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, | |||
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); | |||
void SetCapacity(uint size); | |||
@@ -12,12 +12,12 @@ namespace Svelto.ECS.DataStructures | |||
/// 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 NativeRingBuffer because it can also be used as | |||
/// 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 | |||
{ | |||
#if UNITY_ECS | |||
#if UNITY_COLLECTIONS | |||
[global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction] | |||
#endif | |||
unsafe UnsafeBlob* _queue; | |||
@@ -73,31 +73,11 @@ namespace Svelto.ECS.DataStructures | |||
unsafe | |||
{ | |||
var sizeOf = MemoryUtilities.SizeOf<UnsafeBlob>(); | |||
var listData = (UnsafeBlob*) MemoryUtilities.Alloc<UnsafeBlob>((uint) sizeOf, allocator); | |||
var listData = (UnsafeBlob*) MemoryUtilities.Alloc((uint) sizeOf, allocator); | |||
//clear to nullify the pointers | |||
MemoryUtilities.MemClear((IntPtr) listData, (uint) sizeOf); | |||
listData->allocator = allocator; | |||
#if DEBUG && !PROFILE_SVELTO | |||
listData->id = 0xDEADBEEF; | |||
#endif | |||
_queue = listData; | |||
} | |||
} | |||
public NativeBag(uint bufferID, Allocator allocator) | |||
{ | |||
unsafe | |||
{ | |||
var sizeOf = MemoryUtilities.SizeOf<UnsafeBlob>(); | |||
var listData = (UnsafeBlob*) MemoryUtilities.Alloc<UnsafeBlob>((uint) sizeOf, allocator); | |||
//clear to nullify the pointers | |||
MemoryUtilities.MemClear((IntPtr) listData, (uint) sizeOf); | |||
listData->allocator = allocator; | |||
#if DEBUG && !PROFILE_SVELTO | |||
listData->id = bufferID; | |||
#endif | |||
_queue = listData; | |||
} | |||
} | |||
@@ -113,7 +93,7 @@ namespace Svelto.ECS.DataStructures | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref T ReserveEnqueue<T>(out UnsafeArrayIndex index) where T : unmanaged | |||
public ref T ReserveEnqueue<T>(out UnsafeArrayIndex index) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
@@ -123,14 +103,14 @@ namespace Svelto.ECS.DataStructures | |||
#endif | |||
var sizeOf = MemoryUtilities.SizeOf<T>(); | |||
if (_queue->space - sizeOf < 0) | |||
_queue->Realloc<int>((uint) ((_queue->capacity + sizeOf) * 1.5f)); | |||
_queue->Realloc((uint) ((_queue->capacity + sizeOf) * 2.0f)); | |||
return ref _queue->Reserve<T>(out index); | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Enqueue<T>(in T item) where T : unmanaged | |||
public void Enqueue<T>(in T item) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
@@ -140,7 +120,7 @@ namespace Svelto.ECS.DataStructures | |||
#endif | |||
var sizeOf = MemoryUtilities.SizeOf<T>(); | |||
if (_queue->space - sizeOf < 0) | |||
_queue->Realloc<int>((uint) ((_queue->capacity + sizeOf) * 1.5f)); | |||
_queue->Realloc((uint) ((_queue->capacity + MemoryUtilities.Align4((uint) sizeOf)) * 2.0f)); | |||
_queue->Write(item); | |||
} | |||
@@ -159,7 +139,7 @@ namespace Svelto.ECS.DataStructures | |||
} | |||
} | |||
public T Dequeue<T>() where T : unmanaged | |||
public T Dequeue<T>() where T : struct | |||
{ | |||
unsafe | |||
{ | |||
@@ -167,7 +147,7 @@ namespace Svelto.ECS.DataStructures | |||
} | |||
} | |||
public ref T AccessReserved<T>(UnsafeArrayIndex reserverIndex) where T : unmanaged | |||
public ref T AccessReserved<T>(UnsafeArrayIndex reserverIndex) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
@@ -7,8 +7,8 @@ namespace Svelto.ECS.DataStructures | |||
{ | |||
public struct NativeDynamicArray : IDisposable | |||
{ | |||
#if UNITY_ECS | |||
[global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction] | |||
#if UNITY_COLLECTIONS | |||
[global::Unity.Burst.NoAlias] [global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction] | |||
#endif | |||
unsafe UnsafeArray* _list; | |||
#if DEBUG && !PROFILE_SVELTO | |||
@@ -16,7 +16,7 @@ namespace Svelto.ECS.DataStructures | |||
#endif | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public uint Count<T>() where T:unmanaged | |||
public int Count<T>() where T : struct | |||
{ | |||
unsafe | |||
{ | |||
@@ -26,13 +26,13 @@ namespace Svelto.ECS.DataStructures | |||
if (hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not excepted type used"); | |||
#endif | |||
return (uint) (_list->count / MemoryUtilities.SizeOf<T>()); | |||
#endif | |||
return (_list->count / MemoryUtilities.SizeOf<T>()); | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public uint Capacity<T>() where T:unmanaged | |||
public int Capacity<T>() where T : struct | |||
{ | |||
unsafe | |||
{ | |||
@@ -42,12 +42,12 @@ namespace Svelto.ECS.DataStructures | |||
if (hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not excepted type used"); | |||
#endif | |||
return (uint) (_list->capacity / MemoryUtilities.SizeOf<T>()); | |||
#endif | |||
return (_list->capacity / MemoryUtilities.SizeOf<T>()); | |||
} | |||
} | |||
public static NativeDynamicArray Alloc<T>(Allocator allocator, uint newLength = 0) where T : unmanaged | |||
public static NativeDynamicArray Alloc<T>(Allocator allocator, uint newLength = 0) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
@@ -55,17 +55,16 @@ namespace Svelto.ECS.DataStructures | |||
#if DEBUG && !PROFILE_SVELTO | |||
rtnStruc.hashType = TypeHash<T>.hash; | |||
#endif | |||
var sizeOf = MemoryUtilities.SizeOf<T>(); | |||
var sizeOf = MemoryUtilities.SizeOf<T>(); | |||
uint pointerSize = (uint) MemoryUtilities.SizeOf<UnsafeArray>(); | |||
UnsafeArray* listData = (UnsafeArray*) MemoryUtilities.Alloc(pointerSize, allocator); | |||
uint pointerSize = (uint) MemoryUtilities.SizeOf<UnsafeArray>(); | |||
UnsafeArray* listData = | |||
(UnsafeArray*) MemoryUtilities.Alloc<UnsafeArray>(pointerSize, allocator); | |||
//clear to nullify the pointers | |||
MemoryUtilities.MemClear((IntPtr) listData, pointerSize); | |||
listData->allocator = allocator; | |||
listData->Realloc<T>((uint) (newLength * sizeOf)); | |||
listData->Realloc((uint) (newLength * sizeOf)); | |||
rtnStruc._list = listData; | |||
@@ -74,7 +73,7 @@ namespace Svelto.ECS.DataStructures | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref T Get<T>(uint index) where T : unmanaged | |||
public ref T Get<T>(uint index) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
@@ -91,7 +90,7 @@ namespace Svelto.ECS.DataStructures | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Set<T>(uint index, in T value) where T : unmanaged | |||
public void Set<T>(uint index, in T value) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
@@ -101,24 +100,24 @@ namespace Svelto.ECS.DataStructures | |||
if (hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not excepted type used"); | |||
if (index >= Capacity<T>()) | |||
throw new Exception($"NativeDynamicArray: out of bound access, index {index} count {Count<T>()}"); | |||
#endif | |||
throw new Exception($"NativeDynamicArray: out of bound access, index {index} capacity {Capacity<T>()}"); | |||
#endif | |||
_list->Set(index, value); | |||
} | |||
} | |||
public unsafe void Dispose() | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
#endif | |||
_list->Dispose(); | |||
_list = null; | |||
_list->Dispose(); | |||
_list = null; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Add<T>(in T item) where T : unmanaged | |||
public void Add<T>(in T item) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
@@ -129,16 +128,52 @@ namespace Svelto.ECS.DataStructures | |||
throw new Exception("NativeDynamicArray: not excepted type used"); | |||
#endif | |||
var structSize = (uint) MemoryUtilities.SizeOf<T>(); | |||
if (_list->space - (int)structSize < 0) | |||
_list->Realloc<T>((uint) ((Count<T>() + 1) * structSize * 1.5f)); | |||
if (_list->space - (int) structSize < 0) | |||
_list->Realloc((uint) (((uint) ((Count<T>() + 1) * 1.5f) * (float) structSize))); | |||
_list->Add(item); | |||
} | |||
} | |||
public void Grow<T>(uint newCapacity) 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 excepted 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); | |||
} | |||
} | |||
public void SetCount<T>(uint count) 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 excepted type used"); | |||
#endif | |||
uint structSize = (uint) MemoryUtilities.SizeOf<T>(); | |||
uint size = (uint) (count * structSize); | |||
_list->SetCountTo((uint) size); | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void AddWithoutGrow<T>(in T item) where T : unmanaged | |||
public void AddWithoutGrow<T>(in T item) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
@@ -157,6 +192,29 @@ namespace Svelto.ECS.DataStructures | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void UnorderedRemoveAt<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 excepted type used"); | |||
if (Count<T>() == 0) | |||
throw new Exception("NativeDynamicArray: empty array invalid operation"); | |||
#endif | |||
var count = Count<T>() - 1; | |||
if (index < count) | |||
{ | |||
Set<T>(index, Get<T>((uint) count)); | |||
} | |||
_list->Pop<T>(); | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Clear() | |||
{ | |||
@@ -182,6 +240,21 @@ namespace Svelto.ECS.DataStructures | |||
return (T*) _list->ptr; | |||
} | |||
public IntPtr ToIntPTR<T>() 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 excepted type used"); | |||
#endif | |||
return (IntPtr) _list->ptr; | |||
} | |||
} | |||
public T[] ToManagedArray<T>() where T : unmanaged | |||
{ | |||
unsafe | |||
@@ -193,17 +266,19 @@ namespace Svelto.ECS.DataStructures | |||
throw new Exception("NativeDynamicArray: not excepted type used"); | |||
#endif | |||
var ret = new T[Count<T>()]; | |||
var count = Count<T>(); | |||
var ret = new T[count]; | |||
var lengthToCopyInBytes = count * MemoryUtilities.SizeOf<T>(); | |||
fixed (void * handle = ret) | |||
fixed (void* handle = ret) | |||
{ | |||
Buffer.MemoryCopy(_list->ptr, handle, _list->count, _list->count); | |||
Unsafe.CopyBlock(handle, _list->ptr, (uint) lengthToCopyInBytes); | |||
} | |||
return ret; | |||
} | |||
} | |||
public T[] ToManagedArrayUntrimmed<T>() where T : unmanaged | |||
{ | |||
unsafe | |||
@@ -213,17 +288,82 @@ namespace Svelto.ECS.DataStructures | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
if (hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not excepted type used"); | |||
#endif | |||
var ret = new T[Capacity<T>()]; | |||
var capacity = Capacity<T>(); | |||
var lengthToCopyInBytes = capacity * MemoryUtilities.SizeOf<T>(); | |||
var ret = new T[capacity]; | |||
fixed (void * handle = ret) | |||
fixed (void* handle = ret) | |||
{ | |||
Buffer.MemoryCopy(_list->ptr, handle, _list->capacity, _list->capacity); | |||
Unsafe.CopyBlock(handle, _list->ptr, (uint) lengthToCopyInBytes); | |||
} | |||
return ret; | |||
} | |||
} | |||
public void RemoveAt<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 excepted type used"); | |||
#endif | |||
var sizeOf = MemoryUtilities.SizeOf<T>(); | |||
//Unsafe.CopyBlock may not be memory overlapping safe (memcpy vs memmove) | |||
Buffer.MemoryCopy(_list->ptr + (index + 1) * sizeOf, _list->ptr + index * sizeOf, _list->count | |||
, (uint) ((Count<T>() - (index + 1)) * sizeOf)); | |||
_list->Pop<T>(); | |||
} | |||
} | |||
public void MemClear() | |||
{ | |||
unsafe | |||
{ | |||
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(); } | |||
} | |||
} | |||
} |
@@ -0,0 +1,23 @@ | |||
#if UNITY_COLLECTIONS | |||
using Unity.Collections; | |||
using Unity.Collections.LowLevel.Unsafe; | |||
namespace Svelto.ECS.DataStructures | |||
{ | |||
public static class NativeDynamicArrayUnityExtension | |||
{ | |||
public static NativeArray<T> ToNativeArray<T>(this NativeDynamicArray array) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
var nativeArray = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<T>( | |||
(void*) array.ToIntPTR<T>(), (int) array.Count<T>(), Allocator.None); | |||
#if ENABLE_UNITY_COLLECTIONS_CHECKS | |||
NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref nativeArray, AtomicSafetyHandle.Create()); | |||
#endif | |||
return nativeArray; | |||
} | |||
} | |||
} | |||
} | |||
#endif |
@@ -6,7 +6,7 @@ namespace Svelto.ECS.DataStructures | |||
{ | |||
public struct SharedNativeInt: IDisposable | |||
{ | |||
#if UNITY_ECS | |||
#if UNITY_COLLECTIONS | |||
[global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction] | |||
#endif | |||
unsafe int* data; | |||
@@ -1,70 +0,0 @@ | |||
using System; | |||
using System.Runtime.CompilerServices; | |||
using System.Runtime.InteropServices; | |||
using System.Threading; | |||
namespace Svelto.ECS.DataStructures | |||
{ | |||
public struct SharedNativeUInt: IDisposable | |||
{ | |||
#if UNITY_ECS | |||
[global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction] | |||
#endif | |||
unsafe uint* data; | |||
public SharedNativeUInt(uint t) | |||
{ | |||
unsafe | |||
{ | |||
data = (uint*) Marshal.AllocHGlobal(sizeof(uint)); | |||
*data = t; | |||
} | |||
} | |||
public static implicit operator uint(SharedNativeUInt t) | |||
{ | |||
unsafe | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (t.data == null) | |||
throw new Exception("using disposed SharedUInt"); | |||
#endif | |||
return *t.data; | |||
} | |||
} | |||
public void Dispose() | |||
{ | |||
unsafe | |||
{ | |||
if (data != null) | |||
{ | |||
Marshal.FreeHGlobal((IntPtr) data); | |||
data = null; | |||
} | |||
} | |||
} | |||
public void Decrement() | |||
{ | |||
unsafe | |||
{ | |||
int result = Interlocked.Decrement(ref Unsafe.As<uint, int>(ref *data)); | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (result < 0) | |||
throw new Exception("can't have negative numbers"); | |||
#endif | |||
} | |||
} | |||
public void Increment() | |||
{ | |||
unsafe | |||
{ | |||
Interlocked.Increment(ref Unsafe.As<uint, int>(ref *data)); | |||
} | |||
} | |||
} | |||
} |
@@ -2,202 +2,468 @@ | |||
using System.Runtime.CompilerServices; | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Hybrid; | |||
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 = | |||
_type.IsUnmanaged() && (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; | |||
public TypeSafeDictionary(uint size) | |||
{ | |||
_implementation = new FasterDictionary<uint, TValue>(size); | |||
if (_isUmanaged) | |||
implUnmgd = new SveltoDictionary<uint, TValue, NativeStrategy<FasterDictionaryNode<uint>>, NativeStrategy<TValue>>(size); | |||
else | |||
{ | |||
implMgd = new SveltoDictionary<uint, TValue, NativeStrategy<FasterDictionaryNode<uint>>, ManagedStrategy<TValue>>(size); | |||
} | |||
} | |||
public TypeSafeDictionary() | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Add(uint egidEntityId, in TValue entityComponent) | |||
{ | |||
_implementation = new FasterDictionary<uint, TValue>(1); | |||
if (_isUmanaged) | |||
implUnmgd.Add(egidEntityId, entityComponent); | |||
else | |||
implMgd.Add(egidEntityId, entityComponent); | |||
} | |||
/// <summary> | |||
/// Add entities from external typeSafeDictionary | |||
/// Add entities from external typeSafeDictionary | |||
/// </summary> | |||
/// <param name="entitiesToSubmit"></param> | |||
/// <param name="groupId"></param> | |||
/// <exception cref="TypeSafeDictionaryException"></exception> | |||
public void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId) | |||
public void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId) | |||
{ | |||
var typeSafeDictionary = entitiesToSubmit as TypeSafeDictionary<TValue>; | |||
if (_isUmanaged) | |||
{ | |||
var typeSafeDictionary = (entitiesToSubmit as TypeSafeDictionary<TValue>).implUnmgd; | |||
foreach (var tuple in typeSafeDictionary) | |||
foreach (var tuple in typeSafeDictionary) | |||
try | |||
{ | |||
if (_hasEgid) | |||
SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing( | |||
ref tuple.Value, new EGID(tuple.Key, groupId)); | |||
implUnmgd.Add(tuple.Key, tuple.Value); | |||
} | |||
catch (Exception e) | |||
{ | |||
Console.LogException( | |||
e, "trying to add an EntityComponent with the same ID more than once Entity: ".FastConcat(typeof(TValue).ToString()).FastConcat(", group ").FastConcat(groupId).FastConcat(", id ").FastConcat(tuple.Key)); | |||
throw; | |||
} | |||
} | |||
else | |||
{ | |||
try | |||
{ | |||
if (_hasEgid) | |||
SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref tuple.Value, new EGID(tuple.Key, groupId)); | |||
var typeSafeDictionary = (entitiesToSubmit as TypeSafeDictionary<TValue>).implMgd; | |||
_implementation.Add(tuple.Key, tuple.Value); | |||
} | |||
catch (Exception e) | |||
{ | |||
Svelto.Console.LogException(e, | |||
"trying to add an EntityComponent with the same ID more than once Entity: " | |||
.FastConcat(typeof(TValue).ToString()).FastConcat(", group ").FastConcat(groupId) | |||
.FastConcat(", id ").FastConcat(tuple.Key)); | |||
foreach (var tuple in typeSafeDictionary) | |||
try | |||
{ | |||
if (_hasEgid) | |||
SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing( | |||
ref tuple.Value, new EGID(tuple.Key, groupId)); | |||
throw; | |||
} | |||
implMgd.Add(tuple.Key, tuple.Value); | |||
} | |||
catch (Exception e) | |||
{ | |||
Console.LogException( | |||
e, "trying to add an EntityComponent with the same ID more than once Entity: ".FastConcat(typeof(TValue).ToString()).FastConcat(", group ").FastConcat(groupId).FastConcat(", id ").FastConcat(tuple.Key)); | |||
throw; | |||
} | |||
} | |||
} | |||
public void AddEntitiesToEngines | |||
(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityComponentEnginesDB | |||
, ITypeSafeDictionary realDic, ExclusiveGroupStruct group, in PlatformProfiler profiler) | |||
{ | |||
if (_isUmanaged) | |||
{ | |||
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)); | |||
} | |||
else | |||
{ | |||
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 implMgd) | |||
AddEntityComponentToEngines(entityComponentEnginesDB, ref typeSafeDictionary[value.Key] | |||
, null, in profiler, new EGID(value.Key, group)); | |||
} | |||
} | |||
public void AddEntityToDictionary(EGID fromEntityGid, EGID toEntityID, ITypeSafeDictionary toGroup) | |||
{ | |||
var valueIndex = _implementation.GetIndex(fromEntityGid.entityID); | |||
if (_isUmanaged) | |||
{ | |||
var valueIndex = implUnmgd.GetIndex(fromEntityGid.entityID); | |||
DBC.ECS.Check.Require(toGroup != null | |||
, "Invalid To Group"); //todo check this, if it's right merge GetIndex | |||
{ | |||
var toGroupCasted = toGroup as ITypeSafeDictionary<TValue>; | |||
ref var entity = ref implUnmgd.GetDirectValueByRef(valueIndex); | |||
if (_hasEgid) | |||
SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID); | |||
if (toGroup != null) | |||
toGroupCasted.Add(fromEntityGid.entityID, entity); | |||
} | |||
} | |||
else | |||
{ | |||
var toGroupCasted = toGroup as ITypeSafeDictionary<TValue>; | |||
ref var entity = ref _implementation.unsafeValues[(int) valueIndex]; | |||
var valueIndex = implMgd.GetIndex(fromEntityGid.entityID); | |||
if (_hasEgid) SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID); | |||
DBC.ECS.Check.Require(toGroup != null | |||
, "Invalid To Group"); //todo check this, if it's right merge GetIndex | |||
{ | |||
var toGroupCasted = toGroup as ITypeSafeDictionary<TValue>; | |||
ref var entity = ref implMgd.GetDirectValueByRef(valueIndex); | |||
if (_hasEgid) | |||
SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID); | |||
toGroupCasted.Add(fromEntityGid.entityID, entity); | |||
toGroupCasted.Add(fromEntityGid.entityID, entity); | |||
} | |||
} | |||
} | |||
public void AddEntitiesToEngines(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityComponentEnginesDB, | |||
ITypeSafeDictionary realDic, | |||
ExclusiveGroupStruct @group, | |||
in PlatformProfiler profiler) | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Clear() | |||
{ | |||
var typeSafeDictionary = realDic as ITypeSafeDictionary<TValue>; | |||
if (_isUmanaged) | |||
{ | |||
implUnmgd.Clear(); | |||
} | |||
else | |||
{ | |||
implMgd.Clear(); | |||
} | |||
} | |||
//this can be optimized, should pass all the entities and not restart the process for each one | |||
foreach (var value in _implementation) | |||
AddEntityComponentToEngines(entityComponentEnginesDB, ref typeSafeDictionary.GetValueByRef(value.Key), null, | |||
in profiler, new EGID(value.Key, group)); | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void FastClear() | |||
{ | |||
if (_isUmanaged) | |||
{ | |||
implUnmgd.FastClear(); | |||
} | |||
else | |||
{ | |||
implMgd.FastClear(); | |||
} | |||
} | |||
public void RemoveEntitiesFromEngines( | |||
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityComponentEnginesDB, in PlatformProfiler profiler, | |||
ExclusiveGroupStruct @group) | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool ContainsKey(uint egidEntityId) | |||
{ | |||
foreach (var value in _implementation) | |||
RemoveEntityComponentFromEngines(entityComponentEnginesDB, ref _implementation.GetValueByRef(value.Key), null, | |||
in profiler, new EGID(value.Key, group)); | |||
if (_isUmanaged) | |||
{ | |||
return implUnmgd.ContainsKey(egidEntityId); | |||
} | |||
else | |||
{ | |||
return implMgd.ContainsKey(egidEntityId); | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void FastClear() { _implementation.FastClear(); } | |||
public ITypeSafeDictionary Create() | |||
{ | |||
return TypeSafeDictionaryFactory<TValue>.Create(1); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool Has(uint key) { return _implementation.ContainsKey(key); } | |||
public uint GetIndex(uint valueEntityId) | |||
{ | |||
if (_isUmanaged) | |||
{ | |||
return this.implUnmgd.GetIndex(valueEntityId); | |||
} | |||
else | |||
{ | |||
return this.implMgd.GetIndex(valueEntityId); | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void RemoveEntityFromDictionary(EGID fromEntityGid) | |||
public ref TValue GetOrCreate(uint idEntityId) | |||
{ | |||
_implementation.Remove(fromEntityGid.entityID); | |||
if (_isUmanaged) | |||
{ | |||
return ref this.implUnmgd.GetOrCreate(idEntityId); | |||
} | |||
else | |||
{ | |||
return ref this.implMgd.GetOrCreate(idEntityId); | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void SetCapacity(uint size) { _implementation.SetCapacity(size); } | |||
public IBuffer<TValue> GetValues(out uint count) | |||
{ | |||
if (_isUmanaged) | |||
{ | |||
return this.implUnmgd.GetValues(out count); | |||
} | |||
else | |||
{ | |||
return this.implMgd.GetValues(out count); | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Trim() { _implementation.Trim(); } | |||
public ref TValue GetDirectValueByRef(uint key) | |||
{ | |||
if (_isUmanaged) | |||
{ | |||
return ref this.implUnmgd.GetDirectValueByRef(key); | |||
} | |||
else | |||
{ | |||
return ref this.implMgd.GetDirectValueByRef(key); | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Clear() { _implementation.Clear(); } | |||
public bool Has(uint key) | |||
{ | |||
if (_isUmanaged) | |||
{ | |||
return this.implUnmgd.ContainsKey(key); | |||
} | |||
else | |||
{ | |||
return this.implMgd.ContainsKey(key); | |||
} | |||
} | |||
public void MoveEntityFromEngines(EGID fromEntityGid, | |||
EGID? toEntityID, ITypeSafeDictionary toGroup, | |||
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines, | |||
in PlatformProfiler profiler) | |||
public void MoveEntityFromEngines | |||
(EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup | |||
, FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines, in PlatformProfiler profiler) | |||
{ | |||
var valueIndex = _implementation.GetIndex(fromEntityGid.entityID); | |||
if (_isUmanaged) | |||
{ | |||
var valueIndex = this.implUnmgd.GetIndex(fromEntityGid.entityID); | |||
ref var entity = ref this.implUnmgd.GetDirectValueByRef(valueIndex); | |||
if (toGroup != null) | |||
{ | |||
RemoveEntityComponentFromEngines(engines, ref entity, fromEntityGid.groupID, in profiler | |||
, fromEntityGid); | |||
ref var entity = ref _implementation.unsafeValues[(int) valueIndex]; | |||
var toGroupCasted = toGroup as ITypeSafeDictionary<TValue>; | |||
var previousGroup = fromEntityGid.groupID; | |||
if (toGroup != null) | |||
if (_hasEgid) | |||
SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID.Value); | |||
var index = toGroupCasted.GetIndex(toEntityID.Value.entityID); | |||
AddEntityComponentToEngines(engines, ref toGroupCasted.GetDirectValueByRef(index), previousGroup | |||
, in profiler, toEntityID.Value); | |||
} | |||
else | |||
{ | |||
RemoveEntityComponentFromEngines(engines, ref entity, null, in profiler, fromEntityGid); | |||
} | |||
} | |||
else | |||
{ | |||
RemoveEntityComponentFromEngines(engines, ref entity, fromEntityGid.groupID, in profiler, fromEntityGid); | |||
var valueIndex = this.implMgd.GetIndex(fromEntityGid.entityID); | |||
var toGroupCasted = toGroup as ITypeSafeDictionary<TValue>; | |||
var previousGroup = fromEntityGid.groupID; | |||
ref var entity = ref this.implMgd.GetDirectValueByRef(valueIndex); | |||
if (_hasEgid) SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID.Value); | |||
if (toGroup != null) | |||
{ | |||
RemoveEntityComponentFromEngines(engines, ref entity, fromEntityGid.groupID, in profiler | |||
, fromEntityGid); | |||
var toGroupCasted = toGroup as ITypeSafeDictionary<TValue>; | |||
var previousGroup = fromEntityGid.groupID; | |||
if (_hasEgid) | |||
SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID.Value); | |||
var index = toGroupCasted.GetIndex(toEntityID.Value.entityID); | |||
var index = toGroupCasted.GetIndex(toEntityID.Value.entityID); | |||
AddEntityComponentToEngines(engines, ref toGroupCasted.unsafeValues[(int) index], previousGroup, in profiler, | |||
toEntityID.Value); | |||
AddEntityComponentToEngines(engines, ref toGroupCasted.GetDirectValueByRef(index), previousGroup | |||
, in profiler, toEntityID.Value); | |||
} | |||
else | |||
{ | |||
RemoveEntityComponentFromEngines(engines, ref entity, null, in profiler, fromEntityGid); | |||
} | |||
} | |||
} | |||
public void RemoveEntitiesFromEngines(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines | |||
, in PlatformProfiler profiler, ExclusiveGroupStruct group) | |||
{ | |||
if (_isUmanaged) | |||
{ | |||
foreach (var value in implUnmgd) | |||
RemoveEntityComponentFromEngines(engines, ref implUnmgd.GetValueByRef(value.Key), null | |||
, in profiler, new EGID(value.Key, group)); | |||
} | |||
else | |||
RemoveEntityComponentFromEngines(engines, ref entity, null, in profiler, fromEntityGid); | |||
{ | |||
foreach (var value in implMgd) | |||
RemoveEntityComponentFromEngines(engines, ref implMgd.GetValueByRef(value.Key), null | |||
, in profiler, new EGID(value.Key, group)); | |||
} | |||
} | |||
public uint Count | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void RemoveEntityFromDictionary(EGID fromEntityGid) | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => _implementation.count; | |||
if (_isUmanaged) | |||
{ | |||
this.implUnmgd.Remove(fromEntityGid.entityID); | |||
} | |||
else | |||
{ | |||
this.implMgd.Remove(fromEntityGid.entityID); | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ITypeSafeDictionary Create() { return new TypeSafeDictionary<TValue>(); } | |||
public void SetCapacity(uint size) | |||
{ | |||
if (_isUmanaged) | |||
{ | |||
this.implUnmgd.SetCapacity(size); | |||
} | |||
else | |||
{ | |||
this.implMgd.SetCapacity(size); | |||
} | |||
} | |||
void AddEntityComponentToEngines(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityComponentEnginesDB, | |||
ref TValue entity, | |||
ExclusiveGroupStruct? previousGroup, | |||
in PlatformProfiler profiler, | |||
EGID egid) | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Trim() | |||
{ | |||
//get all the engines linked to TValue | |||
if (!entityComponentEnginesDB.TryGetValue(new RefWrapper<Type>(_type), out var entityComponentsEngines)) return; | |||
if (_isUmanaged) | |||
{ | |||
this.implUnmgd.Trim(); | |||
} | |||
else | |||
{ | |||
this.implMgd.Trim(); | |||
} | |||
} | |||
if (previousGroup == null) | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool TryFindIndex(uint entityId, out uint index) | |||
{ | |||
if (_isUmanaged) | |||
{ | |||
for (var i = 0; i < entityComponentsEngines.count; i++) | |||
try | |||
{ | |||
using (profiler.Sample(entityComponentsEngines[i], _typeName)) | |||
{ | |||
(entityComponentsEngines[i] as IReactOnAddAndRemove<TValue>).Add(ref entity, egid); | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
throw new | |||
ECSException("Code crashed inside Add callback ".FastConcat(typeof(TValue).ToString()), e); | |||
} | |||
return implUnmgd.TryFindIndex(entityId, out index); | |||
} | |||
else | |||
{ | |||
return implMgd.TryFindIndex(entityId, out index); | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool TryGetValue(uint entityId, out TValue item) | |||
{ | |||
if (_isUmanaged) | |||
{ | |||
return this.implUnmgd.TryGetValue(entityId, out item); | |||
} | |||
else | |||
{ | |||
return this.implMgd.TryGetValue(entityId, out item); | |||
} | |||
} | |||
public uint count | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get | |||
{ | |||
if (_isUmanaged) | |||
{ | |||
return this.implUnmgd.count; | |||
} | |||
else | |||
{ | |||
return this.implMgd.count; | |||
} | |||
} | |||
} | |||
public ref TValue this[uint idEntityId] | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get | |||
{ | |||
if (_isUmanaged) | |||
{ | |||
return ref this.implUnmgd.GetValueByRef(idEntityId); | |||
} | |||
else | |||
{ | |||
return ref this.implMgd.GetValueByRef(idEntityId); | |||
} | |||
} | |||
} | |||
static void RemoveEntityComponentFromEngines | |||
(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines, ref TValue entity, uint? previousGroup | |||
, in PlatformProfiler profiler, EGID egid) | |||
{ | |||
if (!engines.TryGetValue(new RefWrapper<Type>(_type), out var entityComponentsEngines)) | |||
return; | |||
if (previousGroup == null) | |||
for (var i = 0; i < entityComponentsEngines.count; i++) | |||
try | |||
{ | |||
using (profiler.Sample(entityComponentsEngines[i], _typeName)) | |||
{ | |||
(entityComponentsEngines[i] as IReactOnSwap<TValue>).MovedTo(ref entity, previousGroup.Value, | |||
egid); | |||
(entityComponentsEngines[i] as IReactOnAddAndRemove<TValue>).Remove(ref entity, egid); | |||
} | |||
} | |||
catch (Exception e) | |||
catch | |||
{ | |||
throw new | |||
ECSException("Code crashed inside MovedTo callback ".FastConcat(typeof(TValue).ToString()), | |||
e); | |||
Svelto.Console.LogError("Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString())); | |||
throw; | |||
} | |||
} | |||
} | |||
static void RemoveEntityComponentFromEngines(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> @group, | |||
ref TValue entity, | |||
uint? previousGroup, | |||
in PlatformProfiler profiler, | |||
EGID egid) | |||
void AddEntityComponentToEngines | |||
(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines, ref TValue entity | |||
, ExclusiveGroupStruct? previousGroup, in PlatformProfiler profiler, EGID egid) | |||
{ | |||
if (!@group.TryGetValue(new RefWrapper<Type>(_type), out var entityComponentsEngines)) return; | |||
//get all the engines linked to TValue | |||
if (!engines.TryGetValue(new RefWrapper<Type>(_type), out var entityComponentsEngines)) | |||
return; | |||
if (previousGroup == null) | |||
{ | |||
@@ -205,86 +471,45 @@ namespace Svelto.ECS.Internal | |||
try | |||
{ | |||
using (profiler.Sample(entityComponentsEngines[i], _typeName)) | |||
(entityComponentsEngines[i] as IReactOnAddAndRemove<TValue>).Remove(ref entity, egid); | |||
{ | |||
(entityComponentsEngines[i] as IReactOnAddAndRemove<TValue>).Add(ref entity, egid); | |||
} | |||
} | |||
catch (Exception e) | |||
catch | |||
{ | |||
throw new | |||
ECSException("Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString()), | |||
e); | |||
Svelto.Console.LogError("Code crashed inside Add callback ".FastConcat(typeof(TValue).ToString())); | |||
throw; | |||
} | |||
} | |||
#if SEEMS_UNNECESSARY | |||
else | |||
{ | |||
for (var i = 0; i < entityComponentsEngines.Count; i++) | |||
for (var i = 0; i < entityComponentsEngines.count; i++) | |||
try | |||
{ | |||
using (profiler.Sample(entityComponentsEngines[i], _typeName)) | |||
(entityComponentsEngines[i] as IReactOnSwap<TValue>).MovedFrom(ref entity, egid); | |||
{ | |||
(entityComponentsEngines[i] as IReactOnSwap<TValue>).MovedTo( | |||
ref entity, previousGroup.Value, egid); | |||
} | |||
} | |||
catch (Exception e) | |||
catch (Exception) | |||
{ | |||
throw new ECSException( | |||
"Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString()), e); | |||
Svelto.Console.LogError("Code crashed inside MoveTo callback ".FastConcat(typeof(TValue).ToString())); | |||
throw; | |||
} | |||
} | |||
#endif | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public TValue[] GetValuesArray(out uint count) | |||
{ | |||
var managedBuffer = _implementation.GetValuesArray(out count); | |||
return managedBuffer; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool ContainsKey(uint egidEntityId) { return _implementation.ContainsKey(egidEntityId); } | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Add(uint egidEntityId, in TValue entityComponent) { _implementation.Add(egidEntityId, entityComponent); } | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public FasterDictionary<uint, TValue>.FasterDictionaryKeyValueEnumerator GetEnumerator() | |||
{ | |||
return _implementation.GetEnumerator(); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref TValue GetValueByRef(uint key) { return ref _implementation.GetValueByRef(key); } | |||
public ref TValue this[uint idEntityId] | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => ref _implementation.GetValueByRef(idEntityId); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public uint GetIndex(uint valueEntityId) { return _implementation.GetIndex(valueEntityId); } | |||
public TValue[] unsafeValues | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => _implementation.unsafeValues; | |||
} | |||
public object GenerateSentinel() | |||
public void Dispose() | |||
{ | |||
return default; | |||
if (_isUmanaged) | |||
implUnmgd.Dispose(); | |||
else | |||
implMgd.Dispose(); | |||
GC.SuppressFinalize(this); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool TryGetValue(uint entityId, out TValue item) { return _implementation.TryGetValue(entityId, out item); } | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref TValue GetOrCreate(uint idEntityId) { return ref _implementation.GetOrCreate(idEntityId); } | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool TryFindIndex(uint entityId, out uint index) { return _implementation.TryFindIndex(entityId, out index); } | |||
internal FasterDictionary<uint, TValue> implementation => _implementation; | |||
readonly FasterDictionary<uint, TValue> _implementation; | |||
} | |||
} |
@@ -9,13 +9,5 @@ namespace Svelto.ECS.Internal | |||
return mapper; | |||
} | |||
internal static NativeEGIDMapper<T> ToNativeEGIDMapper<T>(this TypeSafeDictionary<T> dic, | |||
ExclusiveGroupStruct groupStructId) where T : unmanaged, IEntityComponent | |||
{ | |||
var mapper = new NativeEGIDMapper<T>(groupStructId, dic.implementation.ToNative<uint, T>()); | |||
return mapper; | |||
} | |||
} | |||
} |
@@ -9,12 +9,13 @@ namespace Svelto.ECS.DataStructures | |||
internal unsafe byte* ptr => _ptr; | |||
//expressed in bytes | |||
internal uint capacity => _capacity; | |||
internal int capacity => (int) _capacity; | |||
//expressed in bytes | |||
internal uint count => _writeIndex; | |||
internal int count => (int) _writeIndex; | |||
//expressed in bytes | |||
internal uint space => capacity - count; | |||
internal int space => capacity - count; | |||
/// <summary> | |||
/// </summary> | |||
@@ -26,37 +27,36 @@ namespace Svelto.ECS.DataStructures | |||
#endif | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref T Get<T>(uint index) where T : unmanaged | |||
public ref T Get<T>(uint index) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
T* buffer = (T*) ptr; | |||
return ref buffer[index]; | |||
return ref Unsafe.AsRef<T>(Unsafe.Add<T>(ptr, (int) index)); | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Set<T>(uint index, in T value) where T : unmanaged | |||
public void Set<T>(uint index, in T value) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
int sizeOf = MemoryUtilities.SizeOf<T>(); | |||
uint sizeOf = (uint) MemoryUtilities.SizeOf<T>(); | |||
uint writeIndex = (uint) (index * sizeOf); | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_capacity < writeIndex + sizeOf) | |||
throw new Exception("no writing authorized"); | |||
#endif | |||
T* buffer = (T*) ptr; | |||
buffer[index] = value; | |||
#endif | |||
Unsafe.AsRef<T>(Unsafe.Add<T>(_ptr, (int) index)) = value; | |||
if (_writeIndex < writeIndex + sizeOf) | |||
_writeIndex = (uint) (writeIndex + sizeOf); | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Add<T>(in T value) where T : unmanaged | |||
public void Add<T>(in T value) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
@@ -73,7 +73,15 @@ namespace Svelto.ECS.DataStructures | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal void Realloc<T>(uint newCapacity) where T : unmanaged | |||
public void Pop<T>() where T : struct | |||
{ | |||
var structSize = MemoryUtilities.SizeOf<T>(); | |||
_writeIndex -= (uint)structSize; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal void Realloc(uint newCapacity) | |||
{ | |||
unsafe | |||
{ | |||
@@ -84,9 +92,9 @@ namespace Svelto.ECS.DataStructures | |||
#endif | |||
if (newCapacity >= 0) | |||
{ | |||
newPointer = (byte*) MemoryUtilities.Alloc<T>(newCapacity, allocator); | |||
newPointer = (byte*) MemoryUtilities.Alloc(newCapacity, allocator); | |||
if (count > 0) | |||
Unsafe.CopyBlock(newPointer, ptr, count); | |||
Unsafe.CopyBlock(newPointer, ptr, (uint) count); | |||
} | |||
if (ptr != null) | |||
@@ -117,10 +125,17 @@ namespace Svelto.ECS.DataStructures | |||
_writeIndex = 0; | |||
} | |||
#if UNITY_ECS | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void SetCountTo(uint count) | |||
{ | |||
_writeIndex = count; | |||
} | |||
#if UNITY_COLLECTIONS | |||
[global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction] | |||
#endif | |||
unsafe byte* _ptr; | |||
uint _writeIndex; | |||
uint _capacity; | |||
} |
@@ -1,10 +1,10 @@ | |||
using System; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.Common; | |||
using Unity.Collections.LowLevel.Unsafe; | |||
namespace Svelto.ECS.DataStructures | |||
{ | |||
//ToDO to complete in future version of svelto, maybe removed | |||
public struct UnsafeArrayIndex | |||
{ | |||
internal uint index; | |||
@@ -12,116 +12,119 @@ namespace Svelto.ECS.DataStructures | |||
} | |||
/// <summary> | |||
/// Note: this must work inside burst, so it must follow burst restrictions | |||
/// Note: All the svelto native structures | |||
/// Note: this must work inside burst, so it must follow burst restrictions | |||
/// Note: All the svelto native structures | |||
/// </summary> | |||
struct UnsafeBlob : IDisposable | |||
{ | |||
internal unsafe byte* ptr => _ptr; | |||
internal unsafe byte* ptr { get; set; } | |||
//expressed in bytes | |||
internal uint capacity { get; private set; } | |||
//expressed in bytes | |||
internal uint size => _writeIndex - _readIndex; | |||
//expressed in bytes | |||
internal uint space => capacity - size; | |||
/// <summary> | |||
/// </summary> | |||
internal Allocator allocator; | |||
#if DEBUG && !PROFILE_SVELTO | |||
internal uint id; | |||
#endif | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal void Write<T>(in T item) where T : unmanaged | |||
internal void Write<T>(in T item) 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 | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (space - (int)structSize < 0) | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (space - (int) structSize < 0) | |||
throw new Exception("no writing authorized"); | |||
#endif | |||
var writeHead = _writeIndex % capacity; | |||
if (writeHead + structSize <= capacity) | |||
{ | |||
Unsafe.Write(_ptr + writeHead, item); | |||
Unsafe.Write(ptr + writeHead, item); | |||
} | |||
else | |||
//copy with wrap, will start to copy and wrap for the reminder | |||
//copy with wrap, will start to copy and wrap for the reminder | |||
{ | |||
var byteCountToEnd = capacity - writeHead; | |||
fixed (T* readFrom = &item) | |||
{ | |||
//read and copy the first portion of Item until the end of the stream | |||
Unsafe.CopyBlock(_ptr + writeHead, readFrom, byteCountToEnd); | |||
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; | |||
var restCount = structSize - byteCountToEnd; | |||
//read and copy the remainder | |||
var @from = (byte*) readFrom; | |||
Unsafe.CopyBlock(_ptr, @from + byteCountToEnd, restCount); | |||
} | |||
//read and copy the remainder | |||
Unsafe.CopyBlock(ptr, (byte*) Unsafe.AsPointer(ref localCopyToAvoidGcIssues) + byteCountToEnd | |||
, restCount); | |||
} | |||
uint paddedStructSize = Align4(structSize); | |||
//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); | |||
_writeIndex += paddedStructSize; | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal void WriteUnaligned<T>(in T item) where T : unmanaged | |||
{ | |||
unsafe | |||
{ | |||
uint structSize = (uint) MemoryUtilities.SizeOf<T>(); | |||
//the idea is, considering the wrap, a read pointer must always be behind a writer pointer | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (space - (int)structSize < 0) | |||
throw new Exception("no writing authorized"); | |||
#endif | |||
var pointer = _writeIndex % capacity; | |||
if (pointer + structSize <= capacity) | |||
Unsafe.Write(_ptr + pointer, item); | |||
else | |||
{ | |||
var byteCount = capacity - pointer; | |||
fixed (T* readFrom = &item) | |||
{ | |||
Unsafe.CopyBlockUnaligned(_ptr + pointer, readFrom, byteCount); | |||
var restCount = structSize - byteCount; | |||
var @from = (byte*) readFrom; | |||
Unsafe.CopyBlockUnaligned(_ptr, @from + byteCount, restCount); | |||
} | |||
} | |||
// [MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
// //ToDo: remove this and create an UnsafeBlobUnaligned, used on NativeRingBuffer where T cannot change | |||
// internal void WriteUnaligned<T>(in T item) 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 | |||
// #if DEBUG && !PROFILE_SVELTO | |||
// if (space - (int) structSize < 0) | |||
// throw new Exception("no writing authorized"); | |||
// #endif | |||
// var pointer = _writeIndex % capacity; | |||
// | |||
// if (pointer + structSize <= capacity) | |||
// { | |||
// Unsafe.Write(ptr + pointer, item); | |||
// } | |||
// else | |||
// { | |||
// var byteCount = capacity - pointer; | |||
// | |||
// var localCopyToAvoidGCIssues = item; | |||
// | |||
// Unsafe.CopyBlockUnaligned(ptr + pointer, Unsafe.AsPointer(ref localCopyToAvoidGCIssues), byteCount); | |||
// | |||
// var restCount = structSize - byteCount; | |||
// Unsafe.CopyBlockUnaligned(ptr, (byte*) Unsafe.AsPointer(ref localCopyToAvoidGCIssues) + byteCount | |||
// , restCount); | |||
// } | |||
// | |||
// _writeIndex += structSize; | |||
// } | |||
// } | |||
_writeIndex += structSize; | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal T Read<T>() where T : unmanaged | |||
internal T Read<T>() where T : struct | |||
{ | |||
unsafe | |||
{ | |||
var structSize = (uint) MemoryUtilities.SizeOf<T>(); | |||
#if DEBUG && !PROFILE_SVELTO | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (size < structSize) //are there enough bytes to read? | |||
throw new Exception("dequeuing empty queue or unexpected type dequeued"); | |||
if (_readIndex > _writeIndex) | |||
throw new Exception("unexpected read"); | |||
#endif | |||
var head = _readIndex % capacity; | |||
uint paddedStructSize = Align4(structSize); | |||
var head = _readIndex % capacity; | |||
var paddedStructSize = MemoryUtilities.Align4(structSize); | |||
_readIndex += paddedStructSize; | |||
if (_readIndex == _writeIndex) | |||
@@ -134,78 +137,78 @@ namespace Svelto.ECS.DataStructures | |||
} | |||
if (head + paddedStructSize <= capacity) | |||
{ | |||
return Unsafe.Read<T>(_ptr + head); | |||
} | |||
return Unsafe.Read<T>(ptr + head); | |||
T item = default; | |||
T* destination = &item; | |||
var byteCountToEnd = capacity - head; | |||
Unsafe.CopyBlock(destination, _ptr + head, byteCountToEnd); | |||
Unsafe.CopyBlock(Unsafe.AsPointer(ref item), ptr + head, byteCountToEnd); | |||
var restCount = structSize - byteCountToEnd; | |||
Unsafe.CopyBlock((byte*) destination + byteCountToEnd, ptr, restCount); | |||
Unsafe.CopyBlock((byte*) Unsafe.AsPointer(ref item) + byteCountToEnd, ptr, restCount); | |||
return item; | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal ref T Reserve<T>(out UnsafeArrayIndex index) where T : unmanaged | |||
internal ref T Reserve<T>(out UnsafeArrayIndex index) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
var sizeOf = (uint) MemoryUtilities.SizeOf<T>(); | |||
T* buffer = (T *)(_ptr + _writeIndex); | |||
ref var buffer = ref Unsafe.AsRef<T>(ptr + _writeIndex); | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_writeIndex > capacity) throw new Exception($"can't reserve if the writeIndex wrapped around the capacity, writeIndex {_writeIndex} capacity {capacity}"); | |||
if (_writeIndex + sizeOf > capacity) throw new Exception("out of bound reserving"); | |||
#endif | |||
if (_writeIndex > capacity) | |||
throw new Exception( | |||
$"can't reserve if the writeIndex wrapped around the capacity, writeIndex {_writeIndex} capacity {capacity}"); | |||
if (_writeIndex + sizeOf > capacity) | |||
throw new Exception("out of bound reserving"); | |||
#endif | |||
index = new UnsafeArrayIndex | |||
{ | |||
capacity = capacity | |||
, index = _writeIndex | |||
}; | |||
var align4 = Align4(sizeOf); | |||
var align4 = MemoryUtilities.Align4(sizeOf); | |||
_writeIndex += align4; | |||
return ref buffer[0]; | |||
return ref buffer; | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal ref T AccessReserved<T>(UnsafeArrayIndex index) where T : unmanaged | |||
internal ref T AccessReserved<T>(UnsafeArrayIndex index) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
var size = MemoryUtilities.SizeOf<T>(); | |||
if (index.index + size > capacity) throw new Exception($"out of bound access, index {index.index} size {size} capacity {capacity}"); | |||
#endif | |||
T* buffer = (T*) (_ptr + index.index); | |||
return ref buffer[0]; | |||
if (index.index + size > capacity) | |||
throw new Exception($"out of bound access, index {index.index} size {size} capacity {capacity}"); | |||
#endif | |||
return ref Unsafe.AsRef<T>(ptr + index.index); | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal void Realloc<T>(uint newCapacity) where T : unmanaged | |||
internal void Realloc(uint newCapacity) | |||
{ | |||
unsafe | |||
{ | |||
//be sure it's multiple of 4. Assuming that what we write is aligned to 4, then we will always have aligned wrapped heads | |||
newCapacity = Align4(newCapacity); | |||
newCapacity = MemoryUtilities.Align4(newCapacity); | |||
byte* newPointer = null; | |||
#if DEBUG && !PROFILE_SVELTO | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (newCapacity <= capacity) | |||
throw new Exception("new capacity must be bigger than current"); | |||
#endif | |||
#endif | |||
if (newCapacity > 0) | |||
{ | |||
newPointer = (byte*) MemoryUtilities.Alloc<T>(newCapacity, allocator); | |||
newPointer = (byte*) MemoryUtilities.Alloc(newCapacity, allocator); | |||
if (size > 0) | |||
{ | |||
var readerHead = _readIndex % capacity; | |||
@@ -214,8 +217,8 @@ namespace Svelto.ECS.DataStructures | |||
if (readerHead < writerHead) | |||
{ | |||
//copy to the new pointer, from th reader position | |||
uint currentSize = _writeIndex - _readIndex; | |||
Unsafe.CopyBlock(newPointer, _ptr + readerHead, currentSize); | |||
var currentSize = _writeIndex - _readIndex; | |||
Unsafe.CopyBlock(newPointer, ptr + readerHead, 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 | |||
@@ -224,19 +227,19 @@ namespace Svelto.ECS.DataStructures | |||
{ | |||
var byteCountToEnd = capacity - readerHead; | |||
Unsafe.CopyBlock(newPointer, _ptr + readerHead, byteCountToEnd); | |||
Unsafe.CopyBlock(newPointer + byteCountToEnd, _ptr, writerHead); | |||
Unsafe.CopyBlock(newPointer, ptr + readerHead, byteCountToEnd); | |||
Unsafe.CopyBlock(newPointer + byteCountToEnd, ptr, writerHead); | |||
} | |||
} | |||
} | |||
if (_ptr != null) | |||
MemoryUtilities.Free((IntPtr) _ptr, allocator); | |||
if (ptr != null) | |||
MemoryUtilities.Free((IntPtr) ptr, allocator); | |||
_writeIndex = size; | |||
_readIndex = 0; | |||
_ptr = newPointer; | |||
ptr = newPointer; | |||
capacity = newCapacity; | |||
} | |||
} | |||
@@ -246,12 +249,12 @@ namespace Svelto.ECS.DataStructures | |||
{ | |||
unsafe | |||
{ | |||
if (_ptr != null) | |||
MemoryUtilities.Free((IntPtr) _ptr, allocator); | |||
if (ptr != null) | |||
MemoryUtilities.Free((IntPtr) ptr, allocator); | |||
_ptr = null; | |||
ptr = null; | |||
_writeIndex = 0; | |||
capacity = 0; | |||
capacity = 0; | |||
} | |||
} | |||
@@ -259,18 +262,9 @@ namespace Svelto.ECS.DataStructures | |||
public void Clear() | |||
{ | |||
_writeIndex = 0; | |||
_readIndex = 0; | |||
_readIndex = 0; | |||
} | |||
uint Align4(uint input) | |||
{ | |||
return (uint)(Math.Ceiling(input / 4.0) * 4); | |||
} | |||
#if UNITY_ECS | |||
[NativeDisableUnsafePtrRestriction] | |||
#endif | |||
unsafe byte* _ptr; | |||
uint _writeIndex, _readIndex; | |||
} | |||
} | |||
} |
@@ -0,0 +1,69 @@ | |||
using Svelto.ECS; | |||
#if DEBUG | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Reflection; | |||
public static class ExclusiveGroupDebugger | |||
{ | |||
static ExclusiveGroupDebugger() | |||
{ | |||
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); | |||
foreach (Assembly assembly in assemblies) | |||
{ | |||
Type[] types = assembly.GetTypes(); | |||
foreach (Type type in types) | |||
{ | |||
if (type != null && type.IsClass && type.IsSealed && type.IsAbstract) //this means only static classes | |||
{ | |||
var fields = type.GetFields(); | |||
foreach (var field in fields) | |||
{ | |||
if (field.IsStatic && typeof(ExclusiveGroup).IsAssignableFrom(field.FieldType)) | |||
{ | |||
string name = $"{type.FullName}.{field.Name}"; | |||
var group = (ExclusiveGroup) field.GetValue(null); | |||
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); | |||
GroupMap.idToName[@group] = name; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
public static string ToName(this in ExclusiveGroupStruct group) | |||
{ | |||
if (GroupMap.idToName.TryGetValue(group, out var name) == false) | |||
name = $"<undefined:{((uint)group).ToString()}>"; | |||
return name; | |||
} | |||
} | |||
public static class GroupMap | |||
{ | |||
static GroupMap() | |||
{ | |||
GroupMap.idToName = new Dictionary<uint, string>(); | |||
} | |||
internal static readonly Dictionary<uint, string> idToName; | |||
} | |||
#else | |||
public static class ExclusiveGroupDebugger | |||
{ | |||
public static string ToName(this in ExclusiveGroupStruct group) | |||
{ | |||
return ((uint)group).ToString(); | |||
} | |||
} | |||
#endif |
@@ -4,7 +4,7 @@ using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
/// <summary> | |||
/// DynamicEntityDescriptor can be used to add entity views to an existing EntityDescriptor that act as flags, | |||
/// DynamicEntityDescriptor can be used to add entity components to an existing EntityDescriptor that act as flags, | |||
/// at building time. | |||
/// This method allocates, so it shouldn't be abused | |||
/// </summary> | |||
@@ -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<EntityInfoComponentView> | |||
ComponentsToBuild[length] = new ComponentBuilder<EntityInfoViewComponent> | |||
( | |||
new EntityInfoComponentView | |||
new EntityInfoViewComponent | |||
{ | |||
componentsToBuild = ComponentsToBuild | |||
} | |||
@@ -80,9 +80,9 @@ namespace Svelto.ECS | |||
//assign it after otherwise the previous copy will overwrite the value in case the item | |||
//is already present | |||
localEntitiesToBuild[index] = new ComponentBuilder<EntityInfoComponentView> | |||
localEntitiesToBuild[index] = new ComponentBuilder<EntityInfoViewComponent> | |||
( | |||
new EntityInfoComponentView | |||
new EntityInfoViewComponent | |||
{ | |||
componentsToBuild = localEntitiesToBuild | |||
} | |||
@@ -100,7 +100,7 @@ namespace Svelto.ECS | |||
for (var i = 0; i < length; i++) | |||
{ | |||
//the special entity already exists | |||
if (defaultEntities[i].GetEntityComponentType() == EntityBuilderUtilities.ENTITY_STRUCT_INFO_VIEW) | |||
if (defaultEntities[i].GetEntityComponentType() == ComponentBuilderUtilities.ENTITY_STRUCT_INFO_VIEW) | |||
{ | |||
index = i; | |||
break; | |||
@@ -9,6 +9,10 @@ namespace Svelto.ECS.Experimental | |||
public static implicit operator T(ECSResources<T> ecsString) { return ResourcesECSDB<T>.FromECS(ecsString.id); } | |||
} | |||
/// <summary> | |||
/// To do. Or we reuse the ID or we need to clear this | |||
/// </summary> | |||
/// <typeparam name="T"></typeparam> | |||
static class ResourcesECSDB<T> | |||
{ | |||
static readonly FasterList<T> _resources = new FasterList<T>(); | |||
@@ -1,18 +1,25 @@ | |||
using System; | |||
using System.Runtime.InteropServices; | |||
namespace Svelto.ECS.Experimental | |||
{ | |||
[Serialization.DoNotSerialize] | |||
[StructLayout(LayoutKind.Explicit)] | |||
/// | |||
/// Note: I should extend this to reuse unused id | |||
/// | |||
public struct ECSString:IEquatable<ECSString> | |||
{ | |||
uint _id; | |||
[FieldOffset(0)] uint _id; | |||
[FieldOffset(4)] uint _versioning; | |||
[FieldOffset(0)] long _realID; | |||
public ECSString(string newText) | |||
public ECSString(string newText):this() | |||
{ | |||
_id = ResourcesECSDB<string>.ToECS(newText); | |||
} | |||
ECSString(uint id) | |||
ECSString(uint id):this() | |||
{ | |||
_id = id; | |||
} | |||
@@ -21,11 +28,24 @@ namespace Svelto.ECS.Experimental | |||
{ | |||
return ResourcesECSDB<string>.FromECS(ecsString._id); | |||
} | |||
/// <summary> | |||
/// Note: Setting null String could be a good way to signal a disposing of the ID so that | |||
/// it can be recycled. | |||
/// Zero id must be a null string | |||
/// </summary> | |||
/// <param name="newText"></param> | |||
public void Set(string newText) | |||
{ | |||
if (_id != 0) | |||
ResourcesECSDB<string>.resources(_id) = newText; | |||
{ | |||
if (ResourcesECSDB<string>.resources(_id).Equals(newText) == false) | |||
{ | |||
ResourcesECSDB<string>.resources(_id) = newText; | |||
_versioning++; | |||
} | |||
} | |||
else | |||
_id = ResourcesECSDB<string>.ToECS(newText); | |||
} | |||
@@ -39,14 +59,34 @@ namespace Svelto.ECS.Experimental | |||
return new ECSString(id); | |||
} | |||
public override string ToString() | |||
{ | |||
return ResourcesECSDB<string>.FromECS(_id); | |||
} | |||
public bool Equals(ECSString other) | |||
{ | |||
return other._id == _id; | |||
return _realID == other._realID; | |||
} | |||
public override string ToString() | |||
public static bool operator==(ECSString options1, ECSString options2) | |||
{ | |||
return ResourcesECSDB<string>.FromECS(_id); | |||
return options1._realID == options2._realID; | |||
} | |||
public static bool operator!=(ECSString options1, ECSString options2) | |||
{ | |||
return options1._realID != options2._realID; | |||
} | |||
public override bool Equals(object obj) | |||
{ | |||
throw new NotSupportedException(); //this is on purpose | |||
} | |||
public override int GetHashCode() | |||
{ | |||
return _realID.GetHashCode(); | |||
} | |||
} | |||
} |
@@ -5,7 +5,6 @@ using System.Runtime.InteropServices; | |||
namespace Svelto.ECS | |||
{ | |||
//todo: add debug map | |||
[Serialization.DoNotSerialize] | |||
[Serializable] | |||
[StructLayout(LayoutKind.Explicit)] | |||
@@ -75,7 +74,7 @@ namespace Svelto.ECS | |||
public override string ToString() | |||
{ | |||
return "id ".FastConcat(entityID).FastConcat(" group ").FastConcat(groupID); | |||
return "id ".FastConcat(entityID).FastConcat(" group ").FastConcat(groupID.ToName()); | |||
} | |||
} | |||
} |
@@ -1,66 +1,67 @@ | |||
using System; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public readonly struct EGIDMapper<T> where T : struct, IEntityComponent | |||
{ | |||
internal readonly ITypeSafeDictionary<T> map; | |||
public uint Length => map.Count; | |||
public ExclusiveGroupStruct groupID { get; } | |||
public uint length => _map.count; | |||
public ExclusiveGroupStruct groupID { get; } | |||
public EGIDMapper(ExclusiveGroupStruct groupStructId, ITypeSafeDictionary<T> dic):this() | |||
public EGIDMapper(ExclusiveGroupStruct groupStructId, ITypeSafeDictionary<T> dic) : this() | |||
{ | |||
groupID = groupStructId; | |||
map = dic; | |||
_map = dic; | |||
} | |||
[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())); | |||
if (_map.TryFindIndex(entityID, out var findIndex) == false) | |||
throw new System.Exception("Entity not found in this group ".FastConcat(typeof(T).ToString())); | |||
#else | |||
map.TryFindIndex(entityID, out var findIndex); | |||
_map.TryFindIndex(entityID, out var findIndex); | |||
#endif | |||
return ref map.unsafeValues[(int) findIndex]; | |||
return ref _map.GetDirectValueByRef(findIndex); | |||
} | |||
public bool TryGetEntity(uint entityID, out T value) | |||
{ | |||
if (map.TryFindIndex(entityID, out var index)) | |||
if (_map != null && _map.TryFindIndex(entityID, out var index)) | |||
{ | |||
value = map.unsafeValues[index]; | |||
value = _map.GetDirectValueByRef(index); | |||
return true; | |||
} | |||
value = default; | |||
return false; | |||
} | |||
public T[] GetArrayAndEntityIndex(uint entityID, out uint index) | |||
public IBuffer<T> GetArrayAndEntityIndex(uint entityID, out uint index) | |||
{ | |||
if (map.TryFindIndex(entityID, out index)) | |||
if (_map.TryFindIndex(entityID, out index)) | |||
{ | |||
return map.unsafeValues; | |||
return _map.GetValues(out _); | |||
} | |||
throw new ECSException("Entity not found"); | |||
} | |||
public bool TryGetArrayAndEntityIndex(uint entityID, out uint index, out T[] array) | |||
public bool TryGetArrayAndEntityIndex(uint entityID, out uint index, out IBuffer<T> array) | |||
{ | |||
if (map.TryFindIndex(entityID, out index)) | |||
index = default; | |||
if (_map != null && _map.TryFindIndex(entityID, out index)) | |||
{ | |||
array = map.unsafeValues; | |||
array = _map.GetValues(out _); | |||
return true; | |||
} | |||
array = default; | |||
return false; | |||
} | |||
readonly ITypeSafeDictionary<T> _map; | |||
} | |||
} | |||
} |
@@ -0,0 +1,148 @@ | |||
using System; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public partial class EnginesRoot | |||
{ | |||
internal class DoubleBufferedEntitiesToAdd | |||
{ | |||
const int MaximumNumberOfItemsPerFrameBeforeToClear = 100; | |||
internal void Swap() | |||
{ | |||
Swap(ref current, ref other); | |||
Swap(ref currentEntitiesCreatedPerGroup, ref otherEntitiesCreatedPerGroup); | |||
} | |||
void Swap<T>(ref T item1, ref T item2) | |||
{ | |||
var toSwap = item2; | |||
item2 = item1; | |||
item1 = toSwap; | |||
} | |||
public void ClearOther() | |||
{ | |||
//do not clear the groups created so far, they will be reused, unless they are too many! | |||
var otherCount = other.count; | |||
if (otherCount > MaximumNumberOfItemsPerFrameBeforeToClear) | |||
{ | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>[] otherValuesArray = other.unsafeValues; | |||
for (int i = 0; i < otherCount; ++i) | |||
{ | |||
var safeDictionariesCount = otherValuesArray[i].count; | |||
ITypeSafeDictionary[] safeDictionaries = otherValuesArray[i].unsafeValues; | |||
{ | |||
for (int j = 0; j < safeDictionariesCount; ++j) | |||
{ | |||
//clear the dictionary of entities create do far (it won't allocate though) | |||
safeDictionaries[j].Dispose(); | |||
} | |||
} | |||
} | |||
otherEntitiesCreatedPerGroup.FastClear(); | |||
other.FastClear(); | |||
return; | |||
} | |||
{ | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>[] otherValuesArray = other.unsafeValues; | |||
for (int i = 0; i < otherCount; ++i) | |||
{ | |||
var safeDictionariesCount = otherValuesArray[i].count; | |||
ITypeSafeDictionary[] safeDictionaries = otherValuesArray[i].unsafeValues; | |||
//do not remove the dictionaries of entities per type created so far, they will be reused | |||
if (safeDictionariesCount <= MaximumNumberOfItemsPerFrameBeforeToClear) | |||
{ | |||
for (int j = 0; j < safeDictionariesCount; ++j) | |||
{ | |||
//clear the dictionary of entities create do far (it won't allocate though) | |||
safeDictionaries[j].FastClear(); | |||
} | |||
} | |||
else | |||
{ | |||
for (int j = 0; j < safeDictionariesCount; ++j) | |||
{ | |||
//clear the dictionary of entities create do far (it won't allocate though) | |||
safeDictionaries[j].Dispose(); | |||
} | |||
otherValuesArray[i].FastClear(); | |||
} | |||
} | |||
otherEntitiesCreatedPerGroup.FastClear(); | |||
} | |||
} | |||
/// <summary> | |||
/// 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; | |||
//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; | |||
readonly FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> | |||
_entityComponentsToAddBufferA = | |||
new FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>(); | |||
readonly FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> | |||
_entityComponentsToAddBufferB = | |||
new FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>(); | |||
readonly FasterDictionary<uint, uint> _entitiesCreatedPerGroupA = new FasterDictionary<uint, uint>(); | |||
readonly FasterDictionary<uint, uint> _entitiesCreatedPerGroupB = new FasterDictionary<uint, uint>(); | |||
public DoubleBufferedEntitiesToAdd() | |||
{ | |||
currentEntitiesCreatedPerGroup = _entitiesCreatedPerGroupA; | |||
otherEntitiesCreatedPerGroup = _entitiesCreatedPerGroupB; | |||
current = _entityComponentsToAddBufferA; | |||
other = _entityComponentsToAddBufferB; | |||
} | |||
public void Dispose() | |||
{ | |||
{ | |||
var otherValuesArray = other.unsafeValues; | |||
for (int i = 0; i < other.count; ++i) | |||
{ | |||
var safeDictionariesCount = otherValuesArray[i].count; | |||
var safeDictionaries = otherValuesArray[i].unsafeValues; | |||
//do not remove the dictionaries of entities per type created so far, they will be reused | |||
for (int j = 0; j < safeDictionariesCount; ++j) | |||
{ | |||
//clear the dictionary of entities create do far (it won't allocate though) | |||
safeDictionaries[j].Dispose(); | |||
} | |||
} | |||
} | |||
{ | |||
var currentValuesArray = current.unsafeValues; | |||
for (int i = 0; i < current.count; ++i) | |||
{ | |||
var safeDictionariesCount = currentValuesArray[i].count; | |||
var safeDictionaries = currentValuesArray[i].unsafeValues; | |||
//do not remove the dictionaries of entities per type created so far, they will be reused | |||
for (int j = 0; j < safeDictionariesCount; ++j) | |||
{ | |||
//clear the dictionary of entities create do far (it won't allocate though) | |||
safeDictionaries[j].Dispose(); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -1,93 +0,0 @@ | |||
using System; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public partial class EnginesRoot | |||
{ | |||
internal class DoubleBufferedEntitiesToAdd | |||
{ | |||
const int MaximumNumberOfItemsPerFrameBeforeToClear = 100; | |||
internal void Swap() | |||
{ | |||
Swap(ref current, ref other); | |||
Swap(ref currentEntitiesCreatedPerGroup, ref otherEntitiesCreatedPerGroup); | |||
} | |||
void Swap<T>(ref T item1, ref T item2) | |||
{ | |||
var toSwap = item2; | |||
item2 = item1; | |||
item1 = toSwap; | |||
} | |||
public void ClearOther() | |||
{ | |||
//do not clear the groups created so far, they will be reused, unless they are too many! | |||
var otherCount = other.count; | |||
if (otherCount > MaximumNumberOfItemsPerFrameBeforeToClear) | |||
{ | |||
otherEntitiesCreatedPerGroup.FastClear(); | |||
other.FastClear(); | |||
return; | |||
} | |||
var otherValuesArray = other.unsafeValues; | |||
for (int i = 0; i < otherCount; ++i) | |||
{ | |||
var safeDictionariesCount = otherValuesArray[i].count; | |||
var safeDictionaries = otherValuesArray[i].unsafeValues; | |||
//do not remove the dictionaries of entities per type created so far, they will be reused | |||
if (safeDictionariesCount <= MaximumNumberOfItemsPerFrameBeforeToClear) | |||
{ | |||
for (int j = 0; j < safeDictionariesCount; ++j) | |||
{ | |||
//clear the dictionary of entities create do far (it won't allocate though) | |||
safeDictionaries[j].FastClear(); | |||
} | |||
} | |||
else | |||
{ | |||
otherValuesArray[i].FastClear(); | |||
} | |||
} | |||
otherEntitiesCreatedPerGroup.FastClear(); | |||
} | |||
/// <summary> | |||
/// 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; | |||
//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; | |||
readonly FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> | |||
_entityComponentsToAddBufferA = | |||
new FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>(); | |||
readonly FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> | |||
_entityComponentsToAddBufferB = | |||
new FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>(); | |||
readonly FasterDictionary<uint, uint> _entitiesCreatedPerGroupA = new FasterDictionary<uint, uint>(); | |||
readonly FasterDictionary<uint, uint> _entitiesCreatedPerGroupB = new FasterDictionary<uint, uint>(); | |||
public DoubleBufferedEntitiesToAdd() | |||
{ | |||
currentEntitiesCreatedPerGroup = _entitiesCreatedPerGroupA; | |||
otherEntitiesCreatedPerGroup = _entitiesCreatedPerGroupB; | |||
current = _entityComponentsToAddBufferA; | |||
other = _entityComponentsToAddBufferB; | |||
} | |||
} | |||
} | |||
} |
@@ -7,7 +7,7 @@ using Svelto.ECS.Schedulers; | |||
namespace Svelto.ECS | |||
{ | |||
public partial class EnginesRoot | |||
public sealed partial class EnginesRoot | |||
{ | |||
public struct EntitiesSubmitter | |||
{ | |||
@@ -24,7 +24,7 @@ namespace Svelto.ECS | |||
readonly Svelto.DataStructures.WeakReference<EnginesRoot> _weakReference; | |||
} | |||
public IEntitiesSubmissionScheduler scheduler { get; } | |||
/// <summary> | |||
@@ -37,34 +37,37 @@ namespace Svelto.ECS | |||
/// </summary> | |||
public EnginesRoot(IEntitiesSubmissionScheduler entitiesComponentScheduler) | |||
{ | |||
_entitiesOperations = new ThreadSafeDictionary<ulong, EntitySubmitOperation>(); | |||
serializationDescriptorMap = new SerializationDescriptorMap(); | |||
_reactiveEnginesAddRemove = new FasterDictionary<RefWrapper<Type>, FasterList<IEngine>>(); | |||
_reactiveEnginesSwap = new FasterDictionary<RefWrapper<Type>, FasterList<IEngine>>(); | |||
_enginesSet = new FasterList<IEngine>(); | |||
_enginesTypeSet = new HashSet<Type>(); | |||
_disposableEngines = new FasterList<IDisposable>(); | |||
_entitiesOperations = new ThreadSafeDictionary<ulong, EntitySubmitOperation>(); | |||
serializationDescriptorMap = new SerializationDescriptorMap(); | |||
_reactiveEnginesAddRemove = new FasterDictionary<RefWrapper<Type>, FasterList<IEngine>>(); | |||
_reactiveEnginesSwap = new FasterDictionary<RefWrapper<Type>, FasterList<IEngine>>(); | |||
_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>>(); | |||
_groupEntityComponentsDB = | |||
new FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>(); | |||
_groupsPerEntity = new FasterDictionary<RefWrapper<Type>, FasterDictionary<uint, ITypeSafeDictionary>>(); | |||
_groupedEntityToAdd = new DoubleBufferedEntitiesToAdd(); | |||
_entitiesStream = new EntitiesStream(); | |||
_entitiesDB = new EntitiesDB(_groupEntityComponentsDB, _groupsPerEntity, _entitiesStream); | |||
_entitiesDB = new EntitiesDB(_groupEntityComponentsDB, _groupsPerEntity, _entitiesStream); | |||
scheduler = entitiesComponentScheduler; | |||
scheduler = entitiesComponentScheduler; | |||
scheduler.onTick = new EntitiesSubmitter(this); | |||
#if UNITY_ECS | |||
#if UNITY_BURST | |||
AllocateNativeOperations(); | |||
#endif | |||
} | |||
public EnginesRoot(IEntitiesSubmissionScheduler entitiesComponentScheduler, bool isDeserializationOnly):this(entitiesComponentScheduler) | |||
public EnginesRoot | |||
(IEntitiesSubmissionScheduler entitiesComponentScheduler, bool isDeserializationOnly) : | |||
this(entitiesComponentScheduler) | |||
{ | |||
_isDeserializationOnly = isDeserializationOnly; | |||
} | |||
/// <summary> | |||
/// Dispose an EngineRoot once not used anymore, so that all the | |||
/// engines are notified with the entities removed. | |||
@@ -74,21 +77,45 @@ namespace Svelto.ECS | |||
{ | |||
using (var profiler = new PlatformProfiler("Final Dispose")) | |||
{ | |||
foreach (var groups in _groupEntityComponentsDB) | |||
foreach (var engine in _disposableEngines) | |||
{ | |||
foreach (var entityList in groups.Value) | |||
try | |||
{ | |||
entityList.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, profiler, | |||
new ExclusiveGroupStruct(groups.Key)); | |||
engine.Dispose(); | |||
} | |||
catch (Exception e) | |||
{ | |||
Svelto.Console.LogException(e); | |||
} | |||
} | |||
foreach (FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>. | |||
KeyValuePairFast groups in _groupEntityComponentsDB) | |||
{ | |||
foreach (FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>.KeyValuePairFast entityList in | |||
groups.Value) | |||
try | |||
{ | |||
entityList.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, profiler | |||
, new ExclusiveGroupStruct(groups.Key)); | |||
} | |||
catch (Exception e) | |||
{ | |||
Svelto.Console.LogException(e); | |||
} | |||
} | |||
foreach (FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>. | |||
KeyValuePairFast groups in _groupEntityComponentsDB) | |||
{ | |||
foreach (FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>.KeyValuePairFast entityList in | |||
groups.Value) | |||
entityList.Value.Dispose(); | |||
} | |||
_groupEntityComponentsDB.Clear(); | |||
_groupsPerEntity.Clear(); | |||
foreach (var engine in _disposableEngines) | |||
engine.Dispose(); | |||
_disposableEngines.Clear(); | |||
_enginesSet.Clear(); | |||
_enginesTypeSet.Clear(); | |||
@@ -97,13 +124,13 @@ namespace Svelto.ECS | |||
_entitiesOperations.Clear(); | |||
_transientEntitiesOperations.Clear(); | |||
scheduler.Dispose(); | |||
#if DEBUG && !PROFILE_SVELTO | |||
_idCheckers.Clear(); | |||
#endif | |||
_groupedEntityToAdd = null; | |||
_groupedEntityToAdd.Dispose(); | |||
_entitiesStream.Dispose(); | |||
scheduler.Dispose(); | |||
} | |||
GC.SuppressFinalize(this); | |||
@@ -116,17 +143,16 @@ namespace Svelto.ECS | |||
Dispose(); | |||
} | |||
public void AddEngine(IEngine engine) | |||
{ | |||
var type = engine.GetType(); | |||
var type = engine.GetType(); | |||
var refWrapper = new RefWrapper<Type>(type); | |||
DBC.ECS.Check.Require(engine != null, "Engine to add is invalid or null"); | |||
DBC.ECS.Check.Require( | |||
_enginesTypeSet.Contains(refWrapper) == false || | |||
type.ContainsCustomAttribute(typeof(AllowMultipleAttribute)) == true, | |||
"The same engine has been added more than once, if intentional, use [AllowMultiple] class attribute " | |||
.FastConcat(engine.ToString())); | |||
_enginesTypeSet.Contains(refWrapper) == false | |||
|| type.ContainsCustomAttribute(typeof(AllowMultipleAttribute)) == true | |||
, "The same engine has been added more than once, if intentional, use [AllowMultiple] class attribute " | |||
.FastConcat(engine.ToString())); | |||
try | |||
{ | |||
if (engine is IReactOnAddAndRemove viewEngine) | |||
@@ -149,7 +175,8 @@ namespace Svelto.ECS | |||
} | |||
catch (Exception e) | |||
{ | |||
throw new ECSException("Code crashed while adding engine ".FastConcat(engine.GetType().ToString(), " "), e); | |||
throw new ECSException("Code crashed while adding engine ".FastConcat(engine.GetType().ToString(), " ") | |||
, e); | |||
} | |||
} | |||
@@ -169,8 +196,8 @@ namespace Svelto.ECS | |||
} | |||
} | |||
static void AddEngine<T>(T engine, Type[] entityComponentTypes, | |||
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines) | |||
static void AddEngine<T> | |||
(T engine, Type[] entityComponentTypes, FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines) | |||
where T : class, IEngine | |||
{ | |||
for (var i = 0; i < entityComponentTypes.Length; i++) | |||
@@ -1,14 +1,14 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Runtime.CompilerServices; | |||
using DBC.ECS; | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
using Svelto.ECS.Schedulers; | |||
namespace Svelto.ECS | |||
{ | |||
public partial class EnginesRoot : IDisposable | |||
public partial class EnginesRoot : IDisposable, IUnitTestingInterface | |||
{ | |||
///-------------------------------------------- | |||
/// | |||
@@ -16,25 +16,19 @@ 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, | |||
IEnumerable<object> implementors = null) | |||
EntityComponentInitializer BuildEntity | |||
(EGID entityID, IComponentBuilder[] componentsToBuild, Type implementorType, IEnumerable<object> implementors = null) | |||
{ | |||
CheckAddEntityID(entityID); | |||
CheckAddEntityID(entityID, implementorType); | |||
Check.Require(entityID.groupID != 0, "invalid group detected"); | |||
var dic = EntityFactory.BuildGroupedEntities(entityID, _groupedEntityToAdd, componentsToBuild, implementors); | |||
var dic = EntityFactory.BuildGroupedEntities(entityID, _groupedEntityToAdd, componentsToBuild | |||
, implementors); | |||
return new EntityComponentInitializer(entityID, dic); | |||
} | |||
@@ -42,85 +36,95 @@ namespace Svelto.ECS | |||
///-------------------------------------------- | |||
void Preallocate<T>(ExclusiveGroupStruct groupID, uint size) where T : IEntityDescriptor, new() | |||
{ | |||
var entityComponentsToBuild = EntityDescriptorTemplate<T>.descriptor.componentsToBuild; | |||
var numberOfEntityComponents = entityComponentsToBuild.Length; | |||
using (var profiler = new PlatformProfiler("Preallocate")) | |||
{ | |||
var entityComponentsToBuild = EntityDescriptorTemplate<T>.descriptor.componentsToBuild; | |||
var numberOfEntityComponents = entityComponentsToBuild.Length; | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> group = GetOrCreateGroup(groupID); | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> group = GetOrCreateGroup(groupID, profiler); | |||
for (var index = 0; index < numberOfEntityComponents; index++) | |||
{ | |||
var entityComponentBuilder = entityComponentsToBuild[index]; | |||
var entityComponentType = entityComponentBuilder.GetEntityComponentType(); | |||
for (var index = 0; index < numberOfEntityComponents; index++) | |||
{ | |||
var entityComponentBuilder = entityComponentsToBuild[index]; | |||
var entityComponentType = entityComponentBuilder.GetEntityComponentType(); | |||
var refWrapper = new RefWrapper<Type>(entityComponentType); | |||
if (group.TryGetValue(refWrapper, out var dbList) == false) | |||
group[refWrapper] = entityComponentBuilder.Preallocate(ref dbList, size); | |||
else | |||
dbList.SetCapacity(size); | |||
var refWrapper = new RefWrapper<Type>(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>(); | |||
if (_groupsPerEntity.TryGetValue(refWrapper, out var groupedGroup) == false) | |||
groupedGroup = _groupsPerEntity[refWrapper] = new FasterDictionary<uint, ITypeSafeDictionary>(); | |||
groupedGroup[groupID] = dbList; | |||
groupedGroup[groupID] = dbList; | |||
} | |||
} | |||
} | |||
///-------------------------------------------- | |||
/// | |||
void MoveEntityFromAndToEngines(IComponentBuilder[] entityBuilders, EGID fromEntityGID, EGID? toEntityGID) | |||
void MoveEntityFromAndToEngines(IComponentBuilder[] componentBuilders, EGID fromEntityGID, EGID? toEntityGID) | |||
{ | |||
using (var sampler = new PlatformProfiler("Move Entity From Engines")) | |||
{ | |||
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>(EntityBuilderUtilities.ENTITY_STRUCT_INFO_VIEW), | |||
out var entityInfoViewDic) && | |||
(entityInfoViewDic as ITypeSafeDictionary<EntityInfoComponentView>).TryGetValue(fromEntityGID.entityID, | |||
out var entityInfoView)) | |||
MoveEntityComponents(fromEntityGID, toEntityGID, entityInfoView.componentsToBuild, fromGroup, sampler); | |||
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); | |||
//otherwise it's a normal static entity descriptor | |||
else | |||
MoveEntityComponents(fromEntityGID, toEntityGID, entityBuilders, fromGroup, sampler); | |||
MoveEntityComponents(fromEntityGID, toEntityGID, componentBuilders, fromGroup, sampler); | |||
} | |||
} | |||
void MoveEntityComponents(EGID fromEntityGID, EGID? toEntityGID, IComponentBuilder[] entitiesToMove, | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup, | |||
PlatformProfiler sampler) | |||
void MoveEntityComponents(EGID fromEntityGID, EGID? toEntityGID, IComponentBuilder[] entitiesToMove | |||
, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup, in PlatformProfiler sampler) | |||
{ | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup = null; | |||
if (toEntityGID != null) | |||
using (sampler.Sample("MoveEntityComponents")) | |||
{ | |||
var toGroupID = toEntityGID.Value.groupID; | |||
var length = entitiesToMove.Length; | |||
toGroup = GetOrCreateGroup(toGroupID); | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup = null; | |||
//Add all the entities to the dictionary | |||
for (var i = 0; i < entitiesToMove.Length; i++) | |||
CopyEntityToDictionary(fromEntityGID, toEntityGID.Value, fromGroup, toGroup, | |||
entitiesToMove[i].GetEntityComponentType()); | |||
} | |||
if (toEntityGID != null) | |||
{ | |||
var toGroupID = toEntityGID.Value.groupID; | |||
toGroup = GetOrCreateGroup(toGroupID, sampler); | |||
//Add all the entities to the dictionary | |||
for (var i = 0; i < length; i++) | |||
CopyEntityToDictionary(fromEntityGID, toEntityGID.Value, fromGroup, toGroup | |||
, entitiesToMove[i].GetEntityComponentType(), sampler); | |||
} | |||
//call all the callbacks | |||
for (var i = 0; i < entitiesToMove.Length; i++) | |||
MoveEntityComponentFromAndToEngines(fromEntityGID, toEntityGID, fromGroup, toGroup, | |||
entitiesToMove[i].GetEntityComponentType(), sampler); | |||
//call all the callbacks | |||
for (var i = 0; i < length; i++) | |||
MoveEntityComponentFromAndToEngines(fromEntityGID, toEntityGID, fromGroup, toGroup | |||
, entitiesToMove[i].GetEntityComponentType(), sampler); | |||
//then remove all the entities from the dictionary | |||
for (var i = 0; i < entitiesToMove.Length; i++) | |||
RemoveEntityFromDictionary(fromEntityGID, fromGroup, 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); | |||
} | |||
} | |||
void CopyEntityToDictionary(EGID entityGID, EGID toEntityGID, | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup, | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup, | |||
Type entityComponentType) | |||
void CopyEntityToDictionary | |||
(EGID entityGID, EGID toEntityGID, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup | |||
, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup, Type entityComponentType, in PlatformProfiler sampler) | |||
{ | |||
var wrapper = new RefWrapper<Type>(entityComponentType); | |||
using (sampler.Sample("CopyEntityToDictionary")) | |||
{ | |||
var wrapper = new RefWrapper<Type>(entityComponentType); | |||
ITypeSafeDictionary fromTypeSafeDictionary = GetTypeSafeDictionary(entityGID.groupID, fromGroup, wrapper); | |||
ITypeSafeDictionary fromTypeSafeDictionary = | |||
GetTypeSafeDictionary(entityGID.groupID, fromGroup, wrapper); | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (fromTypeSafeDictionary.Has(entityGID.entityID) == false) | |||
@@ -128,47 +132,50 @@ namespace Svelto.ECS | |||
throw new EntityNotFoundException(entityGID, entityComponentType); | |||
} | |||
#endif | |||
ITypeSafeDictionary toEntitiesDictionary = | |||
GetOrCreateTypeSafeDictionary(toEntityGID.groupID, toGroup, wrapper, fromTypeSafeDictionary); | |||
ITypeSafeDictionary toEntitiesDictionary = | |||
GetOrCreateTypeSafeDictionary(toEntityGID.groupID, toGroup, wrapper, fromTypeSafeDictionary); | |||
fromTypeSafeDictionary.AddEntityToDictionary(entityGID, toEntityGID, toEntitiesDictionary); | |||
fromTypeSafeDictionary.AddEntityToDictionary(entityGID, toEntityGID, toEntitiesDictionary); | |||
} | |||
} | |||
void MoveEntityComponentFromAndToEngines(EGID entityGID, EGID? toEntityGID, | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup, | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup, | |||
Type entityComponentType, in PlatformProfiler profiler) | |||
void MoveEntityComponentFromAndToEngines | |||
(EGID entityGID, EGID? toEntityGID, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup | |||
, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup, Type entityComponentType | |||
, in PlatformProfiler profiler) | |||
{ | |||
//add all the entities | |||
var refWrapper = new RefWrapper<Type>(entityComponentType); | |||
var fromTypeSafeDictionary = GetTypeSafeDictionary(entityGID.groupID, fromGroup, refWrapper); | |||
using (profiler.Sample("MoveEntityComponentFromAndToEngines")) | |||
{ | |||
//add all the entities | |||
var refWrapper = new RefWrapper<Type>(entityComponentType); | |||
var fromTypeSafeDictionary = GetTypeSafeDictionary(entityGID.groupID, fromGroup, refWrapper); | |||
ITypeSafeDictionary toEntitiesDictionary = null; | |||
if (toGroup != null) | |||
toEntitiesDictionary = toGroup[refWrapper]; //this is guaranteed to exist by AddEntityToDictionary | |||
ITypeSafeDictionary toEntitiesDictionary = null; | |||
if (toGroup != null) | |||
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); | |||
#endif | |||
fromTypeSafeDictionary.MoveEntityFromEngines(entityGID, toEntityGID, toEntitiesDictionary, | |||
toEntityGID == null ? _reactiveEnginesAddRemove : _reactiveEnginesSwap, in profiler); | |||
fromTypeSafeDictionary.MoveEntityFromEngines(entityGID, toEntityGID, toEntitiesDictionary | |||
, toEntityGID == null | |||
? _reactiveEnginesAddRemove | |||
: _reactiveEnginesSwap, in profiler); | |||
} | |||
} | |||
void RemoveEntityFromDictionary(EGID entityGID, | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup, | |||
Type entityComponentType, in PlatformProfiler profiler) | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
void RemoveEntityFromDictionary | |||
(EGID entityGID, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup, Type entityComponentType | |||
, in PlatformProfiler sampler) | |||
{ | |||
var refWrapper = new RefWrapper<Type>(entityComponentType); | |||
var fromTypeSafeDictionary = GetTypeSafeDictionary(entityGID.groupID, fromGroup, refWrapper); | |||
fromTypeSafeDictionary.RemoveEntityFromDictionary(entityGID); | |||
//if (fromTypeSafeDictionary.Count == 0) //clean up | |||
using (sampler.Sample("RemoveEntityFromDictionary")) | |||
{ | |||
//todo: this must be unit tested properly | |||
//_groupsPerEntity[refWrapper].Remove(entityGID.groupID); | |||
//I don't remove the group if empty on purpose, in case it needs to be reused | |||
var refWrapper = new RefWrapper<Type>(entityComponentType); | |||
var fromTypeSafeDictionary = GetTypeSafeDictionary(entityGID.groupID, fromGroup, refWrapper); | |||
fromTypeSafeDictionary.RemoveEntityFromDictionary(entityGID); | |||
} | |||
} | |||
@@ -180,54 +187,63 @@ namespace Svelto.ECS | |||
/// <param name="profiler"></param> | |||
void SwapEntitiesBetweenGroups(uint fromIdGroupId, uint toGroupId, in PlatformProfiler profiler) | |||
{ | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup = GetGroup(fromIdGroupId); | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup = GetOrCreateGroup(toGroupId); | |||
foreach (FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>.KeyValuePairFast dictionaryOfEntities in | |||
fromGroup) | |||
using (profiler.Sample("SwapEntitiesBetweenGroups")) | |||
{ | |||
//call all the MoveTo callbacks | |||
dictionaryOfEntities.Value.AddEntitiesToEngines(_reactiveEnginesAddRemove, dictionaryOfEntities.Value, | |||
new ExclusiveGroupStruct(toGroupId), profiler); | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup = GetGroup(fromIdGroupId); | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup = GetOrCreateGroup(toGroupId, profiler); | |||
ITypeSafeDictionary toEntitiesDictionary = GetOrCreateTypeSafeDictionary(toGroupId, toGroup, | |||
dictionaryOfEntities.Key, dictionaryOfEntities.Value); | |||
foreach (FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>.KeyValuePairFast dictionaryOfEntities | |||
in fromGroup) | |||
{ | |||
//call all the MoveTo callbacks | |||
dictionaryOfEntities.Value.AddEntitiesToEngines(_reactiveEnginesAddRemove | |||
, dictionaryOfEntities.Value | |||
, new ExclusiveGroupStruct(toGroupId), profiler); | |||
FasterDictionary<uint, ITypeSafeDictionary> groupsOfEntityType = | |||
_groupsPerEntity[dictionaryOfEntities.Key]; | |||
ITypeSafeDictionary toEntitiesDictionary = | |||
GetOrCreateTypeSafeDictionary(toGroupId, toGroup, dictionaryOfEntities.Key | |||
, dictionaryOfEntities.Value); | |||
FasterDictionary<uint, ITypeSafeDictionary> groupsOfEntityType = | |||
_groupsPerEntity[dictionaryOfEntities.Key]; | |||
ITypeSafeDictionary typeSafeDictionary = groupsOfEntityType[fromIdGroupId]; | |||
toEntitiesDictionary.AddEntitiesFromDictionary(typeSafeDictionary, toGroupId); | |||
ITypeSafeDictionary typeSafeDictionary = groupsOfEntityType[fromIdGroupId]; | |||
toEntitiesDictionary.AddEntitiesFromDictionary(typeSafeDictionary, toGroupId); | |||
typeSafeDictionary.FastClear(); | |||
typeSafeDictionary.FastClear(); | |||
} | |||
} | |||
} | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> GetGroup(uint fromIdGroupId) | |||
{ | |||
if (_groupEntityComponentsDB.TryGetValue(fromIdGroupId, | |||
out FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup) == false) | |||
if (_groupEntityComponentsDB.TryGetValue(fromIdGroupId | |||
, out FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> | |||
fromGroup) == false) | |||
throw new ECSException("Group doesn't exist: ".FastConcat(fromIdGroupId)); | |||
return fromGroup; | |||
} | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> GetOrCreateGroup(uint toGroupId) | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> GetOrCreateGroup(uint toGroupId, in PlatformProfiler profiler) | |||
{ | |||
if (_groupEntityComponentsDB.TryGetValue(toGroupId, | |||
out FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup) == false) | |||
toGroup = _groupEntityComponentsDB[toGroupId] = | |||
new FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>(); | |||
return toGroup; | |||
using (profiler.Sample("GetOrCreateGroup")) | |||
{ | |||
if (_groupEntityComponentsDB.TryGetValue( | |||
toGroupId, out FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup) == false) | |||
toGroup = _groupEntityComponentsDB[toGroupId] = | |||
new FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>(); | |||
return toGroup; | |||
} | |||
} | |||
ITypeSafeDictionary GetOrCreateTypeSafeDictionary(uint groupId, | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup, | |||
RefWrapper<Type> type, ITypeSafeDictionary fromTypeSafeDictionary) | |||
ITypeSafeDictionary GetOrCreateTypeSafeDictionary | |||
(uint groupId, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup, RefWrapper<Type> type | |||
, ITypeSafeDictionary fromTypeSafeDictionary) | |||
{ | |||
//be sure that the TypeSafeDictionary for the entity Type exists | |||
if (toGroup.TryGetValue(type, out ITypeSafeDictionary toEntitiesDictionary) == | |||
false) | |||
if (toGroup.TryGetValue(type, out ITypeSafeDictionary toEntitiesDictionary) == false) | |||
{ | |||
toEntitiesDictionary = fromTypeSafeDictionary.Create(); | |||
toGroup.Add(type, toEntitiesDictionary); | |||
@@ -235,15 +251,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<uint, ITypeSafeDictionary>(); | |||
groupedGroup[groupId] = toEntitiesDictionary; | |||
return toEntitiesDictionary; | |||
} | |||
static ITypeSafeDictionary GetTypeSafeDictionary(uint groupID, | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> @group, RefWrapper<Type> refWrapper) | |||
static ITypeSafeDictionary GetTypeSafeDictionary | |||
(uint groupID, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> @group, RefWrapper<Type> refWrapper) | |||
{ | |||
if (@group.TryGetValue(refWrapper, out ITypeSafeDictionary fromTypeSafeDictionary) == false) | |||
{ | |||
@@ -258,10 +273,10 @@ namespace Svelto.ECS | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> dictionariesOfEntities = | |||
_groupEntityComponentsDB[groupID]; | |||
foreach (var dictionaryOfEntities in dictionariesOfEntities) | |||
foreach (FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>.KeyValuePairFast dictionaryOfEntities in dictionariesOfEntities) | |||
{ | |||
dictionaryOfEntities.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, profiler, | |||
new ExclusiveGroupStruct(groupID)); | |||
dictionaryOfEntities.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, profiler | |||
, new ExclusiveGroupStruct(groupID)); | |||
dictionaryOfEntities.Value.FastClear(); | |||
FasterDictionary<uint, ITypeSafeDictionary> groupsOfEntityType = | |||
@@ -275,7 +290,7 @@ namespace Svelto.ECS | |||
return _entitiesStream.GenerateConsumer<T>(name, capacity); | |||
} | |||
internal Consumer<T> GenerateConsumer<T>(ExclusiveGroup group, string name, uint capacity) | |||
internal Consumer<T> GenerateConsumer<T>(ExclusiveGroupStruct group, string name, uint capacity) | |||
where T : unmanaged, IEntityComponent | |||
{ | |||
return _entitiesStream.GenerateConsumer<T>(group, name, capacity); | |||
@@ -288,7 +303,8 @@ 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>> _groupEntityComponentsDB; | |||
readonly FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, 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 | |||
@@ -298,5 +314,12 @@ namespace Svelto.ECS | |||
readonly EntitiesDB _entitiesDB; | |||
readonly EntitiesStream _entitiesStream; | |||
EntitiesDB IUnitTestingInterface.entitiesForTesting => _entitiesDB; | |||
} | |||
public interface IUnitTestingInterface | |||
{ | |||
EntitiesDB entitiesForTesting { get; } | |||
} | |||
} |
@@ -1,6 +1,6 @@ | |||
using System.Collections.Generic; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
using System; | |||
using System.Collections.Generic; | |||
using Svelto.Common; | |||
namespace Svelto.ECS | |||
{ | |||
@@ -10,7 +10,7 @@ namespace Svelto.ECS | |||
{ | |||
public GenericEntityFactory(EnginesRoot weakReference) | |||
{ | |||
_enginesRoot = new WeakReference<EnginesRoot>(weakReference); | |||
_enginesRoot = new Svelto.DataStructures.WeakReference<EnginesRoot>(weakReference); | |||
} | |||
public EntityComponentInitializer BuildEntity<T> | |||
@@ -19,26 +19,25 @@ namespace Svelto.ECS | |||
{ | |||
return _enginesRoot.Target.BuildEntity(new EGID(entityID, groupStructId) | |||
, EntityDescriptorTemplate<T>.descriptor.componentsToBuild | |||
, implementors); | |||
, TypeCache<T>.type, implementors); | |||
} | |||
public EntityComponentInitializer BuildEntity<T>(EGID egid, IEnumerable<object> implementors = null) | |||
where T : IEntityDescriptor, new() | |||
{ | |||
return _enginesRoot.Target.BuildEntity( | |||
egid, EntityDescriptorTemplate<T>.descriptor.componentsToBuild | |||
, implementors); | |||
egid, EntityDescriptorTemplate<T>.descriptor.componentsToBuild, TypeCache<T>.type, implementors); | |||
} | |||
public EntityComponentInitializer BuildEntity<T> | |||
(EGID egid, T entityDescriptor, IEnumerable<object> implementors) where T : IEntityDescriptor | |||
{ | |||
return _enginesRoot.Target.BuildEntity(egid, entityDescriptor.componentsToBuild, implementors); | |||
return _enginesRoot.Target.BuildEntity(egid, entityDescriptor.componentsToBuild, TypeCache<T>.type, implementors); | |||
} | |||
#if UNITY_ECS | |||
public NativeEntityFactory ToNative<T>(Unity.Collections.Allocator allocator) where T : IEntityDescriptor, new() | |||
#if UNITY_BURST | |||
public NativeEntityFactory ToNative<T>(string memberName) where T : IEntityDescriptor, new() | |||
{ | |||
return _enginesRoot.Target.ProvideNativeEntityFactoryQueue<T>(); | |||
return _enginesRoot.Target.ProvideNativeEntityFactoryQueue<T>(memberName); | |||
} | |||
#endif | |||
public EntityComponentInitializer BuildEntity<T> | |||
@@ -46,7 +45,7 @@ namespace Svelto.ECS | |||
where T : IEntityDescriptor | |||
{ | |||
return _enginesRoot.Target.BuildEntity(new EGID(entityID, groupStructId) | |||
, descriptorEntity.componentsToBuild, implementors); | |||
, descriptorEntity.componentsToBuild, TypeCache<T>.type, implementors); | |||
} | |||
public void PreallocateEntitySpace<T>(ExclusiveGroupStruct groupStructId, uint size) | |||
@@ -54,10 +53,15 @@ namespace Svelto.ECS | |||
{ | |||
_enginesRoot.Target.Preallocate<T>(groupStructId, size); | |||
} | |||
public EntityComponentInitializer BuildEntity(EGID egid, IComponentBuilder[] componentsToBuild, Type type, IEnumerable<object> implementors = null) | |||
{ | |||
return _enginesRoot.Target.BuildEntity(egid, componentsToBuild, type, implementors); | |||
} | |||
//enginesRoot is a weakreference because GenericEntityStreamConsumerFactory can be injected inside | |||
//engines of other enginesRoot | |||
readonly WeakReference<EnginesRoot> _enginesRoot; | |||
readonly Svelto.DataStructures.WeakReference<EnginesRoot> _enginesRoot; | |||
} | |||
} | |||
} |
@@ -1,6 +1,6 @@ | |||
using System; | |||
using System.Diagnostics; | |||
using System.Runtime.CompilerServices; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.Common; | |||
namespace Svelto.ECS | |||
{ | |||
@@ -27,7 +27,8 @@ namespace Svelto.ECS | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void RemoveEntity<T>(EGID entityEGID) where T : IEntityDescriptor, new() | |||
{ | |||
_enginesRoot.Target.CheckRemoveEntityID(entityEGID); | |||
DBC.ECS.Check.Require(entityEGID.groupID != 0, "invalid group detected"); | |||
_enginesRoot.Target.CheckRemoveEntityID(entityEGID, TypeCache<T>.type); | |||
_enginesRoot.Target.QueueEntitySubmitOperation<T>( | |||
new EntitySubmitOperation(EntitySubmitOperationType.Remove, entityEGID, entityEGID, | |||
@@ -50,6 +51,7 @@ namespace Svelto.ECS | |||
public void RemoveGroupAndEntities(ExclusiveGroupStruct groupID) | |||
{ | |||
_enginesRoot.Target.RemoveGroupID(groupID); | |||
DBC.ECS.Check.Require(groupID != 0, "invalid group detected"); | |||
_enginesRoot.Target.QueueEntitySubmitOperation( | |||
new EntitySubmitOperation(EntitySubmitOperationType.RemoveGroup, new EGID(0, groupID), new EGID())); | |||
@@ -103,15 +105,15 @@ namespace Svelto.ECS | |||
SwapEntityGroup<T>(fromID, toID); | |||
} | |||
#if UNITY_ECS | |||
public NativeEntityRemove ToNativeRemove<T>() where T : IEntityDescriptor, new() | |||
#if UNITY_BURST | |||
public NativeEntityRemove ToNativeRemove<T>(string memberName) where T : IEntityDescriptor, new() | |||
{ | |||
return _enginesRoot.Target.ProvideNativeEntityRemoveQueue<T>(); | |||
return _enginesRoot.Target.ProvideNativeEntityRemoveQueue<T>(memberName); | |||
} | |||
public NativeEntitySwap ToNativeSwap<T>() where T : IEntityDescriptor, new() | |||
public NativeEntitySwap ToNativeSwap<T>(string memberName) where T : IEntityDescriptor, new() | |||
{ | |||
return _enginesRoot.Target.ProvideNativeEntitySwapQueue<T>(); | |||
return _enginesRoot.Target.ProvideNativeEntitySwapQueue<T>(memberName); | |||
} | |||
#endif | |||
@@ -119,8 +121,11 @@ namespace Svelto.ECS | |||
public void SwapEntityGroup<T>(EGID fromID, EGID toID) | |||
where T : IEntityDescriptor, new() | |||
{ | |||
_enginesRoot.Target.CheckRemoveEntityID(fromID); | |||
_enginesRoot.Target.CheckAddEntityID(toID); | |||
DBC.ECS.Check.Require(fromID.groupID != 0, "invalid group detected"); | |||
DBC.ECS.Check.Require(toID.groupID != 0, "invalid group detected"); | |||
_enginesRoot.Target.CheckRemoveEntityID(fromID, TypeCache<T>.type); | |||
_enginesRoot.Target.CheckAddEntityID(toID, TypeCache<T>.type); | |||
_enginesRoot.Target.QueueEntitySubmitOperation<T>( | |||
new EntitySubmitOperation(EntitySubmitOperationType.Swap, | |||
@@ -135,7 +140,7 @@ namespace Svelto.ECS | |||
void QueueEntitySubmitOperation(EntitySubmitOperation entitySubmitOperation) | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
entitySubmitOperation.trace = new StackFrame(1, true); | |||
entitySubmitOperation.trace = new System.Diagnostics.StackFrame(1, true); | |||
#endif | |||
_entitiesOperations.Add((ulong) entitySubmitOperation.fromID, entitySubmitOperation); | |||
} | |||
@@ -143,7 +148,7 @@ namespace Svelto.ECS | |||
void QueueEntitySubmitOperation<T>(EntitySubmitOperation entitySubmitOperation) where T : IEntityDescriptor | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
entitySubmitOperation.trace = new StackFrame(1, true); | |||
entitySubmitOperation.trace = new System.Diagnostics.StackFrame(1, true); | |||
if (_entitiesOperations.TryGetValue((ulong) entitySubmitOperation.fromID, out var entitySubmitedOperation)) | |||
{ | |||
@@ -29,10 +29,9 @@ namespace Svelto.ECS | |||
void SingleSubmission(in PlatformProfiler profiler) | |||
{ | |||
#if UNITY_ECS | |||
#if UNITY_BURST | |||
NativeOperationSubmission(profiler); | |||
#endif | |||
if (_entitiesOperations.Count > 0) | |||
{ | |||
using (profiler.Sample("Remove and Swap operations")) | |||
@@ -66,16 +65,19 @@ namespace Svelto.ECS | |||
break; | |||
} | |||
} | |||
catch (Exception e) | |||
catch | |||
{ | |||
var str = "Crash while executing Entity Operation " | |||
.FastConcat(entitiesOperations[i].type.ToString()); | |||
throw new ECSException(str.FastConcat(" ") | |||
Svelto.Console.LogError(str.FastConcat(" ") | |||
#if DEBUG && !PROFILE_SVELTO | |||
.FastConcat(entitiesOperations[i].trace.ToString()) | |||
.FastConcat(entitiesOperations[i].trace.ToString()) | |||
#endif | |||
, e); | |||
); | |||
throw; | |||
} | |||
} | |||
} | |||
@@ -112,7 +114,7 @@ namespace Svelto.ECS | |||
{ | |||
var groupID = groupOfEntitiesToSubmit.Key; | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> groupDB = GetOrCreateGroup(groupID); | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> groupDB = GetOrCreateGroup(groupID, profiler); | |||
//add the entityComponents in the group | |||
foreach (var entityComponentsToSubmit in _groupedEntityToAdd.other[groupID]) | |||
@@ -124,13 +126,13 @@ namespace Svelto.ECS | |||
ITypeSafeDictionary dbDic = GetOrCreateTypeSafeDictionary(groupID, groupDB, wrapper, | |||
targetTypeSafeDictionary); | |||
//Fill the DB with the entity views generate this frame. | |||
//Fill the DB with the entity components generate this frame. | |||
dbDic.AddEntitiesFromDictionary(targetTypeSafeDictionary, groupID); | |||
} | |||
} | |||
} | |||
//then submit everything in the engines, so that the DB is up to date with all the entity views and struct | |||
//then submit everything in the engines, so that the DB is up to date with all the entity components | |||
//created by the entity built | |||
using (profiler.Sample("Add entities to engines")) | |||
{ | |||
@@ -0,0 +1,126 @@ | |||
using System; | |||
using System.Threading; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
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) | |||
return result; | |||
var result1Count = result1.count; | |||
var fasterDictionaryNodes1 = result1.unsafeKeys; | |||
for (int j = 0; j < result1Count; j++) | |||
{ | |||
result.Add(new ExclusiveGroupStruct(fasterDictionaryNodes1[j].key)); | |||
} | |||
return result; | |||
} | |||
public LocalFasterReadOnlyList<ExclusiveGroupStruct> FindGroups<T1, T2>() where T1 : IEntityComponent where T2 : IEntityComponent | |||
{ | |||
FasterList<ExclusiveGroupStruct> result = groups.Value; | |||
result.FastClear(); | |||
if (_groupsPerEntity.TryGetValue(TypeRefWrapper<T1>.wrapper | |||
, out FasterDictionary<uint, ITypeSafeDictionary> result1) == false) | |||
return result; | |||
if (_groupsPerEntity.TryGetValue(TypeRefWrapper<T2>.wrapper | |||
, out FasterDictionary<uint, ITypeSafeDictionary> result2) == false) | |||
return result; | |||
var result1Count = result1.count; | |||
var result2Count = result2.count; | |||
var fasterDictionaryNodes1 = result1.unsafeKeys; | |||
var fasterDictionaryNodes2 = result2.unsafeKeys; | |||
for (int i = 0; i < result1Count; i++) | |||
{ | |||
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) | |||
{ | |||
result.Add(new ExclusiveGroupStruct(fasterDictionaryNodes1[i].key)); | |||
break; | |||
} | |||
} | |||
} | |||
return result; | |||
} | |||
/// <summary> | |||
/// Remember that this operation os O(N*M*P) where N,M,P are the number of groups where each component | |||
/// is found. | |||
/// </summary> | |||
/// <typeparam name="T1"></typeparam> | |||
/// <typeparam name="T2"></typeparam> | |||
/// <typeparam name="T3"></typeparam> | |||
/// <returns></returns> | |||
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) | |||
return result; | |||
if (_groupsPerEntity.TryGetValue(TypeRefWrapper<T3>.wrapper | |||
, out FasterDictionary<uint, ITypeSafeDictionary> result3) == 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 (fasterDictionaryNodes3[j].key == result[i]) | |||
break; | |||
result.UnorderedRemoveAt(i); | |||
i--; | |||
} | |||
return result; | |||
} | |||
struct GroupsList | |||
{ | |||
static GroupsList() | |||
{ | |||
groups = new FasterList<ExclusiveGroupStruct>(); | |||
} | |||
static readonly FasterList<ExclusiveGroupStruct> groups; | |||
public static implicit operator FasterList<ExclusiveGroupStruct>(in GroupsList list) | |||
{ | |||
return list.reference; | |||
} | |||
FasterList<ExclusiveGroupStruct> reference => groups; | |||
} | |||
static readonly ThreadLocal<GroupsList> groups = new ThreadLocal<GroupsList>(); | |||
} | |||
} |
@@ -6,52 +6,38 @@ using System; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Hybrid; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public class EntitiesDB | |||
public partial class EntitiesDB | |||
{ | |||
internal EntitiesDB( | |||
FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> groupEntityComponentsDB, | |||
FasterDictionary<RefWrapper<Type>, FasterDictionary<uint, ITypeSafeDictionary>> groupsPerEntity, | |||
EntitiesStream entityStream) | |||
internal EntitiesDB | |||
(FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> groupEntityComponentsDB | |||
, FasterDictionary<RefWrapper<Type>, FasterDictionary<uint, ITypeSafeDictionary>> groupsPerEntity | |||
, EntitiesStream entityStream) | |||
{ | |||
_groupEntityComponentsDB = groupEntityComponentsDB; | |||
_groupsPerEntity = groupsPerEntity; | |||
_entityStream = entityStream; | |||
_groupsPerEntity = groupsPerEntity; | |||
_entityStream = entityStream; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref T QueryUniqueEntity<T>(ExclusiveGroupStruct group) where T : struct, IEntityComponent | |||
{ | |||
var entities = QueryEntities<T>(group).ToFastAccess(out var count); | |||
var entities = QueryEntities<T>(group); | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (count == 0) | |||
if (entities.count == 0) | |||
throw new ECSException("Unique entity not found '".FastConcat(typeof(T).ToString()).FastConcat("'")); | |||
if (count != 1) | |||
if (entities.count != 1) | |||
throw new ECSException("Unique entities must be unique! '".FastConcat(typeof(T).ToString()) | |||
.FastConcat("'")); | |||
.FastConcat("'")); | |||
#endif | |||
return ref entities[0]; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref T QueryEntity<T>(EGID entityGID) where T : struct, IEntityComponent | |||
{ | |||
T[] array; | |||
if ((array = QueryEntitiesAndIndexInternal<T>(entityGID, out var index)) != null) | |||
return ref array[(int) index]; | |||
throw new EntityNotFoundException(entityGID, typeof(T)); | |||
} | |||
public ref T QueryEntity<T>(uint id, ExclusiveGroupStruct group) where T : struct, IEntityComponent | |||
{ | |||
return ref QueryEntity<T>(new EGID(id, group)); | |||
} | |||
/// <summary> | |||
/// The QueryEntities<T> follows the rule that entities could always be iterated regardless if they | |||
/// are 0, 1 or N. In case of 0 it returns an empty array. This allows to use the same for iteration | |||
@@ -63,105 +49,119 @@ namespace Svelto.ECS | |||
public EntityCollection<T> QueryEntities<T>(ExclusiveGroupStruct groupStructId) | |||
where T : struct, IEntityComponent | |||
{ | |||
T[] ret; | |||
uint count = 0; | |||
//object sentinel = default; | |||
IBuffer<T> buffer; | |||
uint count = 0; | |||
if (SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false) | |||
ret = RetrieveEmptyEntityComponentArray<T>(); | |||
buffer = RetrieveEmptyEntityComponentArray<T>(); | |||
else | |||
{ | |||
var safeDictionary = (typeSafeDictionary as ITypeSafeDictionary<T>); | |||
ret = safeDictionary.GetValuesArray(out count); | |||
// sentinel = safeDictionary.GenerateSentinel(); | |||
buffer = safeDictionary.GetValues(out count); | |||
} | |||
return new EntityCollection<T>(ret, count); | |||
return new EntityCollection<T>(buffer, count); | |||
} | |||
public EntityCollection<T1, T2> QueryEntities<T1, T2>( | |||
ExclusiveGroupStruct groupStruct) | |||
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 DEBUG && !PROFILE_SVELTO | |||
if (T1entities.count != T2entities.count) | |||
throw new ECSException("Entity views 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("'"))); | |||
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 | |||
return new EntityCollection<T1, T2>(T1entities, T2entities); | |||
} | |||
public EntityCollection<T1, T2, T3> | |||
QueryEntities<T1, T2, T3>(ExclusiveGroupStruct groupStruct) | |||
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 DEBUG && !PROFILE_SVELTO | |||
if (T1entities.count != T2entities.count || T2entities.count != T3entities.count) | |||
throw new ECSException("Entity views 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))); | |||
return new EntityCollection<T1, T2, T3>(T1entities, | |||
T2entities, T3entities); | |||
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 new EntityCollection<T1, T2, T3>(T1entities, T2entities, T3entities); | |||
} | |||
public int IterateOverGroupsAndCount<T> | |||
(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) where T : struct, IEntityComponent | |||
{ | |||
int count = 0; | |||
for (int i = 0; i < groups.count; i++) | |||
{ | |||
count += Count<T>(groups[i]); | |||
} | |||
public EntityCollections<T> QueryEntities<T>(ExclusiveGroup[] groups) where T : struct, IEntityComponent | |||
return 0; | |||
} | |||
public TupleRef<T> QueryEntities<T> | |||
(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) where T : struct, IEntityComponent | |||
{ | |||
return new EntityCollections<T>(this, groups); | |||
return new TupleRef<T>(new EntityCollections<T>(this, groups), new GroupsEnumerable<T>(this, groups)); | |||
} | |||
public EntityCollections<T1, T2> QueryEntities<T1, T2>(ExclusiveGroup[] groups) | |||
public TupleRef<T1, T2> QueryEntities<T1, T2>(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) | |||
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent | |||
{ | |||
return new EntityCollections<T1, T2>(this, groups); | |||
return new TupleRef<T1, T2>(new EntityCollections<T1, T2>(this, groups) | |||
, new GroupsEnumerable<T1, T2>(this, groups)); | |||
} | |||
public EntityCollections<T1, T2, T3> QueryEntities<T1, T2, T3>(ExclusiveGroup[] groups) | |||
public TupleRef<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 EntityCollections<T1, T2, T3>(this, groups); | |||
return new TupleRef<T1, T2, T3>(new EntityCollections<T1, T2, T3>(this, groups) | |||
, new GroupsEnumerable<T1, T2, T3>(this, groups)); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public EGIDMapper<T> QueryMappedEntities<T>(ExclusiveGroupStruct groupStructId) | |||
where T : struct, IEntityComponent | |||
public TupleRef<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 | |||
{ | |||
if (SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false) | |||
throw new EntityGroupNotFoundException(typeof(T)); | |||
return (typeSafeDictionary as ITypeSafeDictionary<T>).ToEGIDMapper(groupStructId); | |||
return new TupleRef<T1, T2, T3, T4>(new EntityCollections<T1, T2, T3, T4>(this, groups) | |||
, new GroupsEnumerable<T1, T2, T3, T4>(this, groups)); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public NativeEGIDMapper<T> QueryNativeMappedEntities<T>(ExclusiveGroupStruct groupStructId) | |||
where T : unmanaged, IEntityComponent | |||
public EGIDMapper<T> QueryMappedEntities<T>(ExclusiveGroupStruct groupStructId) | |||
where T : struct, IEntityComponent | |||
{ | |||
if (SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false) | |||
throw new EntityGroupNotFoundException(typeof(T)); | |||
return (typeSafeDictionary as TypeSafeDictionary<T>).ToNativeEGIDMapper<T>(groupStructId); | |||
return (typeSafeDictionary as ITypeSafeDictionary<T>).ToEGIDMapper(groupStructId); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool TryQueryMappedEntities<T>(ExclusiveGroupStruct groupStructId, | |||
out EGIDMapper<T> mapper) | |||
where T : struct, IEntityComponent | |||
public bool TryQueryMappedEntities<T> | |||
(ExclusiveGroupStruct groupStructId, out EGIDMapper<T> mapper) where T : struct, IEntityComponent | |||
{ | |||
mapper = default; | |||
if (SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false || | |||
typeSafeDictionary.Count == 0) | |||
if (SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false | |||
|| typeSafeDictionary.count == 0) | |||
return false; | |||
mapper = (typeSafeDictionary as ITypeSafeDictionary<T>).ToEGIDMapper(groupStructId); | |||
@@ -169,60 +169,11 @@ namespace Svelto.ECS | |||
return true; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool TryQueryNativeMappedEntities<T>(ExclusiveGroupStruct groupStructId, | |||
out NativeEGIDMapper<T> mapper) | |||
where T : unmanaged, IEntityComponent | |||
{ | |||
mapper = default; | |||
if (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 T[] QueryEntitiesAndIndex<T>(EGID entityGID, out uint index) where T : struct, IEntityComponent | |||
{ | |||
T[] array; | |||
if ((array = QueryEntitiesAndIndexInternal<T>(entityGID, out index)) != null) | |||
return array; | |||
throw new EntityNotFoundException(entityGID, typeof(T)); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool TryQueryEntitiesAndIndex<T>(EGID entityGid, out uint index, out T[] array) | |||
where T : struct, IEntityComponent | |||
{ | |||
if ((array = QueryEntitiesAndIndexInternal<T>(entityGid, out index)) != null) | |||
return true; | |||
return false; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public T[] QueryEntitiesAndIndex<T>(uint id, ExclusiveGroupStruct @group, out uint index) | |||
where T : struct, IEntityComponent | |||
{ | |||
return QueryEntitiesAndIndex<T>(new EGID(id, @group), out index); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool TryQueryEntitiesAndIndex | |||
<T>(uint id, ExclusiveGroupStruct group, out uint index, out T[] array) | |||
where T : struct, IEntityComponent | |||
{ | |||
return TryQueryEntitiesAndIndex(new EGID(id, @group), out index, out array); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool Exists<T>(EGID entityGID) where T : struct, IEntityComponent | |||
{ | |||
if (SafeQueryEntityDictionary<T>(entityGID.groupID, out var casted) == false) return false; | |||
if (SafeQueryEntityDictionary<T>(entityGID.groupID, out var casted) == false) | |||
return false; | |||
return casted != null && casted.ContainsKey(entityGID.entityID); | |||
} | |||
@@ -230,7 +181,8 @@ namespace Svelto.ECS | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool Exists<T>(uint id, ExclusiveGroupStruct group) where T : struct, IEntityComponent | |||
{ | |||
if (SafeQueryEntityDictionary<T>(group, out var casted) == false) return false; | |||
if (SafeQueryEntityDictionary<T>(group, out var casted) == false) | |||
return false; | |||
return casted != null && casted.ContainsKey(id); | |||
} | |||
@@ -238,8 +190,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<RefWrapper<Type>, ITypeSafeDictionary> group) == true) | |||
{ | |||
return group.count > 0; | |||
} | |||
@@ -250,73 +202,59 @@ namespace Svelto.ECS | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool HasAny<T>(ExclusiveGroupStruct groupStruct) where T : struct, IEntityComponent | |||
{ | |||
return QueryEntities<T>(groupStruct).count > 0; | |||
return Count<T>(groupStruct) > 0; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public uint Count<T>(ExclusiveGroupStruct groupStruct) where T : struct, IEntityComponent | |||
public int Count<T>(ExclusiveGroupStruct groupStruct) where T : struct, IEntityComponent | |||
{ | |||
return QueryEntities<T>(groupStruct).count; | |||
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 | |||
{ | |||
_entityStream.PublishEntity(ref QueryEntity<T>(egid), egid); | |||
_entityStream.PublishEntity(ref this.QueryEntity<T>(egid), egid); | |||
} | |||
[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) | |||
{ | |||
var entities = (pair.Value as ITypeSafeDictionary<T>).GetValuesArray(out var count); | |||
IBuffer<T> entities = (pair.Value as ITypeSafeDictionary<T>).GetValues(out var count); | |||
if (count > 0) | |||
action(entities, new ExclusiveGroupStruct(pair.Key), count, this); | |||
} | |||
} | |||
[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 | |||
{ | |||
if (_groupsPerEntity.TryGetValue(TypeRefWrapper<T>.wrapper, out var dic)) | |||
foreach (var pair in dic) | |||
{ | |||
var entities = (pair.Value as ITypeSafeDictionary<T>).GetValuesArray(out var innerCount); | |||
IBuffer<T> entities = (pair.Value as ITypeSafeDictionary<T>).GetValues(out var innerCount); | |||
if (innerCount > 0) | |||
action(entities, new ExclusiveGroupStruct(pair.Key), innerCount, this, | |||
ref value); | |||
action(entities, new ExclusiveGroupStruct(pair.Key), innerCount, this, ref value); | |||
} | |||
} | |||
public QueryGroups CreateQueryGroup<T>() where T : IEntityComponent | |||
{ | |||
return new QueryGroups(FindGroups<T>()); | |||
} | |||
public bool FoundInGroups<T1>() where T1 : IEntityComponent | |||
{ | |||
return _groupsPerEntity.ContainsKey(TypeRefWrapper<T1>.wrapper); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
T[] QueryEntitiesAndIndexInternal<T>(EGID entityGID, out uint index) where T : struct, IEntityComponent | |||
{ | |||
index = 0; | |||
if (SafeQueryEntityDictionary<T>(entityGID.groupID, out var safeDictionary) == false) | |||
return null; | |||
if (safeDictionary.TryFindIndex(entityGID.entityID, out index) == false) | |||
return null; | |||
return (safeDictionary as ITypeSafeDictionary<T>).unsafeValues; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
bool SafeQueryEntityDictionary<T>(uint group, out ITypeSafeDictionary typeSafeDictionary) | |||
where T : struct, IEntityComponent | |||
internal bool SafeQueryEntityDictionary<T>(uint group, out ITypeSafeDictionary typeSafeDictionary) | |||
where T : IEntityComponent | |||
{ | |||
if (UnsafeQueryEntityDictionary(group, TypeCache<T>.type, out var safeDictionary) == false) | |||
{ | |||
@@ -345,32 +283,43 @@ namespace Svelto.ECS | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
static T[] RetrieveEmptyEntityComponentArray<T>() | |||
static IBuffer<T> RetrieveEmptyEntityComponentArray<T>() where T : struct, IEntityComponent | |||
{ | |||
return EmptyList<T>.emptyArray; | |||
} | |||
static class EmptyList<T> | |||
static class EmptyList<T> where T : struct, IEntityComponent | |||
{ | |||
internal static readonly T[] emptyArray = new T[0]; | |||
} | |||
internal static readonly IBuffer<T> emptyArray; | |||
internal FasterDictionary<uint, ITypeSafeDictionary> FindGroups<T1>() where T1 : IEntityComponent | |||
{ | |||
if (_groupsPerEntity.ContainsKey(TypeRefWrapper<T1>.wrapper) == false) | |||
return _emptyDictionary; | |||
return _groupsPerEntity[TypeRefWrapper<T1>.wrapper]; | |||
static EmptyList() | |||
{ | |||
if (ComponentBuilder<T>.IS_ENTITY_VIEW_COMPONENT) | |||
{ | |||
MB<T> b = default; | |||
emptyArray = b; | |||
} | |||
else | |||
{ | |||
NB<T> b = default; | |||
emptyArray = b; | |||
} | |||
} | |||
} | |||
readonly FasterDictionary<uint, ITypeSafeDictionary> _emptyDictionary = new FasterDictionary<uint, ITypeSafeDictionary>(); | |||
readonly FasterDictionary<uint, ITypeSafeDictionary> _emptyDictionary = | |||
new FasterDictionary<uint, ITypeSafeDictionary>(); | |||
readonly EntitiesStream _entityStream; | |||
//grouped set of entity views, this is the standard way to handle entity views entity views are grouped per | |||
//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 views 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; | |||
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 | |||
@@ -1,69 +1,68 @@ | |||
using System; | |||
using System.Runtime.CompilerServices; | |||
using System.Runtime.InteropServices; | |||
using System.Threading; | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public struct EntityCollection<T> where T : IEntityComponent | |||
public readonly ref struct EntityCollection<T> where T : struct, IEntityComponent | |||
{ | |||
public EntityCollection(T[] array, uint count) : this() | |||
{ | |||
_buffer.Set(array, count); | |||
_count = count; | |||
} | |||
public EntityCollection(MB<T> buffer, uint count) | |||
static readonly bool IsUnmanaged = TypeSafeDictionary<T>._isUmanaged; | |||
public EntityCollection(IBuffer<T> buffer, uint count):this() | |||
{ | |||
_buffer = buffer; | |||
if (IsUnmanaged) | |||
_nativedBuffer = (NB<T>) buffer; | |||
else | |||
_managedBuffer = (MB<T>) buffer; | |||
_count = count; | |||
_buffer = buffer; | |||
} | |||
public uint count => _count; | |||
readonly MB<T> _buffer; | |||
readonly uint _count; | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public T[] ToFastAccess(out uint actualCount) | |||
{ | |||
actualCount = _count; | |||
return _buffer.ToManagedArray(); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public NB<NT> ToNativeBuffer<NT>() where NT : unmanaged, T | |||
{ | |||
return new NB<NT>(_buffer.Pin(), _count, _buffer.capacity); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public MB<T> ToBuffer() | |||
{ | |||
return _buffer; | |||
} | |||
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 => ref _buffer[i]; | |||
get | |||
{ | |||
if (IsUnmanaged) | |||
return ref _nativedBuffer[i]; | |||
else | |||
return ref _managedBuffer[i]; | |||
} | |||
} | |||
public ref T this[int i] | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => ref _buffer[i]; | |||
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); } | |||
public struct EntityIterator | |||
//todo to remove | |||
public ref struct EntityIterator | |||
{ | |||
public EntityIterator(MB<T> array, uint count) : this() | |||
public EntityIterator(IBuffer<T> array, uint count) : this() | |||
{ | |||
_array = array.ToManagedArray(); | |||
_array = array; | |||
_count = count; | |||
_index = -1; | |||
} | |||
@@ -77,14 +76,13 @@ namespace Svelto.ECS | |||
get => ref _array[_index]; | |||
} | |||
readonly T[] _array; | |||
readonly uint _count; | |||
int _index; | |||
readonly IBuffer<T> _array; | |||
readonly uint _count; | |||
int _index; | |||
} | |||
} | |||
public struct EntityCollection<T1, T2> | |||
where T1 : IEntityComponent where T2 : IEntityComponent | |||
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) | |||
{ | |||
@@ -93,13 +91,14 @@ namespace Svelto.ECS | |||
} | |||
public uint count => _array1.count; | |||
//todo to remove | |||
public EntityCollection<T2> Item2 | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => _array2; | |||
} | |||
//todo to remove | |||
public EntityCollection<T1> Item1 | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
@@ -108,55 +107,23 @@ namespace Svelto.ECS | |||
readonly EntityCollection<T1> _array1; | |||
readonly EntityCollection<T2> _array2; | |||
public (T1[], T2[]) ToFastAccess(out uint count) | |||
{ | |||
count = this.count; | |||
return (_array1.ToFastAccess(out _), _array2.ToFastAccess(out _)); | |||
} | |||
public BT<MB<T1>, MB<T2>> ToBuffers() | |||
{ | |||
var bufferTuple = new BT<MB<T1>, MB<T2>> | |||
(_array1.ToBuffer(), _array2.ToBuffer(), count); | |||
return bufferTuple; | |||
} | |||
public BT<NB<NT1>, NB<NT2>> ToNativeBuffers<NT1, NT2>() | |||
where NT2 : unmanaged, T2 where NT1 : unmanaged, T1 | |||
{ | |||
var bufferTuple = new BT<NB<NT1>, NB<NT2>> | |||
(_array1.ToNativeBuffer<NT1>(), _array2.ToNativeBuffer<NT2>(), count); | |||
return bufferTuple; | |||
} | |||
//todo to remove | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public EntityIterator GetEnumerator() | |||
{ | |||
return new EntityIterator(this); | |||
} | |||
public struct EntityIterator | |||
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; | |||
_count = array1.count; | |||
_index = -1; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool MoveNext() | |||
{ | |||
return ++_index < _count; | |||
} | |||
public bool MoveNext() { return ++_index < _count; } | |||
public void Reset() | |||
{ | |||
_index = -1; | |||
} | |||
public void Reset() { _index = -1; } | |||
public ValueRef<T1, T2> Current | |||
{ | |||
@@ -165,35 +132,36 @@ namespace Svelto.ECS | |||
} | |||
readonly EntityCollection<T1, T2> _array1; | |||
readonly uint _count; | |||
int _index; | |||
readonly uint _count; | |||
int _index; | |||
} | |||
} | |||
public struct EntityCollection<T1, T2, T3> | |||
where T3 : IEntityComponent where T2 : IEntityComponent where T1 : IEntityComponent | |||
public readonly ref struct EntityCollection<T1, T2, T3> where T3 : struct, IEntityComponent | |||
where T2 : struct, IEntityComponent | |||
where T1 : struct, IEntityComponent | |||
{ | |||
public EntityCollection( | |||
in EntityCollection<T1> array1, in EntityCollection<T2> array2, | |||
in EntityCollection<T3> array3) | |||
public 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 | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => _array1; | |||
} | |||
//todo to remove | |||
public EntityCollection<T2> Item2 | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => _array2; | |||
} | |||
//todo to remove | |||
public EntityCollection<T3> Item3 | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
@@ -202,241 +170,69 @@ namespace Svelto.ECS | |||
public uint count => Item1.count; | |||
public (T1[], T2[], T3[]) ToFastAccess(out uint count) | |||
{ | |||
count = this.count; | |||
return (_array1.ToFastAccess(out _), _array2.ToFastAccess(out _), _array3.ToFastAccess(out _)); | |||
} | |||
public BT<MB<T1>, MB<T2>, MB<T3>> ToBuffers() | |||
{ | |||
var bufferTuple = new BT<MB<T1>, MB<T2>, MB<T3>> | |||
(_array1.ToBuffer(), _array2.ToBuffer(), _array3.ToBuffer(), count); | |||
return bufferTuple; | |||
} | |||
public BT<NB<NT1>, NB<NT2>, NB<NT3>> ToNativeBuffers<NT1, NT2, NT3>() | |||
where NT2 : unmanaged, T2 where NT1 : unmanaged, T1 where NT3 : unmanaged, T3 | |||
{ | |||
var bufferTuple = new BT<NB<NT1>, NB<NT2>, NB<NT3>> | |||
(_array1.ToNativeBuffer<NT1>(), _array2.ToNativeBuffer<NT2>(), _array3.ToNativeBuffer<NT3>(), count); | |||
return bufferTuple; | |||
} | |||
readonly EntityCollection<T1> _array1; | |||
readonly EntityCollection<T2> _array2; | |||
readonly EntityCollection<T3> _array3; | |||
} | |||
public struct EntityCollections<T> where T : struct, IEntityComponent | |||
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 EntityCollections(EntitiesDB db, ExclusiveGroup[] groups) : this() | |||
public EntityCollection | |||
(in EntityCollection<T1> array1, in EntityCollection<T2> array2, in EntityCollection<T3> array3, in EntityCollection<T4> array4) | |||
{ | |||
_db = db; | |||
_groups = groups; | |||
_array1 = array1; | |||
_array2 = array2; | |||
_array3 = array3; | |||
_array4 = array4; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public EntityGroupsIterator GetEnumerator() | |||
//todo to remove | |||
public EntityCollection<T1> Item1 | |||
{ | |||
return new EntityGroupsIterator(_db, _groups); | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => _array1; | |||
} | |||
readonly EntitiesDB _db; | |||
readonly ExclusiveGroup[] _groups; | |||
public struct EntityGroupsIterator | |||
//todo to remove | |||
public EntityCollection<T2> Item2 | |||
{ | |||
public EntityGroupsIterator(EntitiesDB db, ExclusiveGroup[] 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.Length) | |||
{ | |||
_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 ExclusiveGroup[] _groups; | |||
EntityCollection<T> _array; | |||
uint _count; | |||
int _index; | |||
int _indexGroup; | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => _array2; | |||
} | |||
} | |||
public struct EntityCollections<T1, T2> | |||
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent | |||
{ | |||
public EntityCollections(EntitiesDB db, ExclusiveGroup[] groups) : this() | |||
//todo to remove | |||
public EntityCollection<T3> Item3 | |||
{ | |||
_db = db; | |||
_groups = groups; | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => _array3; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public EntityGroupsIterator GetEnumerator() | |||
//todo to remove | |||
public EntityCollection<T4> Item4 | |||
{ | |||
return new EntityGroupsIterator(_db, _groups); | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => _array4; | |||
} | |||
readonly EntitiesDB _db; | |||
readonly ExclusiveGroup[] _groups; | |||
public struct EntityGroupsIterator | |||
{ | |||
public EntityGroupsIterator(EntitiesDB db, ExclusiveGroup[] 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.Length) | |||
{ | |||
_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 ExclusiveGroup[] _groups; | |||
int _index; | |||
int _indexGroup; | |||
public uint count => _array1.count; | |||
EntityCollection<T1, T2> _array1; | |||
} | |||
readonly EntityCollection<T1> _array1; | |||
readonly EntityCollection<T2> _array2; | |||
readonly EntityCollection<T3> _array3; | |||
readonly EntityCollection<T4> _array4; | |||
} | |||
public struct EntityCollections<T1, T2, T3> | |||
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent where T3 : struct, IEntityComponent | |||
{ | |||
public EntityCollections(EntitiesDB db, ExclusiveGroup[] groups) : this() | |||
{ | |||
_db = db; | |||
_groups = groups; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public EntityGroupsIterator GetEnumerator() | |||
{ | |||
return new EntityGroupsIterator(_db, _groups); | |||
} | |||
readonly EntitiesDB _db; | |||
readonly ExclusiveGroup[] _groups; | |||
public struct EntityGroupsIterator | |||
{ | |||
public EntityGroupsIterator(EntitiesDB db, ExclusiveGroup[] 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.Length) | |||
{ | |||
_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 ExclusiveGroup[] _groups; | |||
uint _count; | |||
int _index; | |||
int _indexGroup; | |||
EntityCollection<T1, T2, T3> _array1; | |||
} | |||
} | |||
public readonly struct BT<BufferT1, BufferT2, BufferT3, BufferT4> : IDisposable where BufferT1 : IDisposable | |||
where BufferT2 : IDisposable | |||
where BufferT3 : IDisposable | |||
where BufferT4 : IDisposable | |||
public readonly struct BT<BufferT1, BufferT2, BufferT3, BufferT4> | |||
{ | |||
public readonly BufferT1 buffer1; | |||
public readonly BufferT2 buffer2; | |||
public readonly BufferT3 buffer3; | |||
public readonly BufferT4 buffer4; | |||
public readonly uint count; | |||
public readonly int count; | |||
public BT(BufferT1 bufferT1, BufferT2 bufferT2, BufferT3 bufferT3, BufferT4 bufferT4, uint count) : this() | |||
{ | |||
@@ -444,65 +240,76 @@ namespace Svelto.ECS | |||
this.buffer2 = bufferT2; | |||
this.buffer3 = bufferT3; | |||
this.buffer4 = bufferT4; | |||
this.count = count; | |||
} | |||
public void Dispose() | |||
{ | |||
buffer1.Dispose(); | |||
buffer2.Dispose(); | |||
buffer3.Dispose(); | |||
buffer4.Dispose(); | |||
this.count = (int) count; | |||
} | |||
} | |||
public readonly struct BT<BufferT1, BufferT2, BufferT3> : IDisposable where BufferT1 : IDisposable | |||
where BufferT2 : IDisposable | |||
where BufferT3 : IDisposable | |||
public readonly struct BT<BufferT1, BufferT2, BufferT3> | |||
{ | |||
public readonly BufferT1 buffer1; | |||
public readonly BufferT2 buffer2; | |||
public readonly BufferT3 buffer3; | |||
public readonly uint count; | |||
public readonly int count; | |||
public BT(BufferT1 bufferT1, BufferT2 bufferT2, BufferT3 bufferT3, uint count) : this() | |||
{ | |||
this.buffer1 = bufferT1; | |||
this.buffer2 = bufferT2; | |||
this.buffer3 = bufferT3; | |||
this.count = count; | |||
this.count = (int) count; | |||
} | |||
public void Dispose() | |||
public void Deconstruct(out BufferT1 bufferT1, out BufferT2 bufferT2, out BufferT3 bufferT3, out int count) | |||
{ | |||
bufferT1 = buffer1; | |||
bufferT2 = buffer2; | |||
bufferT3 = buffer3; | |||
count = this.count; | |||
} | |||
} | |||
public readonly struct BT<BufferT1> | |||
{ | |||
public readonly BufferT1 buffer; | |||
public readonly int count; | |||
public BT(BufferT1 bufferT1, uint count) : this() | |||
{ | |||
this.buffer = bufferT1; | |||
this.count = (int) count; | |||
} | |||
public void Deconstruct(out BufferT1 bufferT1, out int count) | |||
{ | |||
buffer1.Dispose(); | |||
buffer2.Dispose(); | |||
buffer3.Dispose(); | |||
bufferT1 = buffer; | |||
count = this.count; | |||
} | |||
public static implicit operator BufferT1(BT<BufferT1> t) => t.buffer; | |||
} | |||
public readonly struct BT<BufferT1, BufferT2> : IDisposable | |||
where BufferT1 : IDisposable where BufferT2 : IDisposable | |||
public readonly struct BT<BufferT1, BufferT2> | |||
{ | |||
public readonly BufferT1 buffer1; | |||
public readonly BufferT2 buffer2; | |||
public readonly uint count; | |||
public readonly int count; | |||
public BT(BufferT1 bufferT1, BufferT2 bufferT2, uint count) : this() | |||
{ | |||
this.buffer1 = bufferT1; | |||
this.buffer2 = bufferT2; | |||
this.count = count; | |||
this.count = (int) count; | |||
} | |||
public void Dispose() | |||
public void Deconstruct(out BufferT1 bufferT1, out BufferT2 bufferT2, out int count) | |||
{ | |||
buffer1.Dispose(); | |||
buffer2.Dispose(); | |||
bufferT1 = buffer1; | |||
bufferT2 = buffer2; | |||
count = this.count; | |||
} | |||
} | |||
public ref struct ValueRef<T1, T2> where T2 : IEntityComponent where T1 : IEntityComponent | |||
public readonly ref struct ValueRef<T1, T2> where T2 : struct, IEntityComponent where T1 : struct, IEntityComponent | |||
{ | |||
readonly EntityCollection<T1, T2> array1; | |||
@@ -511,7 +318,7 @@ namespace Svelto.ECS | |||
public ValueRef(in EntityCollection<T1, T2> entity2, uint i) | |||
{ | |||
array1 = entity2; | |||
index = i; | |||
index = i; | |||
} | |||
public ref T1 entityComponentA | |||
@@ -527,8 +334,9 @@ namespace Svelto.ECS | |||
} | |||
} | |||
public ref struct ValueRef<T1, T2, T3> | |||
where T2 : IEntityComponent where T1 : IEntityComponent where T3 : IEntityComponent | |||
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; | |||
@@ -551,11 +359,11 @@ namespace Svelto.ECS | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => ref array1.Item2[index]; | |||
} | |||
public ref T3 entityComponentC | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => ref array1.Item3[index]; | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,266 @@ | |||
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; | |||
} | |||
} | |||
} |
@@ -12,6 +12,8 @@ namespace Svelto.ECS | |||
_ID = id; | |||
} | |||
public EGID EGID => _ID; | |||
public void Init<T>(T initializer) where T : struct, IEntityComponent | |||
{ | |||
if (_group.TryGetValue(new RefWrapper<Type>(ComponentBuilder<T>.ENTITY_COMPONENT_TYPE), | |||
@@ -23,7 +25,7 @@ namespace Svelto.ECS | |||
SetEGIDWithoutBoxing<T>.SetIDWithoutBoxing(ref initializer, _ID); | |||
if (dictionary.TryFindIndex(_ID.entityID, out var findElementIndex)) | |||
dictionary.unsafeValues[findElementIndex] = initializer; | |||
dictionary.GetDirectValueByRef(findElementIndex) = initializer; | |||
} | |||
public ref T GetOrCreate<T>() where T : struct, IEntityComponent | |||
@@ -37,16 +37,16 @@ namespace Svelto.ECS.Internal | |||
static void BuildEntitiesAndAddToGroup(EGID entityID, | |||
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> group, | |||
IComponentBuilder[] entityBuilders, IEnumerable<object> implementors) | |||
IComponentBuilder[] componentBuilders, IEnumerable<object> implementors) | |||
{ | |||
var count = componentBuilders.Length; | |||
#if DEBUG && !PROFILE_SVELTO | |||
HashSet<Type> types = new HashSet<Type>(); | |||
#endif | |||
var count = entityBuilders.Length; | |||
#if DEBUG && !PROFILE_SVELTO | |||
for (var index = 0; index < count; ++index) | |||
{ | |||
var entityComponentType = entityBuilders[index].GetEntityComponentType(); | |||
var entityComponentType = componentBuilders[index].GetEntityComponentType(); | |||
if (types.Contains(entityComponentType)) | |||
{ | |||
throw new ECSException("EntityBuilders must be unique inside an EntityDescriptor"); | |||
@@ -57,7 +57,7 @@ namespace Svelto.ECS.Internal | |||
#endif | |||
for (var index = 0; index < count; ++index) | |||
{ | |||
var entityComponentBuilder = entityBuilders[index]; | |||
var entityComponentBuilder = componentBuilders[index]; | |||
var entityComponentType = entityComponentBuilder.GetEntityComponentType(); | |||
BuildEntity(entityID, @group, entityComponentType, entityComponentBuilder, implementors); | |||
@@ -0,0 +1,11 @@ | |||
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 EntityInfoComponentView: IEntityComponent | |||
struct EntityInfoViewComponent: IEntityComponent | |||
{ | |||
public IComponentBuilder[] componentsToBuild; | |||
} |
@@ -5,7 +5,7 @@ namespace Svelto.ECS | |||
public class EntityNotFoundException : Exception | |||
{ | |||
public EntityNotFoundException(EGID entityEGID, Type entityType) : base( | |||
$"entity of type '{entityType}' with ID '{entityEGID.entityID}', group '{(uint) entityEGID.groupID}' not found!") | |||
$"entity of type '{entityType}' with ID '{entityEGID.entityID}', group '{entityEGID.groupID.ToName()}' not found!") | |||
{ | |||
} | |||
} |
@@ -5,12 +5,11 @@ using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
/// <summary> | |||
/// Do not use this class in place of a normal polling. | |||
/// I eventually realised than in ECS 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, so EntityStreams must be used | |||
/// only if: | |||
/// - you want to polling engine to be able to track all the entity changes happening in between polls and not | |||
/// just the current state | |||
/// 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 | |||
@@ -26,7 +25,7 @@ namespace Svelto.ECS | |||
return (_streams[TypeRefWrapper<T>.wrapper] as EntityStream<T>).GenerateConsumer(name, capacity); | |||
} | |||
public Consumer<T> GenerateConsumer<T>(ExclusiveGroup group, string name, uint capacity) | |||
public Consumer<T> GenerateConsumer<T>(ExclusiveGroupStruct group, string name, uint capacity) | |||
where T : unmanaged, IEntityComponent | |||
{ | |||
if (_streams.ContainsKey(TypeRefWrapper<T>.wrapper) == false) | |||
@@ -107,7 +106,7 @@ namespace Svelto.ECS | |||
return consumer; | |||
} | |||
internal Consumer<T> GenerateConsumer(ExclusiveGroup group, string name, uint capacity) | |||
internal Consumer<T> GenerateConsumer(ExclusiveGroupStruct group, string name, uint capacity) | |||
{ | |||
var consumer = new Consumer<T>(group, name, capacity); | |||
@@ -140,7 +139,7 @@ namespace Svelto.ECS | |||
} | |||
} | |||
internal Consumer(ExclusiveGroup group, string name, uint capacity) : this(name, capacity) | |||
internal Consumer(ExclusiveGroupStruct group, string name, uint capacity) : this(name, capacity) | |||
{ | |||
this.@group = @group; | |||
hasGroup = true; | |||
@@ -195,7 +194,7 @@ namespace Svelto.ECS | |||
readonly RingBuffer<ValueTuple<T, EGID>> _ringBuffer; | |||
internal readonly ExclusiveGroup @group; | |||
internal readonly ExclusiveGroupStruct @group; | |||
internal readonly bool hasGroup; | |||
internal IntPtr mustBeDisposed; | |||
@@ -5,5 +5,7 @@ namespace Svelto.ECS.Schedulers | |||
public interface IEntitiesSubmissionScheduler: IDisposable | |||
{ | |||
EnginesRoot.EntitiesSubmitter onTick { set; } | |||
bool paused { get; set; } | |||
} | |||
} |
@@ -48,38 +48,42 @@ namespace Svelto.ECS | |||
entityComponentBlazingFastReflection, out var count); | |||
//todo this should happen once per T, not once per Build<T> | |||
foreach (var implementor in implementors) | |||
if (implementors != null) | |||
{ | |||
if (implementor != null) | |||
foreach (var implementor in implementors) | |||
{ | |||
var type = implementor.GetType(); | |||
if (implementor != null) | |||
{ | |||
var type = implementor.GetType(); | |||
if (cachedTypeInterfaces.TryGetValue(type, out var interfaces) == false) | |||
interfaces = cachedTypeInterfaces[type] = type.GetInterfacesEx(); | |||
if (cachedTypeInterfaces.TryGetValue(type, out var interfaces) == false) | |||
interfaces = cachedTypeInterfaces[type] = type.GetInterfacesEx(); | |||
for (var iindex = 0; iindex < interfaces.Length; iindex++) | |||
{ | |||
var componentType = interfaces[iindex]; | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (implementorsByType.TryGetValue(componentType, out var implementorData)) | |||
for (var iindex = 0; iindex < interfaces.Length; iindex++) | |||
{ | |||
implementorData.numberOfImplementations++; | |||
implementorsByType[componentType] = implementorData; | |||
} | |||
else | |||
implementorsByType[componentType] = new ECSTuple<object, int>(implementor, 1); | |||
var componentType = interfaces[iindex]; | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (implementorsByType.TryGetValue(componentType, out var implementorData)) | |||
{ | |||
implementorData.numberOfImplementations++; | |||
implementorsByType[componentType] = implementorData; | |||
} | |||
else | |||
implementorsByType[componentType] = new ECSTuple<object, int>(implementor, 1); | |||
#else | |||
implementorsByType[componentType] = implementor; | |||
#endif | |||
} | |||
} | |||
} | |||
#if DEBUG && !PROFILE_SVELTO | |||
else | |||
{ | |||
Console.Log(NULL_IMPLEMENTOR_ERROR.FastConcat(" entityComponent ", | |||
componentBuilder.GetEntityComponentType().ToString())); | |||
} | |||
else | |||
{ | |||
Console.Log(NULL_IMPLEMENTOR_ERROR.FastConcat(" entityComponent " | |||
, componentBuilder | |||
.GetEntityComponentType().ToString())); | |||
} | |||
#endif | |||
} | |||
} | |||
for (var i = 0; i < count; i++) | |||
@@ -1,27 +1,11 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using Svelto.ECS.Internal; | |||
using System.Runtime.CompilerServices; | |||
#pragma warning disable 660,661 | |||
namespace Svelto.ECS | |||
{ | |||
/// <summary> | |||
/// still experimental alternative to ExclusiveGroup, use this like: | |||
/// use this like: | |||
/// public class TriggersGroup : ExclusiveGroup<TriggersGroup> {} | |||
/// </summary> | |||
/// <typeparam name="T"></typeparam> | |||
public abstract class NamedExclusiveGroup<T> | |||
{ | |||
public static ExclusiveGroup Group = new ExclusiveGroup(); | |||
public static string name = typeof(T).FullName; | |||
// protected NamedExclusiveGroup() { } | |||
// protected NamedExclusiveGroup(string recognizeAs) : base(recognizeAs) {} | |||
// protected NamedExclusiveGroup(ushort range) : base(range) {} | |||
} | |||
/// <summary> | |||
/// Exclusive Groups guarantee that the GroupID is unique. | |||
/// | |||
@@ -1,7 +1,7 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Diagnostics; | |||
using System.Runtime.InteropServices; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
@@ -51,7 +51,7 @@ namespace Svelto.ECS | |||
public override string ToString() | |||
{ | |||
return _id.ToString(); | |||
return this.ToName(); | |||
} | |||
internal static ExclusiveGroupStruct Generate(byte bitmask = 0) | |||
@@ -109,6 +109,8 @@ namespace Svelto.ECS | |||
[FieldOffset(0)] uint _id; | |||
[FieldOffset(3)] byte _bytemask; | |||
static uint _globalId; | |||
static uint _globalId = 1; //it starts from 1 because default EGID is considered not initalized value | |||
} | |||
} |
@@ -10,8 +10,8 @@ namespace Svelto.ECS | |||
{ | |||
var iterationsPerBatch = totalIterations / processorCount; | |||
if (iterationsPerBatch < 16) | |||
return 16; | |||
if (iterationsPerBatch < 32) | |||
return 32; | |||
return (int) iterationsPerBatch; | |||
} | |||
@@ -1,32 +1,37 @@ | |||
using System; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public readonly struct NativeAllGroupsEnumerable<T1> where T1 : unmanaged, IEntityComponent | |||
/// <summary> | |||
/// ToDo it would be interesting to have a version of this dedicated to unmanaged, IEntityComponent | |||
/// that can be burstifiable | |||
/// </summary> | |||
/// <typeparam name="T1"></typeparam> | |||
public readonly struct AllGroupsEnumerable<T1> where T1 : struct, IEntityComponent | |||
{ | |||
public NativeAllGroupsEnumerable(EntitiesDB db) | |||
public ref struct GroupCollection | |||
{ | |||
internal EntityCollection<T1> collection; | |||
internal ExclusiveGroupStruct group; | |||
public void Deconstruct(out EntityCollection<T1> collection, out ExclusiveGroupStruct group) | |||
{ | |||
collection = this.collection; | |||
group = this.@group; | |||
} | |||
} | |||
public AllGroupsEnumerable(EntitiesDB db) | |||
{ | |||
_db = db; | |||
} | |||
public struct NativeGroupsIterator | |||
public ref struct GroupsIterator | |||
{ | |||
public struct CurrentGroup: IDisposable | |||
{ | |||
public NB<T1> buffer; | |||
public ExclusiveGroupStruct group; | |||
public void Dispose() | |||
{ | |||
buffer.Dispose(); | |||
} | |||
} | |||
public NativeGroupsIterator(EntitiesDB db) : this() | |||
public GroupsIterator(EntitiesDB db) : this() | |||
{ | |||
_db = db.FindGroups<T1>().GetEnumerator(); | |||
_db = db.FindGroups_INTERNAL<T1>().GetEnumerator(); | |||
} | |||
public bool MoveNext() | |||
@@ -35,13 +40,11 @@ namespace Svelto.ECS | |||
while (_db.MoveNext() == true) | |||
{ | |||
FasterDictionary<uint, ITypeSafeDictionary>.KeyValuePairFast group = _db.Current; | |||
ITypeSafeDictionary<T1> typeSafeDictionary = @group.Value as ITypeSafeDictionary<T1>; | |||
if (typeSafeDictionary.Count == 0) continue; | |||
_array.buffer = new EntityCollection<T1>(typeSafeDictionary.GetValuesArray(out var count), count) | |||
.ToNativeBuffer<T1>(); | |||
if (typeSafeDictionary.count == 0) continue; | |||
_array.collection = new EntityCollection<T1>(typeSafeDictionary.GetValues(out var count), count); | |||
_array.@group = new ExclusiveGroupStruct(group.Key); | |||
return true; | |||
@@ -50,19 +53,15 @@ namespace Svelto.ECS | |||
return false; | |||
} | |||
public void Reset() | |||
{ | |||
} | |||
public CurrentGroup Current => _array; | |||
public GroupCollection Current => _array; | |||
FasterDictionary<uint, ITypeSafeDictionary>.FasterDictionaryKeyValueEnumerator _db; | |||
CurrentGroup _array; | |||
FasterDictionary<uint, ITypeSafeDictionary>.FasterDictionaryKeyValueEnumerator _db; | |||
GroupCollection _array; | |||
} | |||
public NativeGroupsIterator GetEnumerator() | |||
public GroupsIterator GetEnumerator() | |||
{ | |||
return new NativeGroupsIterator(_db); | |||
return new GroupsIterator(_db); | |||
} | |||
readonly EntitiesDB _db; | |||
@@ -99,7 +98,7 @@ namespace Svelto.ECS | |||
if (typeSafeDictionary1.Count == 0) continue; | |||
_array = new BT<NB<T1>, NB<T2>>()(new EntityCollection<T1>(typeSafeDictionary1.GetValuesArray(out var count), count) | |||
.ToNativeBuffer<T1>(); | |||
.ToBuffer(); | |||
return true; | |||
} |
@@ -0,0 +1,162 @@ | |||
using System; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Hybrid; | |||
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 | |||
{ | |||
buffer = ec._nativedBuffer; | |||
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 | |||
{ | |||
buffer1 = ec.Item1._nativedBuffer; | |||
buffer2 = ec.Item2._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.Item1._nativedBuffer; | |||
buffer2 = ec.Item2._nativedBuffer; | |||
buffer3 = ec.Item3._nativedBuffer; | |||
count = (int) ec.count; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static BT<NB<T1>> ToBuffer<T1>(in this EntityCollection<T1> ec) where T1 : unmanaged, IEntityComponent | |||
{ | |||
return new BT<NB<T1>>(ec._nativedBuffer, ec.count); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static BT<NB<T1>, NB<T2>> ToBuffers<T1, T2> | |||
(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); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static BT<NB<T1>, NB<T2>, NB<T3>> ToBuffers<T1, T2, T3> | |||
(in this EntityCollection<T1, T2, T3> ec) | |||
where T2 : unmanaged, IEntityComponent | |||
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); | |||
} | |||
} | |||
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 | |||
{ | |||
buffer = ec._managedBuffer; | |||
count = (int) ec.count; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static BT<MB<T1>> ToBuffer<T1>(in this EntityCollection<T1> ec) where T1 : struct, IEntityViewComponent | |||
{ | |||
return new BT<MB<T1>>(ec._managedBuffer, ec.count); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static (MB<T1> buffer1, MB<T2> buffer2, uint count) ToBuffers<T1, T2> | |||
(in this EntityCollection<T1, T2> ec) | |||
where T2 : struct, IEntityViewComponent where T1 : struct, IEntityViewComponent | |||
{ | |||
return (ec.Item1._managedBuffer, ec.Item2._managedBuffer, ec.count); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static (MB<T1> buffer1, MB<T2> buffer2, MB<T3> buffer3, uint count) ToBuffers<T1, T2, T3> | |||
(in this EntityCollection<T1, T2, T3> ec) | |||
where T2 : struct, IEntityViewComponent | |||
where T1 : struct, IEntityViewComponent | |||
where T3 : struct, IEntityViewComponent | |||
{ | |||
return (ec.Item1._managedBuffer, ec.Item2._managedBuffer, ec.Item3._managedBuffer, ec.count); | |||
} | |||
} | |||
public static class EntityCollectionExtensionC | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static (NB<T1> buffer1, MB<T2> buffer2, uint count) ToBuffers<T1, T2> | |||
(in this EntityCollection<T1, T2> ec) | |||
where T1 : unmanaged, IEntityComponent where T2 : struct, IEntityViewComponent | |||
{ | |||
return (ec.Item1._nativedBuffer, ec.Item2._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.Item1._nativedBuffer; | |||
buffer2 = ec.Item2._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) | |||
where T1 : unmanaged, IEntityComponent | |||
where T2 : struct, IEntityViewComponent | |||
where T3 : struct, IEntityViewComponent | |||
{ | |||
return (ec.Item1._nativedBuffer, ec.Item2._managedBuffer, ec.Item3._managedBuffer, 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 | |||
{ | |||
buffer1 = ec.Item1._nativedBuffer; | |||
buffer2 = ec.Item2._nativedBuffer; | |||
buffer3 = ec.Item3._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) | |||
where T1 : unmanaged, IEntityComponent | |||
where T2 : unmanaged, IEntityComponent | |||
where T3 : struct, IEntityViewComponent | |||
{ | |||
return (ec.Item1._nativedBuffer, ec.Item2._nativedBuffer, ec.Item3._managedBuffer, ec.count); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
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); | |||
} | |||
} | |||
} |
@@ -1,77 +1,85 @@ | |||
using System.Collections.Generic; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public static class EntityDBExtensions | |||
{ | |||
public static NativeGroupsEnumerable<T1, T2> NativeGroupsIterator<T1, T2>(this EntitiesDB db, | |||
ExclusiveGroupStruct[] groups) | |||
where T1 : unmanaged, IEntityComponent where T2 : unmanaged, IEntityComponent | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static AllGroupsEnumerable<T1> QueryEntities<T1>(this EntitiesDB db) | |||
where T1 :struct, IEntityComponent | |||
{ | |||
return new NativeGroupsEnumerable<T1, T2>(db, groups, (uint)groups.Length); | |||
} | |||
public static NativeGroupsEnumerable<T1, T2> NativeGroupsIterator<T1, T2>(this EntitiesDB db, | |||
FasterList<ExclusiveGroupStruct> groups) | |||
where T1 : unmanaged, IEntityComponent where T2 : unmanaged, IEntityComponent | |||
{ | |||
return new NativeGroupsEnumerable<T1, T2>(db, groups, groups.count); | |||
return new AllGroupsEnumerable<T1>(db); | |||
} | |||
[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) | |||
return array; | |||
public static NativeGroupsEnumerable<T1, T2, T3> NativeGroupsIterator | |||
<T1, T2, T3>(this EntitiesDB db, ExclusiveGroupStruct[] groups) | |||
where T1 : unmanaged, IEntityComponent where T2 : unmanaged, IEntityComponent | |||
where T3 : unmanaged, IEntityComponent | |||
{ | |||
return new NativeGroupsEnumerable<T1, T2, T3>(db, groups); | |||
} | |||
public static NativeGroupsEnumerable<T1, T2, T3, T4> NativeGroupsIterator | |||
<T1, T2, T3, T4>(this EntitiesDB db, ExclusiveGroupStruct[] groups) | |||
where T1 : unmanaged, IEntityComponent where T2 : unmanaged, IEntityComponent | |||
where T3 : unmanaged, IEntityComponent where T4 : unmanaged, IEntityComponent | |||
{ | |||
return new NativeGroupsEnumerable<T1, T2, T3, T4>(db, groups); | |||
} | |||
public static NativeGroupsEnumerable<T1> NativeGroupsIterator<T1>(this EntitiesDB db, ExclusiveGroupStruct[] groups) | |||
where T1 : unmanaged, IEntityComponent | |||
{ | |||
return new NativeGroupsEnumerable<T1>(db, groups); | |||
} | |||
throw new EntityNotFoundException(entityGID, typeof(T)); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
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) | |||
return array; | |||
public static NativeAllGroupsEnumerable<T1> NativeGroupsIterator<T1>(this EntitiesDB db) | |||
where T1 : unmanaged, IEntityComponent | |||
{ | |||
return new NativeAllGroupsEnumerable<T1>(db); | |||
} | |||
#if TO_BE_FINISHED | |||
public static NativeAllGroupsEnumerable<T1, T2> NativeGroupsIterator<T1, T2>(this EntitiesDB db) | |||
where T1 : unmanaged, IEntityComponent where T2 : unmanaged, IEntityComponent | |||
{ | |||
return new NativeAllGroupsEnumerable<T1, T2>(db); | |||
} | |||
#endif | |||
public static NB<T> NativeEntitiesBuffer<T>(this EntitiesDB db, ExclusiveGroupStruct @group) | |||
where T : unmanaged, IEntityComponent | |||
{ | |||
return db.QueryEntities<T>(group).ToNativeBuffer<T>(); | |||
} | |||
public static BT<NB<T1>, NB<T2>> NativeEntitiesBuffer<T1, T2>(this EntitiesDB db, ExclusiveGroupStruct @group) | |||
where T1 : unmanaged, IEntityComponent | |||
where T2 : unmanaged, IEntityComponent | |||
{ | |||
return db.QueryEntities<T1, T2>(group).ToNativeBuffers<T1, T2>(); | |||
} | |||
throw new EntityNotFoundException(entityGID, typeof(T)); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
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) | |||
return true; | |||
return false; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
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) | |||
return true; | |||
return false; | |||
} | |||
public static BT<NB<T1>, NB<T2>, NB<T3>> NativeEntitiesBuffer<T1, T2, T3>(this EntitiesDB db, ExclusiveGroupStruct @group) | |||
where T1 : unmanaged, IEntityComponent | |||
where T2 : unmanaged, IEntityComponent | |||
where T3 : unmanaged, IEntityComponent | |||
{ | |||
return db.QueryEntities<T1, T2, T3>(group).ToNativeBuffers<T1, T2, T3>(); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
static bool QueryEntitiesAndIndexInternal<T>(this EntitiesDB entitiesDb, EGID entityGID, out uint index, out NB<T> buffer) where T : unmanaged, IEntityComponent | |||
{ | |||
index = 0; | |||
buffer = default; | |||
if (entitiesDb.SafeQueryEntityDictionary<T>(entityGID.groupID, out var safeDictionary) == false) | |||
return false; | |||
if (safeDictionary.TryFindIndex(entityGID.entityID, out index) == false) | |||
return false; | |||
buffer = (NB<T>) (safeDictionary as ITypeSafeDictionary<T>).GetValues(out _); | |||
return true; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static ref T QueryEntity<T>(this EntitiesDB entitiesDb, EGID entityGID) where T : unmanaged, IEntityComponent | |||
{ | |||
var array = entitiesDb.QueryEntitiesAndIndex<T>(entityGID, out var index); | |||
return ref array[(int) index]; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static ref T QueryEntity<T>(this EntitiesDB entitiesDb, uint id, ExclusiveGroupStruct group) where T : unmanaged, IEntityComponent | |||
{ | |||
return ref entitiesDb.QueryEntity<T>(new EGID(id, group)); | |||
} | |||
} | |||
} |
@@ -0,0 +1,69 @@ | |||
using System.Runtime.CompilerServices; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Hybrid; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public static class EntityDBExtensionsB | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static MB<T> QueryEntitiesAndIndex<T>(this EntitiesDB entitiesDb, EGID entityGID, out uint index) where T : struct, IEntityViewComponent | |||
{ | |||
if (entitiesDb.QueryEntitiesAndIndexInternal<T>(entityGID, out index, out MB<T> array) == true) | |||
return array; | |||
throw new EntityNotFoundException(entityGID, typeof(T)); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static bool TryQueryEntitiesAndIndex<T>(this EntitiesDB entitiesDb, EGID entityGID, out uint index, out MB<T> array) | |||
where T : struct, IEntityViewComponent | |||
{ | |||
if (entitiesDb.QueryEntitiesAndIndexInternal<T>(entityGID, out index, out array) == true) | |||
return true; | |||
return false; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static bool TryQueryEntitiesAndIndex<T>(this EntitiesDB entitiesDb, uint id, ExclusiveGroupStruct group, out uint index, out MB<T> array) | |||
where T : struct, IEntityViewComponent | |||
{ | |||
if (entitiesDb.QueryEntitiesAndIndexInternal<T>(new EGID(id, group), out index, out array) == true) | |||
return true; | |||
return false; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
static bool QueryEntitiesAndIndexInternal<T>(this EntitiesDB entitiesDb, EGID entityGID, out uint index, out MB<T> buffer) where T : struct, IEntityViewComponent | |||
{ | |||
index = 0; | |||
buffer = default; | |||
if (entitiesDb.SafeQueryEntityDictionary<T>(entityGID.groupID, out var safeDictionary) == false) | |||
return false; | |||
if (safeDictionary.TryFindIndex(entityGID.entityID, out index) == false) | |||
return false; | |||
buffer = (MB<T>) (safeDictionary as ITypeSafeDictionary<T>).GetValues(out _); | |||
return true; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static ref T QueryEntity<T>(this EntitiesDB entitiesDb, EGID entityGID) where T : struct, IEntityViewComponent | |||
{ | |||
var array = entitiesDb.QueryEntitiesAndIndex<T>(entityGID, out var index); | |||
return ref array[(int) index]; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static ref T QueryEntity<T>(this EntitiesDB entitiesDb, uint id, ExclusiveGroupStruct group) where T : struct, IEntityViewComponent | |||
{ | |||
return ref entitiesDb.QueryEntity<T>(new EGID(id, group)); | |||
} | |||
} | |||
} |
@@ -0,0 +1,249 @@ | |||
using DBC.ECS; | |||
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 | |||
/// </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 | |||
{ | |||
readonly EntitiesDB _db; | |||
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups; | |||
public GroupsEnumerable(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) | |||
{ | |||
_db = db; | |||
_groups = groups; | |||
} | |||
public ref struct GroupsIterator | |||
{ | |||
public GroupsIterator(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) : this() | |||
{ | |||
_groups = groups; | |||
_indexGroup = -1; | |||
_entitiesDB = db; | |||
} | |||
public bool MoveNext() | |||
{ | |||
//attention, the while is necessary to skip empty groups | |||
while (++_indexGroup < _groups.count) | |||
{ | |||
var entityCollection1 = _entitiesDB.QueryEntities<T1, T2, T3>(_groups[_indexGroup]); | |||
if (entityCollection1.count == 0) | |||
continue; | |||
var entityCollection2 = _entitiesDB.QueryEntities<T4>(_groups[_indexGroup]); | |||
if (entityCollection2.count == 0) | |||
continue; | |||
Check.Assert(entityCollection1.count == entityCollection2.count | |||
, "congratulation, you found a bug in Svelto, please report it"); | |||
EntityCollection<T1, T2, T3> array = entityCollection1; | |||
var array2 = entityCollection2; | |||
_buffers = new EntityCollection<T1, T2, T3, T4>( | |||
array.Item1, array.Item2, array.Item3, array2); | |||
break; | |||
} | |||
var moveNext = _indexGroup < _groups.count; | |||
if (moveNext == false) | |||
Reset(); | |||
return moveNext; | |||
} | |||
public void Reset() { _indexGroup = -1; } | |||
public EntityCollection<T1, T2, T3, T4> Current => _buffers; | |||
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups; | |||
int _indexGroup; | |||
EntityCollection<T1, T2, T3, T4> _buffers; | |||
readonly EntitiesDB _entitiesDB; | |||
} | |||
public GroupsIterator GetEnumerator() { return new GroupsIterator(_db, _groups); } | |||
} | |||
/// <summary> | |||
/// 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 | |||
{ | |||
readonly EntitiesDB _db; | |||
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups; | |||
public GroupsEnumerable(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) | |||
{ | |||
_db = db; | |||
_groups = groups; | |||
} | |||
public ref struct GroupsIterator | |||
{ | |||
public GroupsIterator(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) : this() | |||
{ | |||
_groups = groups; | |||
_indexGroup = -1; | |||
_entitiesDB = db; | |||
} | |||
public bool MoveNext() | |||
{ | |||
//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]); | |||
if (entityCollection.count == 0) | |||
continue; | |||
_buffers = entityCollection; | |||
break; | |||
} | |||
var moveNext = _indexGroup < _groups.count; | |||
if (moveNext == false) | |||
Reset(); | |||
return moveNext; | |||
} | |||
public void Reset() { _indexGroup = -1; } | |||
public EntityCollection<T1, T2, T3> Current => _buffers; | |||
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups; | |||
int _indexGroup; | |||
EntityCollection<T1, T2, T3> _buffers; | |||
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 GroupsEnumerable(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) | |||
{ | |||
_db = db; | |||
_groups = groups; | |||
} | |||
public ref struct GroupsIterator | |||
{ | |||
public GroupsIterator(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) : this() | |||
{ | |||
_db = db; | |||
_groups = groups; | |||
_indexGroup = -1; | |||
} | |||
public bool MoveNext() | |||
{ | |||
//attention, the while is necessary to skip empty groups | |||
while (++_indexGroup < _groups.count) | |||
{ | |||
var entityCollection = _db.QueryEntities<T1, T2>(_groups[_indexGroup]); | |||
if (entityCollection.count == 0) | |||
continue; | |||
_buffers = entityCollection; | |||
break; | |||
} | |||
var moveNext = _indexGroup < _groups.count; | |||
if (moveNext == false) | |||
Reset(); | |||
return moveNext; | |||
} | |||
public void Reset() { _indexGroup = -1; } | |||
public EntityCollection<T1, T2> Current => _buffers; | |||
readonly EntitiesDB _db; | |||
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups; | |||
int _indexGroup; | |||
EntityCollection<T1, T2> _buffers; | |||
} | |||
public GroupsIterator GetEnumerator() { return new GroupsIterator(_db, _groups); } | |||
readonly EntitiesDB _db; | |||
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups; | |||
} | |||
public readonly ref struct GroupsEnumerable<T1> where T1 : struct, IEntityComponent | |||
{ | |||
public GroupsEnumerable(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) | |||
{ | |||
_db = db; | |||
_groups = groups; | |||
} | |||
public ref struct GroupsIterator | |||
{ | |||
public GroupsIterator(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) : this() | |||
{ | |||
_db = db; | |||
_groups = groups; | |||
_indexGroup = -1; | |||
} | |||
public bool MoveNext() | |||
{ | |||
//attention, the while is necessary to skip empty groups | |||
while (++_indexGroup < _groups.count) | |||
{ | |||
var entityCollection = _db.QueryEntities<T1>(_groups[_indexGroup]); | |||
if (entityCollection.count == 0) | |||
continue; | |||
_buffer = entityCollection; | |||
break; | |||
} | |||
return _indexGroup < _groups.count; | |||
} | |||
public void Reset() { _indexGroup = -1; } | |||
public EntityCollection<T1> Current => _buffer; | |||
readonly EntitiesDB _db; | |||
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups; | |||
int _indexGroup; | |||
EntityCollection<T1> _buffer; | |||
} | |||
public GroupsIterator GetEnumerator() { return new GroupsIterator(_db, _groups); } | |||
readonly EntitiesDB _db; | |||
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups; | |||
} | |||
} |
@@ -1,229 +0,0 @@ | |||
using DBC.ECS; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
public readonly struct NativeGroupsEnumerable<T1, T2, T3, T4> where T1 : unmanaged, IEntityComponent | |||
where T2 : unmanaged, IEntityComponent | |||
where T3 : unmanaged, IEntityComponent | |||
where T4 : unmanaged, IEntityComponent | |||
{ | |||
readonly EntitiesDB _db; | |||
readonly ExclusiveGroupStruct[] _groups; | |||
public NativeGroupsEnumerable(EntitiesDB db, ExclusiveGroupStruct[] groups) | |||
{ | |||
_db = db; | |||
_groups = groups; | |||
} | |||
public struct NativeGroupsIterator | |||
{ | |||
public NativeGroupsIterator(EntitiesDB db, ExclusiveGroupStruct[] groups) : this() | |||
{ | |||
_groups = groups; | |||
_indexGroup = -1; | |||
_entitiesDB = db; | |||
} | |||
public bool MoveNext() | |||
{ | |||
//attention, the while is necessary to skip empty groups | |||
while (++_indexGroup < _groups.Length) | |||
{ | |||
var entityCollection1 = _entitiesDB.QueryEntities<T1, T2, T3>(_groups[_indexGroup]); | |||
if (entityCollection1.count == 0) | |||
continue; | |||
var entityCollection2 = _entitiesDB.QueryEntities<T4>(_groups[_indexGroup]); | |||
if (entityCollection2.count == 0) | |||
continue; | |||
Check.Assert(entityCollection1.count == entityCollection2.count | |||
, "congratulation, you found a bug in Svelto, please report it"); | |||
var array = entityCollection1.ToNativeBuffers<T1, T2, T3>(); | |||
var array2 = entityCollection2.ToNativeBuffer<T4>(); | |||
_array = new BT<NB<T1>, NB<T2>, NB<T3>, NB<T4>>(array.buffer1, array.buffer2, array.buffer3, array2 | |||
, entityCollection1.count); | |||
break; | |||
} | |||
return _indexGroup < _groups.Length; | |||
} | |||
public void Reset() { _indexGroup = -1; } | |||
public BT<NB<T1>, NB<T2>, NB<T3>, NB<T4>> Current => _array; | |||
readonly ExclusiveGroupStruct[] _groups; | |||
int _indexGroup; | |||
BT<NB<T1>, NB<T2>, NB<T3>, NB<T4>> _array; | |||
readonly EntitiesDB _entitiesDB; | |||
} | |||
public NativeGroupsIterator GetEnumerator() { return new NativeGroupsIterator(_db, _groups); } | |||
} | |||
public readonly struct NativeGroupsEnumerable<T1, T2, T3> where T1 : unmanaged, IEntityComponent | |||
where T2 : unmanaged, IEntityComponent | |||
where T3 : unmanaged, IEntityComponent | |||
{ | |||
readonly EntitiesDB _db; | |||
readonly ExclusiveGroupStruct[] _groups; | |||
public NativeGroupsEnumerable(EntitiesDB db, ExclusiveGroupStruct[] groups) | |||
{ | |||
_db = db; | |||
_groups = groups; | |||
} | |||
public struct NativeGroupsIterator | |||
{ | |||
public NativeGroupsIterator(EntitiesDB db, ExclusiveGroupStruct[] groups) : this() | |||
{ | |||
_groups = groups; | |||
_indexGroup = -1; | |||
_entitiesDB = db; | |||
} | |||
public bool MoveNext() | |||
{ | |||
//attention, the while is necessary to skip empty groups | |||
while (++_indexGroup < _groups.Length) | |||
{ | |||
var entityCollection = _entitiesDB.QueryEntities<T1, T2, T3>(_groups[_indexGroup]); | |||
if (entityCollection.count == 0) | |||
continue; | |||
_array = entityCollection.ToNativeBuffers<T1, T2, T3>(); | |||
break; | |||
} | |||
return _indexGroup < _groups.Length; | |||
} | |||
public void Reset() { _indexGroup = -1; } | |||
public BT<NB<T1>, NB<T2>, NB<T3>> Current => _array; | |||
readonly ExclusiveGroupStruct[] _groups; | |||
int _indexGroup; | |||
BT<NB<T1>, NB<T2>, NB<T3>> _array; | |||
readonly EntitiesDB _entitiesDB; | |||
} | |||
public NativeGroupsIterator GetEnumerator() { return new NativeGroupsIterator(_db, _groups); } | |||
} | |||
public struct NativeGroupsEnumerable<T1, T2> where T1 : unmanaged, IEntityComponent | |||
where T2 : unmanaged, IEntityComponent | |||
{ | |||
public NativeGroupsEnumerable(EntitiesDB db, ExclusiveGroupStruct[] groups, uint groupsLength) | |||
{ | |||
_db = db; | |||
_groups = groups; | |||
_groupsLength = groupsLength; | |||
} | |||
public NativeGroupsEnumerable(EntitiesDB db, FasterList<ExclusiveGroupStruct> groups, uint groupsLength) | |||
{ | |||
_db = db; | |||
_groups = groups.ToArrayFast(out _); | |||
_groupsLength = groupsLength; | |||
} | |||
public struct NativeGroupsIterator | |||
{ | |||
public NativeGroupsIterator(EntitiesDB db, ExclusiveGroupStruct[] groups) : this() | |||
{ | |||
_db = db; | |||
_groups = groups; | |||
_indexGroup = -1; | |||
} | |||
public bool MoveNext() | |||
{ | |||
//attention, the while is necessary to skip empty groups | |||
while (++_indexGroup < _groups.Length) | |||
{ | |||
var entityCollection = _db.QueryEntities<T1, T2>(_groups[_indexGroup]); | |||
if (entityCollection.count == 0) | |||
continue; | |||
_array = entityCollection.ToNativeBuffers<T1, T2>(); | |||
break; | |||
} | |||
return _indexGroup < _groups.Length; | |||
} | |||
public void Reset() { _indexGroup = -1; } | |||
public BT<NB<T1>, NB<T2>> Current => _array; | |||
readonly EntitiesDB _db; | |||
readonly ExclusiveGroupStruct[] _groups; | |||
int _indexGroup; | |||
BT<NB<T1>, NB<T2>> _array; | |||
} | |||
public NativeGroupsIterator GetEnumerator() { return new NativeGroupsIterator(_db, _groups); } | |||
readonly EntitiesDB _db; | |||
readonly ExclusiveGroupStruct[] _groups; | |||
readonly uint _groupsLength; | |||
} | |||
public struct NativeGroupsEnumerable<T1> where T1 : unmanaged, IEntityComponent | |||
{ | |||
public NativeGroupsEnumerable(EntitiesDB db, ExclusiveGroupStruct[] groups) | |||
{ | |||
_db = db; | |||
_groups = groups; | |||
} | |||
public struct NativeGroupsIterator | |||
{ | |||
public NativeGroupsIterator(EntitiesDB db, ExclusiveGroupStruct[] groups) : this() | |||
{ | |||
_db = db; | |||
_groups = groups; | |||
_indexGroup = -1; | |||
} | |||
public bool MoveNext() | |||
{ | |||
//attention, the while is necessary to skip empty groups | |||
while (++_indexGroup < _groups.Length) | |||
{ | |||
var entityCollection = _db.QueryEntities<T1>(_groups[_indexGroup]); | |||
if (entityCollection.count == 0) | |||
continue; | |||
_array = entityCollection.ToNativeBuffer<T1>(); | |||
break; | |||
} | |||
return _indexGroup < _groups.Length; | |||
} | |||
public void Reset() { _indexGroup = -1; } | |||
public NB<T1> Current => _array; | |||
readonly EntitiesDB _db; | |||
readonly ExclusiveGroupStruct[] _groups; | |||
int _indexGroup; | |||
NB<T1> _array; | |||
} | |||
public NativeGroupsIterator GetEnumerator() { return new NativeGroupsIterator(_db, _groups); } | |||
readonly EntitiesDB _db; | |||
readonly ExclusiveGroupStruct[] _groups; | |||
} | |||
} |
@@ -0,0 +1,86 @@ | |||
using Svelto.DataStructures; | |||
using Svelto.Common; | |||
namespace Svelto.ECS.Extensions | |||
{ | |||
public interface IStepEngine : IEngine | |||
{ | |||
void Step(); | |||
string name { get; } | |||
} | |||
public interface IGroupEngine : IStepEngine | |||
{ } | |||
public interface IStepEngine<T> : IEngine | |||
{ | |||
void Step(ref T _param); | |||
string name { get; } | |||
} | |||
public interface IStepGroupEngine<T> : IStepEngine<T> | |||
{ | |||
} | |||
/// <summary> | |||
/// Note sorted jobs run in serial | |||
/// </summary> | |||
/// <typeparam name="Interface"></typeparam> | |||
/// <typeparam name="SequenceOrder"></typeparam> | |||
public abstract class SortedEnginesGroup<Interface, SequenceOrder> : IGroupEngine | |||
where SequenceOrder : struct, ISequenceOrder where Interface : class, IStepEngine | |||
{ | |||
protected SortedEnginesGroup(FasterList<Interface> engines) | |||
{ | |||
_name = "SortedEnginesGroup - "+this.GetType().Name; | |||
_instancedSequence = new Sequence<Interface, SequenceOrder>(engines); | |||
} | |||
public void Step() | |||
{ | |||
var sequenceItems = _instancedSequence.items; | |||
using (var profiler = new PlatformProfiler(_name)) | |||
{ | |||
for (var index = 0; index < sequenceItems.count; index++) | |||
{ | |||
var engine = sequenceItems[index]; | |||
using (profiler.Sample(engine.name)) engine.Step(); | |||
} | |||
} | |||
} | |||
public string name => _name; | |||
readonly string _name; | |||
readonly Sequence<Interface, SequenceOrder> _instancedSequence; | |||
} | |||
public abstract class SortedEnginesGroup<Interface, Parameter, SequenceOrder>: IStepGroupEngine<Parameter> | |||
where SequenceOrder : struct, ISequenceOrder where Interface : class, IStepEngine<Parameter> | |||
{ | |||
protected SortedEnginesGroup(FasterList<Interface> engines) | |||
{ | |||
_name = "SortedEnginesGroup - "+this.GetType().Name; | |||
_instancedSequence = new Sequence<Interface, SequenceOrder>(engines); | |||
} | |||
public void Step(ref Parameter param) | |||
{ | |||
var sequenceItems = _instancedSequence.items; | |||
using (var profiler = new PlatformProfiler(_name)) | |||
{ | |||
for (var index = 0; index < sequenceItems.count; index++) | |||
{ | |||
var engine = sequenceItems[index]; | |||
using (profiler.Sample(engine.name)) engine.Step(ref param); | |||
} | |||
} | |||
} | |||
public string name => _name; | |||
readonly string _name; | |||
readonly Sequence<Interface, SequenceOrder> _instancedSequence; | |||
} | |||
} |
@@ -1,30 +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 CopySveltoToUECSEnginesGroup : ComponentSystemGroup, IJobifiedEngine | |||
{ | |||
public JobHandle Execute(JobHandle _jobHandle) | |||
{ | |||
foreach (var engine in Systems) | |||
(engine as ICopySveltoToUECSEngine).jobHandle = _jobHandle; | |||
Update(); | |||
return _jobHandle; | |||
} | |||
readonly SimulationSystemGroup _simulationSystemGroup; | |||
} | |||
public interface ICopySveltoToUECSEngine:IEngine | |||
{ | |||
JobHandle jobHandle { set; } | |||
} | |||
} | |||
#endif |
@@ -1,7 +1,7 @@ | |||
#if UNITY_ECS | |||
#if UNITY_BURST | |||
using System; | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.DataStructures; | |||
using Svelto.ECS.DataStructures.Unity; | |||
using Unity.Jobs.LowLevel.Unsafe; | |||
@@ -19,30 +19,30 @@ namespace Svelto.ECS | |||
readonly AtomicNativeBags _swapOperationQueue = | |||
new AtomicNativeBags(Common.Allocator.Persistent, JobsUtility.MaxJobThreadCount + 1); | |||
NativeEntityRemove ProvideNativeEntityRemoveQueue<T>() where T : IEntityDescriptor, new() | |||
NativeEntityRemove ProvideNativeEntityRemoveQueue<T>(string memberName) where T : IEntityDescriptor, new() | |||
{ | |||
//todo: remove operation array and store entity descriptor hash in the return value | |||
//todo I maybe able to provide a _nativeSwap.SwapEntity<entityDescriptor> | |||
_nativeRemoveOperations.Add( | |||
new NativeOperationRemove(EntityDescriptorTemplate<T>.descriptor.componentsToBuild)); | |||
new NativeOperationRemove(EntityDescriptorTemplate<T>.descriptor.componentsToBuild, TypeCache<T>.type, memberName)); | |||
return new NativeEntityRemove(_removeOperationQueue, _nativeRemoveOperations.count - 1); | |||
} | |||
NativeEntitySwap ProvideNativeEntitySwapQueue<T>() where T : IEntityDescriptor, new() | |||
NativeEntitySwap ProvideNativeEntitySwapQueue<T>(string memberName) where T : IEntityDescriptor, new() | |||
{ | |||
//todo: remove operation array and store entity descriptor hash in the return value | |||
_nativeSwapOperations.Add( | |||
new NativeOperationSwap(EntityDescriptorTemplate<T>.descriptor.componentsToBuild)); | |||
new NativeOperationSwap(EntityDescriptorTemplate<T>.descriptor.componentsToBuild, TypeCache<T>.type, memberName)); | |||
return new NativeEntitySwap(_swapOperationQueue, _nativeSwapOperations.count - 1); | |||
} | |||
NativeEntityFactory ProvideNativeEntityFactoryQueue<T>() where T : IEntityDescriptor, new() | |||
NativeEntityFactory ProvideNativeEntityFactoryQueue<T>(string memberName) where T : IEntityDescriptor, new() | |||
{ | |||
//todo: remove operation array and store entity descriptor hash in the return value | |||
_nativeAddOperations.Add( | |||
new NativeOperationBuild(EntityDescriptorTemplate<T>.descriptor.componentsToBuild)); | |||
new NativeOperationBuild(EntityDescriptorTemplate<T>.descriptor.componentsToBuild, TypeCache<T>.type)); | |||
return new NativeEntityFactory(_addOperationQueue, _nativeAddOperations.count - 1); | |||
} | |||
@@ -59,10 +59,10 @@ namespace Svelto.ECS | |||
{ | |||
var componentsIndex = buffer.Dequeue<uint>(); | |||
var entityEGID = buffer.Dequeue<EGID>(); | |||
CheckRemoveEntityID(entityEGID); | |||
CheckRemoveEntityID(entityEGID, _nativeRemoveOperations[componentsIndex].type); | |||
QueueEntitySubmitOperation(new EntitySubmitOperation( | |||
EntitySubmitOperationType.Remove, entityEGID, entityEGID | |||
, _nativeRemoveOperations[componentsIndex].entityComponents)); | |||
, _nativeRemoveOperations[componentsIndex].components)); | |||
} | |||
} | |||
@@ -75,12 +75,12 @@ namespace Svelto.ECS | |||
var componentsIndex = buffer.Dequeue<uint>(); | |||
var entityEGID = buffer.Dequeue<DoubleEGID>(); | |||
CheckRemoveEntityID(entityEGID.@from); | |||
CheckAddEntityID(entityEGID.to); | |||
CheckRemoveEntityID(entityEGID.@from, _nativeSwapOperations[componentsIndex].type, _nativeSwapOperations[componentsIndex].caller ); | |||
CheckAddEntityID(entityEGID.to, _nativeSwapOperations[componentsIndex].type, _nativeSwapOperations[componentsIndex].caller); | |||
QueueEntitySubmitOperation(new EntitySubmitOperation( | |||
EntitySubmitOperationType.Swap, entityEGID.@from, entityEGID.to | |||
, _nativeSwapOperations[componentsIndex].entityComponents)); | |||
, _nativeSwapOperations[componentsIndex].components)); | |||
} | |||
} | |||
} | |||
@@ -98,8 +98,9 @@ namespace Svelto.ECS | |||
var componentCounts = buffer.Dequeue<uint>(); | |||
EntityComponentInitializer init = | |||
BuildEntity(egid, _nativeAddOperations[componentsIndex].components); | |||
BuildEntity(egid, _nativeAddOperations[componentsIndex].components, _nativeAddOperations[componentsIndex].type); | |||
//only called if Init is called on the initialized (there is something to init) | |||
while (componentCounts > 0) | |||
{ | |||
componentCounts--; | |||
@@ -140,125 +141,43 @@ namespace Svelto.ECS | |||
} | |||
} | |||
public readonly struct NativeEntityRemove | |||
{ | |||
readonly AtomicNativeBags _removeQueue; | |||
readonly uint _indexRemove; | |||
internal NativeEntityRemove(AtomicNativeBags EGIDsToRemove, uint indexRemove) | |||
{ | |||
_removeQueue = EGIDsToRemove; | |||
_indexRemove = indexRemove; | |||
} | |||
public void RemoveEntity(EGID egid, int threadIndex) | |||
{ | |||
var simpleNativeBag = _removeQueue.GetBuffer(threadIndex); | |||
simpleNativeBag.Enqueue(_indexRemove); | |||
simpleNativeBag.Enqueue(egid); | |||
} | |||
} | |||
public readonly struct NativeEntitySwap | |||
{ | |||
readonly AtomicNativeBags _swapQueue; | |||
readonly uint _indexSwap; | |||
internal NativeEntitySwap(AtomicNativeBags EGIDsToSwap, uint indexSwap) | |||
{ | |||
_swapQueue = EGIDsToSwap; | |||
_indexSwap = indexSwap; | |||
} | |||
public void SwapEntity(EGID from, EGID to, int threadIndex) | |||
{ | |||
var simpleNativeBag = _swapQueue.GetBuffer(threadIndex); | |||
simpleNativeBag.Enqueue(_indexSwap); | |||
simpleNativeBag.Enqueue(new DoubleEGID(from, to)); | |||
} | |||
public void SwapEntity(EGID from, ExclusiveGroupStruct to, int threadIndex) | |||
{ | |||
var simpleNativeBag = _swapQueue.GetBuffer(threadIndex); | |||
simpleNativeBag.Enqueue(_indexSwap); | |||
simpleNativeBag.Enqueue(new DoubleEGID(from, new EGID(from.entityID, to))); | |||
} | |||
} | |||
public readonly struct NativeEntityFactory | |||
{ | |||
readonly AtomicNativeBags _addOperationQueue; | |||
readonly uint _index; | |||
internal NativeEntityFactory(AtomicNativeBags addOperationQueue, uint index) | |||
{ | |||
_index = index; | |||
_addOperationQueue = addOperationQueue; | |||
} | |||
public NativeEntityComponentInitializer BuildEntity | |||
(uint eindex, ExclusiveGroupStruct buildGroup, int threadIndex) | |||
{ | |||
NativeBag unsafeBuffer = _addOperationQueue.GetBuffer(threadIndex + 1); | |||
unsafeBuffer.Enqueue(_index); | |||
unsafeBuffer.Enqueue(new EGID(eindex, buildGroup)); | |||
unsafeBuffer.ReserveEnqueue<uint>(out var index) = 0; | |||
return new NativeEntityComponentInitializer(unsafeBuffer, index); | |||
} | |||
} | |||
public readonly ref struct NativeEntityComponentInitializer | |||
{ | |||
readonly NativeBag _unsafeBuffer; | |||
readonly UnsafeArrayIndex _index; | |||
public NativeEntityComponentInitializer(in NativeBag unsafeBuffer, UnsafeArrayIndex index) | |||
{ | |||
_unsafeBuffer = unsafeBuffer; | |||
_index = index; | |||
} | |||
public void Init<T>(in T component) where T : unmanaged, IEntityComponent | |||
{ | |||
uint id = EntityComponentID<T>.ID.Data; | |||
_unsafeBuffer.AccessReserved<uint>(_index)++; | |||
_unsafeBuffer.Enqueue(id); | |||
_unsafeBuffer.Enqueue(component); | |||
} | |||
} | |||
struct NativeOperationBuild | |||
readonly struct NativeOperationBuild | |||
{ | |||
internal readonly IComponentBuilder[] components; | |||
internal readonly Type type; | |||
public NativeOperationBuild(IComponentBuilder[] descriptorEntityComponentsToBuild) | |||
public NativeOperationBuild(IComponentBuilder[] descriptorComponentsToBuild, Type entityType) | |||
{ | |||
components = descriptorEntityComponentsToBuild; | |||
type = entityType; | |||
components = descriptorComponentsToBuild; | |||
} | |||
} | |||
readonly struct NativeOperationRemove | |||
{ | |||
internal readonly IComponentBuilder[] entityComponents; | |||
public NativeOperationRemove(IComponentBuilder[] descriptorEntitiesToBuild) | |||
internal readonly IComponentBuilder[] components; | |||
internal readonly Type type; | |||
internal readonly string caller; | |||
public NativeOperationRemove(IComponentBuilder[] descriptorComponentsToRemove, Type entityType, string caller) | |||
{ | |||
entityComponents = descriptorEntitiesToBuild; | |||
this.caller = caller; | |||
components = descriptorComponentsToRemove; | |||
type = entityType; | |||
} | |||
} | |||
readonly struct NativeOperationSwap | |||
{ | |||
internal readonly IComponentBuilder[] entityComponents; | |||
internal readonly IComponentBuilder[] components; | |||
internal readonly Type type; | |||
internal readonly string caller; | |||
public NativeOperationSwap(IComponentBuilder[] descriptorEntitiesToBuild) | |||
public NativeOperationSwap(IComponentBuilder[] descriptorComponentsToSwap, Type entityType, string caller) | |||
{ | |||
entityComponents = descriptorEntitiesToBuild; | |||
this.caller = caller; | |||
components = descriptorComponentsToSwap; | |||
type = entityType; | |||
} | |||
} | |||
} |
@@ -1,9 +0,0 @@ | |||
using Unity.Jobs; | |||
namespace Svelto.ECS.Extensions.Unity | |||
{ | |||
public interface IJobifiedEngine : IEngine | |||
{ | |||
JobHandle Execute(JobHandle _jobHandle); | |||
} | |||
} |
@@ -1,31 +1,97 @@ | |||
#if UNITY_2019_2_OR_NEWER | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Unity.Jobs; | |||
namespace Svelto.ECS.Extensions.Unity | |||
{ | |||
public abstract class JobifedEnginesGroup<Interface> | |||
where Interface : class, IJobifiedEngine | |||
public interface IJobifiedEngine : IEngine | |||
{ | |||
protected JobifedEnginesGroup(FasterReadOnlyList<Interface> engines, bool completeEachJob = false) | |||
JobHandle Execute(JobHandle _jobHandle); | |||
string name { get; } | |||
} | |||
public interface IJobifiedGroupEngine : IJobifiedEngine | |||
{ } | |||
public interface IJobifiedEngine<T> : IEngine | |||
{ | |||
JobHandle Execute(JobHandle _jobHandle, 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 | |||
{ | |||
protected JobifedEnginesGroup(FasterList<Interface> engines) | |||
{ | |||
_name = "JobifiedEnginesGroup - "+this.GetType().Name; | |||
_engines = engines; | |||
_completeEachJob = completeEachJob; | |||
} | |||
public JobHandle Execute(JobHandle combinedHandles) | |||
public JobHandle Execute(JobHandle inputHandles) | |||
{ | |||
var fasterReadOnlyList = _engines; | |||
for (var index = 0; index < fasterReadOnlyList.Count; index++) | |||
var engines = _engines; | |||
JobHandle combinedHandles = inputHandles; | |||
using (var profiler = new PlatformProfiler(_name)) | |||
{ | |||
var engine = fasterReadOnlyList[index]; | |||
combinedHandles = JobHandle.CombineDependencies(combinedHandles, engine.Execute(combinedHandles)); | |||
if (_completeEachJob) combinedHandles.Complete(); | |||
for (var index = 0; index < engines.count; index++) | |||
{ | |||
ref var engine = ref engines[index]; | |||
using (profiler.Sample(engine.name)) | |||
{ | |||
combinedHandles = JobHandle.CombineDependencies(combinedHandles, engine.Execute(inputHandles)); | |||
} | |||
} | |||
} | |||
return combinedHandles; | |||
} | |||
public string name => _name; | |||
readonly FasterReadOnlyList<Interface> _engines; | |||
readonly bool _completeEachJob; | |||
readonly string _name; | |||
} | |||
public abstract class JobifedEnginesGroup<Interface, Param>: IJobifiedGroupEngine<Param> where Interface : class, IJobifiedEngine<Param> | |||
{ | |||
protected JobifedEnginesGroup(FasterList<Interface> engines) | |||
{ | |||
_name = "JobifiedEnginesGroup - "+this.GetType().Name; | |||
_engines = engines; | |||
} | |||
public JobHandle Execute(JobHandle combinedHandles, ref Param _param) | |||
{ | |||
var engines = _engines; | |||
using (var profiler = new PlatformProfiler(_name)) | |||
{ | |||
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)); | |||
} | |||
} | |||
return combinedHandles; | |||
} | |||
public string name => _name; | |||
readonly string _name; | |||
readonly FasterReadOnlyList<Interface> _engines; | |||
} | |||
} | |||
} | |||
#endif |
@@ -1,4 +1,4 @@ | |||
#if UNITY_ECS | |||
#if UNITY_BURST | |||
namespace Svelto.ECS.Extensions.Unity | |||
{ | |||
public enum JobifiedSveltoEngines | |||
@@ -0,0 +1,83 @@ | |||
#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 |
@@ -0,0 +1,43 @@ | |||
#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 |
@@ -0,0 +1,26 @@ | |||
using Svelto.ECS.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
public readonly ref struct NativeEntityComponentInitializer | |||
{ | |||
readonly NativeBag _unsafeBuffer; | |||
readonly UnsafeArrayIndex _index; | |||
public NativeEntityComponentInitializer(in NativeBag unsafeBuffer, UnsafeArrayIndex index) | |||
{ | |||
_unsafeBuffer = unsafeBuffer; | |||
_index = index; | |||
} | |||
public void Init<T>(in T component) where T : unmanaged, IEntityComponent | |||
{ | |||
uint id = EntityComponentID<T>.ID.Data; | |||
_unsafeBuffer.AccessReserved<uint>(_index)++; | |||
_unsafeBuffer.Enqueue(id); | |||
_unsafeBuffer.Enqueue(component); | |||
} | |||
} | |||
} |
@@ -0,0 +1,42 @@ | |||
#if UNITY_BURST | |||
using Svelto.ECS.DataStructures; | |||
using Svelto.ECS.DataStructures.Unity; | |||
namespace Svelto.ECS | |||
{ | |||
public readonly struct NativeEntityFactory | |||
{ | |||
readonly AtomicNativeBags _addOperationQueue; | |||
readonly uint _index; | |||
internal NativeEntityFactory(AtomicNativeBags addOperationQueue, uint index) | |||
{ | |||
_index = index; | |||
_addOperationQueue = addOperationQueue; | |||
} | |||
public NativeEntityComponentInitializer BuildEntity | |||
(uint eindex, ExclusiveGroupStruct buildGroup, int threadIndex) | |||
{ | |||
NativeBag unsafeBuffer = _addOperationQueue.GetBuffer(threadIndex + 1); | |||
unsafeBuffer.Enqueue(_index); | |||
unsafeBuffer.Enqueue(new EGID(eindex, buildGroup)); | |||
unsafeBuffer.ReserveEnqueue<uint>(out var index) = 0; | |||
return new NativeEntityComponentInitializer(unsafeBuffer, index); | |||
} | |||
public NativeEntityComponentInitializer BuildEntity(EGID egid, int threadIndex) | |||
{ | |||
NativeBag unsafeBuffer = _addOperationQueue.GetBuffer(threadIndex + 1); | |||
unsafeBuffer.Enqueue(_index); | |||
unsafeBuffer.Enqueue(new EGID(egid.entityID, egid.groupID)); | |||
unsafeBuffer.ReserveEnqueue<uint>(out var index) = 0; | |||
return new NativeEntityComponentInitializer(unsafeBuffer, index); | |||
} | |||
} | |||
} | |||
#endif |
@@ -0,0 +1,26 @@ | |||
#if UNITY_BURST | |||
using Svelto.ECS.DataStructures.Unity; | |||
namespace Svelto.ECS | |||
{ | |||
public readonly struct NativeEntityRemove | |||
{ | |||
readonly AtomicNativeBags _removeQueue; | |||
readonly uint _indexRemove; | |||
internal NativeEntityRemove(AtomicNativeBags EGIDsToRemove, uint indexRemove) | |||
{ | |||
_removeQueue = EGIDsToRemove; | |||
_indexRemove = indexRemove; | |||
} | |||
public void RemoveEntity(EGID egid, int threadIndex) | |||
{ | |||
var simpleNativeBag = _removeQueue.GetBuffer(threadIndex); | |||
simpleNativeBag.Enqueue(_indexRemove); | |||
simpleNativeBag.Enqueue(egid); | |||
} | |||
} | |||
} | |||
#endif |
@@ -0,0 +1,33 @@ | |||
#if UNITY_BURST | |||
using Svelto.ECS.DataStructures.Unity; | |||
namespace Svelto.ECS | |||
{ | |||
public readonly struct NativeEntitySwap | |||
{ | |||
readonly AtomicNativeBags _swapQueue; | |||
readonly uint _indexSwap; | |||
internal NativeEntitySwap(AtomicNativeBags EGIDsToSwap, uint indexSwap) | |||
{ | |||
_swapQueue = EGIDsToSwap; | |||
_indexSwap = indexSwap; | |||
} | |||
public void SwapEntity(EGID from, EGID to, int threadIndex) | |||
{ | |||
var simpleNativeBag = _swapQueue.GetBuffer(threadIndex); | |||
simpleNativeBag.Enqueue(_indexSwap); | |||
simpleNativeBag.Enqueue(new DoubleEGID(from, to)); | |||
} | |||
public void SwapEntity(EGID from, ExclusiveGroupStruct to, int threadIndex) | |||
{ | |||
var simpleNativeBag = _swapQueue.GetBuffer(threadIndex); | |||
simpleNativeBag.Enqueue(_indexSwap); | |||
simpleNativeBag.Enqueue(new DoubleEGID(from, new EGID(from.entityID, to))); | |||
} | |||
} | |||
} | |||
#endif |
@@ -21,6 +21,8 @@ namespace Svelto.ECS.Extensions.Unity | |||
return _jobHandle; | |||
} | |||
public string name => nameof(PureUECSSystemsGroup); | |||
readonly World _world; | |||
} | |||
} |
@@ -5,26 +5,70 @@ using Svelto.Common; | |||
namespace Svelto.ECS.Extensions.Unity | |||
{ | |||
public abstract class SortedJobifedEnginesGroup<Interface, SequenceOrder> | |||
/// <summary> | |||
/// Note sorted jobs run in serial | |||
/// </summary> | |||
/// <typeparam name="Interface"></typeparam> | |||
/// <typeparam name="SequenceOrder"></typeparam> | |||
public abstract class SortedJobifedEnginesGroup<Interface, SequenceOrder> : IJobifiedGroupEngine | |||
where SequenceOrder : struct, ISequenceOrder where Interface : class, IJobifiedEngine | |||
{ | |||
protected SortedJobifedEnginesGroup(FasterReadOnlyList<Interface> engines) | |||
protected SortedJobifedEnginesGroup(FasterList<Interface> engines) | |||
{ | |||
_name = "SortedJobifedEnginesGroup - "+this.GetType().Name; | |||
_instancedSequence = new Sequence<Interface, SequenceOrder>(engines); | |||
} | |||
public JobHandle Execute(JobHandle combinedHandles) | |||
public JobHandle Execute(JobHandle inputHandles) | |||
{ | |||
var fasterReadOnlyList = _instancedSequence.items; | |||
for (var index = 0; index < fasterReadOnlyList.Count; index++) | |||
var sequenceItems = _instancedSequence.items; | |||
JobHandle combinedHandles = inputHandles; | |||
using (var profiler = new PlatformProfiler(_name)) | |||
{ | |||
var engine = fasterReadOnlyList[index]; | |||
combinedHandles = JobHandle.CombineDependencies(combinedHandles, engine.Execute(combinedHandles)); | |||
for (var index = 0; index < sequenceItems.count; index++) | |||
{ | |||
var engine = sequenceItems[index]; | |||
using (profiler.Sample(engine.name)) combinedHandles = JobHandle.CombineDependencies(combinedHandles, engine.Execute(combinedHandles)); | |||
} | |||
} | |||
return combinedHandles; | |||
} | |||
public string name => _name; | |||
readonly string _name; | |||
readonly Sequence<Interface, SequenceOrder> _instancedSequence; | |||
} | |||
public abstract class SortedJobifedEnginesGroup<Interface, Parameter, SequenceOrder>: IJobifiedGroupEngine<Parameter> | |||
where SequenceOrder : struct, ISequenceOrder where Interface : class, IJobifiedEngine<Parameter> | |||
{ | |||
protected SortedJobifedEnginesGroup(FasterList<Interface> engines) | |||
{ | |||
_name = "SortedJobifedEnginesGroup - "+this.GetType().Name; | |||
_instancedSequence = new Sequence<Interface, SequenceOrder>(engines); | |||
} | |||
public JobHandle Execute(JobHandle combinedHandles, ref Parameter param) | |||
{ | |||
var sequenceItems = _instancedSequence.items; | |||
using (var profiler = new PlatformProfiler(_name)) | |||
{ | |||
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)); | |||
} | |||
} | |||
return combinedHandles; | |||
} | |||
public string name => _name; | |||
readonly string _name; | |||
readonly Sequence<Interface, SequenceOrder> _instancedSequence; | |||
} | |||
} |
@@ -0,0 +1,39 @@ | |||
#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 |
@@ -1,12 +1,45 @@ | |||
#if UNITY_2019_2_OR_NEWER | |||
#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.Extensions.Unity | |||
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 | |||
{ | |||
@@ -23,9 +56,64 @@ namespace Svelto.ECS.Extensions.Unity | |||
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,32 @@ | |||
#if UNITY_5 || UNITY_5_3_OR_NEWER | |||
using Svelto.ECS.Hybrid; | |||
using UnityEngine; | |||
namespace Svelto.ECS.Extensions.Unity | |||
{ | |||
public interface IEGIDHolder | |||
{ | |||
EGID ID { set; } | |||
} | |||
public struct EGIDTrackerViewComponent : IEntityViewComponent | |||
{ | |||
#pragma warning disable 649 | |||
public IEGIDHolder holder; | |||
#pragma warning restore 649 | |||
EGID _ID; | |||
public EGID ID | |||
{ | |||
get => _ID; | |||
set => _ID = holder.ID = value; | |||
} | |||
} | |||
public class EGIDHolderImplementor : MonoBehaviour, IEGIDHolder, IImplementor | |||
{ | |||
public EGID ID { get; set; } | |||
} | |||
} | |||
#endif |
@@ -4,20 +4,47 @@ 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 | |||
{ | |||
public static T CreateFromPrefab<T>(ref uint startIndex, Transform contextHolder, IEntityFactory factory, | |||
ExclusiveGroup group, string groupNamePostfix = null) where T : MonoBehaviour, IEntityDescriptorHolder | |||
ExclusiveGroup group, bool searchImplementorsInChildren = false, string groupNamePostfix = null) where T : MonoBehaviour, IEntityDescriptorHolder | |||
{ | |||
var holder = Create<T>(new EGID(startIndex++, group), contextHolder, factory); | |||
var childs = contextHolder.GetComponentsInChildren<IEntityDescriptorHolder>(true); | |||
Create<T>(new EGID(startIndex++, group), contextHolder, factory, out var holder); | |||
var children = contextHolder.GetComponentsInChildren<IEntityDescriptorHolder>(true); | |||
foreach (var child in childs) | |||
foreach (var child in children) | |||
{ | |||
IImplementor[] childImplementors; | |||
if (child.GetType() != typeof(T)) | |||
{ | |||
var monoBehaviour = child as MonoBehaviour; | |||
var childImplementors = monoBehaviour.GetComponents<IImplementor>(); | |||
if (searchImplementorsInChildren == false) | |||
childImplementors = monoBehaviour.GetComponents<IImplementor>(); | |||
else | |||
childImplementors = monoBehaviour.GetComponentsInChildren<IImplementor>(true); | |||
startIndex = InternalBuildAll( | |||
startIndex, | |||
child, | |||
@@ -31,27 +58,22 @@ namespace Svelto.ECS.Extensions.Unity | |||
return holder; | |||
} | |||
public static T Create<T>(EGID ID, Transform contextHolder, IEntityFactory factory) | |||
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); | |||
DBC.ECS.Check.Assert(holder != null, $"`{nameof(holder)}` is null! No component of type " + | |||
$"`{typeof(T)}` was found between its children."); | |||
var implementors = holder.GetComponents<IImplementor>(); | |||
factory.BuildEntity(ID, holder.GetDescriptor(), implementors); | |||
holder = contextHolder.GetComponentInChildren<T>(true); | |||
var implementors = searchImplementorsInChildren == false ? holder.GetComponents<IImplementor>() : holder.GetComponentsInChildren<IImplementor>(true) ; | |||
return holder; | |||
return factory.BuildEntity(ID, holder.GetDescriptor(), implementors); | |||
} | |||
public static EntityComponentInitializer CreateWithEntity<T>(EGID ID, Transform contextHolder, | |||
IEntityFactory factory, out T holder) | |||
public static EntityComponentInitializer Create<T>(EGID ID, Transform contextHolder, | |||
IEntityFactory factory, bool searchImplementorsInChildren = false) | |||
where T : MonoBehaviour, IEntityDescriptorHolder | |||
{ | |||
holder = contextHolder.GetComponentInChildren<T>(true); | |||
var implementors = holder.GetComponents<IImplementor>(); | |||
var holder = contextHolder.GetComponentInChildren<T>(true); | |||
var implementors = searchImplementorsInChildren == false ? holder.GetComponents<IImplementor>() : holder.GetComponentsInChildren<IImplementor>(true) ; | |||
return factory.BuildEntity(ID, holder.GetDescriptor(), implementors); | |||
} | |||
@@ -97,8 +119,8 @@ namespace Svelto.ECS.Extensions.Unity | |||
return startIndex; | |||
} | |||
/// <summary> | |||
/// Works like CreateAll but only builds entities with holders that have the same group specfied | |||
/// <summary> | |||
/// Works like CreateAll but only builds entities with holders that have the same group specified | |||
/// </summary> | |||
/// <param name="startId"></param> | |||
/// <param name="group">The group to match</param> | |||
@@ -5,7 +5,7 @@ using UnityEngine; | |||
namespace Svelto.ECS.Schedulers.Unity | |||
{ | |||
//The EntitySubmissionScheduler has been introduced to make the entity views submission logic platform independent | |||
//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 | |||
{ | |||
@@ -27,17 +27,22 @@ namespace Svelto.ECS.Schedulers.Unity | |||
{ | |||
yield return _wait; | |||
onTick.Invoke(); | |||
onTick(); | |||
} | |||
} | |||
readonly WaitForEndOfFrame _wait = new WaitForEndOfFrame(); | |||
readonly IEnumerator _coroutine; | |||
public EnginesRoot.EntitiesSubmitter onTick; | |||
public System.Action onTick; | |||
} | |||
public UnityEntitiesSubmissionScheduler(string name = "ECSScheduler") | |||
{ | |||
_scheduler = new GameObject(name).AddComponent<Scheduler>(); | |||
GameObject.DontDestroyOnLoad(_scheduler.gameObject); | |||
_scheduler.onTick = SubmitEntities; | |||
} | |||
public UnityEntitiesSubmissionScheduler(string name = "ECSScheduler") { _name = name; } | |||
public void Dispose() | |||
{ | |||
@@ -46,23 +51,22 @@ namespace Svelto.ECS.Schedulers.Unity | |||
Object.Destroy(_scheduler.gameObject); | |||
} | |||
} | |||
void SubmitEntities() | |||
{ | |||
if (paused == false) | |||
_onTick.Invoke(); | |||
} | |||
public EnginesRoot.EntitiesSubmitter onTick | |||
EnginesRoot.EntitiesSubmitter IEntitiesSubmissionScheduler.onTick | |||
{ | |||
set | |||
{ | |||
if (_scheduler == null) | |||
{ | |||
_scheduler = new GameObject(_name).AddComponent<Scheduler>(); | |||
GameObject.DontDestroyOnLoad(_scheduler.gameObject); | |||
} | |||
_scheduler.onTick = value; | |||
} | |||
set => _onTick = value; | |||
} | |||
public bool paused { get; set; } | |||
Scheduler _scheduler; | |||
readonly string _name; | |||
readonly Scheduler _scheduler; | |||
EnginesRoot.EntitiesSubmitter _onTick; | |||
} | |||
} | |||
#endif |
@@ -2,33 +2,33 @@ | |||
{ | |||
public abstract class GenericEntityDescriptor<T> : IEntityDescriptor where T : struct, IEntityComponent | |||
{ | |||
static readonly IComponentBuilder[] _entityBuilders; | |||
static GenericEntityDescriptor() { _entityBuilders = new IComponentBuilder[] {new ComponentBuilder<T>()}; } | |||
static readonly IComponentBuilder[] _componentBuilders; | |||
static GenericEntityDescriptor() { _componentBuilders = new IComponentBuilder[] {new ComponentBuilder<T>()}; } | |||
public IComponentBuilder[] componentsToBuild => _entityBuilders; | |||
public IComponentBuilder[] componentsToBuild => _componentBuilders; | |||
} | |||
public abstract class GenericEntityDescriptor<T, U> : IEntityDescriptor | |||
where T : struct, IEntityComponent where U : struct, IEntityComponent | |||
{ | |||
static readonly IComponentBuilder[] _entityBuilders; | |||
static readonly IComponentBuilder[] _componentBuilders; | |||
static GenericEntityDescriptor() | |||
{ | |||
_entityBuilders = new IComponentBuilder[] {new ComponentBuilder<T>(), new ComponentBuilder<U>()}; | |||
_componentBuilders = new IComponentBuilder[] {new ComponentBuilder<T>(), new ComponentBuilder<U>()}; | |||
} | |||
public IComponentBuilder[] componentsToBuild => _entityBuilders; | |||
public IComponentBuilder[] componentsToBuild => _componentBuilders; | |||
} | |||
public abstract class GenericEntityDescriptor<T, U, V> : IEntityDescriptor | |||
where T : struct, IEntityComponent where U : struct, IEntityComponent where V : struct, IEntityComponent | |||
{ | |||
static readonly IComponentBuilder[] _entityBuilders; | |||
static readonly IComponentBuilder[] _componentBuilders; | |||
static GenericEntityDescriptor() | |||
{ | |||
_entityBuilders = new IComponentBuilder[] | |||
_componentBuilders = new IComponentBuilder[] | |||
{ | |||
new ComponentBuilder<T>(), | |||
new ComponentBuilder<U>(), | |||
@@ -36,18 +36,18 @@ | |||
}; | |||
} | |||
public IComponentBuilder[] componentsToBuild => _entityBuilders; | |||
public IComponentBuilder[] componentsToBuild => _componentBuilders; | |||
} | |||
public abstract class GenericEntityDescriptor<T, U, V, W> : IEntityDescriptor | |||
where T : struct, IEntityComponent where U : struct, IEntityComponent where V : struct, IEntityComponent | |||
where W : struct, IEntityComponent | |||
{ | |||
static readonly IComponentBuilder[] _entityBuilders; | |||
static readonly IComponentBuilder[] _componentBuilders; | |||
static GenericEntityDescriptor() | |||
{ | |||
_entityBuilders = new IComponentBuilder[] | |||
_componentBuilders = new IComponentBuilder[] | |||
{ | |||
new ComponentBuilder<T>(), | |||
new ComponentBuilder<U>(), | |||
@@ -56,18 +56,18 @@ | |||
}; | |||
} | |||
public IComponentBuilder[] componentsToBuild => _entityBuilders; | |||
public IComponentBuilder[] componentsToBuild => _componentBuilders; | |||
} | |||
public abstract class GenericEntityDescriptor<T, U, V, W, X> : IEntityDescriptor | |||
where T : struct, IEntityComponent where U : struct, IEntityComponent where V : struct, IEntityComponent | |||
where W : struct, IEntityComponent where X : struct, IEntityComponent | |||
{ | |||
static readonly IComponentBuilder[] _entityBuilders; | |||
static readonly IComponentBuilder[] _componentBuilders; | |||
static GenericEntityDescriptor() | |||
{ | |||
_entityBuilders = new IComponentBuilder[] | |||
_componentBuilders = new IComponentBuilder[] | |||
{ | |||
new ComponentBuilder<T>(), | |||
new ComponentBuilder<U>(), | |||
@@ -77,18 +77,18 @@ | |||
}; | |||
} | |||
public IComponentBuilder[] componentsToBuild => _entityBuilders; | |||
public IComponentBuilder[] componentsToBuild => _componentBuilders; | |||
} | |||
public abstract class GenericEntityDescriptor<T, U, V, W, X, Y> : IEntityDescriptor | |||
where T : struct, IEntityComponent where U : struct, IEntityComponent where V : struct, IEntityComponent | |||
where W : struct, IEntityComponent where X : struct, IEntityComponent where Y : struct, IEntityComponent | |||
{ | |||
static readonly IComponentBuilder[] _entityBuilders; | |||
static readonly IComponentBuilder[] _componentBuilders; | |||
static GenericEntityDescriptor() | |||
{ | |||
_entityBuilders = new IComponentBuilder[] | |||
_componentBuilders = new IComponentBuilder[] | |||
{ | |||
new ComponentBuilder<T>(), | |||
new ComponentBuilder<U>(), | |||
@@ -99,6 +99,6 @@ | |||
}; | |||
} | |||
public IComponentBuilder[] componentsToBuild => _entityBuilders; | |||
public IComponentBuilder[] componentsToBuild => _componentBuilders; | |||
} | |||
} |
@@ -15,7 +15,7 @@ namespace Svelto.ECS | |||
return _enginesRoot.Target.GenerateConsumer<T>(name, capacity); | |||
} | |||
public Consumer<T> GenerateConsumer<T>(ExclusiveGroup group, string name, uint capacity) | |||
public Consumer<T> GenerateConsumer<T>(ExclusiveGroupStruct @group, string name, uint capacity) | |||
where T : unmanaged, IEntityComponent | |||
{ | |||
return _enginesRoot.Target.GenerateConsumer<T>(group, name, capacity); | |||
@@ -30,7 +30,7 @@ namespace Svelto.ECS | |||
{ | |||
Consumer<T> GenerateConsumer<T>(string name, uint capacity) where T : unmanaged, IEntityComponent; | |||
Consumer<T> GenerateConsumer<T>(ExclusiveGroup group, string name, uint capacity) | |||
Consumer<T> GenerateConsumer<T>(ExclusiveGroupStruct @group, string name, uint capacity) | |||
where T : unmanaged, IEntityComponent; | |||
} | |||
} |
@@ -1,95 +1,53 @@ | |||
#if UNITY_ECS | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Reflection; | |||
using System.Threading; | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.DataStructures; | |||
using Unity.Burst; | |||
namespace Svelto.ECS | |||
{ | |||
public class GlobalTypeID | |||
{ | |||
internal static uint NextID<T>() | |||
{ | |||
return (uint) (Interlocked.Increment(ref value) - 1); | |||
} | |||
internal static uint NextID<T>() { return (uint) (Interlocked.Increment(ref value) - 1); } | |||
static GlobalTypeID() | |||
{ | |||
value = 0; | |||
} | |||
static GlobalTypeID() { value = 0; } | |||
static int value; | |||
} | |||
static class EntityComponentID<T> | |||
{ | |||
internal static readonly SharedStatic<uint> ID = SharedStatic<uint>.GetOrCreate<GlobalTypeID, T>(); | |||
} | |||
interface IFiller | |||
interface IFiller | |||
{ | |||
void FillFromByteArray(EntityComponentInitializer init, NativeBag buffer); | |||
} | |||
static class UnmanagedTypeExtensions | |||
{ | |||
private static Dictionary<Type, bool> cachedTypes = | |||
new Dictionary<Type, bool>(); | |||
public static bool IsUnManaged<T>() { return typeof(T).IsUnManaged(); } | |||
public static bool IsUnManaged(this Type t) | |||
{ | |||
var result = false; | |||
if (cachedTypes.ContainsKey(t)) | |||
return cachedTypes[t]; | |||
else if (t.IsPrimitive || t.IsPointer || t.IsEnum) | |||
result = true; | |||
else if (t.IsGenericType || !t.IsValueType) | |||
result = false; | |||
else | |||
result = t.GetFields(BindingFlags.Public | | |||
BindingFlags.NonPublic | BindingFlags.Instance) | |||
.All(x => x.FieldType.IsUnManaged()); | |||
cachedTypes.Add(t, result); | |||
return result; | |||
} | |||
} | |||
delegate void ForceUnmanagedCast<T>(EntityComponentInitializer init, NativeBag buffer) where T : struct, IEntityComponent; | |||
class Filler<T>: IFiller where T : struct, IEntityComponent | |||
class Filler<T> : IFiller where T : struct, IEntityComponent | |||
{ | |||
static readonly ForceUnmanagedCast<T> _action; | |||
static Filler() | |||
{ | |||
var method = typeof(Trick).GetMethod(nameof(Trick.ForceUnmanaged)).MakeGenericMethod(typeof(T)); | |||
_action = (ForceUnmanagedCast<T>) Delegate.CreateDelegate(typeof(ForceUnmanagedCast<T>), method); | |||
DBC.ECS.Check.Require(UnmanagedTypeExtensions.IsUnmanaged<T>() == true, "invalid type used"); | |||
} | |||
//it's an internal interface | |||
void IFiller.FillFromByteArray(EntityComponentInitializer init, NativeBag buffer) | |||
public void FillFromByteArray(EntityComponentInitializer init, NativeBag buffer) | |||
{ | |||
DBC.ECS.Check.Require(UnmanagedTypeExtensions.IsUnManaged<T>() == true, "invalid type used"); | |||
var component = buffer.Dequeue<T>(); | |||
_action(init, buffer); | |||
init.Init(component); | |||
} | |||
static class Trick | |||
{ | |||
public static void ForceUnmanaged<U>(EntityComponentInitializer init, NativeBag buffer) where U : unmanaged, IEntityComponent | |||
{ | |||
var component = buffer.Dequeue<U>(); | |||
} | |||
init.Init(component); | |||
} | |||
static class EntityComponentID<T> | |||
{ | |||
#if UNITY_BURST | |||
internal static readonly Unity.Burst.SharedStatic<uint> ID = | |||
Unity.Burst.SharedStatic<uint>.GetOrCreate<GlobalTypeID, T>(); | |||
#else | |||
internal struct SharedStatic | |||
{ | |||
public uint Data; | |||
} | |||
internal static SharedStatic ID; | |||
#endif | |||
} | |||
static class EntityComponentIDMap | |||
@@ -101,11 +59,7 @@ namespace Svelto.ECS | |||
var location = EntityComponentID<T>.ID.Data = GlobalTypeID.NextID<T>(); | |||
TYPE_IDS.AddAt(location, entityBuilder); | |||
} | |||
internal static IFiller GetTypeFromID(uint typeId) | |||
{ | |||
return TYPE_IDS[typeId]; | |||
} | |||
internal static IFiller GetTypeFromID(uint typeId) { return TYPE_IDS[typeId]; } | |||
} | |||
} | |||
#endif | |||
} |
@@ -1,82 +1,95 @@ | |||
using System; | |||
using Svelto.ECS.Internal; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
public static class GroupCompound<G1, G2, G3> | |||
public abstract class GroupCompound<G1, G2, G3> | |||
where G1 : GroupTag<G1> where G2 : GroupTag<G2> where G3 : GroupTag<G3> | |||
{ | |||
public static readonly ExclusiveGroupStruct[] Groups; | |||
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) | |||
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 ExclusiveGroupStruct[1]; | |||
_Groups = new FasterList<ExclusiveGroupStruct>(1); | |||
var Group = new ExclusiveGroup(); | |||
Groups[0] = Group; | |||
Console.LogDebug("<color=orange>".FastConcat(typeof(G1).ToString().FastConcat("-", typeof(G2).ToString(), "-").FastConcat(typeof(G3).ToString(), "- Initialized ", Groups[0].ToString()), "</color>")); | |||
_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 | |||
} | |||
else | |||
Console.LogDebug(typeof(G1).ToString().FastConcat("-", typeof(G2).ToString(), "-").FastConcat(typeof(G3).ToString(), "-", Groups[0].ToString())); | |||
} | |||
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 static ExclusiveGroupStruct BuildGroup => new ExclusiveGroupStruct(_Groups[0]); | |||
} | |||
public static class GroupCompound<G1, G2> where G1 : GroupTag<G1> where G2 : GroupTag<G2> | |||
public abstract class GroupCompound<G1, G2> where G1 : GroupTag<G1> where G2 : GroupTag<G2> | |||
{ | |||
public static ExclusiveGroupStruct[] Groups; | |||
static FasterList<ExclusiveGroupStruct> _Groups; | |||
public static FasterReadOnlyList<ExclusiveGroupStruct> Groups => new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups); | |||
static GroupCompound() | |||
{ | |||
Groups = GroupCompound<G2, G1>.Groups; | |||
_Groups = GroupCompound<G2, G1>._Groups; | |||
if (Groups == null) | |||
if (_Groups == null) | |||
{ | |||
Groups = new ExclusiveGroupStruct[1]; | |||
_Groups = new FasterList<ExclusiveGroupStruct>(1); | |||
var Group = new ExclusiveGroup(); | |||
Groups[0] = Group; | |||
_Groups.Add(Group); | |||
Console.LogDebug("<color=orange>".FastConcat(typeof(G1).ToString().FastConcat("-", typeof(G2).ToString(), "- initialized ", Groups[0].ToString()), "</color>")); | |||
//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 | |||
} | |||
else | |||
Console.LogDebug(typeof(G1).ToString().FastConcat("-", typeof(G2).ToString(), "-", Groups[0].ToString())); | |||
} | |||
public static ExclusiveGroupStruct BuildGroup => new ExclusiveGroupStruct(Groups[0]); | |||
public static ExclusiveGroupStruct BuildGroup => new ExclusiveGroupStruct(_Groups[0]); | |||
public static void Add(ExclusiveGroupStruct @group) | |||
{ | |||
for (int i = 0; i < Groups.Length; ++i) | |||
if (Groups[i] == group) | |||
for (int i = 0; i < _Groups.count; ++i) | |||
if (_Groups[i] == group) | |||
throw new Exception("temporary must be transformed in unit test"); | |||
Array.Resize(ref Groups, Groups.Length + 1); | |||
Groups[Groups.Length - 1] = group; | |||
GroupCompound<G2, G1>.Groups = Groups; | |||
_Groups.Add(group); | |||
Console.LogDebug(typeof(G1).ToString().FastConcat("-", typeof(G2).ToString(), "- Add ", group.ToString())); | |||
//unit test this to check if it's necessary | |||
// GroupCompound<G2, G1>._Groups = _Groups; | |||
} | |||
} | |||
@@ -86,27 +99,28 @@ namespace Svelto.ECS | |||
//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> | |||
{ | |||
public static ExclusiveGroupStruct[] Groups = new ExclusiveGroupStruct[1]; | |||
static FasterList<ExclusiveGroupStruct> _Groups = new FasterList<ExclusiveGroupStruct>(1); | |||
public static FasterReadOnlyList<ExclusiveGroupStruct> Groups => new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups); | |||
static GroupTag() | |||
{ | |||
Groups[0] = new ExclusiveGroup(); | |||
Console.LogDebug(typeof(T).ToString() + "-" + Groups[0].ToString()); | |||
_Groups.Add(new ExclusiveGroup()); | |||
} | |||
//Each time a new combination of group tags is found a new group is added. | |||
internal static void Add(ExclusiveGroup @group) | |||
internal static void Add(ExclusiveGroupStruct @group) | |||
{ | |||
for (int i = 0; i < Groups.Length; ++i) | |||
if (Groups[i] == group) | |||
for (int i = 0; i < _Groups.count; ++i) | |||
if (_Groups[i] == group) | |||
throw new Exception("temporary must be transformed in unit test"); | |||
Array.Resize(ref Groups, Groups.Length + 1); | |||
Groups[Groups.Length - 1] = group; | |||
Console.LogDebug(typeof(T).ToString().FastConcat("- Add ", group.ToString())); | |||
_Groups.Add(group); | |||
#if DEBUG | |||
GroupMap.idToName[(uint) group] = $"Compound: {typeof(T).Name}"; | |||
#endif | |||
} | |||
public static ExclusiveGroupStruct BuildGroup => new ExclusiveGroupStruct(_Groups[0]); | |||
} | |||
} |
@@ -1,7 +1,9 @@ | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
public delegate void ExecuteOnAllEntitiesAction<T, W>(T[] prefabStruct, ExclusiveGroupStruct group, | |||
public delegate void ExecuteOnAllEntitiesAction<T, W>(IBuffer<T> prefabStruct, ExclusiveGroupStruct group, | |||
uint count, EntitiesDB db, ref W instances); | |||
public delegate void ExecuteOnAllEntitiesAction<T>(T[] entities, ExclusiveGroupStruct group, | |||
public delegate void ExecuteOnAllEntitiesAction<T>(IBuffer<T> entities, ExclusiveGroupStruct group, | |||
uint count, EntitiesDB db); | |||
} |
@@ -1,5 +1,5 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
@@ -34,7 +34,7 @@ namespace Svelto.ECS | |||
/// itself in terms of EntityComponents to build. The Implementors are passed to fill the | |||
/// references of the EntityComponents components. Please read the articles on my blog | |||
/// to understand better the terminologies | |||
/// Using this function is like building a normal entity, but the entity views | |||
/// Using this function is like building a normal entity, but the entity components | |||
/// are grouped by groupID to be more efficiently processed inside engines and | |||
/// improve cache locality. Either class entityComponents and struct entityComponents can be | |||
/// grouped. | |||
@@ -65,8 +65,11 @@ namespace Svelto.ECS | |||
EntityComponentInitializer BuildEntity<T>(EGID egid, T entityDescriptor, IEnumerable<object> implementors = null) | |||
where T : IEntityDescriptor; | |||
#if UNITY_ECS | |||
NativeEntityFactory ToNative<T>(Unity.Collections.Allocator allocator) where T : IEntityDescriptor, new(); | |||
EntityComponentInitializer BuildEntity | |||
(EGID egid, IComponentBuilder[] componentsToBuild, Type type, IEnumerable<object> implementors = null); | |||
#if UNITY_BURST | |||
NativeEntityFactory ToNative<T>(string memberName) where T : IEntityDescriptor, new(); | |||
#endif | |||
} | |||
} |
@@ -27,9 +27,9 @@ namespace Svelto.ECS | |||
void SwapEntityGroup<T>(EGID fromID, EGID toId, ExclusiveGroupStruct mustBeFromGroup) | |||
where T : IEntityDescriptor, new(); | |||
#if UNITY_ECS | |||
NativeEntityRemove ToNativeRemove<T>() where T : IEntityDescriptor, new(); | |||
NativeEntitySwap ToNativeSwap<T>() where T : IEntityDescriptor, new(); | |||
#if UNITY_BURST | |||
NativeEntityRemove ToNativeRemove<T>(string memberName) where T : IEntityDescriptor, new(); | |||
NativeEntitySwap ToNativeSwap<T>(string memberName) where T : IEntityDescriptor, new(); | |||
#endif | |||
} | |||
} |
@@ -5,8 +5,5 @@ namespace Svelto.ECS | |||
public interface IReactOnSwap<T> : IReactOnSwap where T : IEntityComponent | |||
{ | |||
void MovedTo(ref T entityComponent, ExclusiveGroupStruct previousGroup, EGID egid); | |||
#if SEEMS_UNNECESSARY | |||
void MovedFrom(ref T entityComponent, EGID egid); | |||
#endif | |||
} | |||
} |
@@ -0,0 +1,23 @@ | |||
namespace Svelto.ECS | |||
{ | |||
/// <summary> | |||
/// still experimental alternative to ExclusiveGroup, use this like: | |||
/// use this like: | |||
/// public class TriggersGroup : ExclusiveGroup<TriggersGroup> {} | |||
/// </summary> | |||
/// <typeparam name="T"></typeparam> | |||
public abstract class NamedExclusiveGroup<T> | |||
{ | |||
public static ExclusiveGroup Group = new ExclusiveGroup(); | |||
public static string name = typeof(T).FullName; | |||
static NamedExclusiveGroup() | |||
{ | |||
#if DEBUG | |||
GroupMap.idToName[(uint) Group] = name; | |||
#endif | |||
} | |||
// protected NamedExclusiveGroup(string recognizeAs) : base(recognizeAs) {} | |||
// protected NamedExclusiveGroup(ushort range) : base(range) {} | |||
} | |||
} |
@@ -1,79 +0,0 @@ | |||
using System; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
public readonly struct NativeEGIDMapper<T>:IDisposable where T : unmanaged, IEntityComponent | |||
{ | |||
readonly NativeFasterDictionary<uint, T> map; | |||
public ExclusiveGroupStruct groupID { get; } | |||
public NativeEGIDMapper(ExclusiveGroupStruct groupStructId, NativeFasterDictionary<uint, T> toNative):this() | |||
{ | |||
groupID = groupStructId; | |||
map = toNative; | |||
} | |||
public uint Count => map.count; | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref T Entity(uint entityID) | |||
{ | |||
unsafe | |||
{ | |||
#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.unsafeValues[(int) findIndex]; | |||
} | |||
} | |||
public bool TryGetEntity(uint entityID, out T value) | |||
{ | |||
if (map.TryFindIndex(entityID, out var index)) | |||
{ | |||
value = map.GetDirectValue(index); | |||
return true; | |||
} | |||
value = default; | |||
return false; | |||
} | |||
public unsafe NB<T>GetArrayAndEntityIndex(uint entityID, out uint index) | |||
{ | |||
if (map.TryFindIndex(entityID, out index)) | |||
{ | |||
return new NB<T>(map.unsafeValues, map.count, map.capacity); | |||
} | |||
throw new ECSException("Entity not found"); | |||
} | |||
public unsafe bool TryGetArrayAndEntityIndex(uint entityID, out uint index, out NB<T> array) | |||
{ | |||
if (map.TryFindIndex(entityID, out index)) | |||
{ | |||
array = new NB<T>(map.unsafeValues, map.count, map.capacity); | |||
return true; | |||
} | |||
array = default; | |||
return false; | |||
} | |||
public void Dispose() | |||
{ | |||
map.Dispose(); | |||
} | |||
public bool Exists(uint idEntityId) | |||
{ | |||
return map.count > 0 && map.TryFindIndex(idEntityId, out _); | |||
} | |||
} | |||
} |
@@ -1,38 +1,77 @@ | |||
using System.Threading; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
namespace Svelto.ECS.Experimental | |||
{ | |||
public struct QueryGroups | |||
struct GroupsList | |||
{ | |||
public readonly FasterList<ExclusiveGroupStruct> groups; | |||
static GroupsList() | |||
{ | |||
groups = new FasterList<ExclusiveGroupStruct>(); | |||
} | |||
static readonly FasterList<ExclusiveGroupStruct> groups; | |||
public QueryGroups(FasterDictionary<uint, ITypeSafeDictionary> findGroups) | |||
public FasterList<ExclusiveGroupStruct> reference => groups; | |||
} | |||
public ref struct QueryGroups | |||
{ | |||
static readonly ThreadLocal<GroupsList> groups = new ThreadLocal<GroupsList>(); | |||
public QueryGroups(LocalFasterReadOnlyList<ExclusiveGroupStruct> findGroups) | |||
{ | |||
var findGroupsCount = findGroups.count; | |||
groups = new FasterList<ExclusiveGroupStruct>(findGroupsCount); | |||
foreach (var keyvalue in findGroups) | |||
var groupsValue = groups.Value; | |||
var group = groupsValue.reference; | |||
group.FastClear(); | |||
for (int i = 0; i < findGroups.count; i++) | |||
{ | |||
groups.Add(new ExclusiveGroupStruct(keyvalue.Key)); | |||
group.Add(findGroups[i]); | |||
} | |||
} | |||
public QueryGroups Except(ExclusiveGroupStruct[] groupsToIgnore) | |||
public QueryResult Except(ExclusiveGroupStruct[] groupsToIgnore) | |||
{ | |||
var groupsCount = groups.count; | |||
var group = groups.Value.reference; | |||
var groupsCount = group.count; | |||
for (int i = 0; i < groupsToIgnore.Length; i++) | |||
{ | |||
for (int j = 0; j < groupsCount; j++) | |||
if (groupsToIgnore[i] == groups[j]) | |||
if (groupsToIgnore[i] == group[j]) | |||
{ | |||
groups.UnorderedRemoveAt(j); | |||
group.UnorderedRemoveAt(j); | |||
j--; | |||
groupsCount--; | |||
} | |||
} | |||
return this; | |||
return new QueryResult(group); | |||
} | |||
public QueryResult Except(ExclusiveGroupStruct groupsToIgnore) | |||
{ | |||
var group = groups.Value.reference; | |||
var groupsCount = group.count; | |||
for (int j = 0; j < groupsCount; j++) | |||
if (groupsToIgnore == group[j]) | |||
{ | |||
group.UnorderedRemoveAt(j); | |||
j--; | |||
groupsCount--; | |||
} | |||
return new QueryResult(group); | |||
} | |||
} | |||
public readonly ref struct QueryResult | |||
{ | |||
readonly FasterReadOnlyList<ExclusiveGroupStruct> _group; | |||
public QueryResult(FasterList<ExclusiveGroupStruct> @group) { _group = @group; } | |||
public FasterReadOnlyList<ExclusiveGroupStruct> result => _group; | |||
} | |||
} |
@@ -9,9 +9,12 @@ namespace Svelto.ECS.Serialization | |||
var _type = typeof(T); | |||
foreach (var field in _type.GetFields()) | |||
if (field.FieldType.ContainsCustomAttribute(typeof(DoNotSerializeAttribute)) && | |||
{ | |||
var fieldFieldType = field.FieldType; | |||
if (fieldFieldType.ContainsCustomAttribute(typeof(DoNotSerializeAttribute)) && | |||
field.IsPrivate == false) | |||
throw new ECSException("field cannot be serialised ".FastConcat(_type.FullName)); | |||
throw new ECSException($"field cannot be serialised {fieldFieldType} in {_type.FullName}"); | |||
} | |||
if (_type.GetProperties().Length > (ComponentBuilder<T>.HAS_EGID ? 1 : 0)) | |||
throw new ECSException("serializable entity struct must be property less ".FastConcat(_type.FullName)); | |||
@@ -1,33 +1,30 @@ | |||
using System.Collections.Generic; | |||
using Svelto.Common; | |||
namespace Svelto.ECS.Serialization | |||
{ | |||
public class DefaultVersioningFactory<T> : IDeserializationFactory where T : IEntityDescriptor, new() | |||
{ | |||
readonly IEntityFactory _factory; | |||
readonly IEnumerable<object> _implementors; | |||
public DefaultVersioningFactory(IEntityFactory factory) | |||
{ | |||
_factory = factory; | |||
} | |||
public DefaultVersioningFactory() {} | |||
public DefaultVersioningFactory(IEntityFactory factory, IEnumerable<object> implementors) | |||
public DefaultVersioningFactory(IEnumerable<object> implementors) | |||
{ | |||
_factory = factory; | |||
_implementors = implementors; | |||
} | |||
public EntityComponentInitializer BuildDeserializedEntity(EGID egid, | |||
ISerializationData serializationData, | |||
ISerializableEntityDescriptor entityDescriptor, | |||
int serializationType, | |||
IEntitySerialization entitySerialization) | |||
public EntityComponentInitializer BuildDeserializedEntity | |||
(EGID egid, ISerializationData serializationData, ISerializableEntityDescriptor entityDescriptor | |||
, int serializationType, IEntitySerialization entitySerialization, IEntityFactory factory | |||
, bool enginesRootIsDeserializationOnly) | |||
{ | |||
var initializer = _factory.BuildEntity<T>(egid, _implementors); | |||
var entityDescriptorEntitiesToSerialize = enginesRootIsDeserializationOnly ? entityDescriptor.entitiesToSerialize : entityDescriptor.componentsToBuild; | |||
var initializer = factory.BuildEntity(egid, entityDescriptorEntitiesToSerialize, TypeCache<T>.type, _implementors); | |||
entitySerialization.DeserializeEntityComponents(serializationData, entityDescriptor, ref initializer, | |||
serializationType); | |||
entitySerialization.DeserializeEntityComponents(serializationData, entityDescriptor, ref initializer | |||
, serializationType); | |||
return initializer; | |||
} | |||
@@ -43,25 +43,25 @@ namespace Svelto.ECS | |||
uint descriptorHash = serializableEntityHeader.descriptorHash; | |||
SerializationDescriptorMap serializationDescriptorMap = _enginesRoot.serializationDescriptorMap; | |||
var factory = serializationDescriptorMap.GetSerializationFactory(descriptorHash); | |||
IDeserializationFactory factory = serializationDescriptorMap.GetSerializationFactory(descriptorHash); | |||
var entityDescriptor = serializationDescriptorMap.GetDescriptorFromHash(descriptorHash); | |||
//default factory | |||
//todo: we have a default factory, why don't we always register that instead? | |||
if (factory == null) | |||
{ | |||
var initializer = _enginesRoot.BuildEntity(egid, | |||
_enginesRoot._isDeserializationOnly ? entityDescriptor.entitiesToSerialize | |||
: entityDescriptor.componentsToBuild); | |||
DeserializeEntityComponents(serializationData, entityDescriptor, ref initializer, serializationType); | |||
return initializer; | |||
} | |||
// //default factory | |||
// //todo: we have a default factory, why don't we always register that instead? | |||
// if (factory == null) | |||
// { | |||
// var initializer = _enginesRoot.BuildEntity(egid, | |||
// _enginesRoot._isDeserializationOnly ? entityDescriptor.entitiesToSerialize | |||
// : entityDescriptor.componentsToBuild, entityDescriptor.realType); | |||
// | |||
// DeserializeEntityComponents(serializationData, entityDescriptor, ref initializer, serializationType); | |||
// | |||
// return initializer; | |||
// } | |||
//custom factory | |||
return factory.BuildDeserializedEntity(egid, serializationData, entityDescriptor, serializationType, | |||
this); | |||
this, this._enginesRoot.GenerateEntityFactory(), _enginesRoot._isDeserializationOnly); | |||
} | |||
public void DeserializeEntity(ISerializationData serializationData, int serializationType) | |||
@@ -120,14 +120,11 @@ namespace Svelto.ECS | |||
uint descriptorHash = serializableEntityComponent.descriptorHash; | |||
var entityDescriptor = serializationDescriptorMap.GetDescriptorFromHash(descriptorHash); | |||
var entitySubmitOperation = new EntitySubmitOperation( | |||
EntitySubmitOperationType.Swap, | |||
localEgid, | |||
toEgid, | |||
entityDescriptor.componentsToBuild); | |||
var entitySubmitOperation = new EntitySubmitOperation(EntitySubmitOperationType.Swap, | |||
localEgid, toEgid, entityDescriptor.componentsToBuild); | |||
_enginesRoot.CheckRemoveEntityID(localEgid); | |||
_enginesRoot.CheckAddEntityID(toEgid); | |||
_enginesRoot.CheckRemoveEntityID(localEgid, entityDescriptor.realType); | |||
_enginesRoot.CheckAddEntityID(toEgid, entityDescriptor.realType); | |||
_enginesRoot.QueueEntitySubmitOperation(entitySubmitOperation); | |||
} | |||
@@ -141,7 +138,7 @@ namespace Svelto.ECS | |||
SerializationDescriptorMap serializationDescriptorMap = _enginesRoot.serializationDescriptorMap; | |||
var entityDescriptor = serializationDescriptorMap.GetDescriptorFromHash(descriptorHash); | |||
_enginesRoot.CheckRemoveEntityID(egid); | |||
_enginesRoot.CheckRemoveEntityID(egid, entityDescriptor.realType); | |||
var entitySubmitOperation = new EntitySubmitOperation( | |||
EntitySubmitOperationType.Remove, | |||
@@ -26,6 +26,7 @@ namespace Svelto.ECS | |||
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); | |||
// Assembly executingAssembly = Assembly.GetExecutingAssembly(); | |||
Type d1 = typeof(DefaultVersioningFactory<>); | |||
foreach (Assembly assembly in assemblies) | |||
{ | |||
// if (assembly.GetReferencedAssemblies().Contains(executingAssembly.GetName())) | |||
@@ -38,7 +39,7 @@ namespace Svelto.ECS | |||
{ | |||
var descriptor = Activator.CreateInstance(type) as ISerializableEntityDescriptor; | |||
RegisterEntityDescriptor(descriptor); | |||
RegisterEntityDescriptor(descriptor, type, d1); | |||
} | |||
} | |||
} | |||
@@ -60,7 +61,7 @@ namespace Svelto.ECS | |||
} | |||
} | |||
void RegisterEntityDescriptor(ISerializableEntityDescriptor descriptor) | |||
void RegisterEntityDescriptor(ISerializableEntityDescriptor descriptor, Type type, Type d1) | |||
{ | |||
if (descriptor == null) | |||
{ | |||
@@ -78,6 +79,10 @@ namespace Svelto.ECS | |||
#endif | |||
_descriptors[descriptorHash] = descriptor; | |||
Type[] typeArgs = {type}; | |||
var makeGenericType = d1.MakeGenericType(typeArgs); | |||
var instance = Activator.CreateInstance(makeGenericType); | |||
_factories.Add(descriptorHash, instance as IDeserializationFactory); | |||
} | |||
public ISerializableEntityDescriptor GetDescriptorFromHash(uint descriptorID) | |||
@@ -92,13 +97,13 @@ namespace Svelto.ECS | |||
public IDeserializationFactory GetSerializationFactory(uint descriptorID) | |||
{ | |||
return _factories.TryGetValue(descriptorID, out var factory) ? factory : null; | |||
return _factories[descriptorID]; | |||
} | |||
public void RegisterSerializationFactory<Descriptor>(IDeserializationFactory deserializationFactory) | |||
where Descriptor : ISerializableEntityDescriptor, new() | |||
{ | |||
_factories.Add(SerializationEntityDescriptorTemplate<Descriptor>.hash, deserializationFactory); | |||
_factories[SerializationEntityDescriptorTemplate<Descriptor>.hash] = deserializationFactory; | |||
} | |||
@@ -10,6 +10,6 @@ namespace Svelto.ECS.Serialization | |||
_name = name; | |||
} | |||
internal string _name; | |||
internal readonly string _name; | |||
} | |||
} |
@@ -2,8 +2,9 @@ namespace Svelto.ECS.Serialization | |||
{ | |||
public interface IDeserializationFactory | |||
{ | |||
EntityComponentInitializer BuildDeserializedEntity(EGID egid, ISerializationData serializationData, | |||
ISerializableEntityDescriptor entityDescriptor, int serializationType, | |||
IEntitySerialization entitySerialization); | |||
EntityComponentInitializer BuildDeserializedEntity | |||
(EGID egid, ISerializationData serializationData, ISerializableEntityDescriptor entityDescriptor | |||
, int serializationType, IEntitySerialization entitySerialization, IEntityFactory factory | |||
, bool enginesRootIsDeserializationOnly); | |||
} | |||
} |
@@ -1,8 +1,11 @@ | |||
using System; | |||
namespace Svelto.ECS.Serialization | |||
{ | |||
public interface ISerializableEntityDescriptor : IEntityDescriptor | |||
{ | |||
uint hash { get; } | |||
ISerializableComponentBuilder[] entitiesToSerialize { get; } | |||
Type realType { get; } | |||
} | |||
} |
@@ -25,11 +25,13 @@ namespace Svelto.ECS.Serialization | |||
{ | |||
if (myAttributes[j] is PartialSerializerFieldAttribute) | |||
{ | |||
if (myMembers[i].FieldType == typeof(EGID)) | |||
throw new ECSException("EGID fields cannot be serialised ".FastConcat(myType.FullName)); | |||
var fieldType = myMembers[i].FieldType; | |||
if (fieldType.ContainsCustomAttribute(typeof(DoNotSerializeAttribute)) && | |||
myMembers[i].IsPrivate == false) | |||
throw new ECSException($"field cannot be serialised {fieldType} in {myType.FullName}"); | |||
var offset = Marshal.OffsetOf<T>(myMembers[i].Name); | |||
var sizeOf = (uint)Marshal.SizeOf(myMembers[i].FieldType); | |||
var sizeOf = (uint)Marshal.SizeOf(fieldType); | |||
offsets.Add(((uint) offset.ToInt32(), sizeOf)); | |||
totalSize += sizeOf; | |||
} | |||
@@ -14,7 +14,7 @@ namespace Svelto.ECS.Serialization | |||
public class SerializableComponentBuilder<T> : ComponentBuilder<T>, ISerializableComponentBuilder | |||
where T : unmanaged, IEntityComponent | |||
{ | |||
public static readonly uint SIZE = UnsafeUtils.SizeOf<T>(); | |||
public static readonly uint SIZE = (uint) MemoryUtilities.SizeOf<T>(); | |||
public void Serialize | |||
(uint entityID, ITypeSafeDictionary dictionary, ISerializationData serializationData | |||
@@ -28,8 +28,7 @@ namespace Svelto.ECS.Serialization | |||
throw new ECSException("Entity Serialization failed"); | |||
} | |||
var values = safeDictionary.unsafeValues; | |||
ref T val = ref values[(int) index]; | |||
ref T val = ref safeDictionary.GetDirectValueByRef(index); | |||
serializationData.dataPos = (uint) serializationData.data.count; | |||
@@ -50,8 +49,7 @@ namespace Svelto.ECS.Serialization | |||
throw new ECSException("Entity Deserialization failed"); | |||
} | |||
var values = safeDictionary.unsafeValues; | |||
ref T val = ref values[(int) index]; | |||
ref T val = ref safeDictionary.GetDirectValueByRef(index); | |||
componentSerializer.DeserializeSafe(ref val, serializationData); | |||
} | |||
@@ -18,14 +18,14 @@ namespace Svelto.ECS.Serialization | |||
{ | |||
IComponentBuilder[] defaultEntities = EntityDescriptorTemplate<TType>.descriptor.componentsToBuild; | |||
var hashNameAttribute = _type.GetCustomAttribute<HashNameAttribute>(); | |||
var hashNameAttribute = Type.GetCustomAttribute<HashNameAttribute>(); | |||
if (hashNameAttribute == null) | |||
{ | |||
throw new Exception( | |||
"HashName attribute not found on the serializable type ".FastConcat(_type.FullName)); | |||
"HashName attribute not found on the serializable type ".FastConcat(Type.FullName)); | |||
} | |||
_hash = DesignatedHash.Hash(Encoding.ASCII.GetBytes(hashNameAttribute._name)); | |||
Hash = DesignatedHash.Hash(Encoding.ASCII.GetBytes(hashNameAttribute._name)); | |||
var (index, dynamicIndex) = SetupSpecialEntityComponent(defaultEntities, out ComponentsToBuild); | |||
if (index == -1) | |||
@@ -36,13 +36,13 @@ namespace Svelto.ECS.Serialization | |||
// Stores the hash of this EntityDescriptor | |||
ComponentsToBuild[index] = new ComponentBuilder<SerializableEntityComponent>(new SerializableEntityComponent | |||
{ | |||
descriptorHash = _hash | |||
descriptorHash = Hash | |||
}); | |||
// If the current serializable is an ExtendibleDescriptor, I have to update it. | |||
if (dynamicIndex != -1) | |||
{ | |||
ComponentsToBuild[dynamicIndex] = new ComponentBuilder<EntityInfoComponentView>(new EntityInfoComponentView | |||
ComponentsToBuild[dynamicIndex] = new ComponentBuilder<EntityInfoViewComponent>(new EntityInfoViewComponent | |||
{ | |||
componentsToBuild = ComponentsToBuild | |||
}); | |||
@@ -50,18 +50,18 @@ namespace Svelto.ECS.Serialization | |||
///// | |||
var entitiesToSerialize = new FasterList<ISerializableComponentBuilder>(); | |||
_entityComponentsToSerializeMap = new FasterDictionary<RefWrapper<Type>, ISerializableComponentBuilder>(); | |||
EntityComponentsToSerializeMap = new FasterDictionary<RefWrapper<Type>, ISerializableComponentBuilder>(); | |||
foreach (IComponentBuilder e in defaultEntities) | |||
{ | |||
if (e is ISerializableComponentBuilder serializableEntityBuilder) | |||
{ | |||
var entityType = serializableEntityBuilder.GetEntityComponentType(); | |||
_entityComponentsToSerializeMap[new RefWrapper<Type>(entityType)] = serializableEntityBuilder; | |||
EntityComponentsToSerializeMap[new RefWrapper<Type>(entityType)] = serializableEntityBuilder; | |||
entitiesToSerialize.Add(serializableEntityBuilder); | |||
} | |||
} | |||
_entitiesToSerialize = entitiesToSerialize.ToArray(); | |||
EntitiesToSerialize = entitiesToSerialize.ToArray(); | |||
} | |||
static (int indexSerial, int indexDynamic) SetupSpecialEntityComponent | |||
@@ -75,13 +75,13 @@ namespace Svelto.ECS.Serialization | |||
for (var i = 0; i < length; ++i) | |||
{ | |||
if (defaultEntities[i].GetEntityComponentType() == _serializableStructType) | |||
if (defaultEntities[i].GetEntityComponentType() == SerializableStructType) | |||
{ | |||
indexSerial = i; | |||
--newLenght; | |||
} | |||
if (defaultEntities[i].GetEntityComponentType() == EntityBuilderUtilities.ENTITY_STRUCT_INFO_VIEW) | |||
if (defaultEntities[i].GetEntityComponentType() == ComponentBuilderUtilities.ENTITY_STRUCT_INFO_VIEW) | |||
{ | |||
indexDynamic = i; | |||
} | |||
@@ -94,16 +94,17 @@ namespace Svelto.ECS.Serialization | |||
return (indexSerial, indexDynamic); | |||
} | |||
public IComponentBuilder[] componentsToBuild => ComponentsToBuild; | |||
public uint hash => _hash; | |||
public ISerializableComponentBuilder[] entitiesToSerialize => _entitiesToSerialize; | |||
public IComponentBuilder[] componentsToBuild => ComponentsToBuild; | |||
public uint hash => Hash; | |||
public Type realType => Type; | |||
public ISerializableComponentBuilder[] entitiesToSerialize => EntitiesToSerialize; | |||
static readonly IComponentBuilder[] ComponentsToBuild; | |||
static readonly FasterDictionary<RefWrapper<Type>, ISerializableComponentBuilder> _entityComponentsToSerializeMap; | |||
static readonly ISerializableComponentBuilder[] _entitiesToSerialize; | |||
static readonly FasterDictionary<RefWrapper<Type>, ISerializableComponentBuilder> EntityComponentsToSerializeMap; | |||
static readonly ISerializableComponentBuilder[] EntitiesToSerialize; | |||
static readonly uint _hash; | |||
static readonly Type _serializableStructType = typeof(SerializableEntityComponent); | |||
static readonly Type _type = typeof(TType); | |||
static readonly uint Hash; | |||
static readonly Type SerializableStructType = typeof(SerializableEntityComponent); | |||
static readonly Type Type = typeof(TType); | |||
} | |||
} |
@@ -1,5 +1,3 @@ | |||
using System; | |||
namespace Svelto.ECS.Serialization { | |||
public static class SerializerExt | |||
{ | |||
@@ -15,7 +13,7 @@ namespace Svelto.ECS.Serialization { | |||
// size == 0 is a special case when we don't know the size in advance | |||
if (componentSerializer.size != 0 && serializationData.dataPos != posBefore + componentSerializer.size) | |||
{ | |||
throw new IndexOutOfRangeException( | |||
throw new System.IndexOutOfRangeException( | |||
$"Size mismatch when serializing {typeof(T).FullName} using {componentSerializer.GetType().FullName}, " | |||
+ $"expected offset {posBefore + componentSerializer.size}, got {serializationData.dataPos}"); | |||
} | |||
@@ -34,7 +32,7 @@ namespace Svelto.ECS.Serialization { | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (componentSerializer.size != 0 && serializationData.dataPos != posBefore + componentSerializer.size) | |||
{ | |||
throw new IndexOutOfRangeException( | |||
throw new System.IndexOutOfRangeException( | |||
$"Size mismatch when deserializing {typeof(T).FullName} using {componentSerializer.GetType().FullName}, " | |||
+ $"expected offset {posBefore + componentSerializer.size}, got {serializationData.dataPos}"); | |||
} | |||
@@ -1,4 +1,4 @@ | |||
using System; | |||
using System.Runtime.CompilerServices; | |||
namespace Svelto.ECS.Internal | |||
{ | |||
@@ -14,16 +14,25 @@ namespace Svelto.ECS.Internal | |||
{ | |||
if (ComponentBuilder<T>.HAS_EGID) | |||
{ | |||
#if !ENABLE_IL2CPP | |||
var method = typeof(Trick).GetMethod(nameof(Trick.SetEGIDImpl)).MakeGenericMethod(typeof(T)); | |||
return (SetEGIDWithoutBoxingActionCast<T>) Delegate.CreateDelegate( | |||
return (SetEGIDWithoutBoxingActionCast<T>) System.Delegate.CreateDelegate( | |||
typeof(SetEGIDWithoutBoxingActionCast<T>), method); | |||
#else | |||
return (ref T target, EGID egid) => | |||
{ | |||
var needEgid = (target as INeedEGID); | |||
needEgid.ID = egid; | |||
target = (T) needEgid; | |||
}; | |||
#endif | |||
} | |||
return null; | |||
} | |||
static class Trick | |||
{ | |||
{ | |||
public static void SetEGIDImpl<U>(ref U target, EGID egid) where U : struct, INeedEGID | |||
{ | |||
target.ID = egid; | |||
@@ -7,13 +7,16 @@ namespace Svelto.ECS | |||
{ | |||
public void SubmitEntities() | |||
{ | |||
_onTick.Invoke(); | |||
if (paused == false) | |||
_onTick.Invoke(); | |||
} | |||
EnginesRoot.EntitiesSubmitter IEntitiesSubmissionScheduler.onTick | |||
{ | |||
set => _onTick = value; | |||
} | |||
public bool paused { get; set; } | |||
public void Dispose() { } | |||
@@ -4,6 +4,7 @@ | |||
"Unity.Entities", | |||
"Unity.Collections", | |||
"Unity.Burst", | |||
"Unity.Jobs", | |||
"Svelto.Common_3" | |||
], | |||
"includePlatforms": [], | |||
@@ -20,6 +21,21 @@ | |||
"name": "com.unity.entities", | |||
"expression": "", | |||
"define": "UNITY_ECS" | |||
}, | |||
{ | |||
"name": "com.unity.burst", | |||
"expression": "", | |||
"define": "UNITY_BURST" | |||
}, | |||
{ | |||
"name": "com.unity.collections", | |||
"expression": "", | |||
"define": "UNITY_COLLECTIONS" | |||
}, | |||
{ | |||
"name": "com.unity.jobs", | |||
"expression": "", | |||
"define": "UNITY_JOBS" | |||
} | |||
], | |||
"noEngineReferences": false |
@@ -15,7 +15,7 @@ | |||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\Svelto.Common\Svelto.Common.csproj" /> | |||
<ProjectReference Include="..\Svelto.Common_3\Svelto.Common.csproj" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<PackageReference Include="System.Memory" Version="4.5.2" /> | |||
@@ -0,0 +1,58 @@ | |||
namespace Svelto.ECS | |||
{ | |||
public readonly ref struct TupleRef<T1> where T1 : struct, IEntityComponent | |||
{ | |||
public readonly EntityCollections<T1> entities; | |||
public readonly GroupsEnumerable<T1> groups; | |||
public TupleRef(in EntityCollections<T1> entityCollections, in GroupsEnumerable<T1> groupsEnumerable) | |||
{ | |||
this.entities = entityCollections; | |||
groups = groupsEnumerable; | |||
} | |||
} | |||
public readonly ref struct TupleRef<T1, T2> where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent | |||
{ | |||
public readonly EntityCollections<T1, T2> entities; | |||
public readonly GroupsEnumerable<T1, T2> groups; | |||
public TupleRef(in EntityCollections<T1, T2> entityCollections, in GroupsEnumerable<T1, T2> groupsEnumerable) | |||
{ | |||
this.entities = entityCollections; | |||
groups = groupsEnumerable; | |||
} | |||
} | |||
public readonly ref struct TupleRef<T1, T2, T3> where T1 : struct, IEntityComponent | |||
where T2 : struct, IEntityComponent | |||
where T3 : struct, IEntityComponent | |||
{ | |||
public readonly EntityCollections<T1, T2, T3> entities; | |||
public readonly GroupsEnumerable<T1, T2, T3> groups; | |||
public TupleRef | |||
(in EntityCollections<T1, T2, T3> entityCollections, in GroupsEnumerable<T1, T2, T3> groupsEnumerable) | |||
{ | |||
this.entities = entityCollections; | |||
groups = groupsEnumerable; | |||
} | |||
} | |||
public readonly ref struct TupleRef<T1, T2, T3, T4> where T1 : struct, IEntityComponent | |||
where T2 : struct, IEntityComponent | |||
where T3 : struct, IEntityComponent | |||
where T4 : struct, IEntityComponent | |||
{ | |||
public readonly EntityCollections<T1, T2, T3, T4> entities; | |||
public readonly GroupsEnumerable<T1, T2, T3, T4> groups; | |||
public TupleRef | |||
(in EntityCollections<T1, T2, T3, T4> entityCollections | |||
, in GroupsEnumerable<T1, T2, T3, T4> groupsEnumerable) | |||
{ | |||
this.entities = entityCollections; | |||
groups = groupsEnumerable; | |||
} | |||
} | |||
} |
@@ -6,9 +6,9 @@ namespace Svelto.ECS | |||
{ | |||
public static ITypeSafeDictionary Create() | |||
{ | |||
return new TypeSafeDictionary<T>(); | |||
return new TypeSafeDictionary<T>(1); | |||
} | |||
public static ITypeSafeDictionary Create(uint size) | |||
{ | |||
return new TypeSafeDictionary<T>(size); | |||