Advice for drawing VU meters


#1

I’m having a major issue with the CPU load caused by calling the repaint function in my meter objects. i have a timer loop set to about 25ms that polls for peak data from my hardware then calls my meter object passing the peak value where the meter object receives the value and repaints its self. I have about 8 meter objects on the screen and I’m getting between 8 and 15% CPU usage on a High end PC and about 45% on a Mac mini.

After going through and commenting out a lot of code i found that this over head is caused by just calling the repaint function itself even if there is no code in the paint function to execute.
I’ve experimented with only repainting a small portion that as actually changing with no significant increase in performance.

does anyone think it could be an issue to have my meter object nested in another composite component with other objects?

does anyone have an efficient way of doing fast responsive VU meters with juce??

Thanks


#2

Have you made sure that your meter components are opaque? If they’re not, then all their parent components will have to get repainted every time, which can create enormous overheads. Check out the setOpaque() method in Component.

Also, to speed them up in tracktion I keep an image buffered and just draw that each time rather than rendering the colour gradients.

And obviously only paint the smallest bit that’s changed - to help with debugging all this, try turning on the JUCE_ENABLE_REPAINT_DEBUGGING flag in juce_Config.h, which will draw little coloured rectangles around the bits that get drawn, so you can see what’s going on.


#3

ouch i just called the setOpaque(true); method inside my meter class and my CPU use on the PC shot up to about 60%

Also I’m using an Image with ImageCache then i set it as an ImageBrush and filling a rectangle that’s height is set by the metervalue
but even with that implementation commented out i still get the high CPU
how are you buffering your image?


#4

setOpaque can’t slow things down - it can only reduce the amount of repainting, not increase it… are you sure that there’s not some other stuff getting repainted that doesn’t need to be? Try turning on that debug paint flag and see what happens.

ImageBrush isn’t quite as fast as just drawing an image. And make sure you’re using 100% opacity to draw it. I think I just kept an image that was the same size as the component (regenerating it in the resized() callback). Imagecache will slow things down a little bit if you use it in the paint() callback as it has to find the image in its list.

You could also avoid drawing any background that would be drawn underneath the meter graphics - that’d eliminate a bit of wasted time.


#5

Having the same issue here. It really pisses me off. When I draw about 16 level meters I get CPU usage of 40% (!), without 1 single line of code in the paint() function!!! And my level meters are all using setOpaque(true). :frowning:


#6

Ah I just found out something: My mixer takes a whole monitor (monitor 2 of 2), and as long as the level meter positions do not cross the right border of the monitor, CPU stays 0% (remember my paint() function does nothing at the moment). But as soon as the level meters cross the right border the CPU jumps up to 30% (kernel times, so something is going wrong deep inside Windows). So somehow the problem seems to be there. I think I’ll just write some small test app without all the code around to find out what’s going on…


#7

Wow - that’s seriously lame! Which version of Windows are you using?


#8

My OS is XP SP2 32Bit.

It seems like resizing the window to a smaller size (70%) also eliminates the problem. The problem is definitely not due to slow software rendering. It seems to be something tricky.
Is there any good profiler to find out where the CPU is eaten ? I tried AMD CodeAnalyst but couldn’t draw any conclusion.
Just to make sure, I’m still using 1.46 - maybe there was some known bug in there that has been fixed by now?


#9

It seems like some file called intelppm.sys (power management?) creates this high load. It’s funny that it occurs depending on my MixerComponent’s size. And it’s also funny that it happens “inside” of my app. I mean, I’m not calling anything in intelppm.sys.


#10

Disregard my previous posts, I found the problem! In my case, it was Component::setBufferedToImage(true) that caused the problem! I used this function for all my MixerChannelComponent’s (each of these components is a GUI Mixer Channel, and there are a lot of!).
Jules, do you know why this caused a problem? I don’t really understand it, because the MixerChannel components are not redrawn, as the level meters are opaque.


#11

No idea why it’d cause a problem - maybe just by using a lot of memory and triggering some paging activity?


#12

Very unlikely, the memory used was only 10MB more with setBufferedToImage(true).


#13

Still can’t make any sense of this: I cover half of my Mixer with some other window -> CPU usage (of which 95% is Kernel Time) jumps to 2% (although half of the Mixer is repainted()!), then I hide the window, so the whole Mixer is shown -> CPU usage goes up to 50% (of which 48% kernel time). How can I find out what’s suddenly eating this kernel time?


#14

It sounds like some unfathomable weirdness inside Windows - probably the graphics kernel blocking for some bizarre reason depending on the amount of data it has to pass across the bus… e.g. if it detects that you’re trying to redraw a sufficiently large portion of the screen, maybe it waits until it can synchronise with the graphics refresh rate to prevent tearing…


#15

The problem is reproducable on my Desktop PC, that’s why I have the impression this is a JUCE problem. Other software with simliar level meters only uses 2% CPU, even when not OpenGL-based.

I found out some things, maybe you have a clue now:

  • just doing nothing ({return;}) in my LevelMeter::paint() does not change CPU consumption, it stays at 50%
  • just doing nothing ({return;}) in Win32ComponentPeer::handlePaintMessage() doesn’t change anything either
  • removing call to InvalidateRect() in Win32ComponentPeer::repaint (int x, int y, int w, int h) HOWEVER DOES make all difference: CPU immediately drops down to 0%

#16

From your debugger, what’s the size of the rect passed into InvalidateRect? Is it NULL? If so that’s your entire area being invalidated and might account for the cpu spike?


#17

No it’s not 0, but nearly entire screen size (normal, since my Mixer containing the level meters is about screen size).


#18

I’ve just created a (non-JUCE) Win32 test app for myself that also uses InvalidateRect for a screen-sized window. It just fills the screen and draws some animated ellipse. And guess what: It does not use 50% CPU, but only 2%. On exactly the same system. It’s weird.


#19

Yeah, none of those findings are surprising - clearly the blockage is inside the win32 function that blits the rendered image onto the screen.

In your test app, I doubt whether you’re double-buffering everything to an temporary bitmap, but juce has to do that to avoid horrible flickering. The problem is obviously because win32 is sometimes taking too long to copy the finished bitmap across to the graphics card, so it doesn’t matter whether your paint() method gets called or not, it still has to move the same size of bitmap, regardless of what’s inside it.

It really can’t be a juce problem, and I doubt whether there’s anything you could possibly do to improve it (except maybe update to Windows 7…)


#20

Where is this blitting going on? Remember, I put some {return;} into your handlePaintMessage() function that gets called upon WM_PAINT and it did not change anything: although nothing was drawn on screen, CPU usage still 50%! I suppose your double-buffering mechanism gets called in that function (or some sub-function of it)? BTW, I’ve just sent you a test app that takes 50% on my PC without doing anything else than calling paint() at 50Hz on a Component that is about 1000x600 pixels.