[Feature Request] In between systems main thread Delegate

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

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

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

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:

public abstract class MainThreadSystemBase : SystemBase
{
    public event Action OnExecute;

    public void AddDependency(JobHandle jobHandle)
    {
        Dependency = JobHandle.CombineDependencies(Dependency, jobHandle);
    }

    protected override void OnUpdate()
    {
        Dependency.Complete();
        OnExecute?.Invoke();
        OnExecute = null;
    }
}

[UpdateInGroup(SimulationSystemGroup, OrderFirst = true)]
[UpdateAfter(BeginSimulationCommandBufferSystem)]
public sealed class EarlyMainThreadSystem : SystemBase { }

[UpdateInGroup(TransformSystemGroup, OrderFirst = true)]
public sealed class MainThreadSystem : SystemBase { }

[UpdateInGroup(SimulationSystemGroup, OrderLast = true)]
[UpdateAfter(LateSimulationSystemGroup)]
[UpdateBefore(EndSimulationCommandBufferSystem)]
public sealed class LateMainThreadWorkloadSystem : SystemBase { }

And one example of how to use it (untested, but should work after fixing some possible typos):

public class ExampleSystem : SystemBase
{
    private MainThreadSystemBase _mainThreadSystem;

    protected override void OnCreate()
    {
        _mainThreadSystem = World.GetExistingSystem<MainThreadSystem>();
    }

    protected override void OnUpdate()
    {
        JobHandle jobHandle = new SomeJob().Schedule();
        _mainThreadSystem.AddDependency(jobHandle);
        _mainThreadSystem.OnExecute += () => Debug.Log("Tadah!");
    }
}
1 Like