views:

256

answers:

3

I have a form with a picturebox which displays a picture and some overlay drawn in the Paint event handler of the picturebox. The overlay updates itslef (basically changes opacity of different parts of the overlay) based on mouse movement.

Currently, I am calling pictureBox.Invalidate() from mouse moved handler to ensure that the overlay is repainted. I have also implemented some logic to determine whether the repaint is really needed - if no object changed its opacity with the mouse move, the PictureBox is not invalidated.

I am still getting 50 percent CPU usage on a dual core machine when I move the mouse faster than very slowly - I am guessing the painting routine just does not manage to repaint as often as the mouse moved events are generated.

There are not many objects drawn, up to 10 filled rectangles with a 4 filled triangle corners each. The problem is there with a single overlay object already. Basically only FillRectangle and FillArea methods are used to perform the painting.

What approach would you propose in this situation to prevent such high CPU usage?

A: 

If you're redrawing in the mouse moved event, then you'll be redrawing a lot. The question is, do you really need it to draw every time the mouse is actually moved, or do you want it to draw when it's finished moving? Or somewhere in between? Add some logic that only redraws at the end of the mouse movement, or something to that effect.

BFree
I definitely need to redraw as a reaction to mouse being moved - there are areas that are changing opacity based on mouse distance
Marek
+1  A: 

Limit the draw cycles.

For example in game programming a typical framerate is 30-60fps or thirty to sixty frames per second. That means that the screen is drawn from 30-60 times a second. Apply some sort of limit to your application too. 12fps is roughly the lowest frame rate the the human eye is tricked into believing is animation, so I wouldn't go below this.

On mouse moves seems wrong to be honest. For example, spamming the mouse rapidly would increase the draw rate.

Finglas
+1  A: 

Well, for starters, figure out how often you actually need to repaint in order to get the effect you're looking for. Right now, you're redrawing in response to mouse events, but there can be a lot more of those than you might think, and you probably don't need to redraw for each and every one of them. Painting (in response to Invalidate()) is low-priority for the most part, but that just means you'll end up using up any spare CPU to do it - better that you keep track of the time you last repainted and avoid doing it again too soon.

Using a timer to fix the refresh rate at some constant (start with a 40ms delay for a roughly 25hz refresh rate, and increase or decrease as needed) is an easy way to do this... Timers (System.Windows.Forms.Timer at least) are also low-priority, so you don't really have to worry about your refresh logic preempting more important event handlers.

Of course, keep the existing code you're using to determine whether or not anything actually changed. Set a flag when it does, and when that flag is not set, simply do nothing in your timer event handler.

With this done, you should see an immediate decrease in the maximum processing time used, as you'll have decoupled the refresh rate from the mouse rate. You'll also find that you have more control over the median processing time, since the refresh rate is under your control: too high, decrease the timer tick rate; not smooth enough, increase it!

Hackles