Laggy GUIs on OSX El Capitan?


#1

Hi All-

Has anyone else experienced laggy GUI behavior in their plug-ins on El Capitan? When we have several of our plug-in GUIs open at once, the metering and other parameter updating seems to slow down quite substantially in each plug-in. Profiling the code doesn't show anything immediately apparent, so while I keep banging away I figured I'd see if anyone has experienced similar behavior? We've never experienced this issue on other OSX versions until now, and Windows continues to work fine.

Thanks!

 


Steppy automation recording on OSX
#2

Are you seeing this in different hosts or one in particular?

We have had issues since Yosemite in Pro Tools 11 and 12. Other hosts are OK. 


#3

We noticed similar issues in Logic Pro. The affected plugins had a lot of parameters that where synced using timers. After changing the sync from timers to a threaded approach where a message is only posted when a parameter needs to be changed improved matters.

I might be wrong, but my impression is that using a lot of timers and other stuff that posts messages (listeners etc.) “overloads” OS X’s message queue so other messages like repaints will be cast away resulting in the seen GUI lags. Another reason might be that all the timers together took to much CPU but on the other hand they mainly compared two values which shouldn’t need that much CPU even with the calling overhead.


#4

Hi All-

We are seeing this in all hosts, though Pro Tools and Logic seem to be the most affected.

We use an actionListenerCallback() to update meters in the plug-in as their values change (which in the case of input/output meters, is close to continuously). We also have some linked sliders in the GUI that are updated in the actionListenerCallback(), which will be very laggy to update when the user changes one. This seems to indicate that the message queue is getting overloaded.

ckk -

I"ve looked at the message queue and it does seem to be quite slow. When you say that repaints might be "cast away", do you mean that they'll be delayed, or dropped entirely?

Thanks,

Tom


#5

I don't know of any differences in the way 10.11 handles the message thread, but that could explain this kind of thing.

FWIW ActionListener isn't a very good class to use for things like meters, as it emits a stream of messages which all get delivered - better to use a ChangeListener or AsyncUpdater, which coalesces messages to reduce the number that are needed.


#6

Hi Jules-

I've tried changing the meters to update with a timerCallback() instead of an actionListenerCallback(), which seems to have helped the problem substantially. This would seem to indicate that 10.11 is less effective in handling the message thread, since this is the first version of OSX where this behavior has shown consistently for us. (We've had several users in the past report issues), but never to this extent of sluggishness.)

I will certainly look into using a ChangeListener or AsyncUpdater, using an ActionListener does seem a somewhat naive way to update meters.


#7

I forgot to mention, the problem occured with 10.9 or 10.10 in our case.

Can’t say whether a repaint message is dropped if another one is pending but it at least messas up a regular repaint.


#8

I've been poking around on some other audio forums, and it seems that Apple has tightened up the time available for redraws in recent OSX versions, which is likely the base of this problem.


#9

Hi,

we've been experiencing the same problem with each of our plugins containing a meter.

I'v been  reproducing the problem with a simple test plugin that basically do nothing but drawing a little animation like our meter implementation does: by asking a repaint on a Timer. The lag is the most visible on ProTools 12, but you can kind of feel it aswell on Steinberg hosts. 

At around 30 FPS (so 33ms of timer update), PT GUI become really laggy, and as you increase the FPS, it become more and more visible.

It looks like asking so much setNeedsDisplayInRect just saturate the message thread to a point that PT can't update his own GUI. This is kind of worrying in my opinion because I don't see anyway around this but switching to full OpenGL mode everytime one need a smooth animation... kind of drastic solution isn't it?

 

 

 

 

 


#10

Nico : did you try to use a ChangeListener instead of a systematic repaint in the timerCallback ?

Anyway, one of my clients reported a very high CPU load for some simple visualization stuff on Mac OS X for a plug-in I have been working on, and the only solution available we have found is to use OpenGL too...


#11

I get that ChangeBroadcaster / ChangeListener can coalesces message, so it can be useful if we would ask for too much repaint than we effectively get / need, but that's not the case here.

Let's keep the 30 FPS example (which is not a crazy framerate for an animation): I still want to have 30 repaint per second, so I need to ask for a repaint approximately every 33ms, and get it.... so I don't see how using a ChangeBroadcaster / ChangeListener would help me, because there is no message to coalesces, I'll still send ask for the same number of repaint per second.

Right now the only way to go would be effectively to go OpenGL mode. But what are you going to do if you have several part of your plugin that needs animations? And what if the animated place of your gui can move? Are you going to create several openGL context and move them around? that sounds inefficient. Are you going to have one big OpenGL context for all of your GUI? You can forget about Juce::Component for your GUI component then...

 

That will sound alarming but in my opinion here's the status right now: you can't use Juce drawing for any animation inside your plugin, unless you don't care about performance on El Capitan. 

 

 

 

 


#12

When was the last time you updated JUCE? There were some very recent changes that might help with this, mainly this one: https://github.com/julianstorer/JUCE/commit/d63fe244b3adb78dbbb70b24f7287abfa5b41d29


#13

I'v just tried with plugin built from a ProJucer generated project, with current codebase.... same problem.


#14

We're experiencing this on a desktop application (the Raven) and it also seems to be related to frequent calls being made to repaint for our meters. This only became an issue after the El Capitan update. In our case, we have a separate thread running at 60fps which looks for any changes made to our incoming metering values and calls repaint() on the meter whenever necessary. After the El Capitan update, it seems that something is happening where the repaint calls are "piling up" and causing our main thread to become unresponsive.

For instance if you start playback (meter values start changing and calling repaint) then try to hit the stop button, you'll get a small delay (1 second) from when the button is clicked and when it actually changes. If you let playback continue for 20 seconds or so, this delay becomes much longer (6 seconds, etc.) so it seems like the frequent calls to repaint are "piling up" and preventing the main thread from doing anything else until they are cleared. Interestingly, the CPU of our program hardly even rises during this situation (totaling ~20%) so I have no idea what's causing the lag. It's almost as if the OS is preventing our app from drawing more CPU in order to account for the frequent repaints. 

As a sanity check, going off of Jules' comments in similar threads suggesting simplifying the paint callback, I tried commenting out all code in our meters' paint function, so they were completely blank. This had no effect on the result and I still experienced the accumulating lag. Does anyone have a clue as to what could be going on here? What even happens when repaint is called on a blank child component that could be causing slowdown? Is there overhead associated with drawing the section of the parent component over which it exists? Isn't that cached?

Here's a gif showing what I'm talking about with paint debugging enabled. Despite the fact that the meters are completely blank, we're still seeing drastic slowdown: 

 

  

 

 


Message queue problem with repaint throttling
#15

*I should note that lowering things to 15fps has worked as a temporary, albeit less-than-ideal, solution. It seems that the improvement is more than linear to the change, so maybe there's some sort of hard limit to repaint call frequency after which the system invokes a penalty (I know that sounds ridiculous). 


#16

 

This had no effect on the result and I still experienced the accumulating lag. 

If you call repaint() on a component (even if its opaque)  all dirty regions will be merged into one big region which contains all dirty regions, which also repaints the gap between the VU-Meters, in your case the mixer. I think this is the explanation.

 

  


#17

No, that's not true.

I've explained this countless times, but here goes again! CoreGraphics maintains a correct multi-rectangle region when you invalidate separate rectangles, and it doesn't repaint any areas between them. What you're getting this confused with is that CoreGraphics doesn't provide a way to ask whether a rectangle intersects the invalid region, so when JUCE components are being redrawn, any components that lie entirely inside hidden regions that are between visible regions can't ask whether they're completely occluded, so they have to call their paint routines anyway, even though nothing they draw will hit the screen.

Re: the original problem, there is some kind of bug which started in OSX10.11 where repaint messages can get queued rather than being merged. We fixed a similar problem involving window resizing, and although I've not seen it happen in normal repaint behaviour, it's certainly possible. Perhaps you could send me a simple bit of demo code that reproduces the problem?


#18

Hi Jule,

 

thx for the explanation. Like I said above, it's really easy to reproduce. I'v been reproducing it with a juce plugin built from a ProJucer generated project. I simply created a little animation running at 60 FPS, but you could just have a component drawing a line and asking his repaint through a Timer at at least 50 FPS, and you'll see the problem if you run this plugin under ProTools for examples.

 

Tomorrow I'll send  the plugin sources...


#19

We've noticed the problem occurring primarily with redraws happening on a timer of 30ms intervals (33.3fps) or lower....in our case these are intput and output meters, which need to redraw constantly. 

As you suggested, we also think this is repaint messages getting backlogged, but have also noticed some slow downs in OSX 10.10 in addition to 10.11. Maybe it's possible that this bug you mentioned in 10.11 was included in a 10.10 update as well?


#20

The bug that we fixed (which involved redraws when resizing windows) was definitely not in the later versions of 10.10, it was only in 10.11. But no idea if this is the same thing or not.