Add experimental virtual display support for Linux#1477
Conversation
|
Thank you! This looks like a plausible solution to Linux without touching kernel source. One question, is it fully behavior compatible with the Windows version? Like on demand virtual display requested from client? Also please don't touch unrelated parts like code formatting. This is adding tons of noise to the diff, and things like sticking And if you're ok with that, can you name the feature with Apollo instead of Sunshine, if you're not planning to commiting it to Sunshine? |
Yes, I've tested starting streams from Artemis by specifying that it should start in a vdisplay and it correctly picks up the request and outputs to it. The client hash is also the vdisplay's serial number so compositors do seem to respect and remember changed window layouts, the primary display and so on. Though Artemis does complain that the "server doesn't support virtual displays". Could this be hardcoded somewhere? I'll revert unrelated formatting, the repo root has As for renaming to Apollo, sure. I'll change the new group name to |
Yes, it's hardcoded in |
|
@AdivonSlav how does this work on apollo client for linux since this is a .exe? or is running this on lutris or bottles completely fine? |
I built it via cmake on cachyos. its not a exe. but im struggling to get it to use the 'virtual' display, maybe due to using a nvidia card. Ended up reverting to the aur package. |
|
I built it also from source on cachyOS but it required changing the permissions of |
Yeah I was not able to actually test anything regarding Nvidia. Do you have any logs?
Yeah this is by design since the binary should only be exec'd by the apollo group, hence the 750 default. The user installing apollo should be added to the |
actually i got it working completely fine on nvidia now, literally no issues as of yet, had to massage the build and permissions a bit. Build environment: CachyOS, GCC 16, CUDA 13.2, KDE Plasma 6 Wayland Boost version mismatch — repo requires 1.89.0, system has 1.91.0. Fixed by patching cmake/dependencies/Boost_Sunshine.cmake to use 1.91.0. Virtual display: I set the permissions as noted by @spyral98, on apollo-vdisplay-helper to 0755 I didnt verify if it was necessary sudo chmod 0755 $(which apollo-vdisplay-helper) trigger_hotplug fails, but KWin picked up the connector within ~400ms via normal DRM polling, so it was non-fatal in this case. Im sure there is much more elegant ways of doing all of this, but im learning :) |
|
@AdivonSlav Yeah I see you added it in @Toremous Same for me, I used |
|
I mostly compiled for Fedora inside Docker. I'm guessing compilation should be a breeze for Arch over the AUR. As for display management, if you disable the physical displays once manually when the vdisplay is up and set it as the primary then KDE should remember this. And it should also revert to enabling physical monitors when the vdisplay is killed. In my testing I found it wasn't necessary to have kscreen-doctor hooks. |
|
@AdivonSlav /etc/mkinitcpio.conf then edited this file to point it the FILES=(/usr/lib/firmware/edid/virtual_clean.bin), |
you should not need to dump EDIDs or regen initramfs with this PR, the idea is that it generates an EDID on the fly based on the client and dynamically loads it onto an unused connector. |
|
@AdivonSlav I tried without it but it kept failing, and forcing the VDisplay to 768p at 60fps, 4:3 screen, so I did the edid myself and got 1080,144fps, 16:9 |
weird, did you try setting the vdisplay resolution manually through KDE settings and seeing if it sticks? I've tested on multiple different devices and it worked. What's your system and client? |
|
Any progress on this pull request? |
|
Just installed CachyOS (replacing my Windows 10 instance) and virtual display is a killer feature - thank you ClassicOldSong for forking and maintaining Apollo/Artemis and thank you AdivonSlav for this PR to bring VD support to Linux -- very excited to see this! All that being said, I have an NVIDIA GTX 1080 Ti card in my setup and could help testing if it's helpful If I'm following correctly it seems like just building using cmake and possibly correcting permissions on the apollo-vdisplay-helper binary? I'll try and set this all up tonight. |
|
No luck yet, but I did get it compiled and the frontend running at least.. # Checkout PR and Submodules
git clone https://github.com/AdivonSlav/ApolloStream.git
cd ApolloStream
git checkout vdisplay-linux
git submodule update --init --recursive
# Install Build Deps (CachyOS):
sudo pacman -S --needed git cmake ninja make pkgconf gcc clang nodejs npm boost avahi curl libayatana-appindicator libevdev libmfx libnotify libpulse libva libvdpau libx11 libxcb libxfixes libxrandr libxtst numactl openssl opus wayland wayland-protocols libdrm libcap
# Build Frontend
npm install
# Run CMake
cmake -B build -G Ninja -S . \
-DCMAKE_BUILD_TYPE=Release \
-DSUNSHINE_ENABLE_WAYLAND=ON \
-DSUNSHINE_ENABLE_X11=ON \
-DSUNSHINE_ENABLE_DRM=ON \
-DSUNSHINE_ENABLE_CUDA=OFF
# Build / Install Apollo
ninja -C build
sudo ninja -C build install
# Set Binary Capabilities and Run Sunshine
sudo setcap cap_sys_admin+p $(readlink -f $(which sunshine))
sunshineI can hit the front end, login, but I don't see a Virtual Display option.. Here are the logs from TroubleShooting: Some notes here:
|
There should be a checkbox for the vdisplay in the Audio/Video tab. Looking at the logs this is pretty suspicious: Can't find any encoder or display session. Are your drivers okay, does As for your compilation there's a linux_build.sh script but i'm not sure if it would match CachyOS since it specifically looks at Arch. Your build flags may be off and that's why some assets could be missing if you're not seeing the checkbox, try adding these cmake flags and rebuild: and then do |
Seeing the CUDA failures in the log isn't very inspiring, but it's more functional than it was yesterday :) I followed the steps that @Toremous took:
Removed cmake -B build -G Ninja -S . \
-DCMAKE_INSTALL_PREFIX=/usr \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_CUDA_COMPILER=/opt/cuda/bin/nvcc \
-DCMAKE_CUDA_HOST_COMPILER=/usr/bin/g++-14 \
-DSUNSHINE_EXECUTABLE_PATH=/usr/bin/sunshine \
-DSUNSHINE_ASSETS_DIR="share/sunshine" \
-DSUNSHINE_ENABLE_WAYLAND=ON \
-DSUNSHINE_ENABLE_X11=ON \
-DSUNSHINE_ENABLE_DRM=ON \
-DSUNSHINE_ENABLE_CUDA=ONAlso ran the I have 3 physical monitors connected to my PC (2 DP, 1 HDMI) and 1 empty HDMI slot (that I want the virtual display to use). Audio/Video I enable "Virtual Display (experimental)" and then restart Apollo Going to dig deeper on this bit later. Might be a permission issue because I do have 1 empty/under HDMI port on the card |
|
I'm realizing my problem here is, if I don't initially login after a reboot, no Wayland session is created. So for my setup (KDE on CachyOS) I could theoretically create Do/Undo commands that:
Obviously this is specific to Wayland (vs. X11) and KWin (vs. mutter, cage), but is this overkill? I could setup KDEConnect, jiggle the mouse and login that way if I reboot, but I was hoping for a more automated solution like how it works on Windows. |
|
After continued use id say that this solution seems very robust, its handled every weird display / resolution ive thrown at it, and it releases the display exceptionally quickly on closing the session. I hope to see it merged (and subsequently pushed to aur). for testing I used Artemis as the client on shield tv, android phone (on various resolutions), and linux via this project. All functioned well. |
Did not really think of that situation where this would be used prior to a login but I can see the usecase, I'll have to investigate this further. Theoretically you could just enable autologin on your system if you don't mind the security implications? |
Right, this is what I'm going to do here to get logged in / Wayland initialized. Still playing with some settings to get all other monitors disabled (and reenabled) when using Virtual Display launcher, Kwin won't let me disable all monitors at once, so I can't achieve this with a Do/Undo command set (at least I don't think I can) |
|
I'm doing this in my fork but using EVDI, not EDID. Requires MOK enrollment for secure boot enabled machines. Not sure EDID is much better than a quick/easy MOK enrollment. While EDID may avoid MOK enrollment, would require secure boot lockdown to be disabled in order to get debugfs access. EVDI is a one step MOK signing and might be preferred/required to keep secure boot, which makes EDID not possible on Secure Boot enabled devices. In my fork, EVDI is used to create the virtual display on demand but Mutter/PipeWire used for capture. Sharing here to collaborate. Happy to see if there's another path forward, but EVDI path on my fork is proving to be incredibly stable and responsive. https://github.com/primez-x/Apollo-Ubuntu EDIT: Also worth noting that I did have to enable password-less login for the same Wayland/Gnome session creation limitation. Exploring this further as well. Don't have a great option otherwise at the moment. |
|
@primez-x do you really know what EDID and EVDI are? Can you explain why "EDID" needs disabling secure boot? Purely vibe coded PRs won't stand a chance here. |
|
He's probably right regarding secure boot as the kernel lockdown man page does specify that kernel lockdown (due to secure boot being on) does limit access to hardware and configuring it, which should include debugfs. I don't run secure boot so I failed to notice this. I would not call this a big problem due to the fact that secure boot is not used by many users and it's not even supported out of the box for many distros, Ubuntu and Fedora being the only exceptions I know of. This issue regarding system76-scheduler does make it likely: pop-os/system76-scheduler#50 Due to this being marked "experimental" anyway, a simple error message indicating that secure boot being on does not allow for virtual displays is fine imo. As for EVDI, I've taken a look and the implementation seems much more complex and dependency-heavy. Given that the fork is called Apollo-Ubuntu and you do mention Mutter, is it safe to assume this only works on Ubuntu/Gnome? What's the deal with that. |
|
My understanding on EDID is that it's using a debug route that basically forces the GPU to recognize a physical monitor that doesn't actually exist via a physical GPU port, so you're essentially telling the GPU that "a monitor exists here, so output to that GPU monitor port", which Apollo then captures and streams. My fork actually started from https://github.com/MrOz59/Apollo-Linux. I wanted Apollo support for Ubuntu 26.04 (current Ubuntu dev build). At the time, there were no other Linux forks that supported virtual displays. The fork above, however, did not work at all with Ubuntu. I was able to vibe code it to a barely functioning EVDI capture but that barely functioned in practice, far too slow, laggy, choppy, unusable. I think the MrOz59 fork was using an X11 capture, don't recall exactly. My understanding is that Ubuntu is planning to drop X11 support entirely so Wayland is simply the path forward, and given its also the default for Ubuntu, I wanted to support this natively. I am also using Secure Boot, as it sounds like that's the general direction Ubuntu is heading in. EVDI is used to emulate the monitor, but all the capture is actually happening on GPU via Mutter. EVDI capture directly is awful. It can be done. But barely functions even at 30fps 720p. I'm tested the EVDI/Mutter capture route up to 4k resolution with no issues, and have thrown a few random combinations of screen resolution without any issues as well. Song noted in my other PR that I created just to bump the visibility that it's full of noise and thousands of changes - which is likely because of the Wayland/Mutter capture dependencies and also, this fork started life as a fork from a ~2 year not super maintained fork. I would be willing to bet that I could get a fresh fork from the last ClassicOldSong commit that would be far less bloated and less dependency heavy. I really don't think EVDI"s dependencies are too dramatic, just the big one which is MOK signing. |
|
Just wanted to follow-up on my experience so far. Other than needing a method to wake up my displays, I've had a flawless experience gaming and using remote desktop with the Virtual Display. For my environment I use this script, which is KDE-specific (
#!/bin/bash
# Parameters
export WAYLAND_DISPLAY=wayland-0
export XDG_RUNTIME_DIR="/run/user/$(id -u)"
CARD_SLOT="card1"
VIRTUAL_DISPLAY_CARD="HDMI-A-2"
# Gather displays
DISPLAYS=()
for DISPLAY in $(ls /sys/class/drm); do
if [[ ${DISPLAY} == *"${CARD_SLOT}-"* ]]; then
DISPLAYS+=("${DISPLAY}")
fi
done
# Determine if a display is on
DISPLAY_ON=0
for DISPLAY in ${DISPLAYS[@]}; do
if grep -q "enabled" /sys/class/drm/${DISPLAY}/enabled; then
DISPLAY_ON=1
break
fi
done
# If no displays are on, toggle VIRTUAL_DISPLAY_CARD.
if [[ ${DISPLAY_ON} -eq 0 ]]; then
/usr/bin/kscreen-doctor output.${VIRTUAL_DISPLAY_CARD}.enable
fiI would love to have this be Command Preparation Do/Undo commands, but the Virtual Display attempts to create before the Command Preparation steps run, so doing something like: Do: won't work, so I ultimately ssh in and run I can connect after a few seconds (I previously had a poll loop that would wait for a valid display resolution, but since I have to ssh in anyways, by the time I'm using Artemis, it's already ready). Is there a way to make the Virtual Display creation happen after the Command Preparations, or is there an earlier point I can inject this to run? Thanks again for this PR, I run it daily 👍 |
|
Tested this on CachyOS / KDE Plasma Wayland / NVIDIA RTX 4090. It works for me now with the PR branch. Apollo creates the virtual connector, KWin picks it up, and KMS capture switches to it correctly. Relevant setup:
Config I needed: capture = kms
encoder = nvenc
adapter_name = /dev/dri/renderD128
vdisplay_connector = card1-DP-2
linux_virtual_display_experimental = enabledApp config: "virtual-display": trueSuccessful log path: Two notes from testing:
Apollo logs: On this system that is harmless. KWin still notices the connector and assigns a CRTC after about 600ms. Might be worth downgrading this warning when the file does not exist, or only warning if the connector does not become active afterwards.
"terminate-on-pause": trueWithout that, Apollo paused the Desktop session and left So the feature works on NVIDIA/KDE/Wayland here, but the pause behavior can leave the virtual display alive unless the app is configured to terminate on pause. |
|
Honestly, this project will determine whether I'll ever switch to Linux. Apollo on Windows is a godsend. |
|
For whatever reason, I don't recommend switching to Linux, if you need your computer to dual role for some daily tasks. It's a common problem in open source, if something is satisfactory, it'll stay there forever, until someone gets annoyed by the current state. Gaming on Linux improves because someone is not satisfied and valve needs it to make money, other than these, they're still far from being able to daily drive. |
|
I have to strongly contradict you. |
|
In case you don't know, I've been using Linux for nearly 20 years, but at the same time, I'm a creator, and Linux never satisfied me. My time is precious, I don't have that much time to improve something that's broken at the beginning and is refusing contributions. I wanted Linux to get better, so I and my partener tried to submit patches to it, but the maintainer just rejected them without any good reasons. Years later, the maintainer suddenly replied saying I was right, but still nothing happened to the kernel source, nothing is fixed there. I don't think you have strong enough evidence to convince me that Linux is getting better. Also checkout how Asahi Linux starts and ends. I know how much you want to escape the Windows bloat, but believe me, if you have any professional needs, despite doing website hosting/networking/server management, no matter which you need, CAD/image editing/movie editing/music production, none of them are great on Linux. You'll spend more time on figuring out why things don't work, finding alternatives that seems work for this exact issue you're facing but later you discovered that it can't complete your other basic requirements. With the time you spent, you lose more than paying for those commercial softwares. Since you have no idea how the reality of Linux development is, it's understandable that you think it's a paradise and can't bear hearing any bad words about it. But I don't want to see more downvotes on the previous reply, because now you know. |
|
And most importantly, it's still an open source issue. Open source project maintainers are massively underpaid for their contributions. That's why it's only getting better recently, since Valve use it for SteamDeck and SteamMachine, which they sell to make money for them. |
|
I welcome opinions here, but not altitude and emotions. If anyone of you can solve the fragmentation of Linux, unify developing resources, gather funds for developers, it can get even better, but it won't if you blame someone that can and had tried to make it better. |
|
Id rather we not clutter a code PR with unrelated discussions |
I'll check this out tomorrow probably. If vdisplay creation for Win already happens before command prep then id rather not introduce inconsistencies. Thanks a lot for testing this in detail |
The hot plug trigger is mostly just a "fuck it why not" type of thing to try and coerce the compositor to recognize it. Can definitely just be a warning As for pausing, do we know what the behavior is on Win? |
|
Terminate on pause will terminate the session once all clients are disconnected. Probably it's a timing issue |
|
So this behavior should be consistent with Windows, we're not killing the vdisplay if paused? As far as I can see |
|
Remove should be called whenever a session terminates, it doesn't matter that check is ticked or not. Also, by design, virtual display should be created prior to prep commands and tear down after the undo commands. |
|
@DizzyThermal win has the same ordering, so we can't change it here. |
|
@ClassicOldSong anything else that this needs in your opinion or are we good for now? |
|
I'll take a look once I get some free time, thanks again for the PR! |
|
How is the progress going? |
Summary
Adds support for creating a virtual display on the fly for Linux using EDID overrides via debugfs. Works similar to the Windows impl in behavior. This method is being floated around via scripts already online, but I thought packaging it up into actual functionality would at least provide for experimental support.
How it works
Note on privilege model
The nature of this approach requires access to debugfs which technically is not supposed to be accessed by userspace like this. To actually get this and not have apollo run as root we'd need the app process to have
CAP_DAC_OVERRIDEas a capability. However giving apollo the ability to elevate to this cap is not ideal, so debugfs ops are moved to a separate minimal helper binaryapollo-vdisplay-helper.I've added several things to make sure we mitigate security risks as much as possible around this:
apollogroup which is automatically created as part ofpostinstwith the user installing the pkg being added to it.This should make this pretty hardened, but I'm open to any suggestions/alternatives to improve this if possible.
Testing
I've performed tests on an AMD and Intel system with both KDE and GNOME. In all cases compositors respect the connector changes, react to them and restore display configurations as expected. However, I've not been able to test Nvidia/X11 as I do not have access atm. From what I've seen online Nvidia may be shaky going with this approach.