Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.

Feedback [Feature Request] In between systems main thread Delegate

Discussion in '2020.2 Beta' started by Guedez, Jul 24, 2020.

  1. Guedez

    Guedez

    Joined:
    Jun 1, 2012
    Posts:
    825
    TLDR: a
    Application.onBeforeRender
    equivalent, but it would run in between every single system on the main thread, so it would run as many times as there are systems

    Right now, when I need something that can only be done in the main thread right after a job, I need to either do that in the next frame, or schedule the job as soon as possible (
    InitializationSystemGroup 
    is the earliest I think) and then make a system that runs in
    LateSimulationSystemGroup 
    just to call some main thread functions on the job results.

    Although probably not a good way to implement it, it could be implemented by scheduling main thread jobs being actually just a queue of lamdas that check if the
    JobHandle 
    has completed, if it did, execute the lambda and remove from queue, otherwise keep on the queue. This could run before the next system update runs. Sure is probably slow to keep checking the
    JobHandle 
    all the time, but it's better than not being able at all.
    I wouldn't even mind if this would be barred from scheduling jobs for the whole consistency thing.

    Edit: By "This could run before the next system update runs" I mean, before every system update runs, check the whole queue, currently the best I could do is make one system for each execution group and statically check the same queue there, this, would run before each system rather than before each new execution group starts
     
    Last edited: Jul 25, 2020
  2. brunocoimbra

    brunocoimbra

    Joined:
    Sep 2, 2015
    Posts:
    679
    You can schedule your main thread work after one of the existing sync points (like after EndSimulationCommandBufferSystem), this will ensure that all jobs have completed
     
  3. Guedez

    Guedez

    Joined:
    Jun 1, 2012
    Posts:
    825
    You mean creating a system and setting it to
    UpdateAfter(typeof(EndSimulationCommandBufferSystem))
    and check for the queue contents in it's
    Update
    ?
    It is hardly different from waiting for the next frame,thus each alternation of job work -> main thread -> job work takes at least a whole frame. As I said above, at most, one could place one of such systems at each of the
    SystemGroups
    , and considering that some of them will disproportionately take longer than others, the chances of the main thread code running right after the job ended will decrease by a whole lot.

    Actually, after thinking through this, I think all that the devs actually need to implement is a
    Application.onBeforeRender
    equivalent, but it would run in between every single system on the main thread, the rest I could implement myself
     
  4. brunocoimbra

    brunocoimbra

    Joined:
    Sep 2, 2015
    Posts:
    679
    Seems like you want to check for that queue of JobHandles between every single system update, this could be a performance killer, I really think you need to re-think your solution.

    Now, one suggestion I have for you is to have, for example, 3 systems: one would run right after the BeginSimulationCommandBufferSystem, another right before TransformSystemGroup and another right before EndSimulationCommandBufferSystem. This way, there would be 3 possible sync points for your main thread work, and you would be only including 1 new sync point (the one before TransformSystemGroup).

    Also, this would allow to you better control about which JobHandles needs to complete before running those system (thus not requiring to create a sync point at all), for example:
    Code (CSharp):
    1. public abstract class MainThreadSystemBase : SystemBase
    2. {
    3.     public event Action OnExecute;
    4.  
    5.     public void AddDependency(JobHandle jobHandle)
    6.     {
    7.         Dependency = JobHandle.CombineDependencies(Dependency, jobHandle);
    8.     }
    9.  
    10.     protected override void OnUpdate()
    11.     {
    12.         Dependency.Complete();
    13.         OnExecute?.Invoke();
    14.         OnExecute = null;
    15.     }
    16. }
    17.  
    18. [UpdateInGroup(SimulationSystemGroup, OrderFirst = true)]
    19. [UpdateAfter(BeginSimulationCommandBufferSystem)]
    20. public sealed class EarlyMainThreadSystem : SystemBase { }
    21.  
    22. [UpdateInGroup(TransformSystemGroup, OrderFirst = true)]
    23. public sealed class MainThreadSystem : SystemBase { }
    24.  
    25. [UpdateInGroup(SimulationSystemGroup, OrderLast = true)]
    26. [UpdateAfter(LateSimulationSystemGroup)]
    27. [UpdateBefore(EndSimulationCommandBufferSystem)]
    28. public sealed class LateMainThreadWorkloadSystem : SystemBase { }
    And one example of how to use it (untested, but should work after fixing some possible typos):
    Code (CSharp):
    1. public class ExampleSystem : SystemBase
    2. {
    3.     private MainThreadSystemBase _mainThreadSystem;
    4.  
    5.     protected override void OnCreate()
    6.     {
    7.         _mainThreadSystem = World.GetExistingSystem<MainThreadSystem>();
    8.     }
    9.  
    10.     protected override void OnUpdate()
    11.     {
    12.         JobHandle jobHandle = new SomeJob().Schedule();
    13.         _mainThreadSystem.AddDependency(jobHandle);
    14.         _mainThreadSystem.OnExecute += () => Debug.Log("Tadah!");
    15.     }
    16. }
     
    Guedez likes this.