Skip to content
 
 

Latest commit

 

History

3,353 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Strix Halo Virtual Display — Setup Procedure

Procedure to replicate the working Apollo-on-Linux virtual display on a Strix Halo machine (Ubuntu 26.04, kernel 7.1.2, dracut initramfs, GDM Wayland session, SSH admin, Moonlight client).

Result: a true virtual monitor at a custom resolution (1920x1280@60) that Sunshine streams to Moonlight with keyboard/mouse control, independent of any physical monitor — equivalent to Apollo's virtual display feature on Windows.

How it works

A custom EDID is forced onto a spare (disconnected) DisplayPort output via two kernel parameters. The DRM stack then believes a monitor is attached there, the GNOME desktop session renders onto it, and Sunshine (capture=kms) captures it:

  • drm.edid_firmware=<port>:edid/virtual-display.bin — serve our EDID on that port
  • video=<port>:e — force the connector on at boot (preserve mode)

Do not plug a real monitor into the chosen port afterwards — it inherits the virtual EDID.

Prerequisites

  • root (sudo) access
  • Kernel with drm.edid_firmware support (4.14+; verified on 7.1.2)
  • A spare disconnected DP/HDMI output on the GPU (Strix Halo: DP-1..DP-8, HDMI-A-1)
  • python3; optionally edid-decode for a sanity check of the generated EDID
  • The GPU must drive the target pixel clock on that port (1920x1280@60 ≈ 162 MHz is trivial for DP/HDMI)

Files

  • tools/gen_virtual_edid.py — generates the EDID (correct Samsung-style DTD byte layout)
  • tools/virtual-display-setup.sh — installs everything, patches GRUB + initramfs + Sunshine config
  • plan.md — design notes and the lessons learned (EDID DTD layout bug, dracut install_items, etc.)

Procedure

1. Run the setup script

cd /home/me/Apollo
sudo ./tools/virtual-display-setup.sh

Defaults: auto-detected spare port, 1920x1280@60. Customize with flags:

sudo ./tools/virtual-display-setup.sh --port DP-2 --width 1920 --height 1280 --refresh 60

What it does:

  1. Auto-detects the first disconnected DP/HDMI port (override with --port).
  2. Generates and installs /lib/firmware/edid/virtual-display.bin (shows an edid-decode DTD check if available).
  3. Writes the initramfs hook:
    • dracut: /etc/dracut.conf.d/99-virtual-display.conf (install_items, not add_firmware — unsupported in dracut 110)
    • initramfs-tools: /etc/initramfs-tools/hooks/virtual-display then rebuilds the initramfs.
  4. Appends drm.edid_firmware=<port>:edid/virtual-display.bin video=<port>:e to the last GRUB_CMDLINE_LINUX_DEFAULT line in /etc/default/grub (only the last line takes effect — duplicate lines exist) and runs update-grub. A backup is kept alongside.
  5. Sets capture=kms in ~/.config/sunshine/sunshine.conf.

It ends by printing the reboot command.

2. Reboot

sudo systemctl reboot -i

-i is required: the block inhibitors (gnome-session, gsd-power, unattended-upgrades) otherwise abort shutdown.

3. Verify

sudo ./tools/virtual-display-setup.sh --verify

Checks that the params are active in /proc/cmdline, the connector is connected, the target resolution is in its mode list, the sysfs EDID matches the firmware, and pins output_name=<N> in sunshine.conf from the Sunshine monitor list ("Monitor N is ").

The sysfs EDID comparison needs root (the sysfs file is root-owned).

4. Connect with Moonlight

  • Desktop session renders the virtual port (it becomes a GNOME logical monitor).
  • If the desktop comes up at the wrong resolution, apply a GNOME monitor config for the port at 1920x1280:
    gdbus call --session --dest org.gnome.Mutter.DisplayConfig \
      --object-path /org/gnome/Mutter/DisplayConfig \
      --method org.gnome.Mutter.DisplayConfig.ApplyMonitorsConfig \
      <serial> 0 [<connector-id-array>] [<logical-monitor-specs>] [{}]
    
    (get the current serial, connector IDs, and monitor specs from org.gnome.Mutter.DisplayConfig.GetCurrentState first).
  • Moonlight should show the desktop at 1920x1280 with working keyboard/mouse. Input needs /dev/uinput writable — Sunshine's inputtino devices ("Mouse passthrough", "Keyboard passthrough") are created at startup.

Admin / rollback notes

  • Admin over SSH; do not RDP into the box while the auto-login session is active (it kills the session).
  • If a real monitor is ever plugged into the chosen port, it will show the virtual modes; remove the two kernel params + drop-in and update-grub + rebuild the initramfs to revert.

Troubleshooting

Symptom Cause / fix
Virtual port stuck at 640x480 EDID DTDs rejected — regenerate with tools/gen_virtual_edid.py (old generators used a wrong DTD byte layout) and reinstall
update-initramfs doesn't embed the EDID dracut: use install_items, not add_firmware; verify with sudo lsinitramfs /boot/initrd.img-$(uname -r) (non-sudo silently fails on root-owned initrds)
Kernel params don't appear in /proc/cmdline GRUB edit went to a dead duplicate line — only the last GRUB_CMDLINE_LINUX_DEFAULT is used
Reboot aborts with "inhibitors exist" sudo systemctl reboot -i
"No keyboard or mouse" in Moonlight Usually a tiny/black stream — fix resolution first; input devices exist as long as /dev/uinput is writable
modetest unavailable libdrm-tools isn't installable on Ubuntu 26.04 — use the --verify checks instead

Known limitations

  • Not a kernel-level virtual output like amdgpu.virtual_display (which disables physical outputs, so it was rejected) or VKMS — the EDID-on-spare-port trick is per-machine setup, not a plug-and-play feature.
  • The repo's SUNSHINE_ENABLE_VIRTUAL (capture=virtual) path is a separate, fragile mechanism (steals a connected-but-idle physical connector) and is not what this setup uses.

About

Sunshine fork - The easiest way to stream with the native resolution of your client device

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages