Dynamic Switching Problems with Metal on Big Sur

On a dynamic switching capable Mac, calling MTLCreateSystemDefaultDevice() in a Metal app is supposed to return the discrete GPU and have the system switch the display to being driven by the discrete GPU. As explained in the comment in MTLDevice.h above that method:

"On Mac OS X systems that support automatic graphics switching, calling this API to get a Metal device will cause the system to switch to the high power GPU.  On other systems that support more than one GPU it will return the GPU that is associated with the main display."

However on my application I am not seeing this occur anymore. I've tested the app on a Catalina partition on the exact same machine and verified it would work properly on 10.15.7 but on any build of Big Sur I have tried so far, it hasn't worked.

I even tested this functionality using one of Apple's demo apps (can find it by searching for "Using Metal to Draw a View's Contents"). It is a very simple app that just draws a blue screen but when ran on Catalina, I verified that dynamic switching was occurring properly (Activity Monitor's energy section will list the app with a Yes in the Graphics Card column specifying that it requires a High Performance GPU). For an app running via Xcode, it will change Xcode to have a Yes in the Graphics Card column. Running the exact same demo on Big Sur no longer triggers that change.

Note: You have to make sure you are using a machine with the Automatic Graphics Switching option enabled in the battery settings of System Preferences and you can't be connected to an external display (this will force the system to use the discrete gpu).

Has anyone else seen this issue? And if so, has anyone managed to fix it? I wasn't able to find any mention of a new api or an api change that is needed for this. I have also tried using NSSupportsAutomaticGraphicsSwitching in the Info.plist of the app set to both YES or NO, just in case maybe the api was updated to listen to that flag for Metal, but it had no effect.

I have submitted a ticket for this using the Feedback Assistant app, but I wanted to see if any other developers have been having this issue as well or have found a solution.
What is the FB number for your ticket?

Does MTLCopyAllDevices() return the discrete GPU device as one of the devices?
The FB number for my ticket is FB9029620.

In the case of my Mac, the MTLCreateSystemDefaultDevice() call returns the MTLDevice for my AMD Radeon Pro 560 and the call to MTLCopyAllDevices() returns an array of two devices: the same AMD Radeon Pro 560 and the Intel(R) HD Graphics 630. So yes, it does return the discrete GPU device as one of the devices.
So I'm unclear on the problem here. I thought that you saw MTLCreateSystemDefaultDevice() wasn't returning the discrete GPU, but you're saying it is.

If the device returned to you by MTLCreateSystemDefaultDevice() is the AMD Radeon Pro 560, then your app will render with that discrete GPU device regardless of what the Activity Monitor says. You may need to actually commit a command buffer with the device work for the GPU to show up in Activity Monitor. Merely getting the device may not fire up the GPU.
The problem is that the Mac's display is not switching to the discrete GPU in response to the MTLCreateSystemDefaultDevice() like it used to in Catalina (and how the comments for the method describe).

I have Automatic Graphics Switching enabled in the Battery settings on the Mac and have tested the same build of our app on both Catalina and Big Sur on the same machine. On Catalina, I have verified that the display switched to the discrete GPU in response to the MTLCreateSystemDefaultDevice(). This would be reflected in the Activity Monitor like I mentioned, but I also tested it by requesting the metal device running the display using CGDirectDisplayCopyCurrentMetalDevice() where I pass in the display ID of the display (no external displays connected). On Catalina, it would return the discrete gpu as I expected from it switching, but on Big Sur this method will return the Integrated gpu instead. I put that method on a button press so I could test after command buffers have been committed like you said and there is no change.

I verified the same behavior I describe above by using the small Apple sample app I mentioned as well. That way the problem could be isolated to a much smaller code base.



Okay. I understand; the display isn't switching (which means the window server needs to blit your drawables to the other GPU for display). I will ask some of our engineers why this behavior may have changed (and whether it was intentional)
So I'm told the behavior would have changed in Catalina, not Big Sur. (So the policy would have changed after Mojave).

Setting the device property of the CAMetalLayer (not getting it with MTLCreateSystemDefaultDevice()) should trigger the display to switch. Are you checking CGDirectDisplayCopyCurrentMetalDevice() after setting the device in CAMetalLayer?

Also, is it possible that there are other Metal apps running that set a Layer to the integrated device thereby preventing your app from switching?

For my testing, it was always on a brand new launch of the machine after shutting down. I put the CGDirectDisplayCopyCurrentMetalDevice() on a button press, so it was definitely checking after the device was assigned to the CAMetalLayer and had already rendered frames to it. 

As for the question about other Metal apps running that would prevent it, this is why I would test on a new launch of the machine in each OS, with no other applications running. And by using the simple "Using Metal to Draw a View's Contents" app on both OS's it was very easy to tell that what was working in Catalina, is not working on Big Sur, since the amount of code is much smaller to check. You can very easily add a menu item or button on screen to check CGDirectDisplayCopyCurrentMetalDevice() and confirm that on Catalina it switches to the Discrete GPU but on Big Sur it does not. 

Since I can’t attach files here, I attached to the feedback ticket the demo app where I added a menu item to the main app menu named “Check Display GPU” and it prints out the description of the GPU coming from the main display. On Catalina, after it runs it tells me the display is now being driven by the Discrete GPU. On Big Sur, it still says the Intel. Both fresh launches of the OS on the same machine, no other applications running.

The fact that the display will update to the discrete GPU on Big Sur if I run an app using Open GL (for example the default Chess.app) seems to confirm that there isn't another app running preventing the switching from occurring. You can run my demo on a fresh launch of Big Sur, see that the menu prints out the description of the Intel GPU to console. Then launch the Chess.app and once that is launched, the menu on the demo app will print out the description of the Discrete GPU. Close the Chess.app and then check the menu button again with the demo app, and it prints out the description of the Intel GPU cause it went back. Even though the app is set to use the Discrete GPU using the api and should be keeping the display in that state. Hopefully this can help show what's wrong.
Just adding a "me too": we are seeing this exact behavior down to the last detail. MTLCreateSystemDefaultDevice will not switch the window server to the discrete GPU, and setting the device in the CAMetalLayer does not force it either.

Opening Chess.app (or Photos.app) will force the change, and we'll then see it reflected in our apps.

The only other additional note we've got is that we don't see the problem when running in a fullscreen state, only windowed.

I'm also seeing this issue with Big Sur. In my case I am using a MTKView via NSViewRepresentable as my app is built using SwiftUI.

MTLCreateSystemDefaultDevice will not trigger the display to use the discrete GPU. It returns the discrete GPU but the display is still using the integrated. My machine also has the AMD Radeon Pro 560 and the Intel(R) HD Graphics 630. Opening Pixelmator Pro will immediately trigger the switch to the discrete GPU and when closing the display switches back to the integrated.

Any feedback would be most appreciated!

I'm seeing this issue in macOS Monterey 12.1 (on a 2016 MBP). I'm using a CAMetalLayer with MTLCreateSystemDefaultDevice.

Are there any known workarounds aside from fullscreen or keeping another app like Photos open?

Facing the same problem with the CAD-style app drawing its contents using Metal. To save resources the app redraws/animates the contents only when a change has occurred (like changing a radius of a circle). Apparently, macOS interprets this behavior as not requiring too much GPU power and keeps the display attached to the integrated GPU. I suspect it also optimizes some resources used for blitting into the integrated GPU (like releasing some buffers after some timeout), so there are annoying redraw stalls of 1-2 seconds, especially after 10-15 seconds of inactivity.

Dynamic Switching Problems with Metal on Big Sur
 
 
Q