Closed Bug 1579664 Opened 5 years ago Closed 5 years ago

Reduced performance after GPU switch

Categories

(Core :: Graphics: Layers, defect, P3)

All
macOS
defect

Tracking

()

RESOLVED FIXED
mozilla72
Tracking Status
firefox-esr68 --- unaffected
firefox70 --- wontfix
firefox71 --- wontfix
firefox72 --- fixed

People

(Reporter: mstange, Assigned: mstange)

References

(Regression)

Details

(Keywords: perf, regression)

Attachments

(2 files, 1 obsolete file)

On macOS systems with two GPUs (integrated and discrete), scrolling performance in Firefox on macOS is reduced when the current GPU is different from the GPU that was in use when the Firefox window was opened.

Steps to reproduce:

  1. Install gfxCardStatus from https://gfx.io/
  2. Start Firefox with one window, while the integrated GPU is in use.
  3. Go to https://www.mozilla.org/en-US/.
  4. Switch to the discrete GPU using gfxCardStatus.
  5. Open another window of the same size and go to https://www.mozilla.org/en-US/.
  6. Compare scrolling smoothness in the two windows, for example using Quartz Debug's FrameMeter overlay window.
  7. Switch back to the integrated GPU and compare scrolling smoothness again.

Expected results:
Scrolling should be equally smooth in both windows and as smooth as possible.

Actual results:

Opened on integrated GPU Opened on discrete GPU
Scrolled on integrated GPU 55fps 20fps
Scrolled on discrete GPU 40fps-55fps 60fps

Chrome recreates GLContexts when switching GPUs: https://codereview.chromium.org/231863002

Recreating the GLContext would mean that we'd also need to recompile shaders on GPU switch, which is, in some ways, the opposite of what bug 1494763 wants to achieve. Maybe we could keep around two GLContexts, one for each GPU, and reuse them.
Switching GPUs changes a window's CGDirectDisplayID ([[[[window screen] deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue]).
I think the following might be a nice solution:

  1. Create one GLContext per CGDirectDisplayID. For compositing into a window, use the GLContext for that window's screen.
  2. When a CGDirectDisplayID becomes unused, because no window is on a screen with that ID any more, keep around the GLContext if that display ID is for the internal display (CGDisplayIsBuiltin), otherwise throw the GLContext away.
Keywords: perf
Priority: -- → P3
Assignee: nobody → mstange
Status: NEW → ASSIGNED

This is a regression from the Core Animation work. In the past, the OpenGL context from the compositor would follow the GPU switch. Today, all Firefox windows keep using the GPU that was driving the internal display when that Firefox window was opened.

Keywords: regression
Regressed by: 1574538
Attachment #9109794 - Attachment description: Bug 1579664 - Experimental WIP: transfer GLContexts across GPUs on GPU switch → Bug 1579664 - Transfer compositor GLContexts across GPUs on GPU switch. r=jgilbert

I've done some research on this. Useful resources:

One thing that was not clear to me in the past was the fact that, on a dual-GPU Macbook Pro, both GPUs can be used at all times. Only one of the GPUs drives the internal display at any given time, but you are free to use the other GPU for your own rendering. If you do so, getting your context onto the screen will incur a slow copy. You can pick the GPU during pixel format creation, or after creation using CGLSetVirtualScreen.
For example, if you include NSOpenGLPFAAllowOfflineRenderers, using NSOpenGLPFAScreenMask, 1 << 0 in the NSOpenGLPixelFormatAttribute list will pick the integrated GPU on my machine, whereas NSOpenGLPFAScreenMask, 1 << 6 will pick the discrete GPU, without forcing a GPU switch for the internal display, and regardless of which GPU currently drives the internal display.
Metal makes GPU selection a lot clearer: there's one MTLDevice for each GPU, and you can pick any. (You'll usually use the "default" device (which is the discrete GPU) or you get the device that currently drives a given screen.)

When an NSOpenGLContext is associated with an NSView, calling -[NSOpenGLContext update] migrates the context to the GPU that drives the screen that the window is on. This is how we were handling GPU switches before we switched to Core Animation.
For NSOpenGLContexts which are not associated with NSViews, -[NSOpenGLContext update] does not seem to have any useful effect.

Here's a small program that can be used to play with these things.

Only internal display, when using integrated GPU:

mstange@Markuss-MacBook-Pro enumerate-display-masks % ./test
Active display CGDirectDisplayIDs:
 - display mask: 00000000000000000000000000000001, display ID: 01111011110101111111001111111001
Available display masks for pixel format:
 - display mask: 00000000000000000000000000111111, display ID: 01111011110101111111001111111001, renderer: kCGLRendererIntelHD5000ID
 - display mask: 00000000000000000000011111000000, display ID: 01111011110101111111001111111001, renderer: kCGLRendererATIRadeonX4000ID (Radeon HD 7xxx)
 - display mask: 00000000000000000000000000000000, display ID: 00000000000000000000000000000000, renderer: kCGLRendererGenericFloatID (Apple Software Renderer)

Only internal display, when using discrete GPU:

mstange@Markuss-MacBook-Pro enumerate-display-masks % ./test
Active display CGDirectDisplayIDs:
 - display mask: 00000000000000000000000001000000, display ID: 00000100001010000000110000000110
Available display masks for pixel format:
 - display mask: 00000000000000000000000000111111, display ID: 00000100001010000000110000000110, renderer: kCGLRendererIntelHD5000ID
 - display mask: 00000000000000000000011111000000, display ID: 00000100001010000000110000000110, renderer: kCGLRendererATIRadeonX4000ID (Radeon HD 7xxx)
 - display mask: 00000000000000000000000000000000, display ID: 00000000000000000000000000000000, renderer: kCGLRendererGenericFloatID (Apple Software Renderer)

With connected external display, using discrete GPU:

mstange@Markuss-MacBook-Pro enumerate-display-masks % ./test
Active display CGDirectDisplayIDs:
 - display mask: 00000000000000000000000001000000, display ID: 00000100001010000000110000000110
 - display mask: 00000000000000000000001000000000, display ID: 00011011010101100010011001010101
Available display masks for pixel format:
 - display mask: 00000000000000000000000000111111, display ID: 00000100001010000000110000000110, renderer: kCGLRendererIntelHD5000ID
 - display mask: 00000000000000000000011111000000, display ID: 00000100001010000000110000000110, renderer: kCGLRendererATIRadeonX4000ID (Radeon HD 7xxx)
 - display mask: 00000000000000000000000000000000, display ID: 00000000000000000000000000000000, renderer: kCGLRendererGenericFloatID (Apple Software Renderer)

I've filed bug 1597547 on WebGL.

Attachment #9109794 - Attachment description: Bug 1579664 - Transfer compositor GLContexts across GPUs on GPU switch. r=jgilbert → Bug 1579664 - Migrate compositor GLContexts to the active GPU after a GPU switch. r=jgilbert
Pushed by mstange@themasta.com:
https://hg.mozilla.org/integration/autoland/rev/4ec011830a0b
Migrate compositor GLContexts to the active GPU after a GPU switch. r=jgilbert
Status: ASSIGNED → RESOLVED
Closed: 5 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla72

There's only one beta build left before Fx71 goes to RC and this seems like something which could use more baking time than the schedule allows for at this point in the cycle. Marking this as wontfix for 71 but feel free to nominate for uplift if you feel strongly otherwise about that assessment.

I agree, this is a risky change that needs some time to bake.

Regressions: 1599862
Regressions: 1602813
Regressions: 1604633
Has Regression Range: --- → yes
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: