Laggy GUIs on OSX El Capitan?


#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.


#21

Thanks for the feedback on the original problem. The queuing you're describing seems exactly in line with the behavior I'm experiencing. I'll make a simple demo when I get a chance that we can use as a "control group." Thanks again,  


#22

Hi Jules,

Please find attache a simple Component that will help you reproduce and see the problem.

It contains a slider that will allow you to update the refresh rate of the timer to the desire value. The timer callback will then ask for a repaint, and the paint of the component will simply draw a cube and approximately calculate the FPS that we're really getting/

 

The best way to see the issue IMO is to compile an AAX plugin, and just add this component in the PluginEditor. If you then instantiate this plugin on El Capitan & PT12, you'll notice that as you increase the timer's refresh rate, PT12 Meter FPS will drop and become really sluggy, and the approximated FPS we're calculating will also deviate more and more from the timer's refresh rate we're asking. 

Please take a look at this and tell us what you think. 

 

Regards,

 

Nicolas

 


#23

I tried running this, but it doesn't seem to actually be doing anything wrong.. Yes, it does tie-up the app's event loop with constant repainting if you crank up the frame rate, but I debugged to see what's going on internally and can't find anything amiss.

There's no evidence that it's stacking up a queue of repaint events, even if you turn the speed way up, that doesn't seem to be the case at all. I guess the maybe in 10.11 Apple changed some kind of priority balancing algorithm that affects the way different views in the app get their paint methods called, but I can't see anything I could do to change or work around that..


#24

Hi Jules,

 

Thanks for taking the time to check this. We kind of reach the same conclusion. The problem is that right now, we can't really do any smooth animation using Juce on El Capitan, so I'm guessing that as plugin user will switch to 10.11 you'll hear more and more about this... unless Apple do something about it.

 

The only work-around is to go the OpenGL way, and we did it for several product already, but sometime it's not that easy because we want multiple part of the pluin to be animated, so we'd need multiple OpenGL context.


#25

Jules, thanks for taking the time to look into this. We've had similar conclusions to you and Nico_, and I definitely agree that this issue will likely pop up more going forward as end users upgrade to El Capitan.


#26

It is all a bit strange. We also saw some odd interactions between Tracktion's repainting of level meters and plugin GUIs. But I'm really not sure at all what we could do about it, it does seem like an artefact of the 10.11 event queue. It's something we're aware of and will do our best to see if there's a workaround.


#27

It looks like the Reaper guy found a solution

 

http://forum.cockos.com/showthread.php?t=169884

 

Maybe worth contacting him ?


#28

From that thread it sounds like he tried some workarounds but wasn't really successful - people were still saying it didn't work on some machines.

This is a really annoying one, it might be something that Apple has to sort out. But it sounds like many other apps are struggling with it too.