GPU switching issue on macOS

85 views
Skip to first unread message

Marshall Greenblatt

unread,
Mar 13, 2018, 5:22:59 PM3/13/18
to graphics-dev, Zhenyao Mo
Hi All,

GPU switching stopped working (now always "Requires High Perf GPU") in my Chromium-based application some time between M61 and M62 (July thru Sept). Based on my debugging [1] it appears that DisplayReconfigCallback [2] is only called on application startup before the ImageTransportSurfaceOverlayMac object has been created, and consequently ImageTransportSurfaceOverlayMac::OnGpuSwitched is never called to set the |gl_renderer_id_| value. This causes GLContextCGL::ForceGpuSwitchIfNeeded [4] to bail out early instead of calling CGLSetVirtualScreen to switch the GPU. Can anyone explain how DisplayReconfigCallback (or similar) is triggered in Chrome such that GPU switching works as expected there?

Thanks,
Marshall

Zhenyao Mo

unread,
Mar 14, 2018, 2:01:38 PM3/14/18
to Marshall Greenblatt, graphics-dev
DisplayReconfigCallback is called right before/after GPU switching. So unless you actively call ForceGpuSwitch(), GPU switching won't happen. In Chrome, when we create a WebGL context, we force to switch to discrete GPU, therefore DisplayReconfigCallback will be triggered at that point.

Does this answer your question?

Marshall Greenblatt

unread,
Mar 14, 2018, 2:11:25 PM3/14/18
to Zhenyao Mo, graphics-dev
Hi Zhenyao,

Where in the WebGL code does it force the switch to/from discrete GPU? If no WebGL content is loaded what might cause the application to start with the discrete GPU enabled instead of the integrated GPU?

Thanks,
Marshall

Zhenyao Mo

unread,
Mar 14, 2018, 5:37:22 PM3/14/18
to Marshall Greenblatt, graphics-dev
If you pass in --force_discrete_gpu, it will force Chrome to start with the discrete GPU.  This is
​wired ​
through GpuDriverBugList and the gpu/config/gpu_switching.cc.

As for WebGL, search in cs.chromium.org for PreferDiscreteGpu and you will see how it is wired up for WebGL.

John Bauman

unread,
Mar 14, 2018, 6:15:53 PM3/14/18
to Zhenyao Mo, Marshall Greenblatt, graphics-dev
At least in the past, the info.plist for the application also needed to include NSSupportsAutomaticGraphicsSwitching to allow use of the integrated GPU. I'm not sure if that's necessary anymore. Also ensure kCGLPFAAllowOfflineRenderers is being passed when creating GL contexts.

Zhenyao Mo

unread,
Mar 14, 2018, 9:14:50 PM3/14/18
to John Bauman, Marshall Greenblatt, graphics-dev
Yes, that was the time when Apple doesn't have a public way to support dual GPU selection. Now they do, so info.plist hack is no longer necessary.

Marshall Greenblatt

unread,
Mar 15, 2018, 3:07:55 PM3/15/18
to Zhenyao Mo, John Bauman, graphics-dev
Thanks for the feedback.

I think I've identified the primary symptom of the problem: CGLGetVirtualScreen [1] is returning 0 for Chrome and 1 for my app from the beginning, and the GPU is not actually switching. If I'm understanding the documentation [2] correctly this means that Chrome is targeting the integrated GPU and my app is targeting the discrete GPU. Both Chrome and my app are being initialized with the same GLContextAttribs.gpu_preference (PreferIntegratedGpu) and GLImplementation (DesktopGLCoreProfile) values in CreateGLContext [3].

The problem also reproduces with a local build of content_shell (I'm testing at version 65.0.3325.146). Both my app and content_shell create the NSWindow with the same attributes [4]. Could something here be causing the problem?

Thanks,

Ken Russell

unread,
Mar 15, 2018, 4:04:45 PM3/15/18
to Marshall Greenblatt, Zhenyao Mo, John Bauman, graphics-dev
I don't think the call to CGLSetVirtualScreen is actually needed to get the GPU to switch. That was found necessary a while ago for best performance, but according to a tool like gfxCardStatus, the thing which actually causes the discrete GPU to activate is the allocation of a CGLPixelFormatObj which says that it doesn't support offline renderers, thereby activating the discrete GPU.


Not sure why content_shell behaves differently from Chrome.

Marshall Greenblatt

unread,
Mar 15, 2018, 5:03:33 PM3/15/18
to Ken Russell, Zhenyao Mo, John Bauman, graphics-dev
On Thu, Mar 15, 2018 at 4:04 PM, Ken Russell <k...@chromium.org> wrote:
I don't think the call to CGLSetVirtualScreen is actually needed to get the GPU to switch. That was found necessary a while ago for best performance, but according to a tool like gfxCardStatus, the thing which actually causes the discrete GPU to activate is the allocation of a CGLPixelFormatObj which says that it doesn't support offline renderers, thereby activating the discrete GPU.

Thanks for the hint. It looks like InitializeOnOffForSandbox [1] is the culprit, calling CGLChoosePixelFormat with an empty CGLPixelFormatAttribute array due to
GLContext::SwitchableGPUsSupported returning false (SetSwitchableGPUsSupported [2] is called later in the startup process). In Chrome something is triggering an immediate switch back to the integrated GPU -- I haven't figured that one out yet.

Chrome:

CGDisplayRegisterReconfigurationCallback

InitializeOneOffForSandbox

InitializeOneOffForSandbox CGLChoosePixelFormat  << empty CGLPixelFormatAttribute array

DisplayReconfigCallback flags=1d

DisplayReconfigCallback flags=1d

DisplayReconfigCallback flags=8476d

DisplayReconfigCallback gpu_changed=1  << switch to discrete GPU

GpuDataManagerImplPrivate::HandleGpuSwitch

GpuSwitchingManager::NotifyGpuSwitched

DisplayReconfigCallback flags=8736d

DisplayReconfigCallback gpu_changed=0

InitializeOneOffForSandbox CGLChoosePixelFormat success

DisplayReconfigCallback flags=1d

DisplayReconfigCallback flags=1d

DisplayReconfigCallback flags=8476d

DisplayReconfigCallback gpu_changed=1  << switch to integrated GPU (not sure what causes this)

GpuDataManagerImplPrivate::HandleGpuSwitch

GpuSwitchingManager::NotifyGpuSwitched

DisplayReconfigCallback flags=8736d

DisplayReconfigCallback gpu_changed=0

InitializeSwitchableGPUs  << call SetSwitchableGPUsSupported


My app:

CGDisplayRegisterReconfigurationCallback

InitializeOneOffForSandbox

InitializeOneOffForSandbox CGLChoosePixelFormat  << empty CGLPixelFormatAttribute array

DisplayReconfigCallback flags=1d

DisplayReconfigCallback flags=1d

DisplayReconfigCallback flags=8476d

DisplayReconfigCallback gpu_changed=1  << switch to discrete GPU

GpuDataManagerImplPrivate::HandleGpuSwitch

GpuSwitchingManager::NotifyGpuSwitched

DisplayReconfigCallback flags=8736d

DisplayReconfigCallback gpu_changed=0

InitializeOneOffForSandbox CGLChoosePixelFormat success

InitializeSwitchableGPUs  << call SetSwitchableGPUsSupported


BTW, it looks like there might also be a bug in IOSurfaceContext::Get [3] because it adds the "end of array" 0 value twice.



Marshall Greenblatt

unread,
Mar 15, 2018, 6:58:04 PM3/15/18
to Ken Russell, Zhenyao Mo, John Bauman, graphics-dev
We added the check for SwitchableGPUsSupported before specifying kCGLPFAAllowOfflineRenderers back in 2012 because of https://crbug.com/132235. Removing this check in InitializeOneOffForSandbox seems by far the easiest fix. Is it safe today to always specify kCGLPFAAllowOfflineRenderers?

Thanks,
Marshall

Ken Russell

unread,
Mar 15, 2018, 7:37:06 PM3/15/18
to Marshall Greenblatt, Zhenyao Mo, John Bauman, graphics-dev
I don't know. You could try it and see if anything breaks.

Maybe the info.plist does in fact need to specify NSSupportsAutomaticGraphicsSwitching. See https://developer.apple.com/library/content/qa/qa1734/_index.html .

Marshall Greenblatt

unread,
Mar 16, 2018, 3:03:04 PM3/16/18
to Ken Russell, Zhenyao Mo, John Bauman, graphics-dev
On Thu, Mar 15, 2018 at 7:36 PM, Ken Russell <k...@chromium.org> wrote:
I don't know. You could try it and see if anything breaks.

Thanks, I'll run a trial and report my results in https://crbug.com/822837
Reply all
Reply to author
Forward
0 new messages