Search Unity

Evaluating multiple Timelines simultaneously

Discussion in 'Timeline' started by james7132, Oct 27, 2019.

  1. james7132

    james7132

    Joined:
    Mar 6, 2015
    Posts:
    166
    I'm currently working on a game with rollback netcode where part of the game simulation requires evaluating a Timeline per active player at each timestep. This is done by manually setting the PlayableDirector's time property and calling Evaluate for every player. Upon rolling back, the game may need to resimulate multiple game steps. With 4 active players and a 12+ game steps to step forward, we're looking at 48+ separate timeline evaluations in a rollback, each evaluation is taking ~1-2 ms, so a full rollback may cause notable visual lag for the player, running up to potentially 96ms or more.

    In the profiller, I noticed that Evaluate ultimately just starts and waits for a evaluation job to run on a Job thread. Is there a way to schedule the evaluation and get the JobHandle so I can run all 2-4 evaluations simultaneously? The underlying Timelines are doing nothing more than composing multiple Animation clips into a single action (i.e. character bone movement on one track and hitbox statuses in another).

    Side question: Is there any downside to this approach? Would Timeline signals still be executed when manually evaluating? If it doesn't, is there an alternative to controlling time flow?
     
  2. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    1,516
    Yes, using Evaluate() is the least performant way to execute a Timeline, exactly for the reason you described - it is a main thread call that waits for the entire timeline to be evaluated.

    A timeline can be 'played' or 'evaluated'. When a timeline is 'playing', the time is automatically updated based on the clock of the playable director, and the evaluation knows that time is advancing. Because time is continuous in this mode, Audio will play, and signals will trigger.

    Playing a timeline (well, a playable graph) is generally done in two passes that occur between Update() and LateUpdate(). The first pass is PrepareFrame, where all timelines have their time updated and unfortunately this portion is single threaded. The work here is however intended to be lighter weight, updating time, weights and connections of the graph.

    The next pass is ProcessFrame. While scripted tracks still run single threaded, the advantage comes from this being where all Animation Evaluation happens, which tends to be the most expensive part of the timeline and is multi-threaded.

    When calling Evaluate(), both passes happen, and only for that timeline and all on the main thread. Because there is no guarantee that the time is advancing continuously, signals and audio are not triggered. This is also true when the PlayableDirector clock is set to manual.

    Two things you can do with a playing timeline is 'rewind' it by changing the playableDirector.time value, and set it's speed using playableDirector.playableGraph.GetRootPlayable(0).SetSpeed(s). You can use those on a playing timeline to manipulate the flow of time as you see fit, and keep the benefits of avoiding manual Evaluates().
     
    DTAli likes this.
  3. james7132

    james7132

    Joined:
    Mar 6, 2015
    Posts:
    166
    Is there no other way to manually control the time flow? My game's architecture demands there be accurate state rollback for networking purposes, so I need to be able to accurately set the time upon each timestep, as well as forward simulate it independently from the normal Unity game loop. That last part is the clincher: the aforementioned APIs instantaneously rewind the time, but do not allow for the playable to be advanced without being updated from the main engine loop. This is not a hard blocker right now: Evaluate works, just not efficiently, and precludes us from using some features (like Signals).

    Is it possible to set up a separate API that gives more manual control over those two passes? Say: manually call PrepareFrame for each director I want, queuing them all up, then run ProcessFrame concurrently: completely independent from the Update loop? Or better yet, just provide the JobHandles for each of them so we can schedule our own jobs that may depend on them.

    Alternatively, is there another way to mix multiple animation streams? I'm mainly using Timeline/Playables as a way to overlay hitbox changes authored in Unity with skinned character animation from an external source.
     
    Last edited: Oct 28, 2019
  4. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    1,516
    No, there isn't. Playing means the main engine loop will update the timeline with other animations. Evaluation means you control when, but pay the cost of evaluating outside of playback.

    AnimationPlayables (which state machine and timeline both use) are the way to do that.
     
  5. Kleptine

    Kleptine

    Joined:
    Dec 23, 2013
    Posts:
    282
    What I'd really like a way to have a custom PlayableDirector. A mode where the playing is custom, but is guaranteed to be advancing so that everything works like normal. This would allow me to implement my FixedUpdate mode that I wanted in this thread:
    https://forum.unity.com/threads/tim...-sent-in-playabledirector-manual-mode.711494/

    I'm circling back to this now, because while using Evaluate in my FixedUpdate works, it's massively slow and losing out on AudioClips is really a bummer. Maybe you could point us to the place in the source where we could add in a different mode alongside GameTime and DSPClock?