Advice for drawing VU meters

I’ve just checked up again, and it’s definitely NOT the blitting (DrawDibDraw()) that’s causing this, and which is called by Win32ComponentPeer::handlePaintMessage(). You can completely remove all calls to handlePaintMessage(), the CPU usage will still be the same.

I had the same problems with a wavelet analyzer window. When it reached a certein size, the CPU jumped from 2% to full. I tried weeks to fix it (tried all the things with removing code like you did) but finally I had to drop Juce for that specific project because I could not get it to work.
It would be great, if someone could find out what this is - it would make Juce even more valuable.

Found out a few more things:

  • I said that the CPU would go up to 50% if handlePaintMessage() just immediately returned. True - but if you put a Sleep(1) (followed by return) into it this function is no true anymore, then the CPU goes down to nearly 0%.
  • On my laptop, the CPU usage is only so high when displaying stuff on the external monitor, but not on the laptop screen itself (the external screen is bigger).
  • On my Desktop, where the CPU usage was enormous, I could drop it from 50% to 15% via using 32Bit as WindowsBitmapImage instead of 24Bit. It must be some NVIDIA thing :wink:

I think I’ll just go OpenGL then for the meters. I’d love a Component class that somehow sticks to GDI more closely as this would probably allow drawing without much overhead. But then again, how would that work on Mac? …

Hey Friscokid, was this on PC or Mac? And how did you solve the problem (what API or what code did you use)?

As said, on my Desktop PC, I could reduce the problem by modifying JUCE internally (24 Bit to 32 Bit). What also works is grabbing the HWND of the ComponentPeer and then using the DeviceContext, so HDC dc=GetDC((HWND)getPeer()->getNativeHandle()); and then using it for drawing via GDI, I’m kind of experimenting with that at the moment.

The whole problem is definitely graphics-card- and driver-related, because on my laptop’s main screen the problem does not arise. I have the impression that only the main screen is hardware accelerated and the external monitor screen not. I tried some OpenGL and on the main screen it’s fast and on the second screen super-slow.

Here is an example of how to use GDI’s BitBlt with pre-loaded Bitmaps (they can be loaded from disk or generated programatically as in this example) that reside in the GFX-card’s VRAM. So it’s not the same as blitting from CPU memory to GFX memory, as JUCE does.
I have found this to be the fastest way of displaying VU meters. For a screen full of 90 level meters, it takes 0% on my desktop PC (for screen size 1680x1020) and 2% on my laptop for screen size (2 screens) 2960x1080 @100Hz refresh rate. It’s much faster than OpenGL (OpenGL completely fails on screen 2 of my laptop, as if it wasn’t capable of using OpenGL’s hardware-accelerated rendering). Don’t use any other GDI functions (Rectangle, Ellipse) as they can also be quite slow, as I found out! Just use BitBlt.

I don’t think there’s a faster way of displaying level meters on windows. Jules, is there a similar way to do this on Mac (if the problem exists on Mac, haven’t tried yet)?

I doubt whether you’d even see the same problem on Vista - the chances of seeing anything remotely similar on the mac are tiny!

I’ll try with Windows 7 and let you know. But I think you can’t force everybody to use Windows 7 or Vista just because your JUCE app is slow on XP. If all other apps run smoothly on XP, JUCE apps should do the same.

I’ve just tested on Windows 7 on the same Desktop PC (with NVIDIA FX5700).

Results:

  1. JUCE’s CPU-RAM to VRAM blitting is even slower than on XP
  2. The GDI code is still only using 2% - but I noticed that the whole CPU usage fluctuates around 20% due to csrss.exe starting to use CPU power. I suspect the GDI implementation of BitBlt (and GDI in general) on Windows 7 is not good for the NVIDIA card, and probably a lot of work is done in that csrss.exe doing some kind of software-rendering?

All in both implementations work better on XP. Might be graphics-card dependant, I don’t know. I wouldn’t recommend 7 to anyone.

The GDI is pretty much deprecated in Windows 7 (at long last!) That’s why juce will be moving to the almost-ready Direct2D renderer. So don’t spend too much of your life obsessing about this stuff, it will all hopefully be irrelevant in the not-too-distant future.

Well, at least I’m happy to have solved the problem on XP (and on 7 it’s still much better performance than the original JUCE rendering!). On XP, it works amazingly well now: 0-2% CPU usage for 2 full screens of level meters animated at 100Hz ain’t much CPU usage, is it? :smiley:

That’s awesome news ! Will it be just a matter of switching a #define on in juce_Config.h, or will we have to re-write our graphics code ? (Just to know if I should spend some time on graphics now, or wait for Direct2D to be ready)
I guess that for crossplatform sake, it’s gonna be just a config switch, but I prefer to be sure beforehand !

It’ll just be a config switch - same as on the mac.

For anyone who’s interested, meanwhile here’s the GDIComponent class with some demo. I’ll use that for my upcoming project. Tried it on 2 systems and it’s as fast as it can get (better than OpenGL in my case). Just remember to only use BitBlt with preloaded BITMAP’s and you’re on the safe side.

Hi Zamrate !

Thanks for your app :I’ve been trying it out a bit, since I run in pretty much the same issue…
Here are a couple of remarks and questions, though. Since you obviously spent a lot of time with this problem, I hope you can help…

On my Desktop, I get approximately 15% CPU in Debug, 13% CPU in Release (full screen, 1680x1050). First : why is the difference so tiny ? Second, why is that so much more than what you report ? Possible difference could be :
[list]
I’m using Vista 32bits (not XP - though aero theme is disabled, so it should pretty much work quite like XP on the graphic side)

I have an ATI graphic card (not an Nvidia - but if I get it right, we don’t involve hardware acceleration in this demo app)

Maybe my computer is less powerful than yours - but I doubt it is that less powerful than your laptop (unless maybe you work at NASA…)
[/list]

Then, I see you store all possible “states” (filled with one pixel, with two pixels, with 3 pixels etc.) of your bargraphs (2000 in the case of your test app) But what if you need much more ? I am actually working on a phase oscilloscope. (This means that an approach similar to yours would be to storing billions of BITMAPS. Clearly not an option !) I hence need to produce the image every time I want to bitblt it. Now, doing this with the GDI tools is a pain in the neck, and this is why I wanted to be able to use JUCE (Graphics, Image, etc.) But if I do so, then what I’ll get is a juce::Image, not a BITMAP (or a HBITMAP, a DC, a HDC or anything GDI can use…). Of course, I can access juce::Image::BitmapData and copy this buffer to a BITMAP, that will in turn then copy it to the DC, and so on a dozen more times until it finally lands on my screen, needless to mention, with terrible performance issues (I tried it a while ago, was constantly hitting 100%, so I dropped the idea…)
[list]
Any idea ?
[/list]

Thanks in advance for your help…

Val

On Vista/Windows7 it takes more CPU because GDI is not as performant as on XP (I think they “virtualized” it, whatever that means). I think it used around 15% here too, but it was still far better than using JUCE components.

The second best thing after the Juce framework is, without a doubt, this forum. I was facing a similar problem with meters eating too much CPU, and with a simple search I found this post, and zamrate's sample code.

Had to struggle a bit to implement some clipping in the repaint, as my meters are within a viewport, but now it works perfectly and fast. Thanks a lot to you, zamrate, and to other Juce users. You rock!