Performance of image animations especially in Linux


#1

Hello!

I tried to implement animated transitions between different screens in my application in the following way:

  • Take a snapshot of the component on the screen before the transition
  • Take a snapshot of the new component
  • Start a timer
  • While the timer is running, do the animation effect step by step in each timer callback e.g. by fading out the old image and moving it from the screen while fading in the new image and moving it onto the screen. After that trigger a repaint.
  • Stop the timer and display the new component, when the transition is complete.

That works in general, but the problem with that solution is the performance and here especially with Linux.
I use a Timer that fires every 30 milliseconds, what gives me a frame rate of about 33 per second and that is the minimum to get a smooth effect for the user. If now the drawing of the two images in between two timer callbacks lasts longer than 30 milliseconds, in Windows sometimes two timer callbacks happen without a repaint in between. With that the animation is not so smooth any more, but not really broken.

In Linux always a timer callback and then the repaint is done, but if the next timer callback comes immediatly after the repaint (because the repaint lasts that long), the screen is not redisplayed completely any more, which really breaks the animation. Is there anything that can be done in Linux so that the screen is fully redisplayed, even if the next timer callback comes immediately?

Or the other way round: the drawing of two images (1024*768) with alpha effects into a Graphics lasts about 20 milliseconds. When I try to add rescaling effects, you easily need more than 30 milliseconds and that breaks my animation. The “Graphics::drawImage” seems to be to slow to do such things!?! Is there a better way to that with Juce?

Thank you,
Andreas


#2

That sounds very slow - you’re compiling an optimised build, right? Also check what you’re actually drawing - are you still doing any heavy rendering of components underneath the animated one?

I’m confused about you saying it doesn’t fully draw though, even if it’s slow, there’s no case where it should fail to actually complete a draw operation. Any idea exactly what’s failing there?


#3

I am using an optimised build. When using a debug build, I get the problem much earlier.
The time measured is on Linux about 15 to 20 milliseconds for the following code sequence:

g.setOpacity(currentOpacity); g.drawImage(oldImg, rectOld.getX(), rectOld.getY(), rectOld.getWidth(), rectOld.getHeight(), 0, 0, oldImg->getWidth(), oldImg->getHeight()); g.setOpacity(currentOpacityB); g.drawImage(newImg, rectNew.getX(), rectNew.getY(), rectNew.getWidth(), rectNew.getHeight(), 0, 0, newImg->getWidth(), newImg->getHeight());
with varying opacity where the image size is 1024x768 and identical to the target area (no scaling). If you add scaling to the animation (changing the size of the target rectangle) you easily get execution times of about 200(!) milliseconds.

It does not fully draw, means that in some frames not the complete image is drawn but only the first 10% of it and the rest of the screen gets white, which really looks like a bug.
I did not have the time to dig into the code, but I assume that it has to do with the LinuxRepaintManager class, that uses itself a timer to do the repainting.

Thank you,
Andreas


#4

I digged a little bit deeper into the code and I found that disabling the JUCE_USE_XSHM option solves the repaint problem. So this seems to be a problem of XShm.
But since the performance is nevertheless not sufficient to do the image animations I want to do, I will try to implement that differently.


#5

I’ve had similar issues, and the simplest solution I’ve found is:
in timer callback:
if (repaintDone) { repaintDone = false; repaint(); }
in paint call:
// Perform your painting call
repaintDone = true;

That’s really simple. There is no repaint scheduled if the current painting isn’t finished.

In order to set your opacity value, you should take relative time from first timer callback / totalTime so even if you miss one or two painting, it degrades correctly, and still display the animation as the expected duration.


#6

Yes, I did that also. But it helps only for the simple cases. If you want to do something really fancy, you have to use e.g. an OpenGLComponent, and that’s what I did.

Andreas