Search Unity

Event System

Discussion in 'Entity Component System' started by tertle, Nov 19, 2019.

  1. khalid_mightybear

    khalid_mightybear

    Joined:
    Sep 10, 2017
    Posts:
    36
    Has anyone used v2? Are there any issues with it?
    I'm using v1 currently but forced to use UsePersistentAllocator because of fixedtimestep systems.
    It's much slower than before
     
  2. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    Please note, one big change in V2 at the moment is that you can no longer send events to separate EventSystems (i.e. from fixed to update). This does mean there is no more need for a persistent allocator mode now though and all events are allocated with TempJob.

    I will probably at some point add a separate optional event forwarding service to merge events from different update rates but I want it to be decoupled from as it added a lot of limitations.

    Site note: I've been waiting a year for Unity to release their new memory management to optimize this!
     
  3. khalid_mightybear

    khalid_mightybear

    Joined:
    Sep 10, 2017
    Posts:
    36
    Thanks for the heads up. I don't have any events that is shared between different event systems so that's not a problem for me. The issues I'm facing is the memory leaks without persistent allocator. It seems to be only obvious on a mobile device so it's hard to track.

    For performance, I do need to use TempJob so I'm going to try out v2. The other alternative to fix the memory leaks with v1 with TempJob which I probably need help with.
     
  4. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    If most of your events are in regular update then you can work around this a little if you aren't passing events across fixed to regular update loops. You can set the event system in fixed/update to different buses so they don't share events and therefore your regular update system can use TempJob instead.

    - Change WorldMode to Custom on your EventSystems
    - Set CustomKey to something different from your Update and FixedUpdate EventSystems

    Then you can turn off UsePersistentAllocator in your Update systems.
    You'll still need to make your fixed system as persistent but your regular event system should be fine on TempJob now.

    As I said before, I have been hoping for ages to use Unitys new memory manage to remove the need for Persistent memory but just waiting on new Entities..
     
  5. khalid_mightybear

    khalid_mightybear

    Joined:
    Sep 10, 2017
    Posts:
    36
    Why do the FixedUpdate event systems need UsePersistentAllocator?
     
  6. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    Because if your writer is after your reader it won't be executed till next frame.
    With FixedUpdate the next frame could be 4+ render frames later which is the max allowed time for TempJob before it leaks
     
  7. khalid_mightybear

    khalid_mightybear

    Joined:
    Sep 10, 2017
    Posts:
    36
    Ah understood. That's probably what I'm seeing on device. Does v2 manage to get around this limitation?
     
  8. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    Maybe anti-pattern is too strong of a term, but I think in most cases I've seen people using events like this go against general ECS principles and are often used as a crutch when they are transitioning from a object oriented mindset to entities and a more data driven approach.

    I do not go into writing any system thinking, ok let's use events. Instead this library is just a tool in my pocket that I can bring out on the rare case when I need to efficiently get some data out of a system.

    For me, this is mostly debugging tools. The 3 main libraries of mine at the moment that use this are
    - Gizmos - allow drawing from burst jobs while completely stripping it out in release builds
    - Telemetry - firing events to telemetry services
    - AI debugging - my utility AI system fires events out for each state allowing recording AI, rendering as a graph and playing back states for debugging. again completely stripped out in release

    I'm not claiming there is never a case for it, I'm just saying I haven't yet seen good, scalable architecture that's based around it.

    I could go into a lot of reasons why I prefer doing things other ways, debugging, performance etc. But at the end of the day there is no one best way of coding, it's just my opinion that I've developed from experience and there's a good chance I am not in the majority
     
    Last edited: Oct 26, 2021
    Bivens32 likes this.
  9. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    Not really, if anything I think you'll have more problems if you are using events in fixed updates as there's no way to set persistent now. It's still waiting for Unity's new memory management.

    -edit- unless you can guarantee your producers update before your consumers so the events are consumed the same frame. in which case it should work perfectly. though untested. -endedit-

    I could have written my own memory manager ages ago but seemed a bit pointless with what Unity showed off, though wasn't expecting it to take so long.
     
  10. khalid_mightybear

    khalid_mightybear

    Joined:
    Sep 10, 2017
    Posts:
    36
    For v1, is there a way for me to log a warning if events are not consumed in the same frame?
     
  11. lclemens

    lclemens

    Joined:
    Feb 15, 2020
    Posts:
    761
    Thanks - That's a really well written reply. I agree... In an ideal ECS world, we'd add a tag to the component and any systems would pick that up and do what they need and then another system would remove that tag at the end of the frame. However, since the cost of adding a component is so heavy, we don't have that option. I have read that there are some implementations of ECS that don't have that tagging cost, but they suffer in other aspects. Until (or if) the situation changes, tertles events get the job done.
     
  12. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    There are more performant alternatives to just adding a component to an entity to flag an event.

    A common pattern we use is simply to add events to a buffer on the entity. This is particularly useful if you have multiple of the same type of events in a frame that execute on an entity, for example maybe the events are animator params or something. You can simply have a system much later react and set them on the Animator.

    If you want to get really complicated you can even make these buffer elements completely generic with state so that you can have 1 event buffer with lots of different types of events. I might make something like that public at some point.
     
    Last edited: Nov 8, 2021
  13. lclemens

    lclemens

    Joined:
    Feb 15, 2020
    Posts:
    761
    I use that dynamic buffer model for several things, and yes, that does work, but in many ways it's also a "polling" architecture. Every listener system is constantly running every frame saying "Is there something on the buffer for me to process?". When adding/removing components, the queries don't match and the systems don't run so it saves computing power. However, when using a dynamic buffer, the updates get called for every single entity that has that buffer and for every single frame even if there is no need to do so, so in many ways it' similar to the debate of setting a bit flag vs tagging. If there were 100,000 entities with a dynamic buffer and 20 systems that were all polling it, that means that every single frame there are two million branch statements being run even if there isn't a single event being sent.
     
    Krajca likes this.
  14. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    > Every listener system is constantly running every frame saying "Is there something on the buffer for me to process?

    You can avoid this with change filtering.
     
  15. lclemens

    lclemens

    Joined:
    Feb 15, 2020
    Posts:
    761
    That's an interesting idea... but I'm thinking there are two problems with that. One is that change filters operate on a per-chunk basis, so the query is going to return a lot of unnecessary results if only one event buffer is changed. The other issue is that there needs to be at least one system that clears the events. That system must declare the event buffer as write, which causes the change filter to return everything in that query for all the other read-only systems.

    I can think of a few ways around the problems, but they're messy and hacky and it seems to me like at that point, your event system would be easier and faster.
     
  16. Guedez

    Guedez

    Joined:
    Jun 1, 2012
    Posts:
    827
    How do I fire events from the main thread?
    This code works on editor but not on build, on build the event system claims there are not event readers and don't update the system. The code snips are like how I needed to do to make it work, but it's easy to figure out what it was when it was not working.

    Code (CSharp):
    1.     public class ResponsibilityAddedEventSystem : ConsumeSingleEventSystemBase<ResponsibilityAdded> {
    2. #if UNITY_EDITOR
    3.         public static Dictionary<Entity, GameObject> DebugResponsabilitySourceMap = new Dictionary<Entity, GameObject>();
    4. #endif
    5.         GameObjectToEntityMapSystem GameObjectToEntityMapSystem;
    6.         protected override void Create() {
    7.             GameObjectToEntityMapSystem = World.GetOrCreateSystem<GameObjectToEntityMapSystem>();
    8.         }
    9.         protected override void OnEvent(ResponsibilityAdded e) {
    10. #if UNITY_EDITOR
    11.             if (!EntityManager.HasComponent<Responsibility>(e.Responsibility)) {
    12.                 Debug.LogError("Entity: " + EntityManager.GetName(e.Responsibility) + " has no responsability data but was added as a responsability",
    13.                     ResponsibilityAddedEventSystem.DebugResponsabilitySourceMap[e.Responsibility]);
    14.             }
    15. #endif
    16.             RunForNow(e);//The content would run here, this is never reached on builds
    17.         }
    18.  
    19.         public static void RunForNow(ResponsibilityAdded e) {
    20.             ResponsibilityAddedEventSystem ResponsibilityAddedEventSystem = World.DefaultGameObjectInjectionWorld.GetOrCreateSystem<ResponsibilityAddedEventSystem>();
    21.             Responsibility Responsibility = ResponsibilityAddedEventSystem.EntityManager.GetComponentData<Responsibility>(e.Responsibility);
    22.             foreach (ResponsibilityData ResponsibilityData in Responsibility.Value) {
    23.                 if (ResponsibilityAddedEventSystem.GameObjectToEntityMapSystem.TryGetGameObject(ResponsibilityData.Target, out GameObject target)) {
    24.                     NPCBrain NPCBrain = target.GetComponent<NPCBrain>();
    25.                     if (NPCBrain != null) {
    26.                         NPCBrain.RegisterResponsibility(e.Responsibility, ResponsibilityData.Priority);
    27.                     }
    28.                 }
    29.             }
    30.         }
    31.     }

    Code (CSharp):
    1.             EventSystem EventSystem = World.DefaultGameObjectInjectionWorld.GetOrCreateSystem<EventSystem>();
    2.             Unity.Jobs.JobHandle Dependency = EventSystem.GetEventReaders<ResponsibilityAdded>(default, out var events);
    3.             NativeEventStream.ThreadWriter EventWriterResponsibilityAdded = EventSystem.CreateEventWriter<ResponsibilityAdded>();
    4. #if UNITY_EDITOR
    5.             ResponsibilityAddedEventSystem.DebugResponsabilitySourceMap[entity] = gameObject;
    6. #endif
    7.             //EventWriterResponsibilityAdded.Write(new ResponsibilityAdded() { Responsibility = entity });
    8.             ResponsibilityAddedEventSystem.RunForNow(new ResponsibilityAdded() { Responsibility = entity });
    9.             EventSystem.AddJobHandleForProducer<ResponsibilityAdded>(Dependency);
     
  17. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    What's going on here?

    Code (CSharp):
    1. Unity.Jobs.JobHandle Dependency = EventSystem.GetEventReaders<ResponsibilityAdded>(default, out var events);
    2. NativeEventStream.ThreadWriter EventWriterResponsibilityAdded = EventSystem.CreateEventWriter<ResponsibilityAdded>();

    Why do you have a reader and writer
     
  18. Guedez

    Guedez

    Joined:
    Jun 1, 2012
    Posts:
    827
    It was a desperate attempt at getting a dependency since
    EventSystem.AddJobHandleForProducer<ResponsibilityAdded>(default);
    was not working.

    Edit: Could it be that I need to create a writer every frame for the reader to read every frame?
    I will check again when things are happening, it could be that I am writing to the writer before the world is created or something.
     
  19. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    > Could it be that I need to create a writer every frame for the reader to read every frame?

    You most definitely have to do this. I'm surprised you aren't crashing from using disposed memory.

    (This is on V1 right?)
     
    Last edited: Nov 17, 2021
  20. Guedez

    Guedez

    Joined:
    Jun 1, 2012
    Posts:
    827
    Good question, I will check tomorrow morning.
    So how does one have a monobehaviour OnStart send events? It's not like they get OnStart every frame.
     
  21. Guedez

    Guedez

    Joined:
    Jun 1, 2012
    Posts:
    827
    I am at Version 1.2.2, which seems to be the newest.
    The Writer was being created on Update of a Monobehaviour, which then deletes the GameObject (I am manually using Monobehaviour to create and configure an Entity). This worked fine on Editor but somehow didn't work on build.
    This also mean that every Responsibility being instantiated was calling it's own
    NativeEventStream.ThreadWriter EventWriterResponsibilityAdded = EventSystem.CreateEventWriter<ResponsibilityAdded>();
    which is probably wrong?

    Maybe the system is not made for events that only fire a couple in a single frame per game load/new game?
    Specifically when loading (I never implemented it, so it was causing a bug), the way I implemented loading IComponentDatas is that each of them will fire a method if they implement a PostLoadIComponentData, so they would not have access to an external writer, and would need to create an writer per event, which sounds terrible.

    I am not sure how I should use, or even if I should use the system for this use case.
     
  22. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    It was only ever designed to be used with systems, I pretty much never write MonoBehaviour code anymore.

    As general practices go, I actually STRONGLY advise against any pattern, events or otherwise, that has the GameObject world accessing the entity world. I HIGHLY recommend only ever having the entity world accessing the GameObject world, not vice versa. From experience you will run into a lot of issues long term in your project if you do this.
     
  23. Guedez

    Guedez

    Joined:
    Jun 1, 2012
    Posts:
    827
    I guess I will make the placeholder solution final.
     
    tertle likes this.
  24. lclemens

    lclemens

    Joined:
    Feb 15, 2020
    Posts:
    761
    Thanks for the tip! I'll keep that in mind from now on.
     
  25. lclemens

    lclemens

    Joined:
    Feb 15, 2020
    Posts:
    761
    Whenever I run this code from the main thread (a monobehaviour event that happens when the player clicks a button), the line
    Code (CSharp):
    1. es.AddJobHandleForConsumer<T>(dependency);
    fails at runtime with "NullReferenceException: Object reference not set to an instance of an object". I tried passing in default as well as new Unity.Jobs.JobHandle(). I'm not even sure what is the thing that is the null reference. Any ideas?
     
  26. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    This should be AddJobHandleForProducer, you're writing not reading. Not sure why it's throwing a null though, should throw a proper error about imbalance.
     
    lclemens likes this.
  27. calabi

    calabi

    Joined:
    Oct 29, 2009
    Posts:
    232
    I can't seem to get this to download through the packages system I just get an error saying the URL is not a string it just cannot find or download it.
     
  28. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    How are you trying to download it? Via OpenUPM? https://openupm.com/packages/com.bovinelabs.event/
     
    calabi likes this.
  29. lclemens

    lclemens

    Joined:
    Feb 15, 2020
    Posts:
    761

    haha I just figured it out on my own and came back here to report my mistake. I had copied it from Ylly-avvyland's EventHelper class. oops, that's what I get for copying and pasting :).

    It does throw an improper imbalance error right after it throws the null error.
     
  30. calabi

    calabi

    Joined:
    Oct 29, 2009
    Posts:
    232
  31. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    I haven't had the time to read the whole thread, but I have a quick question:

    are there any pitfalls to creating events that need Entity fields? It looks like events stay in NativeStreams the whole time, and so a structural change happening after the creation of an event & before the processing of that event would have a chance to invalidate that entity field (?). Then again my comprehension of Entity Remapping is pretty vague and I don't know for sure if it works the way I think it works
     
    Last edited: Jan 21, 2022
    lclemens likes this.
  32. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    There's nothing in the library that ensures the entity exists, either ensure your reader is before the ECBS or check.
    Also you shouldn't put virtual entities into the stream from ecb as they won't be remapped (only ecb does that.)
     
  33. Abbrew

    Abbrew

    Joined:
    Jan 1, 2018
    Posts:
    417
    Does IJobEventReader execute during EntityCommandBufferSystem jobs? The errors in my game seem to imply that IJobEventReader is executing while structural changes are being made. I tried putting
    Code (CSharp):
    1.             EndECBS.AddJobHandleForProducer(Dependency);
    2.  
    to the end of the system that schedules IJobEventReader but the jobs keep producing errors saying that Components (which are removed at the end of the frame by EndECBS) do not exist.

    Edit: also, is it possible that events produced in one frame won't be processed until the next one? If so, how can I enforce this besides

    Code (CSharp):
    1. [UpdateBefore(typeof(ConsumerSystem))]
    Edit2: It may be caused by my using non-deterministic writing. Sometimes within one IJobEntityBatch chunkBatch the job writes to the EventSystem more than once, with the assumption that those writes will be processed in order. Could this be an issue?
     
    Last edited: Mar 5, 2022
  34. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    There's nothing stopping the system running at the same time as an ECBS except the regular safety system. It should be as safe as any other job and can totally run after an ECBS if there's nothing stopping it.

    Totally. If you don't order your systems producer, consumer then the consumer won't read it till the next frame. This is very much part of it's design. Events should never be lost and play back as soon as possible, even across different tick rates (note this isn't the same in v2 though)
     
    Abbrew likes this.
  35. Abbrew

    Abbrew

    Joined:
    Jan 1, 2018
    Posts:
    417
    I tried following the guidelines from Gitlab, but I'm still getting issues that imply the event consumers aren't consuming all events within one frame.

    Code (CSharp):
    1. [UpdateBefore(typeof(BehaviorUpdateSystem))]
    2.     public abstract class BehaviorSystem : SimulationSystem
    3.     {
    4.         private EventSystem queue;
    5.  
    6.         protected sealed override void OnUpdate()
    7.         {
    8.             PreProcess(queue, out var progressWriter, out var completeWriter);
    9.             Process(progressWriter, completeWriter);
    10.             PostProcess(Dependency, EndECBS, queue);
    11.         }
    12.  
    13.         public static void PreProcess(EventSystem queue, out NativeEventStream.ThreadWriter progressWriter, out NativeEventStream.ThreadWriter completeWriter)
    14.         {
    15.             progressWriter = queue.CreateEventWriter<BehaviorProgressMessage>();
    16.             completeWriter = queue.CreateEventWriter<BehaviorCompleteMessage>();
    17.         }
    18.  
    19.         protected abstract void Process(NativeEventStream.ThreadWriter progressWriter, NativeEventStream.ThreadWriter completeWriter);
    20.  
    21.         public static void PostProcess(JobHandle inputDeps,EntityCommandBufferSystem ecbs, EventSystem queue)
    22.         {
    23.             queue.AddJobHandleForProducer<BehaviorProgressMessage>(inputDeps);
    24.             queue.AddJobHandleForProducer<BehaviorCompleteMessage>(inputDeps);
    25.         }
    26.  
    27.         protected sealed override void Create()
    28.         {
    29.             queue = World.GetOrCreateSystem<EventSystem>();
    30.             BehaviorCreate();
    31.         }
    32.  
    33.         protected virtual void BehaviorCreate()
    34.         {
    35.  
    36.         }
    37.     }
    38.  
    39.  
    This is the class that all event producers inherit from. They write messages to Behavior Complete/Progress. I'm wondering if the AddJobHandleForProducer call is enough to make sure that all inheriting producers go before the consumer, which is the next code block

    Code (CSharp):
    1.  
    2.     [UpdateBefore(typeof(EndFixedStepSimulationEntityCommandBufferSystem))]
    3.     public sealed class BehaviorUpdateSystem : SimulationSystem
    4.     {
    5.         private EventSystem eventSystem;
    6.  
    7.         protected override void OnUpdate()
    8.         {
    9.             Dependency = new BehaviorProgressJob
    10.             {
    11.                 behaviorProgressFromEntity = GetComponentDataFromEntity<BehaviorProgressComponent>()
    12.             }.Schedule<BehaviorProgressJob, BehaviorProgressMessage>(eventSystem, Dependency);
    13.  
    14.             Dependency = new BehaviorCompleteJob
    15.             {
    16.                 ecb = EndECBS.CreateCommandBuffer(),
    17.                 behaviorResultFromEntity = GetComponentDataFromEntity<BehaviorResultComponent>()
    18.             }.Schedule<BehaviorCompleteJob, BehaviorCompleteMessage>(eventSystem, Dependency);
    19.  
    20.             EndECBS.AddJobHandleForProducer(Dependency);
    21.         }
    22.  
    23.         protected override void Create()
    24.         {
    25.             base.Create();
    26.  
    27.             eventSystem = World.GetOrCreateSystem<EventSystem>();
    28.         }
    29.  
    30.         [BurstCompile]
    31.         private struct BehaviorProgressJob : IJobEventReader<BehaviorProgressMessage>
    32.         {
    33.             public ComponentDataFromEntity<BehaviorProgressComponent> behaviorProgressFromEntity;
    34.  
    35.             public void Execute(NativeEventStream.Reader stream, int readerIndex)
    36.             {
    37.                 for (var foreachIndex = 0; foreachIndex < stream.ForEachCount; foreachIndex++)
    38.                 {
    39.                     var count = stream.BeginForEachIndex(foreachIndex);
    40.                     for (var i = 0; i < count; i++)
    41.                     {
    42.                         var message = stream.Read<BehaviorProgressMessage>();
    43.                         if (!behaviorProgressFromEntity.HasComponent(message.behavior))
    44.                         {
    45.                             Debug.Log(message.behavior +  " doesn't have Progress");
    46.                         }
    47.                         behaviorProgressFromEntity[message.behavior] = new BehaviorProgressComponent
    48.                         {
    49.                             state = message.newState
    50.                         };
    51.                     }
    52.                     stream.EndForEachIndex();
    53.                 }
    54.             }
    55.         }
    56.  
    57.         [BurstCompile]
    58.         private struct BehaviorCompleteJob : IJobEventReader<BehaviorCompleteMessage>
    59.         {
    60.             public EntityCommandBuffer ecb;
    61.  
    62.             public ComponentDataFromEntity<BehaviorResultComponent> behaviorResultFromEntity;
    63.  
    64.             public void Execute(NativeEventStream.Reader stream, int readerIndex)
    65.             {
    66.                 for (var foreachIndex = 0; foreachIndex < stream.ForEachCount; foreachIndex++)
    67.                 {
    68.                     var count = stream.BeginForEachIndex(foreachIndex);
    69.                     for (var i = 0; i < count; i++)
    70.                     {
    71.                         var message = stream.Read<BehaviorCompleteMessage>();
    72.                         var behavior = message.behavior;
    73.                         behaviorResultFromEntity[behavior] = new BehaviorResultComponent
    74.                         {
    75.                             success = message.success,
    76.                             complete = true
    77.                         };
    78.  
    79.                         ecb.RemoveComponent<BehaviorProgressComponent>(behavior);
    80.                     }
    81.                     stream.EndForEachIndex();
    82.                 }
    83.             }
    84.         }
    85.     }
    86.  
    87.  
    88.  
    I tried making the consumer system go before the ECBS as well as supplying its jobhandle, but I'm still getting the Debug.Log error messages

    Code (CSharp):
    1.  
    2.  
    3.     public struct BehaviorProgressMessage : IMessage
    4.     {
    5.         public Entity behavior;
    6.         public int newState;
    7.     }
    8.  
    9.     public struct BehaviorCompleteMessage : IMessage
    10.     {
    11.         public Entity behavior;
    12.         public bool success;
    13.     }
    14.  
    15.     public static class BehaviorSystemActions
    16.     {
    17.         [MethodImpl(MethodImplOptions.AggressiveInlining)]
    18.         public static void Progress(NativeEventStream.ThreadWriter writer, Entity behavior, int newState)
    19.         {
    20.             writer.Write(new BehaviorProgressMessage
    21.             {
    22.                 behavior = behavior,
    23.                 newState = newState
    24.             });
    25.         }
    26.  
    27.         [MethodImpl(MethodImplOptions.AggressiveInlining)]
    28.         public static void Complete(NativeEventStream.ThreadWriter writer, Entity behavior, bool success)
    29.         {
    30.             writer.Write(new BehaviorCompleteMessage
    31.             {
    32.                 behavior = behavior,
    33.                 success = success
    34.             });
    35.         }
    36.  
    37.         [MethodImpl(MethodImplOptions.AggressiveInlining)]
    38.         public static void Succeed(NativeEventStream.ThreadWriter writer, Entity behavior)
    39.         {
    40.             writer.Write(new BehaviorCompleteMessage
    41.             {
    42.                 behavior = behavior,
    43.                 success = true
    44.             });
    45.         }
    46.  
    47.         [MethodImpl(MethodImplOptions.AggressiveInlining)]
    48.         public static void Fail(NativeEventStream.ThreadWriter writer, Entity behavior)
    49.         {
    50.             writer.Write(new BehaviorCompleteMessage
    51.             {
    52.                 behavior = behavior,
    53.                 success = false
    54.             });
    55.         }
    56.     }
    Inside any event producing job, it can call Progress and Complete/Succeed/Fail. I'm not sure if the event ordering is correct - if you have time, I'd appreciate it if you could deduce if the problem is from my ordering, or possibly from misordered Progress and Complete/Succeed/Fail calls
     
  36. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    I see you're using this - EndFixedStepSimulationEntityCommandBufferSystem

    Is this code all updating in FixedUpdate?

    If so you'll want to implement your own EventSystem inheriting from EventSystemBase that updates in your FixedUpdateSystem and using this system instead of the one that exists in simulation system group.
     
    Abbrew likes this.
  37. Abbrew

    Abbrew

    Joined:
    Jan 1, 2018
    Posts:
    417
    @tertle Thanks a ton, the fact that all my producers were updating in FixedStepSimulationSystemGroup and the default EventSystem was not - that was the issue. Again, thank you for the superb support
     
  38. Abbrew

    Abbrew

    Joined:
    Jan 1, 2018
    Posts:
    417
    I'm running into similar warnings like so

    Code (CSharp):
    1. Internal: deleting an allocation that is older than its permitted lifetime of 4 frames (age = 5)
    2. UnityEngine.StackTraceUtility:ExtractStackTrace ()
    3. BovineLabs.Event.Containers.UnsafeEventStream:Deallocate () (at Library/PackageCache/com.bovinelabs.event@1.2.2/BovineLabs.Event/Containers/UnsafeEventStream.cs:205)
    4. BovineLabs.Event.Containers.UnsafeEventStream/DisposeJob:Execute () (at Library/PackageCache/com.bovinelabs.event@1.2.2/BovineLabs.Event/Containers/UnsafeEventStream.cs:224)
    5. Unity.Jobs.IJobExtensions/JobStruct`1<BovineLabs.Event.Containers.UnsafeEventStream/DisposeJob>:Execute (BovineLabs.Event.Containers.UnsafeEventStream/DisposeJob&,intptr,intptr,Unity.Jobs.LowLevel.Unsafe.JobRanges&,int) (at /Users/bokken/buildslave/unity/build/Runtime/Jobs/Managed/IJob.cs:23)
    and my Scene also happens to have an EventSystem (from Unity's Input System). What equivalent to your UpdateEvents should I try dragging into FirstSelected?

    Edit: Turning Leak Detection to Full Stack Traces gets rid of these issues. I suspect this is a framerate-related problem. I do use an inherited EventSystemBase that updates in FixedStep - could this be the issue?
     
  39. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    If you're in fixed step you need to use Persistent Allocator as the default TempJob allocator might not be cleaned up within the 4 frames due to the missmatch in updates from fixed to regular.

    On your EventSystem toggle UsePersistentAllocator to true
     
    Abbrew likes this.
  40. Abbrew

    Abbrew

    Joined:
    Jan 1, 2018
    Posts:
    417
    I'm using the 2.1.0 branch which has already been updated to be compatible with Entities 0.50, but the events aren't firing. I've got a Monobehavior here

    Code (CSharp):
    1. public sealed class MessageSender : MonoBehaviour
    2.     {
    3.         private FixedStepEventSystem queue;
    4.  
    5.         private static MessageSender instance;
    6.  
    7.         public static void Send<T>(T message)
    8.             where T : unmanaged, IMessage
    9.         {
    10.             var messageProducer = instance.queue.RegisterProducer<T>();
    11.             messageProducer.CreateWriter(default, out var messageQueue);
    12.  
    13.             messageQueue.Write(message);
    14.  
    15.             messageProducer.AddJobHandle(default);
    16.         }
    17.  
    18.         private void Start()
    19.         {
    20.             instance = this;
    21.             instance.queue = World.DefaultGameObjectInjectionWorld.GetOrCreateSystem<FixedStepEventSystem>();
    22.         }
    23.     }
    where Send is called from the FixedUpdate loop. The consumer is in ECS world, and looks like this

    Code (CSharp):
    1.  /*
    2.      * Only use these for sporadic events. These aren't very performant
    3.      */
    4.     [AlwaysUpdateSystem]
    5.     public abstract class MessageReceiveSystem<T> : SimulationSystem
    6.         where T : unmanaged, IMessage
    7.     {
    8.         private FixedStepEventSystem queue;
    9.  
    10.         protected sealed override void OnUpdate()
    11.         {
    12.             Dependency = PreProcess(Dependency, queue, out var hasMessages, out var messages);
    13.             if (hasMessages)
    14.             {
    15.                 Process(messages.AsDeferredJobArray());
    16.             }
    17.             Dependency = PostProcess(Dependency, hasMessages, messages);
    18.         }
    19.  
    20.         public static JobHandle PreProcess(JobHandle inputDeps, FixedStepEventSystem queue, out bool hasMessages, out NativeList<T> messages)
    21.         {
    22.             var messageConsumer = queue.RegisterConsumer<T>();
    23.  
    24.             hasMessages = true;
    25.             inputDeps = messageConsumer.ToNativeList(inputDeps, out messages, Allocator.TempJob);
    26.  
    27.             return inputDeps;
    28.         }
    29.  
    30.         protected abstract void Process(NativeArray<T> messages);
    31.  
    32.         public static JobHandle PostProcess(JobHandle inputDeps, bool hasMessages, NativeList<T> messages)
    33.         {
    34.             if (hasMessages)
    35.             {
    36.                 messages.Dispose(inputDeps);
    37.             }
    38.  
    39.             return inputDeps;
    40.         }
    41.  
    42.         protected sealed override void Create()
    43.         {
    44.             queue = World.GetOrCreateSystem<FixedStepEventSystem>();
    45.             MessageCreate();
    46.         }
    47.  
    48.         protected virtual void MessageCreate()
    49.         {
    50.  
    51.         }
    52.     }
    This system also runs in the FixedUpdate loop, but the messageConsumer.ToNativeList call is not yielding any events. This code used to work with the older 1.2.0 Event System API. What am I doing wrong here?
     
  41. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    the 2.X branch really does not support anything outside of systems and hasn't been tested for this. it's actually quite a stripped back implementation focusing on support for ECS in particular ISystem and performance.

    RegisterProducer should only be called once and in the OnStart of a system and should be re-used every frame.

    I'll be releasing a 1.2.3 version later today when I'm off work that fixes the compile issues with 0.50
     
    Last edited: Mar 18, 2022
    NotaNaN, JesOb, Opeth001 and 2 others like this.
  42. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    Sorry about delay. Finished work friday night and just needed to get off the computer.

    I've pushed 1.2.3 to gitlab and github and should appear soon on openupm
     
    Last edited: Mar 19, 2022
  43. Sylmerria

    Sylmerria

    Joined:
    Jul 2, 2012
    Posts:
    369
    Hi @tertle !
    Can you update 1.X version with Entities 0.51 ?

    Can you also tell my if you have a equivalent for 2.X of "com.bovinelabs.event" ?
     
  44. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    Is it broken in 0.51? We are still using 0.50 at work so I haven't got around to checking. I thought it was pretty minor so I expected it to still work. Though it will give burst warnings about a couple of extensions in burst 1.7 (just from a generic job I overlooked.)

    2.1 is only available on a branch at this stage and updated to 0.51, mostly because I'm too busy to redo documentation at the moment : https://gitlab.com/tertle/com.bovinelabs.event/-/tree/2.1.0

    Personally I use 2.X in my own projects but 1.X is still used at work so will be maintained.
     
    Sylmerria likes this.
  45. Sylmerria

    Sylmerria

    Joined:
    Jul 2, 2012
    Posts:
    369
    Yep because some collections has been renamed : https://docs.unity3d.com/Packages/com.unity.collections@1.3/changelog/CHANGELOG.html
     
  46. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    lclemens and Sylmerria like this.
  47. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    lclemens and Sylmerria like this.
  48. Sylmerria

    Sylmerria

    Joined:
    Jul 2, 2012
    Posts:
    369
    Hey @tertle

    I found a little bug in the last V1 with the safety cunsomer check.

    I can raise a nullref during the first running frame :

    Code (CSharp):
    1. NullReferenceException: Object reference not set to an instance of an object
    2. BovineLabs.Event.Systems.EventContainer.GetStack (System.Diagnostics.StackFrame frame) (at Library/PackageCache/com.bovinelabs.event@1.2.4/BovineLabs.Event/Systems/EventContainer.cs:260)
    3. BovineLabs.Event.Systems.EventContainer.AddJobHandleForConsumer (Unity.Jobs.JobHandle handle) (at Library/PackageCache/com.bovinelabs.event@1.2.4/BovineLabs.Event/Systems/EventContainer.cs:169)
    4. BovineLabs.Event.Systems.EventSystemBase.AddJobHandleForConsumer[T] (Unity.Jobs.JobHandle handle) (at Library/PackageCache/com.bovinelabs.event@1.2.4/BovineLabs.Event/Systems/EventSystemBase.cs:125)
    5. HMH.ECS.Building.ArmySlotSystem.OnUpdate () (at Assets/Scripts/ECS/ArmySlotSystem/ArmySlotSystem.cs:40)
    6. Unity.Entities.SystemBase.Update () (at Library/PackageCache/com.unity.entities@0.51.0-preview.32/Unity.Entities/SystemBase.cs:409)
    7. Unity.Entities.ComponentSystemGroup.UpdateAllSystems () (at Library/PackageCache/com.unity.entities@0.51.0-preview.32/Unity.Entities/ComponentSystemGroup.cs:583)
    8. UnityEngine.Debug:LogException(Exception)
    9. Unity.Debug:LogException(Exception) (at Library/PackageCache/com.unity.entities@0.51.0-preview.32/Unity.Entities/Stubs/Unity/Debug.cs:19)
    10. Unity.Entities.ComponentSystemGroup:UpdateAllSystems() (at Library/PackageCache/com.unity.entities@0.51.0-preview.32/Unity.Entities/ComponentSystemGroup.cs:588)
    11. Unity.Entities.ComponentSystemGroup:OnUpdate() (at Library/PackageCache/com.unity.entities@0.51.0-preview.32/Unity.Entities/ComponentSystemGroup.cs:523)
    12. Unity.Entities.ComponentSystem:Update() (at Library/PackageCache/com.unity.entities@0.51.0-preview.32/Unity.Entities/ComponentSystem.cs:114)
    13. Unity.Entities.DummyDelegateWrapper:TriggerUpdate() (at Library/PackageCache/com.unity.entities@0.51.0-preview.32/Unity.Entities/ScriptBehaviourUpdateOrder.cs:426)
    Code (CSharp title=ArmySlotSystemLine40):
    1.           _eventSystem.AddJobHandleForConsumer<ArmySlotActionSetActivationState>(Dependency);
    2.  
     
  49. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    I have updated both 1.X and 2.X to 1.0 (minimally tested atm while I work on updating other libraries.).

    Master has been replaced with the 2.1 and is now the actively developed branch.
    1.X will likely no longer be maintained however you can get a entities 1.0 update on the 1.2.5 branch to tide you over during your upgrade : https://gitlab.com/tertle/com.bovinelabs.event/-/tree/1.2.5

    I will likely update the API for 2.X in the coming weeks to utilize the new features and make it work more like command buffers do in ISystem.

    OpenUPM will get an update when I'm more confident in the update.
     
    Egad_McDad, Abbrew, NotaNaN and 7 others like this.
  50. sinketita

    sinketita

    Joined:
    Jun 30, 2018
    Posts:
    13
    Hey man, keep your good work, gonna try to use this in my project!
    Tired of these DynamicBuffer consumer\producer boilerplate hellhole.