freedzhost.cloud

A gaming PC that became an AI server, and still plays

Level AdvancedEstimated time 2–3 hRead first Free your graphics cards for AI, and still playA word you don't know? Glossary

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.

The tower, no screenVirtual displayEDID forced at bootApollocaptures (KMS)Integrated GPUencodes HEVCNVIDIA cardsmodels · or the gamethe game draws hereAndroid handheldArtemis / MoonlightWi-Fi or tailnetbuttons, touch
No screen on the tower. A virtual 120 Hz display exists only for the kernel; the streaming server captures it, the integrated GPU encodes the video, and the handheld plays it. The NVIDIA cards keep computing.

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

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.

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.