We’re encountering lots of performance issues with juce::OpenGLRenderer on Windows 10 when running multiple instances of our plugin.
After a few instances things start becoming very unresponsive, and with more instances the rendering of the OpenGLRenderer gets slower as well.
Our plugin itself uses component rendering and an OpenGLRenderer, but we encounter the same issue if we disable the component rendering.
Below is a simple PIP that exhibited the problem for us (we ran on Release as a VST on Reaper and Mixbus). Since there is no component rendering going on (meaning we don’t need to acquire a MessageManager::Lock in OpenGLContext::CachedImage::renderFrame()).
I would have thought that each thread could run its rendering without experiencing these issues since they seem to be more specifically related to the use of the MessageManager::Lock, but the PIP example slows down considerably when we start opening multiple instances ![]()
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: GLTestPlugin
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats, juce_audio_plugin_client,
juce_audio_processors, juce_audio_utils, juce_core, juce_data_structures, juce_events,
juce_graphics, juce_gui_basics, juce_gui_extra, juce_opengl
exporters: vs2017
type: AudioProcessor
mainClass: GLTest
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
//==============================================================================
class GLTestEditor : public AudioProcessorEditor,
public OpenGLRenderer
{
public:
GLTestEditor(AudioProcessor &p) : AudioProcessorEditor(p)
{
setSize(100, 100);
context.setContinuousRepainting(true);
context.setComponentPaintingEnabled(false);
context.setRenderer(this);
context.attachTo(*this);
}
~GLTestEditor()
{
}
void newOpenGLContextCreated() override
{
}
void renderOpenGL() override
{
OpenGLHelpers::clear(Colour((uint32)Random().nextInt()).withAlpha(1.0f));
}
void openGLContextClosing() override
{
}
void paint(Graphics &g)
{
g.setFont(32);
g.setColour(Colours::white);
g.drawText("JUCE", getLocalBounds(), Justification::centred);
}
private:
OpenGLContext context;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(GLTestEditor);
};
//==============================================================================
class GLTest : public AudioProcessor
{
public:
//==============================================================================
GLTest()
: AudioProcessor (BusesProperties().withInput ("Input", AudioChannelSet::stereo())
.withOutput ("Output", AudioChannelSet::stereo()))
{
}
~GLTest()
{
}
//==============================================================================
void prepareToPlay (double, int) override
{
// Use this method as the place to do any pre-playback
// initialisation that you need..
}
void releaseResources() override
{
// When playback stops, you can use this as an opportunity to free up any
// spare memory, etc.
}
void processBlock (AudioBuffer<float>& buffer, MidiBuffer&) override
{
ScopedNoDenormals noDenormals;
auto totalNumInputChannels = getTotalNumInputChannels();
auto totalNumOutputChannels = getTotalNumOutputChannels();
// In case we have more outputs than inputs, this code clears any output
// channels that didn't contain input data, (because these aren't
// guaranteed to be empty - they may contain garbage).
// This is here to avoid people getting screaming feedback
// when they first compile a plugin, but obviously you don't need to keep
// this code if your algorithm always overwrites all the output channels.
for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
buffer.clear (i, 0, buffer.getNumSamples());
// This is the place where you'd normally do the guts of your plugin's
// audio processing...
// Make sure to reset the state if your inner loop is processing
// the samples and the outer loop is handling the channels.
// Alternatively, you can process the samples with the channels
// interleaved by keeping the same state.
for (int channel = 0; channel < totalNumInputChannels; ++channel)
{
auto* channelData = buffer.getWritePointer (channel);
// ..do something to the data...
}
}
//==============================================================================
AudioProcessorEditor* createEditor() override { return new GLTestEditor(*this); }
bool hasEditor() const override { return true; }
//==============================================================================
const String getName() const override { return "GLTestPlugin"; }
bool acceptsMidi() const override { return false; }
bool producesMidi() const override { return false; }
double getTailLengthSeconds() const override { return 0; }
//==============================================================================
int getNumPrograms() override { return 1; }
int getCurrentProgram() override { return 0; }
void setCurrentProgram (int) override {}
const String getProgramName (int) override { return {}; }
void changeProgramName (int, const String&) override {}
//==============================================================================
void getStateInformation (MemoryBlock& destData) override
{
// You should use this method to store your parameters in the memory block.
// You could do that either as raw data, or use the XML or ValueTree classes
// as intermediaries to make it easy to save and load complex data.
}
void setStateInformation (const void* data, int sizeInBytes) override
{
// You should use this method to restore your parameters from this memory block,
// whose contents will have been created by the getStateInformation() call.
}
//==============================================================================
bool isBusesLayoutSupported (const BusesLayout& layouts) const override
{
// This is the place where you check if the layout is supported.
// In this template code we only support mono or stereo.
if (layouts.getMainOutputChannelSet() != AudioChannelSet::mono()
&& layouts.getMainOutputChannelSet() != AudioChannelSet::stereo())
return false;
// This checks if the input layout matches the output layout
if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet())
return false;
return true;
}
private:
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GLTest)
};

