[Bug] Issue with setBufferedToImage and Direct2D Rendering in JUCE 8

When setBufferedToImage is set to true on a child component, and that component contains its own child components, the Direct2D renderer does not clear the buffered image correctly before repainting. This results in an accumulation of frames where past frames remain visible.

Using a custom CachedComponentImage with juce::SoftwareImageType() instead of the default juce::NativeImageType() prevents the issue. However, this causes an assertion failure in juce_RenderingHelpers.h on line 2203 when using high DPI screens with a scale greater than 1.0.

The issue can be demonstrated with a single JUCE slider component.

Direct2D renderer:
direct2d

Custom CachedComponentImage using juce::SoftwareImageType():
softwareRender

Here is the code of the test component I used:

struct TestComponent : public juce::Component
{
public:
    TestComponent() : m_testSlider(juce::Slider::SliderStyle::RotaryHorizontalVerticalDrag,
        juce::Slider::TextEntryBoxPosition::TextBoxBelow)
    {
        setBufferedToImage(true);
        addAndMakeVisible(m_testSlider);
    }

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


    void resized() override
    {
        auto bounds = getLocalBounds();

        bounds.removeFromTop(20);
        bounds.removeFromBottom(20);

        m_testSlider.setBounds(bounds);
    }

private:
    juce::Slider m_testSlider;
};

With the code above, setting setBufferedToImage to true significantly increases CPU and GPU usage. When setBufferedToImage is false, CPU and GPU usage are below 1%. When setBufferedToImage is true, CPU usage increases up to 4x, and GPU usage increases up to 10x during repaints.

Does it make a difference if you set your component to be opaque?

What is your GPU?

Matt

Unfortunately, setOpaque(true) does not make any difference. My GPU is an Nvidia RTX 3080.

I found another bug when using setBufferedToImage(true) with the Direct2D renderer. If a parent component is semi-transparent and a child component is also transparent, the child component may turn opaque in specific situations, such as when dragging a slider.

The key to reproducing this bug, as well as the previous one I reported in this topic, is having the child component be a different size than the parent component. If both are the same size, the issue does not occur.

The images below demonstrate this issue while dragging a slider.

Direct2D renderer (JUCE 8 develop branch):
juce8

JUCE 7:
juce7

This issue doesn’t occur in JUCE 7 or when using a custom CachedComponentImage in JUCE 8 with juce::SoftwareImageType(), as shown in the images.

Here is the code to reproduce the issue:

PluginEditor.h:

struct TestComponent : public juce::Component
{
public:
    TestComponent() : m_testSlider(juce::Slider::SliderStyle::RotaryHorizontalVerticalDrag,
        juce::Slider::TextEntryBoxPosition::TextBoxBelow)
    {
        setBufferedToImage(true);
        addAndMakeVisible(m_testSlider);
    }

    void paint(juce::Graphics& g) override
    {
        g.setColour(juce::Colours::grey.withAlpha(0.75f));
        g.fillAll();
    }


    void resized() override
    {
        auto bounds = getLocalBounds();

        bounds.removeFromTop(20);
        bounds.removeFromBottom(20);

        m_testSlider.setBounds(bounds);
    }

private:
    juce::Slider m_testSlider;
};


class TestProjectAudioProcessorEditor  : public juce::AudioProcessorEditor
{
public:
    TestProjectAudioProcessorEditor (TestProjectAudioProcessor&);
    ~TestProjectAudioProcessorEditor() override;
    void paint (juce::Graphics&) override;
    void resized() override;

private:
    TestProjectAudioProcessor& audioProcessor;
    TestComponent m_testComponent;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TestProjectAudioProcessorEditor)
};

PluginEditor.cpp:

#include "PluginProcessor.h"
#include "PluginEditor.h"

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

TestProjectAudioProcessorEditor::~TestProjectAudioProcessorEditor()
{
}

void TestProjectAudioProcessorEditor::paint(juce::Graphics& g)
{
	g.fillAll(juce::Colours::black);
	g.setColour(juce::Colours::red);

	juce::Random random(12345);

	for (int i = 0; i < 50; ++i)
	{
		int x = random.nextInt(getWidth() - 5);
		int y = random.nextInt(getHeight() - 5);
		g.fillRect(x, y, 3, 3);
	}
}

void TestProjectAudioProcessorEditor::resized()
{
	auto bounds = getLocalBounds();
	m_testComponent.setBounds(bounds);
}

Does it happen in all DAWs?

DocumentWindow has problems when it is transparent since JUCE8 that are apparently DAW-dependent. They’re quite different problem but both linked to transparency.

Both issues I posted in this topic occur in all VST3 hosts on Windows that I’ve tested, including Reaper and JUCE AudioPluginHost.

1 Like

Thanks for reporting this issue, and for providing example code to reproduce the issue. Having examples always makes it much easier for us to track down issues.

In this particular case, I think we’re seeing some unexpected behaviour where Clear and D2D1_PRIMITIVE_BLEND_COPY don’t appear to replace alpha levels in the destination when rendering to a bitmap target while there’s a geometric clip layer active.

The examples you provided trigger this issue because a geometric clip was being used to reduce the drawing area before repainting regions of the buffered image.

I’ve pushed a change that seems to resolve the issue for me. It works by popping all active geometric clip layers and combining their clip regions into a single geometry, which we then fill using the BLEND_COPY mode. I’m not sure that this is the best solution - it’s still possible that I’ve missed some D2D setting that enables the expected behaviour, but I wasn’t able to find anything like that after a few days of experimenting and reading docs.

Please update to the latest develop branch to try out this change, and let us know if you’re still seeing issues. Thanks!

1 Like

Thanks for looking into this issue. I’ve pulled the latest changes from the develop branch and everything works correctly now.

1 Like