Browse Source

improve EntitySubmissionScheduler code

fix code for entity operation submission
clarify variable names
tags/2.7
sebas77 6 years ago
parent
commit
75550701a8
9 changed files with 153 additions and 125 deletions
  1. +15
    -14
      DBC.cs
  2. +8
    -6
      Svelto.ECS/EnginesRoot.Engines.cs
  3. +7
    -10
      Svelto.ECS/EnginesRoot.Entities.cs
  4. +87
    -61
      Svelto.ECS/EnginesRoot.Submission.cs
  5. +3
    -2
      Svelto.ECS/EntityBuilder.cs
  6. +2
    -2
      Svelto.ECS/EntitySubmissionScheduler.cs
  7. +6
    -2
      Svelto.ECS/EntitySubmitOperation.cs
  8. +21
    -19
      Svelto.ECS/Extensions/Unity/UnityEntitySubmissionScheduler.cs
  9. +4
    -9
      Svelto.ECS/SimpleSubmissionEntityViewScheduler.cs

+ 15
- 14
DBC.cs View File

@@ -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)


+ 8
- 6
Svelto.ECS/EnginesRoot.Engines.cs View File

@@ -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)


+ 7
- 10
Svelto.ECS/EnginesRoot.Entities.cs View File

@@ -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);
}



+ 87
- 61
Svelto.ECS/EnginesRoot.Submission.cs View File

@@ -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;
}
}

+ 3
- 2
Svelto.ECS/EntityBuilder.cs View File

@@ -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
- 2
Svelto.ECS/EntitySubmissionScheduler.cs View File

@@ -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; }
}
}

+ 6
- 2
Svelto.ECS/EntitySubmitOperation.cs View File

@@ -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,
}
}

+ 21
- 19
Svelto.ECS/Extensions/Unity/UnityEntitySubmissionScheduler.cs View File

@@ -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

+ 4
- 9
Svelto.ECS/SimpleSubmissionEntityViewScheduler.cs View File

@@ -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; }
}
}

Loading…
Cancel
Save