Skip to content

PyroWave reference

This page is for looking up one PyroWave detail at a time; to find out whether PyroWave is for you and how to turn it on, start with PyroWave.

If no display on the host is in HDR, none of this applies. On a KDE desktop with a display in HDR, PyroWave cannot stream at all, SDR included, when capture goes through kms: Nova connects but shows no picture, then the stream ends. KWin sends HDR frames in a format PyroWave cannot read. This happens with Request HDR when host supports it on or off in Nova. The format check comes before any HDR decision, so both end the same way.

If you set Force a Specific Capture Method (capture, under Settings > Advanced in the Polaris web console) to KMS, it does; skip to Confirm the failure. Capture can also go through kms with that setting on Autodetect (recommended) on a host where polaris-kms is set up. polaris-kms is a separate package that lets Polaris capture straight from the graphics driver; most hosts never install it. A host has it set up when the package is installed and sudo -H polaris --setup-host --enable-kms has been run. As a quick first check, see whether the package is installed with rpm -q polaris-kms, pacman -Q polaris-kms or dpkg -s polaris-kms.

To see which route capture takes, start a stream with any codec, then run journalctl --user --since '10 min ago' | grep 'capture_transport=' on the host. A line containing kms: capture_transport= means capture went through kms. portal: capture_transport= or wlr: capture_transport= means it did not.

After the failed stream, run journalctl --user --since '10 min ago' | grep 'cannot read' on the host. It shows fourcc 1211384385. Doctor in Polaris 1.4.13 does not flag this and labels the capture format bgra8, so it looks normal there.

Pick one of these:

  • Turn HDR off on the host. HDR is a per display option in KDE System Settings, Display & Monitor. If you are not sure which display Polaris captures, turn HDR off on every display before you test, then start a new stream. Turning HDR back on brings the failure back, so this is an ongoing cost: turn HDR off before each PyroWave stream, and back on afterwards if you want it. Leave Request HDR when host supports it off in Nova as well: with the display out of HDR, the host refuses a PyroWave HDR request (Limits).

  • Use HEVC or AV1 instead of PyroWave. The failure is PyroWave’s own (its log line says this codec cannot read), so to keep the host’s monitor in HDR, stream this host with HEVC or AV1 instead. This changes nothing on the host, so HEVC and AV1 keep streaming this host as they do now. In the Nova for Android beta the codec is chosen in Settings > Client Stream Defaults > Change codec settings and applies to every host, so choose PyroWave again before streaming another host. In Nova for Linux, choose HEVC in Play Setup > Video Codec for this host and game.

  • Take capture off kms. Force a Specific Capture Method offers Autodetect (recommended), NvFBC, wlroots, KMS and X11, with no portal choice. Autodetect keeps capture off kms only when polaris-kms is not set up, so you may need both steps:

    1. If the setting is on KMS, set it to Autodetect (recommended).
    2. If polaris-kms is set up, also run sudo -H polaris --setup-host --disable-kms.
    3. Start a stream and confirm the route with the capture_transport= log line above.

    This applies to every codec, HEVC and AV1 included, and it gives up HDR: on a KDE desktop, capture off kms gets no HDR frames (desktop portal capture gives none outside a Gamescope HDR session; see Limits), so HEVC and AV1 stop streaming the desktop in HDR too. PyroWave on a KDE host in HDR after this change has not been tested.

  • Change the launch mode in Settings > Audio/Video > Where games run in the Polaris web console. That is the host’s saved mode for the next launch, so it applies to every client and every codec (Choose where games run).

    • Private Stream (a launch mode that runs a game in its own session) captures with wlr whatever Force a Specific Capture Method says, so you do not need to change it first. It does nothing for streaming the desktop. On a KDE host in HDR it has streamed PyroWave; that test ran with the setting on Autodetect (recommended).
    • For the desktop, the Desktop entry (the library entry that streams your desktop) can run on a Host Virtual Display instead of mirroring your screen (new in Polaris 1.4.13). On KDE that adds a new screen to your desktop session, sized to the client. It does not show your HDR monitor, and it is SDR only, because KWin virtual screens carry no HDR (Choose where games run). Polaris captures that screen through the desktop portal whatever Force a Specific Capture Method says. This route has not been tested with PyroWave on a KDE host in HDR.

Whatever you choose, PyroWave on a KDE desktop streams SDR: no desktop route gives it HDR frames, and HDR on this codec has not been shown end to end. Polaris cannot yet convert KWin’s HDR format for this codec.

The package repositories never serve a beta, so a host that only ever installed Polaris from repo.papi-ux.com runs the release. If you installed a Polaris 1.4.13 beta package by hand, the betas carry the same version, so an update will not replace them. The order is: check which build is installed, reinstall the release if the build dates differ (on Ubuntu or SteamOS, reinstall anyway), restart Polaris, then check again.

Which command replaces a beta depends on the distribution. On Fedora, dnf install of the release file over a beta says the package is already installed and changes nothing; if you installed the release that way, it did not take, and dnf reinstall does. On Arch, pacman -U of the release file reinstalls it. On Ubuntu, apt install of the release file replaced the beta in a test, and apt install --reinstall is the command below.

  • Polaris 1.4.13-beta.1 has no encoder at all.
  • Polaris 1.4.13-beta.2 and beta.3 can crash as a stream starts: reinstall the release on any host that ran either, whatever its GPU. The 1.4.13 release fixes it. The crash is in Polaris’s Vulkan video encoder, not in PyroWave, and any stream that starts that encoder can take Polaris down before its first frame. On an AMD GPU, Polaris’s automatic encoder choice tries it first for Private Stream (a launch mode that runs a game in its own session). These are Polaris betas; Nova’s 1.4.13 betas are a different app.

The version cannot tell a beta from the release, so compare build dates. First download the release package from the Polaris v1.4.13 release, then run these in the folder you saved it to. On Fedora, compare rpm -qi polaris | grep 'Build Date' with rpm -qip ./Polaris-fedora44-x86_64.rpm | grep 'Build Date'. On Arch, compare pacman -Qi polaris | grep 'Build Date' with pacman -Qip ./Polaris-arch-x86_64.pkg.tar.zst | grep 'Build Date'. If the dates match, the release is installed. Bazzite uses the Fedora comparison. On Ubuntu or SteamOS this page has no way to tell a beta from the release, so reinstall anyway; it does no harm, but nothing here confirms it took. After a reinstall on Fedora, Bazzite or Arch, the same comparison confirms that it worked.

If the host also has polaris-kms, compare it too. That package carries its own copy of Polaris, encoder included, and on a host set up with --enable-kms the service runs that copy. Download the release’s polaris-kms file and compare rpm -qi polaris-kms with rpm -qip ./Polaris-kms-fedora44-x86_64.rpm, or pacman -Qi polaris-kms with pacman -Qip ./Polaris-kms-arch-x86_64.pkg.tar.zst, each piped to grep 'Build Date'.

A match covers the installed files, not the copy that is running. If Polaris has not restarted since the install, restart it as described at the end of Reinstall the release.

In the same folder:

Terminal window
sudo dnf reinstall ./Polaris-fedora44-x86_64.rpm # Fedora
sudo pacman -U ./Polaris-arch-x86_64.pkg.tar.zst # Arch
sudo apt install --reinstall ./Polaris-ubuntu24.04-x86_64.deb # Ubuntu

If the host also has polaris-kms (rpm -q polaris-kms, pacman -Q polaris-kms or dpkg -s polaris-kms names it), download the release’s polaris-kms package as well and reinstall it:

Terminal window
sudo dnf reinstall ./Polaris-kms-fedora44-x86_64.rpm # Fedora
sudo pacman -U ./Polaris-kms-arch-x86_64.pkg.tar.zst # Arch
sudo apt install --reinstall ./Polaris-kms-ubuntu24.04-x86_64.deb # Ubuntu

On Bazzite, replace the layered package with the release RPM as in Update on Bazzite, then reboot. That step has not been tested over a beta, so run the Build Date comparison after the reboot.

On SteamOS, run the SteamOS 3.8 command from the Install section of the v1.4.13 release page again. If the host has polaris-kms, download Polaris-kms-steamos3.8-x86_64.pkg.tar.zst and add it to that block’s pacman -U line.

Then restart Polaris: systemctl --user restart polaris. If you started Polaris from the desktop instead of as the service, quit it and start it again, because that command does not restart the copy you launched. A package check covers the installed files, not the running copy: systemctl --user status polaris should show the service active since a time after the reinstall.

Polaris encodes PyroWave with Vulkan compute rather than with the GPU’s video engine, so the encoder needs a Vulkan 1.3 GPU with the features its shaders use. The host checks its GPU once per Polaris run, the first time a client asks what it serves. To check a host before you set up a PyroWave client:

  1. Quit any Polaris you started from the desktop, then run systemctl --user restart polaris, so Polaris runs as the service and logs to your user journal. systemctl --user is-active polaris prints active when the service is the copy running.
  2. Start a stream that is not a Space, from any client, with any codec. Stable Nova or Moonlight is fine.
  3. On the host, as the user who runs Polaris and without sudo, run journalctl --user --since '15 min ago' | grep 'PyroWave:'. Run this within 15 minutes of step 1, or widen --since to reach back to it. Lines from before step 1 belong to the previous run.

These lines are logged at the Info level, which the default Log Level (Settings > General in the Polaris web console) shows. If you changed Log Level, set it back to the default for this check.

  • PyroWave: encoding on <GPU>, queue family ... means the host can serve PyroWave.
  • PyroWave: no GPU on this host has what the encoder needs, or a line saying there is no Vulkan loader or no Vulkan 1.3 instance on this host, means it cannot.
  • If a stream ran after the restart and there is still no PyroWave line, the running Polaris has no encoder: it is 1.4.12 or older, Polaris 1.4.13-beta.1, or a source build without the encoder.

PyroWave needs Nova for Android 1.4.13-beta.2 or later. The PyroWave page asks for beta.3 or newer because its Turn it on steps were written for beta.3; a phone already on beta.2 can play PyroWave, but its screens may differ from those steps.

Nova’s betas are on Nova’s releases, marked Pre-release. For almost any current phone or handheld, take the arm64-v8a APK, which on Nova 1.4.13-beta.3 is Nova-Beta-Android-arm64-v8a.apk. armeabi-v7a is for older 32 bit devices and x86_64 is for Android on Intel or AMD hardware. Android will ask you to allow installs from the browser or file manager you open the APK with.

On an Android device that uses 16 KB memory pages, the PyroWave library in Nova 1.4.13-beta.2 and beta.3 may not load. PyroWave still appears in Settings, but a PyroWave stream then fails as it starts, with the error described under The phone’s GPU. Other codecs and the app itself are unaffected. Neither of the Android devices PyroWave was tested on uses 16 KB pages, so this has not been confirmed on one.

The beta is a separate app with its own settings and host list. On the home screen the beta is called Nova Pre (Nova Beta in later betas); stable Nova is called Nova.

The phone decodes PyroWave with Vulkan compute. The codec asks for a Vulkan 1.3 GPU, and Nova checks the phone in the background by decoding a known frame. In Nova 1.4.13-beta.3, a failed check is recorded, but it does not hide PyroWave in Settings or block choosing it, it does not stop a stream by itself, and it has no message of its own. So there is no check to run on the phone first.

A decoder that cannot initialize is a separate failure, and it does show a message. The stream fails as it starts, and Nova says “Failed to start video stream establishment (error -2)”. If the stream’s video surface is still valid, Nova also shows “Video decoder failed to initialize. Your device may not support the selected resolution or frame rate.” This is read from Nova 1.4.13-beta.3’s source; no phone that fails has been tried. Try a lower resolution if the size is the problem; otherwise choose another codec. On a phone with 16 KB memory pages, beta.2 and beta.3 can fail the same way (Which APK).

The sharp text comes from 4:4:4. 4:4:4 keeps full colour detail for every pixel, which is what keeps small coloured text sharp. 4:2:0, which ordinary H.264 and HEVC streams use, stores colour at a quarter of the resolution. Nova for Android asks for 4:4:4 whenever it asks for PyroWave, and takes it when the host offers it, which a Polaris 1.4.13 host serving PyroWave does. Nova for Linux streams SDR 4:2:0 only.

Set Video bitrate in Settings > Client Stream Defaults before the first stream. Nova’s advice scales with the number of pixels and the frame rate: about 91 Mbps × (width × height ÷ 2,073,600) × (frame rate ÷ 60). That is about 91 Mbps for 1920x1080 at 60 fps, about 114 Mbps for 2400x1080 at 60 fps, and about 182 Mbps for 1920x1080 at 120 fps. The slider stops at 300 Mbps, which is below Nova’s advice for 2560x1440 at 120 fps (about 323 Mbps) and for 3840x2160 at 60 fps (about 364 Mbps). Nova does not raise the bitrate for you; when it is too low, Nova names its figure as the stream starts: “PyroWave asks for about N Mbps at this resolution and frame rate.” Every frame is a key frame, so a small budget goes on the frame rather than on the detail.

A Quality Preset, the first item in Client Stream Defaults, sets resolution, bitrate and codec together, and some presets set 10, 20 or 50 Mbps. After any change of preset, set the bitrate and the codec again.

The codec chosen in Settings > Client Stream Defaults > Change codec settings applies to every host in the beta, and Play Setup in Nova 1.4.13-beta.3 has no codec choice to override it. While PyroWave is chosen, a stream to a Space, or to a host that does not offer PyroWave, is refused. Choose another codec there before you stream one. On a newer Nova beta, if the codec is not under Client Stream Defaults, look for a codec choice in Play Setup.

Auto never picks PyroWave, so it has to be chosen by name. The PyroWave entry is in Nova’s Settings whatever host you use. Whether the host can serve it shows only when a stream starts, or in Check the host’s GPU.

Nova Stream HUD, under Settings > Overlays & Controls, is off by default. During the stream it shows the codec as PYRO, and a long press on it opens Command Center.

Nova 1.4.13 attaches an experimental PyroWave build of Nova for Linux to its release: Nova-Linux-PyroWave-x86_64-alpha.flatpak, beside the standard Nova-Linux-x86_64-alpha.flatpak, which is built without the decoder. Earlier Nova releases, the 1.4.13 betas included, attach only the standard Flatpak, which was named Nova-Deck-x86_64-alpha.flatpak before 1.4.13.

The PyroWave build is an Alpha, like the standard Flatpak. It negotiates SDR 4:2:0 only. No PyroWave stream decoded by it on a Steam Deck has been recorded yet, so treat a Deck as untested.

Download Nova-Linux-PyroWave-x86_64-alpha.flatpak and its .sha256 file from the Nova 1.4.13 release. Close Nova, then run this in the download folder (on a Deck, in Desktop Mode):

Terminal window
sha256sum -c Nova-Linux-PyroWave-x86_64-alpha.flatpak.sha256
flatpak install --user ./Nova-Linux-PyroWave-x86_64-alpha.flatpak

The PyroWave build has the same app ID as the standard Flatpak, com.papi_ux.Nova, so it replaces that app and keeps its pairing and preferences. It has no automatic update feed. That holds when both go into the same installation. If flatpak list --app --columns=application,installation shows com.papi_ux.Nova under system, a --user install sits beside that copy rather than replacing it; remove one of them, for example with flatpak uninstall --system com.papi_ux.Nova. To go back, install Nova-Linux-x86_64-alpha.flatpak from the same release with flatpak install --user, keeping the app data. The PyroWave build still offers H.264 and HEVC, and Auto never picks PyroWave in it.

You can also build it yourself from com.papi_ux.Nova.pyrowave.json, beside com.papi_ux.Nova.json in clients/deck/packaging/flatpak/, with the commands in Nova’s Flatpak README. A build from a later Nova tag works with a Polaris 1.4.13 host only while its clients/deck/pyrowave/protocol.h still names pyrowave-186f0393-sdr420-v1.

  1. Open Play Setup for the game you want to stream.
  2. Choose Video Codec, then PyroWave · Experimental. The choice is saved for that host and game only. The Encoder row then shows PyroWave · Vulkan.
  3. Start the stream. If PyroWave cannot start, Play Setup says why:
Play Setup says Why
“This PC does not offer compatible PyroWave support. Use a matching enabled Polaris build or choose another codec.” The host does not offer PyroWave: Polaris older than 1.4.13, 1.4.13-beta.1, or a GPU that lacks what the encoder needs (Check the host’s GPU).
“PyroWave is not available in Spaces. Choose Auto or H.264.” The stream is a Space.
“PyroWave Vulkan decoding is unavailable on this Linux device. Choose another codec.” This device cannot decode PyroWave.
“This stream size exceeds this device’s PyroWave decoder limits. Choose a smaller size or another codec.” The resolution is above what this device’s decoder takes.
“This Nova build does not include PyroWave. Use an enabled experimental build or choose another codec.” PyroWave is still chosen in a build without the decoder, such as the Alpha.

To stop using PyroWave for that host and game, choose H.264 or HEVC in the same place; those are the alternatives the build offers.

Nova for Linux gives no PyroWave bitrate figure and does not warn when the bitrate is low, so set the bitrate yourself: Bitrate in Play Setup before the stream, or Live Bitrate in Command Center during it. The figures under Bitrate advice are Nova for Android’s advice. This page has no verified figure for the Linux build. As a starting point only, Nova for Android’s formula gives about 45 Mbps for a Steam Deck’s 1280x800 at 60 fps and about 67 Mbps at 90 fps.

The Nova Stream HUD is Nova for Android’s. Nova for Linux’s own HUD has a CODEC reading, which its source at v1.4.13-beta.3 sets to PyroWave during a PyroWave stream. This page has not checked it on a live stream, so confirm the codec in Polaris Mission Control as well.

You do not need to know your capture route to use PyroWave, unless the host is a KDE desktop with a display in HDR (above). The route decides whether each frame is copied to the GPU before it is encoded.

The host side is cheap. Even on a route whose frames arrive in host memory, a 1080p Private Stream on an RTX 4090 measured 0.35 ms to copy each frame to the GPU plus 0.95 ms to encode it. Capture that delivers frames already on the GPU skips the copy; the routes that do are listed next.

The encoder gets a frame already on the GPU only when capture hands it over while it is still in GPU memory (a DMA-BUF). That happens with PipeWire capture that negotiated DMA-BUF, such as a KWin Host Virtual Display, and with capture = kms on an eight bit or ten bit packed scanout, the frame the graphics driver sends to the display (not a KDE display in HDR; see A KDE host with a display in HDR). Private Stream, wlroots and X11 capture, and PipeWire capture that fell back to shared memory, deliver frames in host memory, which are copied to the GPU and converted there. Private Stream and Host Virtual Display are chosen in the Polaris web console under Settings > Audio/Video > Where games run (Choose where games run).

A frame can cost more than that for two reasons. If the GPU path cannot start, colour conversion falls back to the CPU (SDR only; the host log says so). An ultrawide source makes the copy bigger, because the whole source is copied to the GPU every frame. A KWin Host Virtual Display avoids the copy.

Doctor, the stream check in Nova’s Command Center and in Mission Control, names the codec and reports the capture transport and whether frames were on the GPU or in host memory. It cannot yet show where colour was converted: it gives every PyroWave stream the reason “PyroWave is encoding with Vulkan after CPU color conversion.”, even though conversion runs on the GPU by default. Go by the host log instead.

journalctl --user --since '10 min ago' | grep 'PyroWave:' shows the last stream. Run it as the user who runs Polaris, without sudo: Polaris runs as a user service, so its log is in your user journal. Every line below contains PyroWave:.

Log line What it means
encoding on <GPU>, queue family ... The host check passed: this GPU can run the encoder. It appears once per Polaris run.
no GPU on this host has what the encoder needs The host cannot serve PyroWave and does not offer it. no Vulkan loader on this host and no Vulkan 1.3 instance on this host mean the same.
encoding a 1920x1080 frame where capture left it, on <GPU> Capture handed over a DMA-BUF, and the encoder read the frame where capture left it, with no copy.
encoding straight from a 1920x1080 picture on <GPU> The frame arrived in host memory, was copied to the GPU, and its colour was converted there.
falling back to converting frames on the CPU The GPU path could not start, so colour conversion runs on the CPU. This happens for SDR only.
POLARIS_PYROWAVE_GPU_INPUT is off, so frames are converted on the CPU That environment variable forced the CPU converter. If this run’s log has no such line, it is not set.
Error: PyroWave: capture is handing over a dmabuf in a format this codec cannot read (fourcc ...) Capture hands over a format PyroWave cannot read, identified by its fourcc (the four character code of a pixel format), and the stream ends. See A KDE host with a display in HDR.
this client negotiated HDR and the captured display is not in HDR The client asked for HDR and the captured display is not in HDR, or its HDR metadata could not be read, so the stream is refused, whatever the capture route. See Limits.
over 300 frames, ... ms and encode ... ms a frame The average cost of a frame, after 300 frames (five seconds at 60 fps), then every 18,000 frames (five minutes).

One line without the PyroWave: prefix also matters: Skipping FEC for oversized encoded frame(s) means the largest frames went out without their error correction (see Limits).

  • HDR has not been shown end to end. It needs ten bit frames with HDR metadata and the GPU input path. Desktop portal capture (the desktop’s screen sharing service) never provides those frames outside a Gamescope HDR session on the host, so a PyroWave HDR request on a KDE or GNOME desktop is refused, and the host logs this client negotiated HDR and the captured display is not in HDR. The host makes that refusal whenever the captured display is not in HDR, whatever the capture route, kms included. A KDE display in HDR under kms fails before that check, for a different reason, SDR included: see A KDE host with a display in HDR.
  • A KDE host with a display in HDR cannot stream PyroWave at all when capture goes through kms, SDR included (A KDE host with a display in HDR). Polaris cannot yet convert KWin’s HDR format for this codec.
  • Spaces cannot use it.
  • Auto never selects it, on any client.
  • Among published clients, only the Nova for Android beta can use it (Is it for you?). The Nova for Linux PyroWave build is unpublished and negotiates SDR 4:2:0 only.
  • Other clients are unaffected. Polaris adds PyroWave to the codecs it offers every client, when its GPU can run the encoder and the session is not a Space. Moonlight and stable Nova ignore it and keep using H.264, HEVC or AV1.
  • It costs bandwidth. It is intra only, so every frame is a key frame. Polaris applies no PyroWave quality floor and does not recommend a bitrate. The advice in these pages comes from Nova, which advises about 91 to 364 Mbps depending on the mode and whose bitrate slider stops at 300 Mbps (Bitrate advice). Valve quotes 100 to 500 Mbit/s and at least gigabit ethernet for the same codec in Steam Remote Play, and Nova’s advice falls roughly within that range. Steam Remote Play’s PyroWave and Polaris’s are separate streams and cannot connect to each other.
  • Wi-Fi is not blocked, but it rarely keeps up. Nothing stops PyroWave on Wi-Fi, but Wi-Fi usually cannot carry these bitrates, so expect stutter there. A wired host does not help the client’s Wi-Fi link. A 100 Mbps adapter or port is below Nova’s advice for anything above 1920x1080 at 60 fps, and leaves no headroom even there.
  • There is no PyroWave frame size cap. Each frame’s budget is the bitrate divided by the frame rate, at least 4096 bytes. The largest frames are sent without their error correction, so packet loss is least protected when frames are largest.
  • An ultrawide source costs more when frames arrive in host memory, because the whole source is copied to the GPU every frame. A 32:9 source is letterboxed, and the bars cost nothing.
  • Diagnostics do not show where colour was converted. Use the host log.
  • A mode that negotiates is not a performance promise. A given GPU and network may not sustain it.
  • A working SDR stream is not HDR validation. They are separate paths and need separate proof.

This section is for people building Polaris from source or writing a PyroWave client.

POLARIS_ENABLE_PYROWAVE is on by default for Linux, so a normal configuration builds the encoder with no flag to add. It needs two pinned submodules that a plain clone does not fetch:

Terminal window
git submodule update --init --recursive third-party/pyrowave third-party/Granite

With the option on and either submodule empty, configuration stops with an error that prints this command. Pass -DPOLARIS_ENABLE_PYROWAVE=OFF to leave the encoder out, which builds exactly as the tree did before the codec existed. Building covers the rest of a source build.

Polaris pins PyroWave revision 186f0393b77f7755953b5ecde994bb1cec2e4155 (C API 0.6.0) and Granite revision b6cffd5ce81f540f0855e6778428483e14763d9b. Keep client and host pins and the profile token in agreement; the C API version alone does not establish bitstream compatibility. A Nova for Linux build from a Nova tag later than v1.4.13-beta.3 works with a Polaris 1.4.13 host only while its clients/deck/pyrowave/protocol.h still names pyrowave-186f0393-sdr420-v1.

The RTSP offer contains a=rtpmap:99 PYROWAVE/90000 and a=fmtp:99 followed by the profile tokens the host can serve, separated by spaces: pyrowave-186f0393-sdr420-v1, plus pyrowave-186f0393-hdr2020pq420-v1 when the GPU input path is available, which is the default. A client looks for its own token as one whole element of that list. The host sends both lines only when a GPU on it can run the encoder, and never for a Spaces session.

In ServerCodecModeSupport the host sets 0x00800000 for PyroWave and 0x01000000 for PyroWave 4:4:4, and 0x02000000 for PyroWave HDR10 only when the GPU input path is available. Nova’s client video format for PyroWave is 0x10000, and the selected bitStreamFormat is 3.

The host refuses the ANNOUNCE, with a reason in its log, when the codec cannot run on it, when the dynamic range is neither SDR nor HDR10, when HDR is asked for without the GPU input path, when the colour mode is not full range, when an SDR request names a colourspace other than Rec. 709 or an HDR request one other than Rec. 709 or BT.2020 (the HDR stream is BT.2020 PQ either way), or when the width or height is odd.

Each GameStream frame carries one complete raw PyroWave bitstream, with the coefficient packets concatenated in order, and each frame is coded independently. The exact payload length excludes transport padding. Encoding retained capture content still produces a new codec frame at the current bitrate budget; it must not resend the previous codec sequence unchanged.

Each frame’s budget is the bitrate divided by the frame rate, at least 4096 bytes, with no upper limit. There is no PyroWave frame size cap and no payload size minimum. A frame that needs more FEC blocks than the transport allows is sent without FEC parity, and the host logs Skipping FEC for oversized encoded frame(s). The encoder accepts bitrate changes live, without restarting the stream.

Private Stream and the other private compositor modes capture with wlr whatever capture says. A Host Virtual Display made by KWin, EVDI or kscreen-doctor is captured through the portal whatever capture says. When capture names another backend, the host logs cannot be captured through [<backend>]; this session uses [portal] and restores the setting at teardown.

The kms, portal and wlr backends each log one line per capture with the prefix kms:, portal: or wlr: followed by capture_transport=, frame_residency= and frame_format=, naming the backend that actually captured.

Colour conversion runs on the GPU by default. A frame captured into host memory is copied to the GPU and converted in the encode pass, and a DMA-BUF from capture is imported without a host copy. The CPU converter is used only when POLARIS_PYROWAVE_GPU_INPUT is 0, off, no or false, or when the GPU path cannot start on an SDR stream. It is eight bit, so it cannot carry HDR: with the variable off, the host offers no HDR token and no HDR10 bit, and an HDR session that cannot use the GPU path ends.

A DMA-BUF can be read in XRGB8888, ARGB8888, XBGR8888, ABGR8888, XBGR2101010, ABGR2101010, XRGB2101010 or ARGB2101010. A DMA-BUF in any other format, including the sixteen bit float formats, ends the stream on its first frame, because capture keeps producing that format for as long as the display keeps its mode. A KDE display in HDR scans out ABGR16161616F, fourcc 1211384385, printed AB4H, and kms capture hands it over as it is, so the stream ends with Error: PyroWave: capture is handing over a dmabuf in a format this codec cannot read (fourcc 1211384385); ending the stream, because that does not change while the display keeps its mode. This check runs before any dynamic range decision, so an SDR request ends the same way. Polaris 1.4.13’s diagnostics and its kms: log line label that format bgra8. From commit b38588e2 on the development branch, not yet released, the log names it AB4H and Doctor raises capture_format_unreadable_by_pyrowave. That is a clearer diagnosis, not a fix: the conversion for sixteen bit float is not written.

The encoder selection reason in diagnostics is a fixed string, “PyroWave is encoding with Vulkan after CPU color conversion.”, for every PyroWave stream, and the PyroWave session records no encode target, so diagnostics cannot report a GPU native capture path for it. The log lines under Read the host log are the observed path.

Encoder timing leaves out the deliberate wait for the next frame, so it is not a duty cycle. The bitrate the client asked for, the host’s current target, the encoder’s applied budget and the measured received bitrate are four different numbers; do not read one as another.

Build and test both enabled and disabled configurations. Enabled coverage includes whole-frame transport, retained-frame encoding, live budget changes, resizing, letterboxing and colour conversion. The disabled binary must not gain a PyroWave shared-library dependency. Run the matching client parser, Vulkan decode and presentation tests against the host-produced frame as well.

Live automation needs one designated host owner and isolated capture and playback audio services without access to physical outputs. Separate ports and a null capture sink on the desktop audio service do not isolate client playback or prevent changes to desktop routing. Refuse a competing host rather than stopping someone else’s service.

A completed soak, upgrade checks and physical display, input and audio testing remain separate acceptance gates. Decoded-frame counters do not establish physical presentation or audio quality, and an interrupted soak is not a pass.