Several seconds delay when calling Interval.finish() due to privFinalize()

In my application I calculate pool ball trajectories, and then animation them with a composition of Sequences an Parallels. At the highest level is the Parallel self.shot_animation that when looped, creates the total ball trajectory animation:

Nov-30-2021 15-05-48

self.shot_animation is looped until the user would like to make their next shot. At this point, self.shot_animation.finish() is called.

finish() used to execute in a fraction of a second, but now it takes several seconds. I have tracked the cause of this delay to occur somewhere within this seemingly innocuous change to my codebase (click).

In this commit I changed how I calculate the quaternions that are passed the LerpQuatIntervals that compose each ball’s animation sequence. Importantly, I did not change how the ball animations are created at all–I just changed the precomputed quaternion values passed.

Yet this change is somehow enough to drastically change the nature in which finish() operates.

I did some time profiling before and after the change, which revealed that the culprit is Interval.privFinalize().

Here is how it used to be:

privFinalize() runs for 150,859 microseconds.

Here is how it is now:

privFinalize() runs for 8,308,396 microseconds. To be clear, the mentioned commit is the sole cause of this newfound delay.


Unfortunately, I am unable to reproduce this in a minimum reproducible example. Does anyone understand what is going on?

In pseudo-code, this is how each ball’s animation sequence is defined, regardless of whether we are talking about before or after the change:

# playback_dts, xyzs, and quats have all been precomputed

ball_sequence = Sequence()
rotation_sequence = Sequence()

for i in range(len(playback_dts)):
    x, y, z = xyzs[i+1]
    Qm, Qx, Qy, Qz = self.quats[i+1]

    ball_sequence.append(LerpPosInterval(
        nodePath = self.nodes['pos'],
        duration = playback_dts[i],
        pos = (x, y, z),
    ))

    rotation_sequence.append(LerpQuatInterval(
        nodePath = self.nodes['ball'],
        duration = playback_dts[i],
        quat = (Qm, Qx, Qy, Qz),
    ))

self.playback_sequence = Parallel()
self.playback_sequence.append(ball_sequence)
self.playback_sequence.append(rotation_sequence)

Here is the real code before the commit, and here it is after. The only difference is how the passed quaternions are calculated.

Thanks for reading. Cheers.


EDIT I found a workaround, which has been to avoid finish altogether. Instead, I am calling shot_animation.clearToInitial() and then setting the Pos and Quat states of each ball to their final states ‘manually’. This solution is probably thousands of times faster than waiting for finish to finish.

2 Likes