Repaint() terrible performance why?

I have a 60Hz timer to repaint my audio visualizer, I commented out all paint code and left just:

void MyClass::timerCallback()
{
    repaint();
}

void MyClass::Paint() 
{
}

Even on an empty application this results in CPU usage of 30% or more and about half of that for release builds.

The CPU usage also increases if I increase the window size.

Any suggestions or workarounds? I tried enabling OpenGL with little results, if I use repaint(0,0,1,1) or some low value the performance gets good but the graphics are not redrawn. Is there a way to paint graphics without the window repaint which is causing the high cpu usage?

If no workarounds, is there a performance issue with repaint()? The issue happens without any graphics being rendered.

Thanks

The only thing I can imagine is that there might be something underneath your component that takes a while to paint - even if it’s just inside the bounds - that could be affecting the performance. As I understand, all repaint() does is marks the bounds of the component (and all overlapping/child components) as dirty to the os.

Do the JUCE example projects work with low performance? I assume they call repaint on their components all the time. If you create a completely blank project and create one component and call repaint at 60Hz, does that still have low performance?

Yes, the performance issues are there with a blank project with a repaint 60Hz timer, I’m on windows btw, this shouldn’t happen still.

It might be worth trying this. My plugins are OpenGL but there may be a similar issue with non-OpenGL relating to DWM:

Thanks, your topic is a bit complicated and windows specific, I can’t debug to that level but maybe in time, for now all I know is that repaint is very cpu intensive and seems like a problem with the platform, maybe I should create a ticket on gh.

Have you tried building in Release to measure the performance?

Unless there’s a specific thing you can identify that’s causing significantly poor performance, it’s unlikely to get any attention.

JUCE’s graphics engine is fundamentally a CPU-based renderer, reguardless of whether you use OpenGL, Direct2D, CoreGraphics, or whatever other context - the majority of the work will be done on the CPU.

The trick is simply to ensure you’re only drawing what needs to be drawn - repainting the entire UI 60 times is second is always going to use a lot of CPU.

If you want to check if there’s any specific thing causing the poor performance, you could use Visual Studio’s Profiler which will measure which parts of your application are using the majority of the CPU time.

I don’t think rendering 60 times a second should be a problem, there is nothing to render, JSFX and Iplug2 seem to handle the rewrites at 60 fps with no fuss, I am trying to use the Visual Studio Profiler but have to admit I find it complicated, thanks.

Also yes, I tried release build, its about half the performance which is not great, I checked a plugin called SimpleSide and it suffers the same performance issue using a Timer at 60Hz to repaint, I don’t get why it shouldn’t get any attention, seems a rather platform problem that affects any plugin that needs constant rewrites (eg. visualizers), as I said my paint method is empty and no components to redraw.

What system are you running on?
Have you profiled it?
Have you tried thread sanitizer, address sanitizer etc?
Can you paste the whole code?
An isolated example?

Theres a lot of people doing this (although the newer method is to use vblankattachment iirc) without issues so generally it’s going to be something that can be troubleshooted, but more information is needed

I’m running windows 10, I tried profiling it but the function calls are a bit above my paygrade.
I can provide the code but its really just a HelloWorld where I comment the paint() contents of the AudioEditor and add a Timer set to repaint:

NewProjectAudioProcessorEditor::NewProjectAudioProcessorEditor (NewProjectAudioProcessor& p)
    : AudioProcessorEditor (&p), audioProcessor (p)
{
    // Make sure that before the constructor has finished, you've set the
    // editor's size to whatever you need it to be.
    setSize (700, 600);
    setResizable(true, false);
    startTimerHz(60);
}

void NewProjectAudioProcessorEditor::timerCallback()
{
    repaint();
}
void NewProjectAudioProcessorEditor::paint (juce::Graphics& g)
{
    // (Our component is opaque, so we must completely fill the background with a solid colour)
    //g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));

    //g.setColour (juce::Colours::white);
    //g.setFont (15.0f);
    //g.drawFittedText ("Hello World!", getLocalBounds(), juce::Justification::centred, 1);
}

I get 20+ CPU usage, about half of that in release mode, still very high considering I have nothing to render, also increases if I increase the window size.
Where can I find plugins that do this repainting without perf issues? What is vblankattachment? Thanks in advance

Could you share a screenshot of the output of the profiling?

Have you tried the JUCE DemoRunner that’s provided in the examples folder? The first page of the Demo has this animated graphic which is updated repeatedly - similar to your own plugin:

I tried the demos, the AnimationDemo runs nicely (3%), the graphics demo is a bit heavier on the CPU (10%+), still not sure if these are a great way to test as they are not VSTs.

Just noticed I can compile the demos to VST and test, will do that in time.

Here is another screenshot, this time I commented g.fillAll() I was calling on my paint method, still the problem persists, thanks for the debugging tip though I am making some progress on debugging at least.

After some profiling I think it boils down to calling juce::Graphics::fillAll() which calls juce::Graphics::fillRect() which is very inefficient, from what I understand it has for loops for both width and height and setPixel inside the loops.

Think its worth opening a ticket for this? I’m surprised if no one has bumped into this perf issue but not sure if there is a solution.

What happens if you call setOpaque(true) in your constructor?

Painting works the same for VST plugins as it does for standalone apps.

You could also try the direct2d branch.

Matt

All of us here are using Juce and i think NOT experiencing this kind of performance. My app has blinking LEDs and all sorts of stuff updating the GUI all the time and does not experience what you are reporting, So the question is: what is different?

He calls repaint for the entire editor, forcing it to repaint everything. Typical beginner mistake.

2 Likes

It looks like you’re using the software renderer as opposed to the Direct2D renderer. In the demo runner, you can change the renderer from the settings menu to compare the performance differences.

If your goal is to repaint the GUI of a plugin to update knobs, sliders, buttons and all other widgets while parameters are changed by Midi or by host automation, I suggest not using the timer at all, instead you can use the ActionBroadcaster and ActionListener so you only update what needs to be updated and only when an update is necessary.

If you have animated components such as vuMeters or scopes, you can implement a timer inside the single components (30 Hz is more than enough) or have one single timer that calls Update() methods of all components that need to be updated regularly.

1 Like