smooth animation and CPU load on the IPAD

Some notes on balancing the CPU load on the IPad to achieve both rapid/smooth animation and minims the load on the battery.
Newton’s playground (our current iOS app in development) uses the gravitational n-Body calculation these calculations bring their own special problems which make smooth animation difficult.The way we finally solved this so the animation works reasonably smoothly may be interesting in general. Not sure if this is obvious to people who do this kind of thing all the time - for these people I apologise.

For smooth animation in OpenGL, the updates are done every frame interval (1/60th second) and the calculations are done the idle period between frame intervals. It is important that a calculation step takes (quite a lot) less time than a frame interval, and if we can ensure this is just a few percent of the idle time, then the CPU use is kept low and the app doesn’t drain the battery so much. We also need to keep errors low in the calculations. Thus in effect we need to choose the step sizes for the equation solver and the integration time interval between frames so that the time taken is much lower than the frame interval.

At the start, we can estimate the step size from the forces on each body, and the integration algorithm sets individual step sizes for each body. However we don’t really know how long it will take to integrate over a time interval until we do a step and measure how long it takes. Hence in the first few frames we gradually increase the integration time interval until each frame calculation takes about 15% of the frame interval (i.e. ~1/600th of a second). For small numbers of bodies this means the animation goes too fast (and hence the movement is jerky) so we set a cap on the step size based on the fastest moving body, so that means the CPU use is much smaller than the 10% for a small number of bodies.

As the calculation goes forward in time the forces change and the amount of calculation time needed for a fixed time interval will change. HEnce we allow some freedom to the calculation time to drift up to 30% of the frame interval - or down to 5%. If it goes beyond these bounds then we have to chance the integration step between frames, as everything will go wrong if the calculation takes more than a frame interval. Hence the calculations may appear to slow down or speed up sometimes. We try to do this as little and as smoothly as possible but we have to do this to prevent problems and to stop the battery draining.

Here are some snippets (effectively in C) from the Xcode anyway.

Rapid changes to the calculation interval in the first few frames


if(calledCount<101) {

if(totalDifeqTime>upperBoundCPU*targetCPUfraction*outputInterval*calledCount) {
// set an upper limit ofthe frame interval for a solution step
// reduce the interval
double fac = targetCPUfraction*outputInterval*calledCount/totalDifeqTime;
fac = sqrt(fac);
if(fac < .5) fac = 0.5;
interval *= fac;
}
if(totalDifeqTime // set a lower limit of the frame interval for a solution step
// reduce the interval
double fac = targetCPUfraction*outputInterval*calledCount/totalDifeqTime;
fac = sqrt(fac);
if(fac > 2.) fac = 2.;
interval *= fac;
}
if(interval < minStep/speedFac) interval = minStep/speedFac;
if(interval > maxStep/speedFac) interval = maxStep/speedFac;
if(interval < stepSize) {
interval = stepSize; // no point in going for a smaller interval than the step size as it won't make any difference
}
}

after the first few frames use averages over a hundred frames to make slower changes and relax the constraints



else if(calledCount%100==0) {

upperBoundCPU = 2.*calledCount/(100.+calledCount); // gradually relax the upper bound point
if(totalDifeqTime>100.*upperBoundCPU*targetCPUfraction*outputInterval) {
// set an upper limit of 35% the frame interval for a solution step
// reduce the interval
double fac = 100.*targetCPUfraction*outputInterval/totalDifeqTime;
fac = sqrt(sqrt(fac));
if(fac<.5) fac = 0.5;
interval = .8*interval + .2*interval*fac; // make the interval changes smoothly
}
if(totalDifeqTime<100.*lowerBoundCPU*targetCPUfraction*outputInterval) {
// set a lower limit of 25% the frame interval for a solution step
// reduce the interval
double fac = 100.*targetCPUfraction*outputInterval/totalDifeqTime;
fac = sqrt(sqrt(fac));
if(fac > 2.) fac = 2.;
interval = .8*interval + .2*interval*fac; // make the interval changes smoothly
}
if(interval < minStep/speedFac) interval = minStep/speedFac;
if(interval > maxStep/speedFac) interval = maxStep/speedFac; // reset
}

On the other hand it does seem rather weird to write this stuff about doing calculations in a fraction of a frame interval on a hand-held IPad device. Many many years ago (~>30) I did calculations a little like this and they were overnight runs on big mainframes. Hmm..

DW