What is the difference between UMovieSceneEntitySystem::OnSchedulePersistentTasks and UMovieSceneEntitySystem::OnRun?

When does one run vs the other and how should their override implementations differ?

I see Epic-implemented UMovieSceneEntitySystem subclasses override both functions and perform basically the same work in both function, but in slightly different ways. Neither function has a comment in the base class indicating when it runs and how overrides should implement their functionality. In practice, I have found that overriding only OnSchedulePersistentTasks seems to work for my use cases, so I’m confused on why I should add equivalent logic in OnRun. Can anyone clear this up for me?

Hi! We apologize for the lack of comments and clarity on this… the difference is basically about the task scheduling system being used under the hood.

Originally, the Sequencer ECS implementation ran its ECS systems using UE’s basic Task Graph APIs. This would call the OnRun() method on the Sequencer ECS systems, and they would queue up tasks every frame to do their job. However, setting up these tasks, and their dependencies, every frame was showing up on our performance profiling, so we tried to eliminate this cost.

The idea was that about 80% or 90% or the time, we always run the same tasks with the same dependencies. These tasks change only when something changes in the playing sequence… mainly: when we enter or exit a section, bind/unbind an object, start/finish a sub-sequence, etc. So we now have “persistent tasks”, along with a custom task scheduler for it. It’s enabled by default with the CVar “Sequencer.CustomTaskScheduling”, and when enabled, the Sequencer ECS systems get asked for their tasks and dependencies in OnSchedulePersistentTasks. We only ask for it once in a while (again, only when something changes). The rest of the time, we just run those “persistent” tasks in the same way every frame.

Of course, a custom task scheduler is a rather sensitive matter, hence the CVar that would let us turn it off. When off, it goes back to calling “OnRun” and using the Task Graph API. We tried to make both APIs compatible in order to only have one method that transparently queues up tasks in one or the other scheduling system, but it didn’t turn out to be possible, at least not in the time frame we originally had… so for now we have these two methods who do, indeed, very similar-but-not-quite things.

Does that make sense?

Indeed, as long as you don’t turn off Sequencer’s custom task scheduling system, you only need to implement OnSchedulePersistentTasks for evaluation-phase systems. Cheers!

Thanks for the detailed explanation! That makes sense as to why it seemed like I only needed to implement OnSchedulePersistentTasks for our purposes.