OSX Mojave openGLContext.attachTo() issue if more than one context is used

Because JUCE 2D rendering on OSX is quite slow in comparison to Windows we use a method discussed in several threads on this forum by just attaching an OpenGLContext to our components.
That method improves the OSX rendering speed of our audio analyzer GUI by at least a factor of 10 and brings it on the same level as the Windows rendering.

Up to OSX High Sierra we where able to use the method described in the code example below which is just a simple extension of the AudioPlugin and Component templates.

-------- EDIT: Issue has been solved (pls. see below), BUT! ----------

The issue comes with OSX Mojave where the VST3 plugin within the JUCE AudioPluginHost as well as the Standalone Application blocks rendering of the components completely as soon as more than one OpenGLContext is active.
All plugin formats do work in Reaper64, Cubase and Pro Tools but show rendering issues on an intermittent basis.
We’re testing on the latest JUCE development build.
There is no problem on Windows 10 by using this method, although it does not improve the rendering speed anyway.

My questions are:
1.) Is there any other way to accelerate the rendering on OSX?
2.) Do we use the OpenGLContext in the correct way?
3.) Will there be an alternative as soon as Apple drops OpenGL completely after deprecating it in Mojave?

Code Example:
The code example should render the 2 components above each other.
That works quite ok for the plugin formats, although the resizer seems to disappear, which does not happen if there is no or just one OpenGLContext attached.
In case of the Standalone Application and the JUCE AudioPluginHost it seems that the 2 OpenGLContexts block each other so that nothing is rendered at all.
It is even impossible to close the app (beach ball).
If only one context is active - for example attached to the PluginEditor - everything works fine.

PluginEditor Header:

#pragma once

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

class OglconAudioProcessorEditor  : public AudioProcessorEditor
{
public:
    OglconAudioProcessorEditor (OglconAudioProcessor&);
    ~OglconAudioProcessorEditor();

    void paint (Graphics&) override;
    void resized() override;

private:
    OglconAudioProcessor& processor;
    
    TestComponent testComponent1;
    TestComponent testComponent2;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OglconAudioProcessorEditor)
};

PluginEditor:

 #include "PluginProcessor.h"
 #include "PluginEditor.h"
 
 OglconAudioProcessorEditor::OglconAudioProcessorEditor (OglconAudioProcessor& p) : AudioProcessorEditor (p), processor (p)
 {
 	setOpaque(true);
 	setResizeLimits(200, 200, 1000, 1000);
 	addAndMakeVisible(testComponent1);
 	addAndMakeVisible(testComponent2);
 }
 
 OglconAudioProcessorEditor::~OglconAudioProcessorEditor()
 {
 }
 
 void OglconAudioProcessorEditor::paint (Graphics& g)
 {
 }
 
 void OglconAudioProcessorEditor::resized()
 {
 	testComponent1.setBounds(0, 0, getWidth(), getHeight() / 2);
 	testComponent2.setBounds(0, getHeight() / 2, getWidth(), getHeight() / 2);
 }

TestComponent Header:

 #pragma once
 
 #include "../JuceLibraryCode/JuceHeader.h"
 
 class TestComponent    : public Component
 {
 public:
	TestComponent();
	~TestComponent();
 
	void paint (Graphics&) override;
	void resized() override;
 
 private:
	OpenGLContext openGLContext;
     
	JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TestComponent)
 };

TestComponent:

 #include "../JuceLibraryCode/JuceHeader.h"
 #include "TestComponent.h"
 
 
 TestComponent::TestComponent()
 {
	setOpaque(true); 
	openGLContext.attachTo(*this);
 }
 
 TestComponent::~TestComponent()
 {
	openGLContext.detach();
 }
 
 void TestComponent::paint (Graphics& g)
 {
	g.setColour(Colours::black);
	g.fillRect(getLocalBounds());
	g.setColour (Colours::white);
	g.drawRect (getLocalBounds(), 1);
	g.setFont (14.0f);
	g.drawText ("TestComponent", getLocalBounds(),Justification::centred, true);
 }
 
 void TestComponent::resized()
 {
 }

Well, the latest OSX Mojave update solved the issue :slightly_smiling_face:

Nevertheless, it would be extremely important to clarify the following questions:
1.) Is there any other way to accelerate the 2D rendering on OSX?
2.) Do we use the OpenGLContext in the correct way (pls. see above)?
3.) Will there be an alternative as soon as Apple drops OpenGL completely, after deprecating it in Mojave?

2 Likes

Hmm… It looks like you’ve made a lot of job to write that question. And nobody even answer one word :slight_smile:
Sorry for that. But I have exactly the same questions as yours. And have no idea how to find the answers.

In my case it is even worse. Because when I use OpenGLContext then my plugins work extremely slower and with lags. But I am on Apple M1 Big Sur.

I was trying to find something similar to OpenGLContext but designed for Apple Metal. But with no success.

Maybe you’ve already found some improvements for those issues?

For any help great thanks in advance.
Best Regards

There is not a great answer for this in JUCE currently.

Some of the OpenGL issues were introduced recently and seem to affect M1 machines acutely: Poor performance with OpenGL Renderer on Apple M1 machines

For certain types of rendering, Core Graphics can be much faster that OpenGL, but not for more complex scenes or fast animating visualizations.

The whole OpenGL topic is a little bit cumbersome on this forum. I describe this now again for all beginners, as it would have helped me too, because there are a lot of misunderstands.

OpenGL != OpenGLContext.

The openGL Context is just a replacement for the CoreGraphics Render, and uses OpenGL for redrawing the GUI, which can be faster than CoreGraphics, but doesn’t has to,

  • because it still uses the CPU
  • but mainly it also need to lock the Message-Thread (and is therefore constantly interrupted).

Normally you use one OpenGLContext per plugin window and attach it to the main plugin-Editor component.

If you really want fluid high-fps redrawing, you need to draw in inside the OpenGLRenderer::renderOpenGL() callback, via hardcore low-level OpenGL instructions.
And this happens parallel (concurrently) to the (main) message-Thread.

( There is also a technic using the a JUCE Graphics-class OpenGL-LowLevel render we discussed here Ableton Windows OpenGL "freeze-out" on high work load and multi-instance - #2 by chkn, but this maybe doesn’t work stable)

So overall a very complex topic, but doable.