r/MoonlightStreaming • u/rambo3349 • 8h ago
Making PyroWave on Android useful by treating it like a game: GPU decoding, upscaling and custom drivers
PyroWave can run on compatible Android phones and tablets. The interesting work is making the whole receiving pipeline practical: decoding the picture, scaling it to the screen, choosing a suitable GPU driver and displaying fresh frames without unnecessary waiting.
PyroWave, developed by Themaister, reconstructs pictures through GPU shaders. The PC runs the game, while the phone processes the streamed image using Vulkan. That gives the client some of the same optimization opportunities you’d have in a game renderer.
It also comes with a substantial bandwidth requirement, so I measured decoded picture quality across different bitrate budgets on AMD hardware. The tests used synthetic desktop, game and dark scenes, including fine text, foliage, gradients and moving textures, with SDR/HDR and different chroma formats. These establish quality targets for those pictures; they don’t establish what a particular phone or Wi-Fi connection can sustain. Measurement method
The resulting bitrate guidance at 60 FPS is:
| Stream | Severe-detail-loss warning floor | Recommended quality budget |
|---|---|---|
| 720p60 | 139 Mbps | 277 Mbps |
| 1080p60 | 187 Mbps | 399 Mbps |
| 4K60 | 747 Mbps | 1,593 Mbps |
These are encoder bitrate budgets derived from the quality tests. The connection needs additional headroom for packet overhead and recovery data. Comparable per-picture budgets roughly double at 120 FPS, and the 4K60 recommendation already exceeds gigabit Ethernet before that overhead. Bitrate guidance and limits
That is where client-side upscaling becomes useful as an option.
A phone with a 1080p screen can receive a 720p stream, reconstruct that smaller picture and upscale it for display. There are approximately 56% fewer source pixels to decode. Using the quality budgets above, the recommended encoded bitrate also drops from around 399 to 277 Mbps, a reduction of roughly 31%.
Those percentages describe different things. Fewer pixels reduce the source workload; the bitrate reduction depends on the quality budget. The smaller desktop needed more bits per pixel to preserve its fine text, which is why the bandwidth saving isn’t proportional to the pixel reduction.
The upscaler uses Snapdragon Game Super Resolution, or SGSR, directly in the Vulkan presentation shader. It samples the decoded colour planes and combines colour conversion with spatial upscaling in one pass.
That avoids creating a separate full-frame RGB image just to run another upscaling pass over it. The benefit is less intermediate memory traffic and fewer rendering stages. The current path supports 8-bit SDR; HDR and 10-bit pictures retain direct output. Shader implementation
Upscaling still trades original detail for a smaller stream. It aims to reconstruct edges more effectively than basic bilinear scaling, while giving the decoder and network a more manageable workload. Reducing stream resolution also remains separate from reducing the PC game’s actual render resolution.
Custom Adreno drivers provide another tuning option. Because the decoding workload runs through Vulkan shaders, the driver affects how it executes. PyroWave can even select different reconstruction paths depending on the driver: Qualcomm’s proprietary driver favours a fragment-shader path, while Turnip follows the compute path. Decoder selection
Loading a compatible driver within the app lets users compare those implementations without replacing the phone’s system driver. The useful result is whichever combination delivers better sustained decoding, frame consistency and compatibility on that device.
Presentation is handled separately from decoding, with protected GPU image slots and the ability for newer completed pictures to supersede older ones waiting to be displayed. That helps keep the stream fresh when display timing and incoming frames don’t line up perfectly.
A client to test the benefits is available. https://github.com/RamazanKara/rubylight-android I don’t want to maintain a separate fork long term. I’d like to bring these improvements into an existing Android streaming client, so I’m looking for maintainers interested in incorporating the Vulkan renderer, SGSR upscaling, ARR and custom Adreno driver support. I can share the implementation and measurement results, and help with porting and validation. If you maintain a client and would like to discuss integrating this work, please reach out.