Multiple displays result in frame stutters on macOS

tl;dr


When using either CAMetalLayer.nextDrawable or MTKViewDelegate's drawInMTKView: APIs, a consistent 60fps does not seem possible when multiple displays are attached to macOS.


Background


I added Metal support to RetroArch, a popular open source game front end, and have been troubleshooting an issue where the frame rate will periodically drop below 60fps, causing frame and audio stuttering. To eliminate any issues with RetroArch itself, I ended up creating my own minimal libretro front end and using MTKView's drawInMTKView, which is synchronized with a CVDisplayLink timer. This too had frame stuttering issues when multiple displays are connected. When I limited macOS to a single display, there is no stuttering.


I don't yet know if this is a macOS issue or whether I would need to manage my own CVDisplayLink timer and constantly update which CGDisplay it is associated with. Debugging the minimal front end, I added a symbolic breakpoint to determine how the CVDisplayLink was created for triggering the drawInMTKView. I hit a call to CVDisplayLinkCreateWithActiveCGDisplays during initialization of the MTKView. During initialization, a single call was made to CVDisplayLinkSetCurrentCGDisplay, however, moving the window between displays did not result in further calls to this API.


My hypothesis is the CVDisplayLink, CAMetalLayer and CGDisplay presenting the view are not congruent, however, it is unclear if it is even possible to manage all this within user code.

Replies

I think it also gets worse if you have multiple applications drawing to the screen using 'accelerated' APIs like Metal or OpenGL.


I'm the author of a streaming app on OSX that no one uses (CocoaSplit) and I've always noticed that if you have CocoaSplit open and it is visible (on another screen) and rendering a preview view of the stream, games have more hitching randomly. I always assumed it was somehow my fault (maybe the rendering code in my app is somehow terrible) but I did a test with OBS and the same thing happened.


I decided to do some testing.


I modified Apple's HelloTriangle sample metal application to keep track of FPS and to visually indicate when the FPS goes below 58.

On a single display it is rock solid 60fps. With more than one display it will occasionally dip down to 40-50fps, but not for too long.


If I then run a second instance of HelloTriangle, they BOTH start dropping framerates more frequently. It seems to be even more frequent and worse if they're on separate displays.


Another curious thing. On a single display if you enable Quartz Debug's framerate meter, it is a solid 60fps if HelloTriangle is running. If you attach a second display and run HelloTriangle, quartz debug shows a framerate of 120fps. However, the MTKView delegate's draw method is still called at around 60fps (except when it stutters).


There is something very wrong with multiple display performance it seems. I'm going to try to run some more tests and put together a radar on this; I really hope this can get fixed ASAP.


CocoaSplit renders via CAMetalLayer driven by a CVDisplayLink(). I tried making sure the display link was using the display CocoaSplit was rendering to, but that didn't seem to help the problem at all.

I have this stuttering issue as well regardless of if I use a MTKView or roll my own NSView with CAMetalLayer and a DisplayLink callback.


If I set preferredFramesPerSecond to 30 instead of 60, when the lag kicks in it is massively bigger than it normally is, as if the stuttering delay is porportional such that the lag at 30 fps is twice as much as 60 fps or maybe even more since it feels like it hangs alot longer. This is clearly Apple's fault.


I actually submitted a bug report for a related display link lag a week or two ago, but got no response from Apple. Also, Apple has been unable to competently respond to my paid technical support requests regarding metal problems either. I am feeling like maybe everyone who actually knew the real stuff is maybe no longer at Apple, and there is no one over there left who can hold a conversation at my level.


My app is a drawing app, and the stuttering is a really painful and bad experience while drawing. I don't have a cable to connect it to the iPad, so I don't know if this is a mac only issue, but I know for a fact I haven't experienced stuttering in the iPad version of my app for the last year, so I don't think it is my code that is the problem.


Btw, I am on Mojave. Have you guys tried to see if the stuttering problem is in High Sierra as well? I can't revert to try it, because the High Sierra installer is broken for me.


Also, I am on a macbook pro 2017 model with the intel gpu.

Update:


I was finally able to revert to High Sierra by going back to Sierra first, what a chore...


This bug does not appear to happen in High Sierra.


Also, when I moved back to High Sierra any code which was using MTKView was completely broken.


Funny, because in Mojave the Display link approach had critical lag, what a joke.

Hi MoreLightning,


What are the bug report and support incident IDs that you've received?

The first one where I was able to trigger progressive lag was 47498589


The second which is random sporadic lag bursts matching this forum thread is 47947771


I believe these both share the same root problem.


Note: I did not see any trouble in the cpu or gpu performance during the lag, even the progressive one. Which implied it was some kind of buildup in the underlying OS display queue.


Btw, it took me the entire weekend to revert back to High Sierra, and the journey was long and hard with many other severe bugs in disk utility, and I am not going to partition my drive again and install mojave for fear of going through that nightmare again. I would guess the root of this bug to be a trivial one liner change in mojave that you should be able to locate with discipline within a day's work by comparing the source files related to CADisplayLink.

Thank you for citing the bug reports MoreLightning.

Your welcome.


I highly advise locating the person responsible, and firing them on the spot.


Any person who would first create and then let such a critical bug pass into an OS release is not competent enough to work on foundational code.


I am highly dissapointed with the extreme decline in standards and increasing bugs due to incomptence over there.


I hold high standards for the products I release, and my clients pay because of those high standards. I expect the multi-billion dollar company who has chosen to play the OS role to do their equal share of sincere work, and do the job right.


Fire the incompetent childen and fix the problems.

Hello all,


This is clearly a bug that appeared in macOS Mojave. No problem on macOS High Sierra.

I had reported this bug early November 2018 (yes, long time ago). After exchanging with Apple engineers in the comments (providing much details and logs) for a few days, they suddently stopped asking questions. I don't know why, but I had no news.

So I finally paid (!!!) for a technical support request to get some update about this issue : the reply was a bit fuzzy, and I really don't know if Apple engineers are still working on this issue. However, they mentionned a workaround : disabling vsync. But this isn't a true workaround, rather a big regression (at least, if you care about rendering quality).


Anyway, I tested with latest macOS beta (Mojave 10.14.4 18E194D) and the issue persists. Of course, no problem at all with High Sierra, so this is a naughty bug in Mojave software !


Recently, I created a Github project to reproduce this issue with OpenGL or Metal : https://github.com/anome/mojave-issue

So anyone reading this post can try by himself, and create a bug report for Apple : https://bugreport.apple.com



Related bug ID : 45867287, 48235422

Apple intern bug ID : 47968184

I've an LG monitor with the freesync option, toggling the option doesn't fix the issue

Can anyone confirm if this is still an issue in 10.14.4? I THINK it still is, but I'd like others to verify also.

Yes, in my case this is still an issue. I've an UHD 630

Hello.

Yes it is. I updated my samplecode to reproduce the issue here : https://github.com/anome/mojave-issue

The problem can be seen after a few seconds on a regular basis.

I can confirm that this is still a problem in 10.14.5, and for me, it's affecting any kind of graphical application at all, whether it's an application I'm working on, or just a game.


Disabling system-wide Vsync with QuartzDebug is a workaround, but it also results in excessive screen tearing.


Can anyone from Apple confirm that this is even being worked on? This issue's been around for the better part of a year and it doesn't seem like any attempt has been made to fix it.


Will 10.14.6 address it? Was the fix just too big for a patch and is going to be included in 10.15? Or will 10.15 exhibit the same behavior? I don't think people would mind waiting for a fix as much if they at least knew that one was coming.


I love using MacOS, but deprecating OpenGL and then allowing these kinds of bugs to linger for months on end is really hurting its usability.

Hello.


I reported this bug back in November 2018. They have been working on it, but never found a solution apparently.

Apple engineers won't ever share progresse about an issue anyway, so don't espect an ETA.


By the way, I updated my samplecode to reproduce the issue : https://github.com/anome/mojave-issue (click on "releases" to get a compiled application).

This code is simply a slight modified version of Apple first Metal samplecode : https://developer.apple.com/documentation/metal/using_a_render_pipeline_to_render_primitives


Current beta version 10.14.6 (18G29g) still have the issue. Reported this bug one more time to Apple ... but loosing faith.

Tested on first macOS Catalina : so far, the problem seems fixed.