mutter wayland doesn't support three 4k monitors on hybrid graphics system with proprietary driver (lenovo P70)
So a while back @uraeus asked for my help trying to get his P70 laptop (with a 4K screen) to work with two external monitors. I've been kind of poking at it off and on between other work and I wanted to write up some notes.
First details about the setup:
/dev/dri/card0is intel embedded graphics (Skylake/HD Graphics 530)
/dev/dri/card1is nvidia (GM204GLM/Quadro M4000M)
- internal panel is 3840x2160
It's seems like the main problem is the proprietary driver is severely limited on the number of dumb buffers it can create.
dumb_test.c gives these results:
╎halfline@ultron〉⎛/home/halfline⎞ ⌜01:10 PM⌟ ╎❯ ./dumb_test /dev/dri/card0: never failed to allocate buffers (tried 250 3840x2160 buffers, 7910 MB) /dev/dri/card1: allocated 221 MB of memory before failing (7 3840x2160 buffers)
The way mutter handles hybrid graphics, it needs two discreet graphics dumb buffers per monitor (front buffer and back buffer). It does all the rendering on dumb buffers associated with the embedded graphics, then duplicates those pixels to dumb buffers for the discrete graphics and flips between the two mirrored dumb buffers. One thing that surprised me is mutter doesn't actually use eglStreams at all on hybrid graphics systems. It strictly sticks to memcpy's into dumb buffers, and using drm apis to switch between those buffers.
So there are basically enough dumb buffers in the budget for the login screen, but then not enough for the first user session (let alone enough to support user switching between multiple users). Indeed, using autologin, all three monitors work for the lone running session. @mvicomoya any idea why it's so limited? It seems like there's some sort of hard limit at 250MB of total dumb buffer allocations for the system. The error message in dmesg is:
[drm:nv_drm_dumb_create [nvidia_drm]] *ERROR* [nvidia-drm] [GPU ID 0x00000100] Failed to map NvKmsKapiMemory 0x00000000d93bd07e
Anyway, I was able to get all 3 monitors to light up with this troubleshooting patch:
but of course it introduces noticeable tearing. The monitors also aren't the right colors. To fix colors, I had to do this patch:
Not exactly sure the right place to fix that is, mind you, but that got things rendering right, at least.
Performance isn't great regardless. The external monitors are getting less than 30 frames per second, I think.
So a couple of thoughts that still need investigation:
Do we really need two discrete graphics dumb buffer per monitor when copying from the integrated graphics? I think we should be able to use an fbo instead of one of the dumb buffers? I guess that will require using eglstreams in hybrid graphics situations instead of the gbm/drm only approach…
I think maybe we should consider doing rendering twice, once on integrated graphics for the internal panel, and once on discrete graphics for the external monitors. This will use more power than only rendering on integrated graphics and copying, but if the laptop is hooked up to a bunch of monitors it's probably docked and plugged in anyway? And the performance will surely be a lot better than doing copying.
Those are the main things I can think of right now for notes… I just wanted to get things written down, really. I'm going to be going on vacation for a week starting Monday so I may not pick this back up until I get back.