JUCE 8 weird getClipBounds() values when using OpenGL

Hi,

I’m experiencing strange graphical glitches in an OpenGL based JUCE 8 application.

In our application, we have a mixture of both regular JUCE painting and OpenGL renderings. Somehow, the background of one of our components, painted using a g.fillAll() call happens to show up in the OpenGL rendering in an area outside of the bounds of the component that called g.fillAll().

This does not happen if I replace the g.fillAll() call with g.fillRect(getLocalBounds()). This indicates that the issue might be related to graphics clipping. Below, I have an example project demonstrating that getClipBounds() returns unexpected values. This example project does not demonstrate the graphical glitches, I couldn’t manage to reproduce this in an example project.

struct MainComponent : juce::Component
{
    MainComponent()
    {
        setSize (400, 400);
        context.attachTo(*this);
        addAndMakeVisible(rect1);
        addAndMakeVisible(rect2);
        rect1.setBounds(50, 50, 50, 50);
        rect2.setBounds(50, 150, 50, 50);
    }

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

    struct Rect : juce::Component, juce::Timer
    {
        Rect()
        {
            startTimerHz(2);
        }
        
        void paint(juce::Graphics& g) override
        {
            g.fillAll(juce::Colour::fromRGB(juce::Random().nextInt(255), 0, 0));
            
            std::cout << g.getClipBounds().toString() << std::endl;
        }
        
        void timerCallback() override
        {
            repaint();
        }
    };
    
    Rect rect1;
    Rect rect2;
};

// Output JUCE v8.0.0 (starting from "Direct2D: Add initial support" 19061e):
// -50 -50 400 400
// -50 -150 400 400
// 0 0 50 150
// 0 -100 50 150
// 0 0 50 150
// 0 -100 50 150
// 0 0 50 150
// 0 -100 50 150

// Output JUCE v7.0.12:
// 0 0 50 50
// 0 0 50 50
// 0 0 50 50
// 0 0 50 50
// 0 0 50 50
// 0 0 50 50

The output in JUCE v7.0.12 looks normal to me and the output in JUCE v8.0.0 looks off.

Tested on a Macbook Air M1 running OSX Sonoma 14.5.

Best,

Jelle

Could someone from the JUCE team confirm if this issue has been noted?

Thank you for reporting the issue, we are looking into it.

1 Like

I’m sharing a patch with you relative to our current develop.

While this is probably not the fix that will become public, I’d be curious to know if this fixes the graphical glitches you’re seeing, since I haven’t seen those in repro. This does fix the strange reported clip bounds.

Please give this a try, if you can.

opengl_clipbounds.patch (1.0 KB)

I’ve gotten to the bottom of why strange clipBounds are being reported. These strange values aren’t indicating any errors in working with clips, so sadly I still have no idea what may be causing the glitches in your case.

In short, JUCE 8 will more often use an EdgeTableRegion object to work with clips than JUCE 7. In the example project JUCE 7 would only use a RectangleListRegion. An EdgeTableRegion can handle any clipping Path that is non-rectangular. Because of this, Graphics::getClipBounds() will only report an approximate, enclosing clip, that is at least as large as the underlying clipping Path. The reason that we can’t see any glitches in the shared example is because the internal Path is doing it’s job.

I’m sharing another patch with you, that modifies the EdgeTableRegion so that the bounds reported by it enclose the internal Path more tightly. This makes the “weird values” go away, but it’s not changing anything in terms of rendering, so I don’t have high hopes.

Still, it would be interesting to see if it causes any visible changes in your application. This patch applies on develop as well, so the previous patch should not be used, for this to take any effect.

clipbounds2.patch (2.7 KB)

Could you maybe share two screenshots, so we can all see the difference between the two observed behaviours?

I’m unsure from your description if the visual output is different or if you’re only concerned about the numbers being returned.

I encountered a similar issue in JUCE 8 using OpenGL, where fillAll would fill regions outside the component’s local bounds. This patch seems to have resolved the issue, though I’m not entirely sure if it’s the same one the original poster mentioned.

In my case, the easiest way to reproduce the problem was by rendering a lasso component with OpenGL, as it uses fillAll. When the lasso was being redrawn, some areas outside the lasso were incorrectly filled. Additionally, I found that placing the OpenGL context inside a subcomponent, rather than the main editor, and using a high DPI screen were necessary to replicate the issue on my system. After applying this patch, I no longer experience these issues with OpenGL.

The clipbounds2.patch applied on the latest develop (4c5c336e) fixes the graphical glitches in our application.

I’m not interested in the printed numbers as long as the graphics look good. However, the graphics don’t look good, so I shared the numbers because they differed between JUCE 7 and JUCE 8, which might indicate a potential bug. If I could demonstrate it visually, I would have shared that, but I couldn’t reproduce it in a simple example project. @psyDSP provided a method to visually reproduce it. I haven’t tried it yet, but it seems to be the same issue, as the patch fixes that problem too.

Still no luck reproducing the mentioned graphical glitches. Not much use, but I’m sharing my attempt.

I noticed that Components with an OpenGL context will always graphically occlude non-OpenGL contexts, but that’s unchanged between J8 and J7.

J8 does work better in this example though, because at least the occlusion between overlapping OpenGL Components is handled correctly.

#pragma once

#include <JuceHeader.h>

using namespace juce;

struct SubComponent : Component
{
    SubComponent()
    {
        context.attachTo (*this);
    }

    struct LS : LassoSource<int>
    {
        void findLassoItemsInArea (Array<int>& itemsFound, const Rectangle<int>& area) override
        {
        }

        SelectedItemSet<int>& getLassoSelection() override
        {
            return set;
        }

        SelectedItemSet<int> set;
    };

    void mouseDown (const MouseEvent& event) override
    {
        addAndMakeVisible (lasso);
        lasso.beginLasso (event, &ls);
    }

    void mouseDrag (const MouseEvent& event) override
    {
        lasso.dragLasso (event);
    }

    void mouseUp (const MouseEvent& event) override
    {
        lasso.endLasso();
        removeChildComponent (&lasso);
    }

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

    LS ls;
    LassoComponent<int> lasso;
    OpenGLContext context;
};

struct RedComponent : Component
{
    void paint (Graphics& g) override
    {
        g.fillAll (Colours::red);
    }
};

struct MainComponent : Component
{
    MainComponent()
    {
        addAndMakeVisible (subComponent1);
        addAndMakeVisible (subComponent2);
        addAndMakeVisible (redComponent);
        addAndMakeVisible (subComponent3);
        setSize (300, 400);
    }

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

    void resized() override
    {
        subComponent1.setBounds (50, 50, 100, 100);
        subComponent2.setBounds (50, 200, 100, 100);

        redComponent.setBounds (80, 120, 150, 150);

        subComponent3.setBounds (70, 70, 100, 100);
    }

    SubComponent subComponent1, subComponent2;
    RedComponent redComponent;
    SubComponent subComponent3;
};

I was able to reproduce the graphical glitches by modifying your code. The glitches occur during the use of the lasso selection component. See gif below for a visual reference. I’m running this test on Windows 11 with a high DPI display set to 200% scaling. This issue does not appear when Windows is set to use 100% scaling.

Without Patch (JUCE 8 develop):
without-patch

With Patch (JUCE 8 develop):
with-patch

Here is the code I used:

struct SubComponent : juce::Component
{
	SubComponent()
	{
		context.attachTo(*this);
	}

	struct LS : juce::LassoSource<int>
	{
		void findLassoItemsInArea(juce::Array<int>& itemsFound, const juce::Rectangle<int>& area) override
		{
		}

		juce::SelectedItemSet<int>& getLassoSelection() override
		{
			return set;
		}

		juce::SelectedItemSet<int> set;
	};

	void mouseDown(const juce::MouseEvent& event) override
	{
		addAndMakeVisible(lasso);
		lasso.beginLasso(event, &ls);
	}

	void mouseDrag(const juce::MouseEvent& event) override
	{
		lasso.dragLasso(event);
	}

	void mouseUp(const juce::MouseEvent& event) override
	{
		lasso.endLasso();
		removeChildComponent(&lasso);
	}

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

	LS ls;
	juce::LassoComponent<int> lasso;
	juce::OpenGLContext context;
};


class TestProjectAudioProcessorEditor : public juce::AudioProcessorEditor
{
public:

	TestProjectAudioProcessorEditor(TestProjectAudioProcessor& p) : AudioProcessorEditor(&p), audioProcessor(p)
	{
		setSize(300, 300);
		addAndMakeVisible(m_testComponent);
	}

	void resized() override
	{
		m_testComponent.setBounds(getLocalBounds());
	}

private:
	TestProjectAudioProcessor& audioProcessor;
	SubComponent m_testComponent;

	JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(TestProjectAudioProcessorEditor)
};
1 Like

@attila
Could you confirm this issue has been fixed on JUCE develop branch?
If yes, when do you expect the next JUCE release to happen?

Sorry, I forgot to update the thread. The issue is fixed on develop

I can’t say at this point when the next release will be, but it’s likely to be more than two weeks into the future.

1 Like

Okay, I have a few projects that use OpenGL that I want to use the latest JUCE, is the current JUCE develop stable?

We have automated tests and a review process in place to ensure that we don’t knowingly push bugs onto develop.

That said, the purpose of develop is to share the changes with a wide audience and for a number of weeks, before those changes are deemed stable. So by that definition it’s not stable.

1 Like

After testing this commit, I noticed that it now triggers an assertion on line 131 of juce_EdgeTable.h in certain rare cases.

This assertion appears when calling reduceClipRegion with specific paths and affects both the software and OpenGL renderers.

The code below immediately triggers the assertion when running a standalone app with the Local Windows Debugger in Visual Studio. Before this commit, running the same code did not produce this assertion.

PluginEditor.h

#pragma once

#include <JuceHeader.h>
#include "PluginProcessor.h"

struct SubComponent : public juce::Component
{
public:
	void resized() override
	{
		m_clipPath.clear();
		m_clipPath.addRoundedRectangle(getLocalBounds().toFloat(), 8);
	}

	void paint(juce::Graphics& g) override
	{
		auto bounds = getLocalBounds();
		g.reduceClipRegion(m_clipPath);
		g.fillAll(juce::Colours::grey);
		g.setColour(juce::Colours::black);
		g.drawVerticalLine(bounds.getCentreX(), bounds.getY(), bounds.getBottom());
	}

private:
	juce::Path m_clipPath;
};

struct TestComponent : public juce::Component
{
public:
	TestComponent()
	{
		addAndMakeVisible(m_subComponent);
		m_openGLContext.attachTo(*this);
	}

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

	void resized() override
	{
		auto bounds = getLocalBounds();
		m_subComponent.setBounds(bounds.getCentreX() - 10, bounds.getCentreY() - 10, 20, 20);
	}

private:
	juce::OpenGLContext m_openGLContext;
	SubComponent m_subComponent;
};

//==============================================================================
/**
*/
class TestOpenGLAudioProcessorEditor : public juce::AudioProcessorEditor
{
public:
	TestOpenGLAudioProcessorEditor(TestOpenGLAudioProcessor& p) : AudioProcessorEditor(&p), audioProcessor(p)
	{
		addAndMakeVisible(m_glComponent);
		setSize(250, 180);
	}

	void resized() override 
	{
		m_glComponent.setBounds(getLocalBounds());
	}

private:
	TestOpenGLAudioProcessor& audioProcessor;
	TestComponent m_glComponent;

	JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(TestOpenGLAudioProcessorEditor)
};

Thank you for reporting. I investigated the issue and as far as I can tell this will not result in incorrect drawing.

We will however release a fix soon that will prevent the assertion and slightly reduce the size of the underlying EdgeTable.

1 Like

A fix for the assertion has been released on develop

1 Like