fix(sunshine): use Intel rendering and recover remote Niri sessions (#268)

- render Niri on the Intel iGPU while retaining NVIDIA-connected physical outputs
- encode Sunshine streams with Intel VAAPI and wait for a Niri output before startup
- load VKMS persistently for headless sessions and configure `Virtual-1` at 2560x1600@60
- let Moonlight request the stream bitrate instead of enforcing a 20 Mbps host cap
- remove unsupported Dynamic Boost and global NVIDIA session overrides
- grant Sunshine access to virtual input devices
- add a Nushell recovery helper that starts a temporary greetd/Niri session from SSH
- identify and safely replace normal, standalone, or previous transient Niri sessions
- launch the transient service in the background and verify greetd, Niri, and Sunshine before returning
- ignore local `.worktrees` directories and keep eval regression coverage focused on structural behavior
This commit is contained in:
ryan4yin | 二花
2026-09-01 03:40:48 -06:00
committed by GitHub
parent 6f40483462
commit 9db935bc87
13 changed files with 266 additions and 38 deletions
+11 -2
View File
@@ -14,8 +14,17 @@
# kvm virtualization support
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModprobeConfig = "options kvm_intel nested=1";
boot.kernelModules = [
"kvm-intel"
# Provide a connected DRM output when the NVIDIA-attached monitors are off.
# Niri exposes it as Virtual-1 for headless Sunshine sessions.
"vkms"
];
boot.extraModprobeConfig = ''
options kvm_intel nested=1
# Create Virtual-1 as soon as vkms loads, including during normal physical sessions.
options vkms create_default_dev=1
'';
boot.extraModulePackages = [ ];
# Intel Graphics
+18 -7
View File
@@ -1,6 +1,5 @@
{
config,
lib,
pkgs,
...
}:
@@ -42,8 +41,6 @@
# required by most wayland compositors!
modesetting.enable = true;
powerManagement.enable = true;
dynamicBoost.enable = lib.mkForce true;
};
hardware.nvidia-container-toolkit.enable = true;
@@ -54,9 +51,23 @@
};
services.sunshine.settings = {
max_bitrate = 20000; # in Kbps
# NVIDIA NVENC Encoder
nvenc_preset = 3; # 1(fastest + worst quality) - 7(slowest + best quality)
nvenc_twopass = "full_res"; # quarter_res / full_res.
adapter_name = "/dev/dri/by-path/pci-0000:00:02.0-render"; # Intel iGPU
encoder = "vaapi";
};
systemd.user.services.sunshine = {
after = [ "niri.service" ];
environment.LIBVA_DRIVER_NAME = "iHD";
preStart = ''
for _ in {1..20}; do
if ${config.programs.niri.package}/bin/niri msg outputs | ${pkgs.gnugrep}/bin/grep -q '^Output '; then
exit 0
fi
sleep 0.5
done
echo "Sunshine requires at least one Niri output" >&2
exit 1
'';
};
}
+16
View File
@@ -1,4 +1,10 @@
// running `niri msg outputs` to find outputs
debug {
// Keep desktop rendering and ordinary applications on the Intel iGPU.
// Physical outputs remain on the NVIDIA GPU and use cross-GPU scanout.
render-drm-device "/dev/dri/by-path/pci-0000:00:02.0-render"
}
output "DP-1" {
// Uncomment this line to disable this output.
// off
@@ -37,6 +43,16 @@ output "HDMI-A-2" {
position x=2560 y=0 // on the right of DP-2
}
output "Virtual-1" {
// Created by vkms in hardware-intel.nix. Sunshine captures this output when
// the NVIDIA-attached physical monitors are off. It remains available in
// normal physical sessions, with minor idle GPU and framebuffer overhead.
scale 1
transform "normal"
mode "2560x1600@60"
position x=0 y=0
}
// ============= Named Workspaces =============
workspace "4entertainment" { open-on-output "DP-1"; }
workspace "2browser" { open-on-output "DP-1"; }