Repaint() consumes a lot of CPU even though paint() in empty

Hello I found out strange thing, like in subject.

I am working on my monitor class (audio level visualisation) . And my solution is to make my monitor class inherit from Timer. I capture audio envelope in my processor AudioProcessor, and update the monitor in timerCallback(), and also in timerCallback() I call repaint(), like that:

void MyMonitor::timerCallback()
{
    // Update monitor....
    repaint();
}

My callbeck interval is set by startTimer(40), so it repaints 25 times per second.
And I tried to optimise my drawing code in paint(). So I started bit by bit turning off individual parts from paint(), until I found that nothing change in CPU usage even though I remove everything from paint().
I created some debug button which lets me turn off everything in paint(), like that:

void MyMonitor::paint (Graphics& g)
{
    if(turnOff_DEBUG)
        return;
    else
        // painting code
}

So I can compare in the runtime. And I found out when turnOff_DEBUG == true my CPU usage is not less than case when turnOff_DEBUG == false. Maybe little bit less but it’s unnoticeable.

But when I remove repaint() from timerCallback() then CPU usage decreases distinctly.

So it looks like my code in paint() is not problem, but calling repaint() causes most CPU usage. Actually “problem” is wrong word. It’s not big problem, but I just try to optimise my plugin.
So I suppose I don’t need to optimise my drawing code. But I wonder maybe I can optimise in some way usage of repaint(). Maybe I could set my timer interval bigger, but it looks like 25 per second is at least to achieve fluid/smooth visual effect.
So I have no idea how to make it more efficient. I can’t clearly understand behaviour of paint(), repaint() and all those things. I found out paint() is often called even without calling repaint().

Could anyone give some advice?

Forgot to mention I checked all those thing in debug compilation with project optimisation set to none. And of course when I remove repaint() from timerCallback(), or I set turnOff_DEBUG = true then my monitor doesn’t work, but it’s not the case. The case is CPU usage.

Test it with a release build. Debug builds are not meant for optimizing things, they are meant for debugging things.

Another issue could be that a repaint of your component causes other components to repaint too. (This seems to be a common issue with Juce.)

1 Like

Hello thanks for your advice. I will try to test it in release build.
But by the way, what kind of optimisation do you recommend for release build. I have no experience with that at all.
I always set release optimisation in xCode for

“Fastest, Aggressive Optimizations [-Ofast]”

But by default it is set to “Fastest, Smallest [-Os]”
I tried both but I see no difference. So I am not sure what is the best choice and for what purpose?

The defaults for a release build should work fine. If it doesn’t, the problem may be somewhere else, for example what I mentioned above : calling repaint() on your single component may be causing repaints in other components.

OK, thanks, but are there any known methods to avoid repaints in other components?

You can try calling setOpaque(true); in your monitor component’s constructor, if the component completely covers the components behind it. (For example, if you do a Graphics::fillAll call at the beginning of your paint method with a non-transparent color.)

https://docs.juce.com/develop/classComponent.html#a7320d543cba40183c894474ab78798ea

1 Like

Great thanks for your support.

Hello,
last 2 days I’ve made a lot of experimenting with setOpaque(), repaint() and things like that.

Unfortunately there is some strange behaviour which I can’t understand.

I have three separated Components (like input monitor, output and gain reduction).
I’ve set all of them to setOpaque(true). They don’t overlap each other. And all Labels that can change text I disabled.

And when I repaint all of them, like that:

input.repaint();
output.repaint();
gainReduction.repaint();

They cause repainting my whole editor. But when I comment one of them, like that:

input.repaint();
output.repaint();
//gainReduction.repaint();

Then everything is great, my editor is not repainting all the time with monitors, which I want to achieve.

I thought it’s something wrong with my class for gainReduction monitor. But when do that:

//input.repaint();
//output.repaint();
gainReduction.repaint();

It’s still great. Editor is not repainting it self.
So the problem is only if I repaint all monitors.

I can’t figure it out why is that?

Do you happen to be running on OSX? The CoreGraphics renderer will often group multiple “repaints” into one larger area, which means the background may be repainted as well though

I’m not entirely sure if this still applies to opaque components since it’s lower level than JUCE, but it is likely that the CoreGraphics behaviour is your issue…

So how to fight with that? Are there any methods to avid such behaviour?

If you’re on Xcode 9 or lower, try adding JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS=1 into the Preprocessor Definitions section of your Projucer file. If it fixes the repaint calling issue we’ll know that CoreGraphics was the problem

If you’re on Xcode 10… Apple broke some things that JUCE was using so this won’t work anymore and CoreGraphics will be free to group multiple repaints into one, larger area :confused:

I wonder how I can put it in nice words… we had exactly the same conversation not 48 hours ago?

1 Like

You are probably right, because I tried it also on Windows today, and there was no such problem. I thought maybe I do something wrong on Mac. But it looks like it’s not problem in my code.
And to be clear: yes I use xCode 10.

But the question is. How to work with that issue? Do you think if I make all of my monitors as a one Component would help? I mean one component with only one paint() method which paint all monitors.

Of course I can just check it. But actually it’s a lot of work for me now, changing all bounds and moves everything to one class. And I don’t know if is it worth to do that?

If not, what else can I do?

Yes Daniel. I still can’t solve it. But it looks like I am on next stage of that issue.

Ah, it sounds like it must be related to CoreGraphics then

Darn :confused: I was speaking with @t0m about it at ADC back in November and there wasn’t a workaround for that yet, as far as I know

That “could” work since it would let you specify exact regions to repaint… but I’ve found that doing things like this makes working on projects way more difficult. It’s must easier and less error-prone to use juce::Component when you need actual “widgets” in the GUI

If it’s really a problem or making the CPU usage high, you could start out by simplifying your drawing code.

You could also move components that repaint often closer together, so that if CoreGraphics does group repaint regions together that it won’t be such a large area

I think you are micro-optimising something. Is drawing a slider giving you issues?

The thing is, that drawing a bigger area seems to be cheaper than drawing a bunch of independent areas. That seems to be the conclusion of the apple engineers, who designed the backend.

I don’t know your code, but I believe you have too much calculations in your paint code, if it slows stuff down as much to become an issue.

2 Likes

Probably you are right. I will try to reduce code.
Thanks for all your advices.

That’s a good choice, good luck!

A good way to reduce calculations in paint is to put them in a data structure you can keep as member, e.g. Path or RectangleList are particularly useful for that purpose.
Or last resort drawing into an Image, which exists built in by setting setBufferedToImage()