Should I wrap std::atomic to all attributes used in paint() when I use OpenGLContext?

Let’s think an application that user can determine to draw rectangle at the center of the Component.

class MainComponent  : public juce::Component
{
public:
    MainComponent()
    {
        addAndMakeVisible(button);
        button.setClickingTogglesState(true);
        button.onClick = [this]
        {
            drawRect = button.getToggleState();
            repaint();
        };

        setSize (600, 400);
    }

    ~MainComponent() override = default;

    void paint (juce::Graphics& g) override
    {
        g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));

        if (drawRect)
        {
            g.setColour(juce::Colours::red);
            g.fillRect(getLocalBounds().withSizeKeepingCentre(100, 100));
        }
    }

    void resized() override
    {
        button.setBounds(getLocalBounds().removeFromTop(30).removeFromRight(50));
    }

private:
    std::atomic<bool> drawRect = false;
    juce::TextButton button{ "Paint" };

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
};

My question is, should I wrap drawRect with std::atomic for the thread safety?
If MainComponent is attached to the OpenGLContext, paint() should not be called from main thread?
Yes, it’s very rare that user click the button and toggle the variable during the thread pool is in the paint(). But I’m wondering if I can ignore the corner case.

It seems a pretty expensive solution for me. Unless you start to using memory_order flags, 99% of them relaxed, and a minimal amount of acquires and releases.

I do not use OpenGL yet, due to the fact it may be deprecated soon by Apple, but if I would do, I’d use a Fifo from the message thread, to the OpenGL thread. The problem of the fifo is you need to duplicate the data, but if isn’t too much, it seems a better solution for me in the long run

1 Like

If you’re not using a custom renderer, then the MessageManagerLock will always be taken outside the paint callback. It should not be necessary to add additional synchronisation, as long as the data members involved are only modified on the main thread, or inside the paint callback.

If you’re using a custom OpenGLRenderer, then you may need to synchronise accesses to objects that are accessed during the renderOpenGL call. In this case, you should not use the MessageManagerLock (see the docs for renderOpenGL for details), and should use other techniques instead (dedicated CriticalSections, atomics, queues, etc.).

2 Likes

Thank you guys!

@reuk Yes, that’s what I want to ask.
I’m using defaulted OpenGLRenderer, so I feel reliefed that I should not re-write all of my attributes in the Components.
Thanks!