Mac OSX: painting two small child components far apart invalidates entire area between

I have two “flashing LEDs” - one in the upper left is a Tempo LED that blinks on the beat, while tiny ones in the lower right are Pattern LEDs that track the steps of a pattern through a step-editing grid.

As the Pattern LEDs are being driven by some subdivision of the tempo, they blink/change more often than the Tempo Led, like 4:1 as an example.

What happens is that when the the Tempo LED and a Pattern LED happen to execute a paint at the same time, even though one is in the upper left and one is in the lower right, they invalidate the entire large Rect between them, with them at the top/left and bot/right.

I gather this is a known issue since I have seen it mentioned in a few places, and indeed I finally figured out what was going on due to a mention in this thread:

I happen to have Buttons in that area that use a GlowEffect on the text, and hence are expensive to draw, which is not normally a problem as they’re just buttons that get painted when first displayed - except now every time these two LEDs line up, that area gets repainted and it makes a noticeable performance hit - which is why I started searching for the cause.

Is there any solution to this? I can see a few things to try:

  1. Optimize the drawing of all components in that area, and just live with it.

I tried using setBufferedToImage() on the button with the effect, and it helps - but the CPU usage still is more than double what it is if I simply disable one of the LEDs.

  1. Try to delay one of them so they don’t happen at the exact same time. This actually works, but it seems hacky and problematic. And I may have other stuff blinking in the future.

  2. I tried using JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS 1, according to this:

…but it changed nothing, even though I can see the modified code is being called.

=========

Furthermore, why does JUCE_ENABLE_REPAINT_DEBUGGING not work correctly? It doesn’t show what is happening at all!

I put some debug code in the same area of juce_ComponentPeer.cpp in ComponentPeer::handlePaint() to display the clipBounds that supposedly get painted some color:

  #if JUCE_ENABLE_REPAINT_DEBUGGING
   #ifdef JUCE_IS_REPAINT_DEBUGGING_ACTIVE
    if (JUCE_IS_REPAINT_DEBUGGING_ACTIVE)
   #endif
    {
        // enabling this code will fill all areas that get repainted with a colour overlay, to show
        // clearly when things are being repainted.
        g.restoreState();

        static Random rng;

        g.fillAll (Colour ((uint8) rng.nextInt (255),
                           (uint8) rng.nextInt (255),
                           (uint8) rng.nextInt (255),
                           (uint8) 0x50));
        
        // TEMP_FIX to display clipBounds that supposedly gets painted above
        auto r = g.getClipBounds();
        std::cerr << String::formatted("bounds x %03d, y %03d, w %03d, h %03d", r.getX(), r.getY(), r.getWidth(), r. getHeight()) << "\n";
    }
  #endif

This is the result, along with my stylized explanations:

And yet, the huge clipRegion does not color anything on the screen at all. If it did, I might not have had to spend all day figuring this out…

Yeah, well, that is the issue we all face on OSX.
The only real workaround is to use OpenGL instead. But it comes with its set of issues.

Furthermore, why does JUCE_ENABLE_REPAINT_DEBUGGING not work correctly? It doesn’t show what is happening at all!

I’m not too sure about that one but it seems that even though the paint methods are called in the whole area encompassing your two LEDs, only the LEDs are actually drawn on screen. So basically all the paint calls are just wasted CPU time.

I have divided my UI into horizontal “bands”, so that my Editor’s children are like a layer cake, with a multiple rows of component containers. I did this originally to make resizing easier, but it seems to help with repaints as well. Will this help your use case? It seems to me that I found previously that if the components are in separate containers, and the parent containers are arranged in separate rows like this (with no vertical overlap at all between them, not even a single pixel), then only the rows containing the objects requiring repainting are themselves repainted, and not the other rows.

(I have not tested this with objects in two distant rows requiring repainting at the same time, though. I don’t have a way to force that to happen in my UI.)

Thanks. I suspect that if you were to update two distant rows at the exact same time, you would have the same issue, based on my debugging so far. It doesn’t matter how nested the child components are; the parents of the two children will both get coalesced into a single large rectangle encompassing both points.

Yes @stephenk this is correct. The component hierarchy does not matter.

Makes sense. As I mentioned, I hadn’t tested that scenario. Just a thought.