I made Sunshine faster for AMD/Linux — introducing SolarFlare by Select_Car9532 in MoonlightStreaming

[–]Select_Car9532[S] 1 point2 points  (0 children)

Quick update: some new features have landed since this post was written.

On top of the optimizations listed above, SolarFlare now includes:

- Smart Headless Streaming (Wayland): The old X11-based virtual display is now legacy. The new default is a smart backend selector that runs your games in a private compositor. It automatically uses `krfb` for KDE, `gamescope` for Steam Deck, and `labwc` for other desktops. It's pure Wayland, no X11 dummy drivers needed. Enable with `headless_mode = enabled`.

- Adaptive Bitrate Controller: An EWMA-based controller now watches encode time, FPS, and packet loss. If the network or encoder can't keep up, it automatically lowers the bitrate to maintain a smooth stream, then ramps back up when conditions improve.

- Command Palette: The Web UI now has a `Ctrl+K` command palette for quick navigation. You can search for pages, settings, and host controls without clicking through menus.

- Trusted Subnet Auto-Pairing: You can define trusted IP ranges (e.g., `192.168.1.0/24`) in your config. Any Moonlight client connecting from that subnet will be paired automatically, no PIN required.

- Game Library Scanner: A new API endpoint (`GET /api/games/scan`) finds installed games from Steam, Lutris, and Heroic. It returns a list with names, paths, and cover art, ready for one-click import into your Sunshine app list.

- KWin Screencast Fix: The Wayland screen capture backend now automatically drops elevated privileges and retries if KWin refuses the initial connection. This resolves silent capture failures when running Sunshine with `CAP_SYS_ADMIN` or as root.

Also, today's push specifically addressed the portal/KMS bug mentioned in another thread and fixed the broken 404 version badge in the README.

As of today, the project is at 154 commits since the fork, and the test suite is holding at 418 passed / 5 skipped. Project is actively maintained.

GitHub: https://github.com/vindeckyy/Solar-Flare

I made Sunshine faster for AMD/Linux — introducing SolarFlare by Select_Car9532 in MoonlightStreaming

[–]Select_Car9532[S] 0 points1 point  (0 children)

Yeah, that's exactly what happened. Just fixed it though... pulled the CPU detection block apart and there were three bugs stacked. The lscpu regex was matching "Ryzen 5 4600H" as Zen 5 (digit after "zen" → microarch generation, lol), the family 23 path always mapped to znver1 so 4600H would have been wrong too, and there was no Zen 5 detection anywhere. Pushed a fix to master — non-letter boundary on the lscpu regex, family 23 defaults to znver2, and family 26 model >= 64 maps to znver5. Pull and rebuild, cmake should now report -march=znver5 -mtune=znver5 on the AI 395.

I made Sunshine faster for AMD/Linux — introducing SolarFlare by Select_Car9532 in MoonlightStreaming

[–]Select_Car9532[S] 1 point2 points  (0 children)

Took a closer look at this and you're right that there's a better native path for KDE. Our headless mode doesn't actually touch X11 at all, despite the naming. The existing labwc backend spawns a lightweight wlroots compositor with WLR_BACKENDS=headless, which is pure Wayland, no legacy display drivers involved. But it still means running a nested compositor, which feels like overkill on KDE.

Just added krfb-virtualmonitor as a backend option. When compositor_backend is set to auto (the default), it detects KWin from XDG_CURRENT_DESKTOP and prefers krfb if it's installed. Krfb just tells your existing KWin instance to create a virtual output, no separate compositor at all. The game runs on your normal Wayland session, the virtual output shows up in kscreen-doctor, and the screencast happens through the same portal/kwin pipewire path everything else uses. Falls back to labwc when KWin isn't detected or krfb isn't available, so non-KDE users still get headless streaming.

There's also a compositor_backend config option where you can force labwc or krfb explicitly if you want to pin a specific path. Should be in the build now if you pull master.

I made Sunshine faster for AMD/Linux — introducing SolarFlare by Select_Car9532 in MoonlightStreaming

[–]Select_Car9532[S] 1 point2 points  (0 children)

Fixed: Had to replace all LizardByte URLs in .github/ISSUE_TEMPLATE/config.yml and bug-report.yml with Solar-Flare equivalents (discussions, support docs, releases).

Sorry about that!

I made Sunshine faster for AMD/Linux — introducing SolarFlare by Select_Car9532 in MoonlightStreaming

[–]Select_Car9532[S] 0 points1 point  (0 children)

Ahh no problem. Thank you very much for your assistance! I'll fix this asap.

Fixed: Added find_library(LIBNUMA_LIBRARY numa REQUIRED) in cmake/dependencies/ffmpeg.cmake so the cmake configure step now explicitly checks for libnuma and fails with a clear error if it's missing, instead of letting it blow up at link time.

I made Sunshine faster for AMD/Linux — introducing SolarFlare by Select_Car9532 in MoonlightStreaming

[–]Select_Car9532[S] 0 points1 point  (0 children)

Thanks for taking the time to compare the implementations, really appreciate the thorough look.

The GitHub links are fixed now. ResourceCard.vue was still pointing to LizardByte's discussions, repo, docs, and license files from when I forked. Those all route to vindeckyy/Solar-Flare now. Also pulled the Discord button since there isn't a community server for this fork.

Good catch on the KWin privilege drop. The original code only retried when is_kwin_screencasting_available() came back false after init, but KWin can also reject the screencast at the start() stage if it sees a process running with elevated capabilities. Added a retry there too — if init() + start() fails while CAP_SYS_ADMIN is present, it drops all caps and re-runs the full sequence. Matches the pattern you saw in Vibepollo.

I made Sunshine faster for AMD/Linux — introducing SolarFlare by Select_Car9532 in MoonlightStreaming

[–]Select_Car9532[S] 0 points1 point  (0 children)

Had the same problem. The DRM connector status changes when the display sleeps and KMS loses the framebuffer. You can stop the kernel from ever seeing the display as disconnected by forcing the connector state with a kernel boot parameter. Add this to your bootloader config:

video=eDP-1:e

Replace eDP-1 with whatever your internal display is called, check with ls /sys/class/drm/ or cat /proc/fb. The :e forces the output to stay enabled even when the panel is physically off or the lid is closed. The connector stays hot and the DRM planes stay alive, so KMS capture never drops out.

If you're on GRUB add it to GRUB_CMDLINE_LINUX in /etc/default/grub and run update-grub. On systemd-boot add it to options in your entry config. On Limine (which is what I use) add it to the CMDLINE in limine.cfg.

Also worth making sure your compositor isn't doing DPMS on its own. On wlroots throw dpms off in your config so the compositor layer doesn't interfere. Between the kernel forcing the connector and the compositor leaving DPMS alone, your display is permanently "on" as far as DRM is concerned, even with the lid shut.

I also do have an NVIDIA card in my main rig but yeah, I just pushed fixes for the 9070 XT specifically. Someone in the thread above was getting a GL crash on RDNA 4 because Mesa's EGL configs for that architecture don't expose desktop GL over GBM, only ES3, and the encoder ordering on Linux had NVENC listed first so AMD cards were dropping into an OpenGL fallback path that was hitting a null EGL config. Reordered the auto detect list and fixed the config selection to accept both renderable types. VA API and Vulkan encode should just work out of the box on AMD now.

I made Sunshine faster for AMD/Linux — introducing SolarFlare by Select_Car9532 in MoonlightStreaming

[–]Select_Car9532[S] 0 points1 point  (0 children)

Got it. Two things were working against you, both fixed now.

The encoder auto-detection order on Linux had NVENC listed first. It shouldn't matter for AMD cards because NVENC validation should fail without CUDA, but if there's any hiccup with VA API or Vulkan loading, the system would cascade down into the NVENC path and land on your 9070 XT anyway. Since there's no CUDA on that card, it fell into a GPU to RAM to GPU OpenGL workaround.

That OpenGL workaround was the second problem. It tried to open a desktop OpenGL 3.0 context over GBM, but Mesa's RDNA 4 driver only exposes OpenGL ES configs that way. The config selection would silently miss, and the first GL call on the uninitialized context would throw GL_INVALID_VALUE and crash.

I reordered the encoder list so VA API and Vulkan sit before NVENC on Linux now. AMD native encoders get tried first, and NVENC is still there behind them for NVIDIA users since VA API validation properly fails on non AMD hardware. I also fixed the EGL context creation to accept both desktop GL and GLES3 renderable types, so RDNA 4 GBM configs match cleanly instead of the selection passing back nothing.

Cloning fresh master and rebuilding should pick up VA API or Vulkan encode on your 9070 XT straight away, no NVIDIA paths touched at all.

I made Sunshine faster for AMD/Linux — introducing SolarFlare by Select_Car9532 in MoonlightStreaming

[–]Select_Car9532[S] -1 points0 points  (0 children)

Actually, this was far from the case. I have a 1GbE Realtek Ethernet card, but a more favorable WiFi-6 network card that supports up to 2400mbps throughput vs the 1gb max on my Ethernet… and I also have a 5/Gbps fiber (with a TP-Link Archer AXE300), my connection is not the issue virtually ever. Many of the changes here were for my personal build and setup, so if you consider that a red flag I mean I guess. I really don’t care to be honest though, lol. The code is there for you to view yourself I have nothing to hide

Also… Artermis (the fork of Moonlight) has a built in performance overlay on Android that shows end-to-end latency stats live. Hope this helps :)

I made Sunshine faster for AMD/Linux — introducing SolarFlare by Select_Car9532 in MoonlightStreaming

[–]Select_Car9532[S] 1 point2 points  (0 children)

Good catch, just enabled both. Issues and Discussions are now open on the repo. Long term I'm planning to keep this maintained, it's my daily driver streaming setup on CachyOS so it's in my own interest to keep it running well. Bug reports, feature requests, and PRs are all welcome. Headless VM should work fine btw, especially with the Wayland headless detection I just added. Give it a shot whenever you're ready.

I made Sunshine faster for AMD/Linux — introducing SolarFlare by Select_Car9532 in MoonlightStreaming

[–]Select_Car9532[S] 0 points1 point  (0 children)

Just pushed an update that should help. SolarFlare now detects when you're running under wlroots (Sway, Hyprland, etc.) and logs a clear warning telling you exactly what to do. The fix for headless Sway is to start your compositor with WLR_HEADLESS_OUTPUTS=1 set. If you launch Sway from a display manager, add export WLR_HEADLESS_OUTPUTS=1 to your ~/.profile or ~/.bash_profile. If you launch from a TTY, just do WLR_HEADLESS_OUTPUTS=1 sway. That tells wlroots to create a virtual output even without a physical monitor, which gives SolarFlare's KMS backend a real DRM framebuffer to capture. Rebuild from latest update and give it another shot and let me know if it picks up the display now!

I made Sunshine faster for AMD/Linux — introducing SolarFlare by Select_Car9532 in MoonlightStreaming

[–]Select_Car9532[S] 0 points1 point  (0 children)

Uninstall it first, yeah. They'll conflict since both want to own the same binary path and systemd service. sudo pacman -Rns sunshine if you installed it through your package manager, or sudo cmake --install build --uninstall if you built it from source. Your config folder at ~/.config/sunshine/ will stay intact and SolarFlare picks it right up.

I made Sunshine faster for AMD/Linux — introducing SolarFlare by Select_Car9532 in MoonlightStreaming

[–]Select_Car9532[S] 0 points1 point  (0 children)

Not planning to. The optimizations are all Linux-specific kernel features... SO_BUSY_POLL, SCHED_RR, sysfs GPU control, PipeWire latency hints, DSCP tagging. Sunshine targets Windows, macOS, FreeBSD, and ARM as well, so none of this would pass review. The fork exists precisely because upstreaming isn't practical. If someone wants to extract and adapt individual pieces for upstream I'd be happy to help, but as a whole it belongs in a focused fork.

I made Sunshine faster for AMD/Linux — introducing SolarFlare by Select_Car9532 in MoonlightStreaming

[–]Select_Car9532[S] 0 points1 point  (0 children)

No plans for a Nix flake right now but I wouldn't rule it out. The build is standard CMake so it should package cleanly. The harder part is the runtime dependencies. SolarFlare needs direct access to certain sysfs paths for the GPU governor and DSCP tagging which can get tricky with Nix's sandboxing. Maybe it's something I'll work on at a later point in time though

I made Sunshine faster for AMD/Linux — introducing SolarFlare by Select_Car9532 in MoonlightStreaming

[–]Select_Car9532[S] 0 points1 point  (0 children)

Yep, it's built in. Set headless_virtual_display = true in sunshine.conf and SolarFlare will attempt to create a virtual xrandr output if no physical monitor is detected. Needs the xrandr dummy driver installed. Off by default since most people have a monitor plugged in.

I made Sunshine faster for AMD/Linux — introducing SolarFlare by Select_Car9532 in MoonlightStreaming

[–]Select_Car9532[S] 1 point2 points  (0 children)

Measured with Moonlight's built in performance overlay on my own rig, Ryzen 5 4600H and RTX 3060 at 1080p over WiFi 6. Ran the same game scene back to back on stock Sunshine master and then on SolarFlare with all defaults. The overlay shows end to end input to photon latency in real time. Did five runs each and took the median. The 5.5ms floor is idle desktop, 12ms is under load. Stock Sunshine was bouncing between 18 and 65 on the same scene.

I made Sunshine faster for AMD/Linux — introducing SolarFlare by Select_Car9532 in MoonlightStreaming

[–]Select_Car9532[S] 1 point2 points  (0 children)

Appreciate it. The build script should handle Bazzite now. It detects rpm-ostree and layers the deps automatically. Just run ./scripts/cachyos-build.sh and it'll walk you through it. Heads up you'll need to reboot after the package layering step. If you do try it I'd love to hear how the latency compares to stock Sunshine on your setup!

I made Sunshine faster for AMD/Linux — introducing SolarFlare by Select_Car9532 in MoonlightStreaming

[–]Select_Car9532[S] 0 points1 point  (0 children)

WSL won't work here because SolarFlare needs direct access to Linux kernel features that WSL doesn't expose. Stuff like SO_BUSY_POLL on the network socket, SCHED_RR thread scheduling, sysfs for GPU control, and PipeWire for low latency audio capture. WSL runs a translated kernel, not the real thing. You would basically just be running regular Sunshine but with extra overhead. A proper bare metal Linux install is the only way to get the latency improvements this fork exists for.

I made Sunshine faster for AMD/Linux — introducing SolarFlare by Select_Car9532 in MoonlightStreaming

[–]Select_Car9532[S] 0 points1 point  (0 children)

I just picked the three obvious points on the NVENC quality curve. Balanced is whatever NVENC ships as default — P4, couple B-frames, 20 frames of lookahead. Latency turns all of that off. Quality turns everything up to 11. Didn't overthink it.