Repaint issues on Big Sur

Hi,

I’m currently on the latest version on Big Sur (11.4) with the latest JUCE (6.0.8). I was implementing some level meters in my plugin when I found an issue related to the repaint function.

So, my plugin has a parent component (the Editor) with 6 children. Each children has some buttons and sliders. In one of these children there are 4 additional grand-children, the level meters.

These meters are updated periodically with a Timer placed in their parent. At each timer callback I call their repaint function.
This is the meter class, I’ve removed all the code related to the meter itself, leaving only the background, which is a simple black rectangle that covers all the available space of the component.

#pragma once

class MeterComponent  : public Component
{
public:
    
    MeterComponent()
    {
        setOpaque(true);
    }

    ~MeterComponent() override
    {
    }

    void paint (juce::Graphics& g) override
    {
        g.fillAll(Colours::black);
    }

    void resized() override
    {
    }


private:
    
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MeterComponent)
};


As you can see it is extremely simple. I have set setOpaque(true) because I don’t want to repaint the whole UI, but only the meters. If I add only one meter, everything works fine, but when I add the second one (and then the third and the fourth), it starts to repaint also their parent and therefore also the Editor, causing a complete UI repaint.

I suppose that the problem is related to the fact that the “dirty” area is not only the meters, but a single big rectangle. Since the meters are on the sides of the UI, this rectangle is really wide and triggers the repaint of the parent.

I’ve read several past thread about this problem, but I couldn’t find a solution (also most of them are very old).

What is the correct way to repaint level meters 30 times a second without repainting the whole UI every time? I just need a way to disable, if it is possible, the creation of the unified rectangle, because it leads just to a waste of CPU.

Thank you.

Have you tried enabling the JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS preprocessor flag? Does it have any effect?

I’m currently using cmake, in order to enable that flag I have to define it equal to one right?
Something like:

#define JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS 1

Because I have tried but nothing changed, maybe I am enabling it in the wrong way?

With CMake, you can add this to your target_compile_definitions. If you copied one of the example projects, you probably already have a target_compile_definitions call, so you can add JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS=1 in there.

Ok, I’ve tried, nothing changed. I think I will add another opaque component in the background, by doing that I think I could repaint only the meters, their parent and that additional component, instead of the whole UI. Do you any other possibile workarounds?

Btw thank you for your prompt reply!

I checked with my colleague, and it sounds like unfortunately painting multiple small regions is broken in recent versions of macOS. There’s some more information in this thread, but the gist is that the OS no longer keeps an accurate record of the screen regions which need repainting.

Perhaps you could try enabling the OpenGL renderer and test whether that provides acceptable performance.

Sorry for the dumb question, what I have to do in order to enable OpenGL? As before, I am using cmake, wand I have copied one of the examples

You can create an OpenGLContext instance, and then call attachTo to attach it to your top-level component. You could take a look at the OpenGLDemo2D demo, here:

Ok! I will try it, thank you!

@reuk
We switched to OpenGL for this exact same reason. And while it seems to provide better performance and a better experience overall, there is still the jitter issue when resizing the main window (Mac only with Juce 6.0.8).

See:

Any news on that?

I found out that calling OpenGLContext::triggerRepaint() during a resized() call in the main Component of the application helps a bit, at least in my case.

Interesting, I’ll take a look at that. Thanks!

I tried to call triggerRepaint in resized() of my main component but it does not change anything for me. It seems that OpenGLContext::renderFrame() cannot be called during a resized() anyway since it grabs the MessageManager lock.

I see. I’ve now checked my code again, this is what I’m doing.
My hierarchy is: Main Window → Main Component → Child Component.
The context is allocated by the Main Window but it’s attached to the Child Component.
I’ve overridden resized() in the Main Window and I call triggerRepaint() on the context in it (and of course I call also the Main Windows’s base class resized()).
This doesn’t solve things completely, but it seems to help a bit.

I hope the JUCE team can find the time to look at this issue soon :slight_smile:

I see, thanks for the details! I’ll try that.
The issue is that the renderer and the message thread are not in sync. What you do may make the context render a new frame earlier than it would otherwise, making it “better”, but indeed you’ll always get some flickers.

I guess the real workaround is to force synchronous updates when the window gets resized as Jules suggests it here: AudioProcessorEditor drawing with OpenGLRenderer flickers on resizing - #2 by jules