fix code for entity operation submission clarify variable namestags/2.7
@@ -1,7 +1,8 @@ | |||
using System; | |||
#if PROFILER || !DEBUG | |||
#if DISABLE_DBC || !DEBUG || PROFILER | |||
#define DISABLE_CHECKS | |||
using System.Diagnostics; | |||
#endif | |||
using System; | |||
namespace DBC.ECS | |||
{ | |||
@@ -59,7 +60,7 @@ namespace DBC.ECS | |||
/// <summary> | |||
/// Precondition check. | |||
/// </summary> | |||
#if PROFILER || !DEBUG | |||
#if DISABLE_DBC | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Require(bool assertion, string message) | |||
@@ -79,7 +80,7 @@ namespace DBC.ECS | |||
/// Precondition check. | |||
/// </summary> | |||
/// | |||
#if PROFILER || !DEBUG | |||
#if DISABLE_DBC | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Require(bool assertion, string message, Exception inner) | |||
@@ -99,7 +100,7 @@ namespace DBC.ECS | |||
/// Precondition check. | |||
/// </summary> | |||
/// | |||
#if PROFILER || !DEBUG | |||
#if DISABLE_DBC | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Require(bool assertion) | |||
@@ -119,7 +120,7 @@ namespace DBC.ECS | |||
/// Postcondition check. | |||
/// </summary> | |||
/// | |||
#if PROFILER || !DEBUG | |||
#if DISABLE_DBC | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Ensure(bool assertion, string message) | |||
@@ -139,7 +140,7 @@ namespace DBC.ECS | |||
/// Postcondition check. | |||
/// </summary> | |||
/// | |||
#if PROFILER || !DEBUG | |||
#if DISABLE_DBC | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Ensure(bool assertion, string message, Exception inner) | |||
@@ -159,7 +160,7 @@ namespace DBC.ECS | |||
/// Postcondition check. | |||
/// </summary> | |||
/// | |||
#if PROFILER || !DEBUG | |||
#if DISABLE_DBC | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Ensure(bool assertion) | |||
@@ -179,7 +180,7 @@ namespace DBC.ECS | |||
/// Invariant check. | |||
/// </summary> | |||
/// | |||
#if PROFILER || !DEBUG | |||
#if DISABLE_DBC | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Invariant(bool assertion, string message) | |||
@@ -199,7 +200,7 @@ namespace DBC.ECS | |||
/// Invariant check. | |||
/// </summary> | |||
/// | |||
#if PROFILER || !DEBUG | |||
#if DISABLE_DBC | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Invariant(bool assertion, string message, Exception inner) | |||
@@ -219,7 +220,7 @@ namespace DBC.ECS | |||
/// Invariant check. | |||
/// </summary> | |||
/// | |||
#if PROFILER || !DEBUG | |||
#if DISABLE_DBC | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Invariant(bool assertion) | |||
@@ -238,7 +239,7 @@ namespace DBC.ECS | |||
/// <summary> | |||
/// Assertion check. | |||
/// </summary> | |||
#if PROFILER || !DEBUG | |||
#if DISABLE_DBC | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Assert(bool assertion, string message) | |||
@@ -258,7 +259,7 @@ namespace DBC.ECS | |||
/// Assertion check. | |||
/// </summary> | |||
/// | |||
#if PROFILER || !DEBUG | |||
#if DISABLE_DBC | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Assert(bool assertion, string message, Exception inner) | |||
@@ -278,7 +279,7 @@ namespace DBC.ECS | |||
/// Assertion check. | |||
/// </summary> | |||
/// | |||
#if PROFILER || !DEBUG | |||
#if DISABLE_DBC | |||
[Conditional("__NEVER_DEFINED__")] | |||
#endif | |||
public static void Assert(bool assertion) | |||
@@ -14,17 +14,18 @@ namespace Svelto.ECS | |||
{ | |||
public partial class EnginesRoot : IDisposable | |||
{ | |||
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR | |||
static EnginesRoot() | |||
{ | |||
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR | |||
/// <summary> | |||
/// I still need to find a good solution for this. Need to move somewhere else | |||
/// </summary> | |||
UnityEngine.GameObject debugEngineObject = new UnityEngine.GameObject("Svelto.ECS.Profiler"); | |||
debugEngineObject.gameObject.AddComponent<EngineProfilerBehaviour>(); | |||
UnityEngine.GameObject.DontDestroyOnLoad(debugEngineObject); | |||
#endif | |||
} | |||
#endif | |||
/// <summary> | |||
/// Engines root contextualize your engines and entities. You don't need to limit yourself to one EngineRoot | |||
@@ -34,21 +35,22 @@ namespace Svelto.ECS | |||
/// The EntitySubmissionScheduler cannot hold an EnginesRoot reference, that's why | |||
/// it must receive a weak reference of the EnginesRoot callback. | |||
/// </summary> | |||
public EnginesRoot(EntitySubmissionScheduler entityViewScheduler) | |||
public EnginesRoot(IEntitySubmissionScheduler entityViewScheduler) | |||
{ | |||
_entitiesOperations = new FasterList<EntitySubmitOperation>(); | |||
_entityEngines = new Dictionary<Type, FasterList<IHandleEntityViewEngineAbstracted>>(); | |||
_otherEngines = new FasterList<IEngine>(); | |||
_disposableEngines = new FasterList<IDisposable>(); | |||
_transientEntitiesOperations = new FasterList<EntitySubmitOperation>(); | |||
_groupEntityDB = new FasterDictionary<int, Dictionary<Type, ITypeSafeDictionary>>(); | |||
_groupedGroups = new Dictionary<Type, FasterDictionary<int, ITypeSafeDictionary>>(); | |||
_groupsPerEntity = new Dictionary<Type, FasterDictionary<int, ITypeSafeDictionary>>(); | |||
_groupedEntityToAdd = new DoubleBufferedEntitiesToAdd<FasterDictionary<int, Dictionary<Type, ITypeSafeDictionary>>>(); | |||
_DB = new EntitiesDB(_groupEntityDB, _groupedGroups); | |||
_DB = new EntitiesDB(_groupEntityDB, _groupsPerEntity); | |||
_scheduler = entityViewScheduler; | |||
_scheduler.Schedule(new WeakAction(SubmitEntityViews)); | |||
_scheduler.onTick = new WeakAction(SubmitEntityViews); | |||
} | |||
public void AddEngine(IEngine engine) | |||
@@ -1,7 +1,8 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Diagnostics; | |||
using Svelto.DataStructures.Experimental; | |||
using Svelto.DataStructures; | |||
using Svelto.DataStructures.Experimental; | |||
using Svelto.ECS.Internal; | |||
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR | |||
@@ -58,11 +59,9 @@ namespace Svelto.ECS | |||
#endif | |||
var dic = EntityFactory.BuildGroupedEntities(entityID, | |||
_groupedEntityToAdd.current, | |||
descriptorEntitiesToBuild, | |||
descriptorEntitiesToBuild, | |||
implementors); | |||
_newEntitiesBuiltToProcess++; | |||
return new EntityStructInitializer(entityID, dic); | |||
} | |||
@@ -96,7 +95,6 @@ namespace Svelto.ECS | |||
} | |||
} | |||
} | |||
///-------------------------------------------- | |||
void Preallocate<T>(int groupID, int size) where T : IEntityDescriptor, new() | |||
@@ -188,8 +186,8 @@ namespace Svelto.ECS | |||
} | |||
FasterDictionary<int, ITypeSafeDictionary> groupedGroup; | |||
if (_groupedGroups.TryGetValue(entityType, out groupedGroup) == false) | |||
groupedGroup = _groupedGroups[entityType] = new FasterDictionary<int, ITypeSafeDictionary>(); | |||
if (_groupsPerEntity.TryGetValue(entityType, out groupedGroup) == false) | |||
groupedGroup = _groupsPerEntity[entityType] = new FasterDictionary<int, ITypeSafeDictionary>(); | |||
groupedGroup[toGroupID] = dictionaryOfEntities; | |||
} | |||
@@ -202,7 +200,7 @@ namespace Svelto.ECS | |||
if (fromTypeSafeDictionary.Count == 0) //clean up | |||
{ | |||
_groupedGroups[entityType].Remove(entityGID.groupID); | |||
_groupsPerEntity[entityType].Remove(entityGID.groupID); | |||
//I don't remove the group if empty on purpose, in case it needs to be reused | |||
//however I trim it to save memory | |||
@@ -216,7 +214,7 @@ namespace Svelto.ECS | |||
foreach (var dictionaryOfEntities in dictionariesOfEntities) | |||
{ | |||
dictionaryOfEntities.Value.RemoveEntitiesFromEngines(_entityEngines); | |||
var groupedGroupOfEntities = _groupedGroups[dictionaryOfEntities.Key]; | |||
var groupedGroupOfEntities = _groupsPerEntity[dictionaryOfEntities.Key]; | |||
groupedGroupOfEntities.Remove(groupID); | |||
} | |||
@@ -240,7 +238,6 @@ namespace Svelto.ECS | |||
} | |||
readonly EntitiesDB _DB; | |||
int _newEntitiesBuiltToProcess; | |||
Type _entityInfoView = typeof(EntityInfoView); | |||
} | |||
@@ -1,9 +1,12 @@ | |||
using System; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Diagnostics; | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Svelto.DataStructures.Experimental; | |||
using Svelto.ECS.Internal; | |||
using Svelto.ECS.Schedulers; | |||
using Console = Svelto.Utilities.Console; | |||
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR | |||
using Svelto.ECS.Profiler; | |||
@@ -15,69 +18,87 @@ namespace Svelto.ECS | |||
{ | |||
void SubmitEntityViews() | |||
{ | |||
var entitiesOperations = _entitiesOperations.ToArrayFast(); | |||
for (int i = 0; i < _entitiesOperations.Count; i++) | |||
using (new PlatformProfiler("Svelto.ECS submit")) | |||
{ | |||
if (_entitiesOperations.Count > 0) | |||
{ | |||
_transientEntitiesOperations.FastClear(); | |||
_transientEntitiesOperations.AddRange(_entitiesOperations); | |||
_entitiesOperations.FastClear(); | |||
var entitiesOperations = _transientEntitiesOperations.ToArrayFast(); | |||
for (var i = 0; i < _transientEntitiesOperations.Count; i++) | |||
{ | |||
try | |||
{ | |||
switch (entitiesOperations[i].type) | |||
{ | |||
case EntitySubmitOperationType.Swap: | |||
SwapEntityGroup(entitiesOperations[i].builders, entitiesOperations[i].id, | |||
entitiesOperations[i].fromGroupID, entitiesOperations[i].toGroupID); | |||
break; | |||
case EntitySubmitOperationType.Remove: | |||
MoveEntity(entitiesOperations[i].builders, | |||
new EGID(entitiesOperations[i].id, entitiesOperations[i].fromGroupID)); | |||
break; | |||
case EntitySubmitOperationType.RemoveGroup: | |||
RemoveGroupAndEntitiesFromDB(entitiesOperations[i].fromGroupID); | |||
break; | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
#if DEBUG | |||
var str = "Entity ".FastConcat(entitiesOperations[i].type.ToString(), | |||
" with used ID is about to be built: ") | |||
.FastConcat(" id: ") | |||
.FastConcat(entitiesOperations[i].id) | |||
.FastConcat(" from groupid: ") | |||
.FastConcat(entitiesOperations[i].fromGroupID) | |||
.FastConcat(" to groupid: ") | |||
.FastConcat(entitiesOperations[i].toGroupID); | |||
Console.LogError(e.Message.FastConcat(" ", str, " ", entitiesOperations[i].trace)); | |||
throw; | |||
#else | |||
Console.LogException(e); | |||
#endif | |||
} | |||
} | |||
} | |||
try | |||
{ | |||
switch (entitiesOperations[i].type) | |||
if (_groupedEntityToAdd.current.Count > 0) | |||
{ | |||
case EntitySubmitOperationType.Swap: | |||
SwapEntityGroup(entitiesOperations[i].builders, entitiesOperations[i].id, | |||
entitiesOperations[i].fromGroupID, entitiesOperations[i].toGroupID); | |||
break; | |||
case EntitySubmitOperationType.Remove: | |||
MoveEntity(entitiesOperations[i].builders, | |||
new EGID(entitiesOperations[i].id, entitiesOperations[i].fromGroupID)); | |||
break; | |||
case EntitySubmitOperationType.RemoveGroup: | |||
RemoveGroupAndEntitiesFromDB(entitiesOperations[i].fromGroupID); | |||
break; | |||
//use other as source from now on | |||
//current will be use to write new entityViews | |||
_groupedEntityToAdd.Swap(); | |||
//Note: if N entity of the same type are added on the same frame | |||
//the Add callback is called N times on the same frame. | |||
//if the Add callback builds a new entity, that entity will not | |||
//be available in the database until the N callbacks are done | |||
//solving it could be complicated as callback and database update | |||
//must be interleaved. | |||
AddEntityViewsToTheDBAndSuitableEngines(_groupedEntityToAdd.other); | |||
//other can be cleared now, but let's avoid deleting the dictionary every time | |||
_groupedEntityToAdd.ClearOther(); | |||
} | |||
} | |||
catch (ECSException e) | |||
catch (Exception e) | |||
{ | |||
Svelto.Utilities.Console.LogError(e.Message.FastConcat(" ", entitiesOperations[i].trace)); | |||
Console.LogException(e); | |||
#if DEBUG | |||
throw; | |||
#endif | |||
#endif | |||
} | |||
} | |||
_entitiesOperations.FastClear(); | |||
int numberOfReenteringLoops = 0; | |||
//are there new entities built to process? | |||
while ( _newEntitiesBuiltToProcess > 0) | |||
{ | |||
_newEntitiesBuiltToProcess = 0; | |||
//use other as source from now on | |||
//current will be use to write new entityViews | |||
_groupedEntityToAdd.Swap(); | |||
//Note: if N entity of the same type are added on the same frame | |||
//the Add callback is called N times on the same frame. | |||
//if the Add calback builds a new entity, that entity will not | |||
//be available in the database until the N callbacks are done | |||
//solving it could be complicated as callback and database update | |||
//must be interleaved. | |||
if (_groupedEntityToAdd.other.Count > 0) | |||
AddEntityViewsToTheDBAndSuitableEngines(_groupedEntityToAdd.other); | |||
//other can be cleared now, but let's avoid deleting the dictionary every time | |||
_groupedEntityToAdd.ClearOther(); | |||
if (numberOfReenteringLoops > 5) | |||
throw new Exception("possible infinite loop found creating Entities inside IEntityViewsEngine Add method, please consider building entities outside IEntityViewsEngine Add method"); | |||
numberOfReenteringLoops++; | |||
} | |||
} | |||
//todo: groupsToSubmit can be simplified as data structure? | |||
void AddEntityViewsToTheDBAndSuitableEngines(FasterDictionary<int, Dictionary<Type, ITypeSafeDictionary>> groupsOfEntitiesToSubmit) | |||
void AddEntityViewsToTheDBAndSuitableEngines(FasterDictionary<int, Dictionary<Type, ITypeSafeDictionary>> groupsOfEntitiesToSubmit) | |||
{ | |||
//each group is indexed by entity view type. for each type there is a dictionary indexed by entityID | |||
foreach (var groupOfEntitiesToSubmit in groupsOfEntitiesToSubmit) | |||
@@ -97,8 +118,8 @@ namespace Svelto.ECS | |||
if (groupDB.TryGetValue(entityViewTypeSafeDictionary.Key, out dbDic) == false) | |||
dbDic = groupDB[entityViewTypeSafeDictionary.Key] = entityViewTypeSafeDictionary.Value.Create(); | |||
if (_groupedGroups.TryGetValue(entityViewTypeSafeDictionary.Key, out groupedGroup) == false) | |||
groupedGroup = _groupedGroups[entityViewTypeSafeDictionary.Key] = | |||
if (_groupsPerEntity.TryGetValue(entityViewTypeSafeDictionary.Key, out groupedGroup) == false) | |||
groupedGroup = _groupsPerEntity[entityViewTypeSafeDictionary.Key] = | |||
new FasterDictionary<int, ITypeSafeDictionary>(); | |||
//Fill the DB with the entity views generate this frame. | |||
@@ -117,20 +138,25 @@ namespace Svelto.ECS | |||
} | |||
} | |||
} | |||
readonly FasterList<EntitySubmitOperation> _entitiesOperations; | |||
readonly DoubleBufferedEntitiesToAdd<FasterDictionary<int, Dictionary<Type, ITypeSafeDictionary>>> | |||
_groupedEntityToAdd; | |||
//for each entity view type, return the groups (dictionary of entities indexed by entity id) where they are | |||
//found indexed by group id | |||
readonly Dictionary<Type, FasterDictionary<int, ITypeSafeDictionary>> _groupsPerEntity; //yes I am being sarcastic | |||
//one datastructure rule them all: | |||
//split by group | |||
//split by type per group. It's possible to get all the entities of a give type T per group thanks | |||
//to the FasterDictionary capabilities OR it's possible to get a specific entityView indexed by | |||
//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 | |||
readonly FasterDictionary<int, Dictionary<Type, ITypeSafeDictionary>> _groupEntityDB; | |||
//for each entity view type, return the groups (dictionary of entities indexed by entity id) where they are found indexed by group id | |||
readonly Dictionary<Type, FasterDictionary<int, ITypeSafeDictionary>> _groupedGroups; //yes I am being sarcastic | |||
readonly DoubleBufferedEntitiesToAdd<FasterDictionary<int, Dictionary<Type, ITypeSafeDictionary>>> | |||
_groupedEntityToAdd; | |||
readonly EntitySubmissionScheduler _scheduler; | |||
readonly FasterList<EntitySubmitOperation> _entitiesOperations; | |||
readonly IEntitySubmissionScheduler _scheduler; | |||
readonly FasterList<EntitySubmitOperation> _transientEntitiesOperations; | |||
} | |||
} |
@@ -41,7 +41,7 @@ namespace Svelto.ECS | |||
SubCheckFields(fieldFieldType); | |||
} | |||
if (type.Assembly == Assembly.GetCallingAssembly()) | |||
if (type.Assembly == Assembly.GetCallingAssembly() && type != typeof(Svelto.ECS.EGID)) | |||
{ | |||
var methods = type.GetMethods(BindingFlags.Public | | |||
BindingFlags.Instance | BindingFlags.DeclaredOnly); | |||
@@ -141,7 +141,8 @@ namespace Svelto.ECS | |||
public class EntityStructException : Exception | |||
{ | |||
public EntityStructException(Type fieldType):base("EntityStruct must contains only value types and no public methods! " + fieldType.ToString()) | |||
public EntityStructException(Type fieldType) : | |||
base("EntityStruct must contains only value types and no public methods! " + fieldType.ToString()) | |||
{} | |||
} | |||
} |
@@ -2,8 +2,8 @@ using Svelto.WeakEvents; | |||
namespace Svelto.ECS.Schedulers | |||
{ | |||
public abstract class EntitySubmissionScheduler | |||
public interface IEntitySubmissionScheduler | |||
{ | |||
public abstract void Schedule(WeakAction submitEntityViews); | |||
WeakAction onTick { set; } | |||
} | |||
} |
@@ -13,7 +13,11 @@ namespace Svelto.ECS | |||
public string trace; | |||
#endif | |||
public EntitySubmitOperation(EntitySubmitOperationType operation, int entityId, int fromGroupId, int toGroupId, IEntityBuilder[] builders) | |||
public EntitySubmitOperation(EntitySubmitOperationType operation, | |||
int entityId, | |||
int fromGroupId, | |||
int toGroupId, | |||
IEntityBuilder[] builders) | |||
{ | |||
type = operation; | |||
this.builders = builders; | |||
@@ -30,6 +34,6 @@ namespace Svelto.ECS | |||
{ | |||
Swap, | |||
Remove, | |||
RemoveGroup | |||
RemoveGroup, | |||
} | |||
} |
@@ -7,21 +7,8 @@ namespace Svelto.ECS.Schedulers.Unity | |||
{ | |||
//The EntitySubmissionScheduler has been introduced to make the entity views submission logic platform independent | |||
//You can customize the scheduler if you wish | |||
public class UnityEntitySubmissionScheduler : EntitySubmissionScheduler | |||
public class UnityEntitySubmissionScheduler : IEntitySubmissionScheduler | |||
{ | |||
public UnityEntitySubmissionScheduler() | |||
{ | |||
GameObject go = new GameObject("ECSScheduler"); | |||
_scheduler = go.AddComponent<Scheduler>(); | |||
} | |||
public override void Schedule(WeakAction submitEntityViews) | |||
{ | |||
_scheduler.OnTick = submitEntityViews; | |||
} | |||
class Scheduler : MonoBehaviour | |||
{ | |||
IEnumerator Start() | |||
@@ -30,19 +17,34 @@ namespace Svelto.ECS.Schedulers.Unity | |||
{ | |||
yield return _wait; | |||
if (OnTick.IsValid) | |||
OnTick.Invoke(); | |||
if (onTick.IsValid) | |||
onTick.Invoke(); | |||
else | |||
yield break; | |||
} | |||
} | |||
internal WeakAction OnTick; | |||
readonly WaitForEndOfFrame _wait = new WaitForEndOfFrame(); | |||
public WeakAction onTick; | |||
} | |||
public WeakAction onTick | |||
{ | |||
set | |||
{ | |||
if (_scheduler == null) | |||
{ | |||
GameObject go = new GameObject("ECSScheduler"); | |||
_scheduler = go.AddComponent<Scheduler>(); | |||
} | |||
_scheduler.onTick = value; | |||
} | |||
} | |||
readonly Scheduler _scheduler; | |||
Scheduler _scheduler; | |||
} | |||
} | |||
#endif |
@@ -5,18 +5,13 @@ namespace Svelto.ECS | |||
{ | |||
//This scheduler shouldn't be used in production and it's meant to be | |||
//used for Unit Tests only | |||
public class SimpleSubmissionEntityViewScheduler : EntitySubmissionScheduler | |||
public class SimpleSubmissionEntityViewScheduler : IEntitySubmissionScheduler | |||
{ | |||
public void SubmitEntities() | |||
{ | |||
_submitEntityViews.Invoke(); | |||
onTick.Invoke(); | |||
} | |||
public override void Schedule(WeakAction submitEntityViews) | |||
{ | |||
_submitEntityViews = submitEntityViews; | |||
} | |||
WeakAction _submitEntityViews; | |||
public WeakAction onTick { set; private get; } | |||
} | |||
} |