AddInputDependency() issues if used on the first frame?

I am getting this error spammed constantly during play mode:

And here is the OnUpdate() of the CharacterMovementSystem:

protected override void OnUpdate()
        {
            Dependency = new CharacterMovementJob
            {
                DeltaTime = Time.DeltaTime,
                PhysicsWorld = BuildPhysicsWorldSystem.PhysicsWorld,
                (....... more stuff),
            }.ScheduleParallel(CharacterQuery, Dependency);

            // Make BuildPhysicsWorld wait for this job
            BuildPhysicsWorldSystem.AddInputDependency(Dependency);
        }

I would’ve expected this setup work without any problems due to the “BuildPhysicsWorldSystem.AddInputDependency” I do at the end, but looks like that doesn’t do it.

What’s weird is that if, during Play mode, I disable my CharacterMovementSystem in the Entity Debugger and re-enable it, the errors stop and everything starts working again. Almost as if this was a first-frame error that caused errors to repeat every frame. EDIT: I did a test and I can confirm the error doesn’t pop up if I skip scheduling the job on the first few frames

Using:

  • Unity 2020.2a19
  • Unity Physics 0.4.1-preview
  • Entities 0.13.0-preview.24
  • Jobs 0.4.0-preview.18
1 Like

You need to combine with the physics dependency and set it back. You are telling physics about your dependency but you haven’t told your own system about physics. So it’s going to work either sporadically or not at all just depending on how you have everything ordered.

Also if your stuff runs after build you need to set back against end frame.

A reliable setup we have used for a long time is this:

[UpdateInGroup(typeof(SimulationSystemGroup))]
    [UpdateAfter(typeof(Unity.Physics.Systems.BuildPhysicsWorld))]
    [UpdateAfter(typeof(Unity.Physics.Systems.ExportPhysicsWorld))]
    [UpdateBefore(typeof(Unity.Physics.Systems.EndFramePhysicsSystem))]
    [UpdateBefore(typeof(TransformSystemGroup))]
    public class GameLogicGroup : ComponentSystemGroup
    {
    }

And we have a system with these methods where before any jobs that use physics we combine and after we schedule we set back.

public JobHandle Combine(JobHandle other)
        {
            return JobHandle.CombineDependencies(BuildPhysicsWorld.GetOutputDependency(), other);
        }

        public void Set(JobHandle other)
        {
            EndFramePhysicsSystem.AddInputDependency(other);
        }
2 Likes

Just to add to the previous reply, adding the input dependency to BuildPhysicsWorld makes sense if your system is scheduled before BuildPhysicsWorld, if it is after you need to add dependency to EndFramePhysicsSystem, that will implicitly make the next BuildPhysicsWorld wait for it.

Here’s what my whole system looks like now (simplified)

[UpdateBefore(typeof(BuildPhysicsWorld))]
public class CharacterMovementSystem : SystemBase
{
    public EndFramePhysicsSystem EndFramePhysicsSystem;
    public BuildPhysicsWorld BuildPhysicsWorldSystem;
    public EntityQuery CharacterQuery;

    protected override void OnCreate()
    {
        EndFramePhysicsSystem = World.GetOrCreateSystem<EndFramePhysicsSystem>();
        BuildPhysicsWorldSystem = World.GetOrCreateSystem<BuildPhysicsWorld>();
        CharacterQuery = GetEntityQuery(CharacterUtilities.GetCharacterEntityQueryDesc());
    }

    protected override void OnUpdate()
    {
        Dependency = JobHandle.CombineDependencies(EndFramePhysicsSystem.GetOutputDependency(), Dependency);

        Dependency = new CharacterMovementJob
        {
            DeltaTime = Time.DeltaTime,
            PhysicsWorld = BuildPhysicsWorldSystem.PhysicsWorld,
            // ......
        }.ScheduleParallel(CharacterQuery, Dependency);

        // Make BuildPhysicsWorld wait for this job
        BuildPhysicsWorldSystem.AddInputDependency(Dependency);
    }
}
  • updates before BuildPhysicsWorld
  • (not shown) the PhysicsWorld passed to the job is [ReadOnly]
  • Combines dependency with EndFramePhysics before scheduling
  • Adds input dependency to BuildPhysicsWorld after scheduling

But I’m still getting the error every frame, unless I skip scheduling the job on the first frame; then I never get the error and everything works

Maybe irrelevant to this problem, but what is the point of scheduling the job in the first frame before BuildPhysicsWorld, since PhysicsWorld is not valid at all at that point? Maybe if you guarded your job with something like if (PhysicsWorld.NumBodies > 0) Schedule(); ?

Here is a theory about what happens: you schedule your job that reads from m_Bodies array. Your job runs. BuildPhysicsWorld runs the OnUpdate() where it calls PhysicsWorld.Reset() which deallocates old and creates new m_Bodies array (first frame that there are bodies present). And it crashes, because it is deallocating array you are trying to read from.

I gave this a try, but that didn’t solve it.

I need this to run before physics because I have options where the character moves with PhysicsVelocity (as a kinematic or dynamic body, so it deals better with pushing other bodies), and so it would always be one frame late if I ran this after physics. Basically:

  • ColliderCast for collisions
  • determine final target position
  • set a PhysicsVelocity that would bring us to that position over next fixedUpdate
  • Build + Step physics

However I’m realizing now that this is also “one frame late” in a way, because of the PhysicsWorld not being up to date. I think what I’d really need for true frame perfection is:

  • Build+Step physics world
  • Character collisions job and set a PhysicsVelocity
  • Build+Step physics world a second time, and Export

That makes sense. I think I was assuming the BuildPhysicsWorld’s resetting of PhysicsWorld would be done in a job that takes the InputDependencies, but looking at the code I see that this is not the case.

…but now I don’t even understand why I don’t get the error with the first-frame-skip approach.

Try:

  • BuildPhysicsWorld
  • ColliderCast for collisions
  • determine final target position
  • modify MotionVelocity with PostSolveJacobians callback
  • StepPhysicsWorld

Example:

And to reply about the PhysicsWorld.Reset(). It’s not done via a job since it does reallocation of persistent memory. We tried a couple of times but didn’t come up with a better solution.

I decided to move our character controller to before BuildPhysicsWorld and it errors out every frame on reset. And there is no dependency you can set that fixes it. end frame has already been combined, build input dep isn’t completed so has no effect. You can track the dependencies involved yourself and complete them, probably the best solution for now. And I think generally you should have abstractions around the whole combine/set back flow anyways. So if your flow changes you don’t need to change dozens of systems. You are probably going to have a different set of dependencies you combine/set for your physics logic and the rest of your casting outside of the fixed step group also. Another reason to abstract out the combine/set stuff.