Question

I'm measuring time interval for looped animations/particles/etc in my Android app. App is a live wallpaper so it doesn't prevent system from scaling clock down for power saving. Because of this all methods to measure time intervals between last frames doesn't measure monotonic time - I experience animations inconsistently slowing down and speeding up.

I've used all possible methods to retrieve system time - all of them are not monotonic (even SystemClock.elapsedRealtime() and System.nanoTime() which are guaranteed to be monotonic but nope they are not).

If I hold a finger on screen to prevent power saving all animations are smooth. This issue is very noticeable on Nexus10, slightly noticeable on Nexus7 1st gen and on Nexus4 it is not present at all.

Here is code excerpt to measure time:

public void onDrawFrame(GL10 glUnused) {
    // calculate timers for animations based on difference between lastFrameTime and SystemClock.elapsedRealtime()
    ...
    // save last timestamp for this frame
    lastFrameTime = SystemClock.elapsedRealtime();
}
Was it helpful?

Solution

Expanding a little on what I think you're doing based on the outline of your code, your onDrawFrame() method in pseudo code looks like this:

deltaTime = SystemClock.elapsedRealtime() - lastFrameTrime
// point 1, see text below
update animation based on deltaTime
draw frame
// point 2, see text below
lastFrameTime = SystemClock.elapsedRealtime()

The problem with this sequence is that you lose the time between point 1 and point 2 from your total animation time. You need to make sure that the total sum of all your deltaTime values, which are the times applied to your animation, covers the entire wall clock time of the amimation. With the logic you use, this is not the case. You only add up the times between the end of one call to the start of the next call. You do not account for the time needed to execute the method, which can be significant.

This can be fixed with a slight change in the sequence:

currentTime = SystemClock.elapsedRealtime()
deltaTime = currentTime - lastFrameTime
lastFrameTime = currentTime
update animation based on deltaTime
draw frame

The key point is that you only call elapsedRealtime() once per frame.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top