
A gaming PC that became an AI server, and still plays
Our compute node used to be a gaming PC. It now runs AI models around the clock, has no screen, and we still play on it: from an Android handheld, anywhere in the house or outside, through a streaming server. The trick is to give each part of the machine one job.
A bit of history
Game streaming at home started with NVIDIA's GameStream in 2013, built for its Shield devices. Moonlight, an open-source client, reverse-engineered the protocol and brought it to every phone and computer; Sunshine then reimplemented the host side as open source, and Apollo extended it. A tower in a corner can now play on a handheld anywhere in the house.
One job per chip
- NVIDIA cards: models, or the game while you play (see Free your graphics cards for AI).
- Integrated GPU: the desktop, and encoding the video stream. Its video engine is separate from the NVIDIA cards, so streaming takes nothing from AI.
- A virtual display: the streaming server needs a screen to capture. We give it one that does not exist physically.
1. A screen that does not exist
The kernel can be told that a monitor is connected to an output, and what that monitor is, through an EDID file (the identity card a real screen sends). Two kernel parameters, on an output of the integrated GPU:
video=HDMI-A-1:e drm.edid_firmware=HDMI-A-1:edid/virtual-1080p120.bin
Put the EDID file in /usr/lib/firmware/edid/. Generate it for 1920×1080 at 120 Hz with reduced blanking, and make that mode the preferred one, otherwise the desktop will not pick it by default. Open-source EDID generators exist; check the result before rebooting.
- The output is named by connector (
HDMI-A-1,DP-3…), not by card: the card numbers can change from one boot to the next, the connector name does not. - The EDID is read at boot only: any change means a reboot.
- A forced output has no HDR and no variable refresh rate.
- With the proprietary NVIDIA driver loaded,
drm.edid_firmwarealone is silently ignored:video=…:eis required too.
2. The streaming server
Sunshine is the open-source game-streaming server; Apollo is a fork with extras. Three settings make the difference on a machine like this one:
capture = kms # captures the display directly, lowest latency, any desktop
encoder = vaapi # encode on the integrated GPU
adapter_name = /dev/dri/renderD128 # the integrated GPU's render node, check yours
kms capture needs an extra capability (cap_sys_admin) that the packages usually set. The render node number differs from one machine to another: find the integrated GPU's with ls -l /dev/dri/by-path/.
3. The console
On an Android handheld (ours is an AYN Odin 2 Portal), install Moonlight, or Artemis, a fork made to pair with Apollo. Pair with the PIN shown in the server's web page, pick the game, play. Away from home, go through your private network (Tailscale, WireGuard): never open the streaming ports to the internet.
Traps we paid for
An integrated GPU of the Ryzen 7000 generation decodes AV1 but cannot encode it. Use HEVC: the image is excellent and the handheld decodes it in hardware.
The server's web interface did not always save our changes. Edit the configuration file with the service stopped, then read the log to see what was really loaded.
The output_name setting is an index into the server's list of displays, not a connector name. A wrong value makes every encoder fail in a row, and the client only gets an error.
Forget the old xrandr "dummy screen" tutorials: they only work under X11, not in a Wayland session.
Sources
Found this useful? Tell us what to measure next.