Unnecessary paints when parent is setPaintingIsUnclipped

I have 2 components : A and its child AA, both are opaque and setPaintingIsUnclipped (true).

Everytime AA is repainted, the parent A is also repainted. Any reason for that?
(If the parent is not setPaintingIsUnclipped, everything is ok)

here is a simple code to reproduce (basic MainComponent for a projucer generated gui app) tested on 10.12

#ifndef MAINCOMPONENT_H_INCLUDED
#define MAINCOMPONENT_H_INCLUDED

#include "../JuceLibraryCode/JuceHeader.h"

class ChildComponent : public Component, public Timer
{
public:
    ChildComponent()
    {
        setPaintingIsUnclipped (true);
        setOpaque (true);
        startTimerHz (4);
    }

    void paint (Graphics& g) override
    {
        g.setColour (Colours::red);
        g.fillRect (getLocalBounds());
        DBG ("child paint()");
    }

    void timerCallback() override
    {
        repaint();
    }
};


class MainContentComponent   : public Component
{
public:
    MainContentComponent()
    {
        setPaintingIsUnclipped (true);  // everything is ok if you remove this line
        setOpaque (true);
        addAndMakeVisible (child);
        setSize (600, 400);
    }

    void paint (Graphics& g) override
    {
        g.setColour (Colours::green);
        g.fillRect (getLocalBounds());
        DBG ("parent paint()");
    }

    void resized() override
    {
        child.setBounds (getLocalBounds().reduced (10));
    }

private:
    ChildComponent child;
};

#endif  // MAINCOMPONENT_H_INCLUDED

Is any-scaling or transformation involved, if yes, keep in mind that the boundaries could be on sub-pixel positions, which result in a mix of the background and foreground component on the boundary pixel positions.

If not, try to debug and see whats going on

also you may have a second redraw(dirty) region, on some other position, which on mac will be summarized to one big redraw region, which may intersects with areas which don’t have to be redrawed (you can use the JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS, to change this behavior)

I’m on retina. I don’t have anything else that those 2 components, and I have this behaviour whatever the child bounds, even if it has the same bounds as the parent.

standalone or plugin?, with some host/wrapper combination dirty regions are on the top all the time

If not, debug and see whats going on

standalone (example code is above, I tested it within a projucer generated gui app).
I don’t have much time to look further into that at the moment unfortunately.
But I already wanted to know if it was really not expected or if I was missing something.

So this is because in paintComponentAndChildren, clipObscuredRegions() is not called if dontClipGraphicsFlag is true.
As a consequence, if a component is setPaintingIsUnclipped(), it will always get repainted whenever its child is repainted, even if it’s unnecessary.

shouldn’t clipObscuredRegions() be called even if dontClipGraphicsFlag is true?

1 Like

or not? :slight_smile:

(gentle bump). could this be fixed?

We’ll have a look, but it’s fairly low priority for us, and actually may turn out to just be a case which we choose not to optimise. To do a 100% perfect cull of all obscured components is an operation that scales very badly to large numbers of components, so the algorithm we use doesn’t attempt to traverse the entire tree, it just does enough to cover the common use-cases.

At the moment it does not do anything, as the function is not called at all :

The problem is not that it is not optimized, but that you will have worst performance with dontClipGraphicsFlag if your component has children

Sure, but the dontClipGraphics flag was never supposed to be used for components with children. The purpose of that flag is to avoid clipping components which just contain some simple graphics.

In the setPaintingIsUnclipped() documentation, it is written : Your component also can’t have any child components that may be placed beyond its bounds. So I assumed it could be called on components that have children (like a simple slider which has a label child).

We could simply add a numChildComponents condition (line 1969 in paintComponentAndChildren()):

if (flags.dontClipGraphicsFlag && getNumChildComponents() == 0)

2 Likes

I am just running into the same issue.

Wondering why I get unnecessary repaints and realizing, its because I have set setPaintingIsUnclipped(true).

I think it would be good to document this better. In some of his talks, Jules recommends setting setPaintingIsUnclipped(true). Thats where I got the idea from.

So whats the general advice now? Use setPaintingIsUnclipped(true) if you can, but only on components , which have no children. Does that seem sound?

just got bit by this, turned it true on a bunch of stuff with repaint debugging enabled and thought it must be all good only to find CPU went up 100%

Perhaps this is also connected with this other issue: Suspicious behavior in Component::paintComponentAndChildren()
which also has gone unnoticed by the JUCE team so far

3 Likes