Implement an XShmGetImage-based backend#431
Conversation
This only adds the support for the XCB MIT-SHM extension to mss's internal xcb libraries. The actual usage of shared memory for screenshots will be done in a future commit.
This is close to complete, but there's a few things that need to be chased down: notably, the test_thread_safety test is failing, for some reason. It also currently doesn't work correctly if the root window size increases. That said, this is quite promising: on my computer, the new backend can take 4k screenshots at 30-34 fps, while the XGetImage backend could only run at 11-14 fps.
Previously, I had been trying to close the memfd in _shutdown_shm, and ignoring EBADF. It turns out that XCB will close the memfd when you send it to the X server. I think this was one potential cause of the issues I saw in test_thread_safety: the two threads would be reallocated each others' fds, leading to thread A closing an fd that thread B was using, thinking that it was thread A's memfd. Fix so that the memfd is only explicitly closed in an error situation.
Under X11, when the last client disconnects, the server resets. If a new client tries to connect before the reset is complete, it may fail. Since we often run the tests under Xvfb, they're frequently the only clients. Since our tests run in rapid succession, this combination can lead to intermittent failures. To avoid this, we open a connection at the start of the test session and keep it open until the end.
|
Tested: it works fine, and is indeed faster than the Xlib backend 💪 |
|
I'm working on the user docs, so we should talk about how we want to present this. Earlier, I think we considered making a release with the new XCB-based stuff experimental and non-default, to give advanced users a chance to try it out and report issues. Do you think that's what you want to do? The alternative I see is to make XShmGetImage the default backend. I think it's safe to make the new XCB-based XShmGetImage the default, but it's up to you. Once I write the docs, I don't think there's anything else we need to do before committing it, and then you can make a release. What do you think? |
I think too, lets use XShmGetImage as the default backend right now.
Maybe adding a CLI argument to be able to choose the backend would be interesting? |
Also, before marking XShmGetImage as unavailable if the first grab() fails, also test to see if the fallback XGetImage also fails (such as if the user gave an out-of-bounds rect). In that case, just reraise the exception and try XShmGetImage again with the next grab().
Also, fix a minor error in the docs formatting.
|
I've changed the default to use XShmGetImage on Linux, and added a |
| filename.unlink() | ||
|
|
||
| def test_invalid_backend_option(self, with_cursor: bool, capsys: pytest.CaptureFixture) -> None: | ||
| backend = "chuck_norris" |
|
That's terrific, thank you @jholveck 🍾 |
Changes proposed in this PR
Following on to #426's introduction of XCB backends, this change introduces a backend based on XShmGetImage (technically, xcb_shm_get_image). This avoids the need to send the pixel data across the socket. Instead, the X11 server saves the pixel data into a memory buffer that's shared between the server and MSS, and MSS can use that buffer directly.
This is quite performant: on my computer, the new backend can take 4k screenshots at 30-34 fps, while the XGetImage backend could only run at 11-14 fps.
This backend requires the client and server to be on the same machine (so they can share a memory buffer). It often can't be used across separate containers on the same machine (depending on the container configuration). Because of these limitations, the existing XGetImage implementation is still available. It can be explicitly requested by the user, or the new implementation will use it automatically if XShmGetImage isn't possible.
It is very important to keep up to date tests and documentation.
Is your code right?
./check.shpassed