JUCE plugins cause poor DAW UI performance on Windows?

Hey every, I was hoping someone could help me with an issue that’s been the topic of discussion on Reddit lately.

It seems that Ableton Live’s user interface (UI) performance on Windows is frustratingly poor, especially when using certain plugins. When working on projects that use these plugins, the UI frame rate can drop from a smooth 70-80fps to a choppy 20-30fps, which makes it difficult to work efficiently. Interestingly, this issue doesn’t seem to affect Macs. Since Ableton started using the Metal API to render the UI, the software has been incredibly smooth, even when working with large projects that include many of the same plugins.

Ableton is pointing the finger at third-party plugin developers as the source of the problem, and to some extent, this seems reasonable. For instance, if you load up a new Ableton set with 200 FabFilter Pro-Q 3 plugins, it’ll run smoothly. However, adding plugins from other developers who use JUCE can lead to the same UI performance issues that many of us are experiencing.

I’ve taken numerous steps to troubleshoot performance issues, including looking into software, driver, and setting. Despite my efforts, however, I’m still struggling with poor performance. Just to give some context, I’m using an RTX 3090 graphics card with the NVIDIA Studio driver on my PC, and an M1 Pro on my Mac.

Initially, I thought that the issue might be related to the way that Ableton handles graphics APIs on Windows. However, after contacting Ableton’s development team, they’ve indicated that the issue is not caused by their software. Interestingly, this issue seems to be affecting many users, yet it doesn’t seem to be getting much attention.

I’m still trying to understand whether the issue lies with the JUCE library itself, or if it’s a matter of developers not optimizing their plugins properly. If anyone has any insights to share, I would greatly appreciate your input.

1 Like

20-30 fps = choppy? :roll_eyes:

3 Likes

The op is correct. Countless users are experiencing and reporting excruciatingly slow framerates with overall UI in Live PC (and mac apperntly too), when their ableton sessions are getting populated with plugins that are using JUCE. I’d even go as far as saying that my larger sets can crawl down to 4 frames per second and become dangerous to operate.

I should add that, these sets are large BUT the cpu meter usually hovers around 45 to 50%. That’s Live CPU meter. Window’s cpu meter shows an even smaller CPU footprint of under 30%. The Audio side of things in live is smooth, no audio glitch, no stutter. No crackling. Just a ridiculously slow and hazardous framerate that makes Live sets inoperable.

This doesn’t seem like an isolated issue :

Is this while a juce gui is open, or just by simply having the plug-in inserted with its editor closed?

By simply having the plug-ins inserted with their editor closed, the crawling symptoms will start to occure and the issues will start to build up.

The show then stops when opening the GUI’s a (reasonably) large project.

How many tracks or plugins are a reasonbly large project.? And if no plugin Gui is open, what is crawling?

Is the host keeping the editors alive? My thinking leans toward the editors being alive when not shown, leading to a lot of extra work being done.

My random intuition is OpenGL contexts and hidden but not destroyed editors.

By editors do you mean the Ableton 3rd party plugin “container” that allows control over the plugin’s parameter without having to call its UI on screen?

I can send you links with video exemples -

We got a couple of similar reports of this on a recently updated plugin. Both Windows 10 and 11 and both Live 10 and 11.

We can’t replicate the issue, but it looks like Live CPU meter goes over 100%, sometimes if you load more than one instances, sometimes only one.

Are the issues with plugins built against JUCE 7.0.x? Or also 6.x?

Is anyone seeing this in DAWs besides Ableton Live?

I feel like when I first started testing plug-ins in Ableton Live it did some stuff with the plug-in editors that I wasn’t expecting. I naively assumed the plug-in processor instance would be instantiated and its state deserialized before the editor was instantiated on-demand (which seems to be the case in other environments), but in fact the editor gets instantiated before the plug-in is given its state, and before the plug-in editor is actually opened by the user through Ableton’s interface (specifically when a project is loaded).

I’m fairly certain Ableton instantiates all of the plug-in editors, whether they are open or not, right away, but doesn’t show them visibly. Perhaps this is an attempt to speed up hiding/showing plug-in editors by having them already instantiated and in memory. However, I could imagine a scenario where all these plug-ins have things like timers and other stuff going creating a lot of activity on the message thread.

Or as someone else mentioned, having a ton of OpenGL contexts hidden (because of the behavior mentioned above). I’ve also noticed that having a bunch of instances of a plug-in editor window with OpenGL contexts can bring performance to a crawl.

I know every DAW/host is different, and we can’t really expect any particular consistent behavior, but I’m curious what the general expectation is around plug-in editor lifetime is to everyone. Do you all expect the plug-in editor should only be created/destroyed when it is actually shown by the user, or do you design with the anticipation it will be created in memory, but hidden as Ableton seems to do? Perhaps mismatched expectations with how Ableton is handling editors lifetimes has something to do with it?

Edit:

I feel like identifying a list of JUCE plug-ins that cause the issue, as well as how many instances of them in a project are required is a good start. Are there JUCE plug-ins that don’t cause the issue? From there, what version of JUCE were they built with, and do they use OpenGL or software renderer? Do they extensively use things like timers/animation in their editors?

Does anyone have a plug-in they’ve created that suffers from this issue and would be willing to throw their stats into the conversation?

So, for us, it’s a specific plugin for now, Springs, compiled with JUCE 7.0.4 from mid January I think. I’ll try a new build with dev tip tomorrow.

Anyway, we can’t replicate it here, at all.

So far the affected customers reported that the Live CPU meter goes way over 100%:

One customer reported the issue to be fixed after a restart, which doesn’t make any sense…

Live is definitely pre-loading the Editor before the processor is ready. I had to set a check because some of my UI elements were showing wrong values (preset menu, most of the times).

I have a uint32 at the very beginning of the processor construct where I store the current time:

initStartTimeMs = Time::getMillisecondCounter();

and a public method to check the time passed:

uint32 checkTimePassed() { return (Time::getMillisecondCounter()-initStartTimeMs); }

on the Editor, I have a bool that I set at the very beginning of its constructor

calledBeforeInit = (processor.checkTimePassed() < 500);

and, finally, in the timer callback, I perform the check and re-run any method that relies on informations coming from the processor

if(calledBeforeInit)
{
    DBG("calledBeforeInit");
    myMethods();
    repaint(); //in case you need it
    calledBeforeInit = false;
}

Usually, this will only get called once. Hope this could help.

Cheers,
Luca

1 Like

I did some testing on 4 DAWs in Windows 10: Ableton Live, Reaper, Renoise, and Tracktion Waveform, to see how they treated editor lifetime.

First, I’m going to use the term on-demand to indicate that the host only calls the constructor of an editor when the user launches the editor window from the host’s UI, and calls the destructor when the user closes the editor window. Lazy loading, if you will.

Here’s what I found:

Ableton Live
On project load: Loads all plug-in editors into memory, timers run, etc., but keeps them invisible.

The only way to achieve an on-demand behavior is to manually open and then close all editor windows once. After project load and the initial open/close, the editors behave on-demand and construct/destruct when the user shows/hides the editor window.

Reaper
Plug-in editors are loaded on-demand based on the user hiding/showing the editor.

Renoise
Basically, similar to Ableton Live: Initially loads everything into memory, running, but hidden. And after an initial manual open/close, behaves on-demand after that.

Tracktion Waveform
Plug-in editors are loaded on-demand based on the user hiding/showing the editor.


Possible Performance Implications
A lot of plug-in editors using JUCE probably use things like timers, etc. for updating GUI components like level meters or any realtime visualizations they have.

If developers don’t realize this is the case, they may avoid optimizations like checking in the editor’s timerCallback() whether isShowing() method returns false or not and continuing to let the host run their potentially expensive timer update routines even though the editor is not currently visible.

Furthermore, even if a plug-in editor’s update routines aren’t too CPU intensive by themselves, now imagine you have 20 instances of that plug-in editor running those routines at the same time, it could definitely add up, bog down the message thread, and bring GUI performance of the host to a crawl.

Not to mention the potential issues of having a ton of OpenGL contexts initialized and active at the same time.

How to Test
After initial load, if you manually open/close all the plug-in instances’ editor windows once, it resumes the on-demand behavior, and calls the editor’s destructor removing it from memory. Doing this for every plug-in instance in an offending/slow project, after initially opening it, should theoretically result with an improvement to GUI performance if this is actually the cause of the problem.

From a developer’s standpoint, you can just check for isShowing() == false in the editor’s timer callback/update routines and return immediately. In other words, the editor is in memory, but not visible, so don’t run update logic. The timers will still run, but hopefully just having this single check won’t cause much of an impact.

It looks like when repaint() is called within a timerCallback() and isShowing() == false JUCE is optimized to avoid calling paint(), which is good, but if you are doing any expensive operations in your callback before calling repaint() they will still run.

11 Likes

I think renderOpenGL may also be called in this case, which would mean any OpenGL plugins would be calling swapBuffer at their swap interval which can be expensive.

Edit: Looks like at least w/Ableton, the editor is created but newOpenGLContextCreated() isn’t called until the editor is actually made visible. Which is good.

1 Like

Thank you so much for this post, @Cymatic.

I’m tending to think the best way to fix it (either from a plugin devs end, or JUCE end) is to have the editor just wrap a pointer to the “real” editor (really just a Component inside of the editor), which would then be created/destroyed by the editor based on if the plugin is showing or not.

Are there any disadvantages to doing it this way? I also suspect that the visibilityChanged() callback can be used in this case instead of a timer.

1 Like

Although plug-in developers could be checking for isShowing, is it such an unreasonable assumption to make that a host would only construct the editor when it is being viewed?

I dread to think of the RAM wastage having potentially hundreds of UIs constructed but not showing considering that high-DPI bitmaps are required by so many plug-in interfaces.

I’m reading on a Discord audio group that Ableton has been responding via user-level support that they believe it is on plug-in devs not them to fix this. We can do more not to trigger the timers for sure, but we can’t reasonably do much about the RAM wastage.

I’m sure Live users would not want so much memory being dedicated to UIs that they may not even opened once during a session. I think it’s something for Ableton to take another look at.

4 Likes