The pf-vdisplay driver now advertises HDR/FP16 and the full glass-to-glass HDR path works end-to-end — validated LIVE: the Mac client connected to the .173 host WITH HDR (display_hdr=true, FP16 ring -> NVENC P010). The STEP-3 assumption that FP16 needs a higher UmdfExtensions was WRONG: IddCx0102 + CAN_PROCESS_FP16 + the *2 DDIs works (the oracle proved it; confirmed on-glass IddCxAdapterInitAsync -> 0x0 WITH the FP16 cap set). Driver-only change — the host FP16-ring -> NVENC-P010 path and the HDR EDID were already in place. - adapter.rs: caps.Flags = IDDCX_ADAPTER_FLAGS_CAN_PROCESS_FP16. - entry.rs: register the 6 *2/HDR callbacks (ParseMonitorDescription2, MonitorQueryTargetModes2, AdapterCommitModes2, AdapterQueryTargetInfo, MonitorSetDefaultHdrMetaData, MonitorSetGammaRamp) ALONGSIDE the v1 set (matching the oracle — CAN_PROCESS_FP16 OBLIGATES the *2 DDIs or the framework rejects the adapter at init; STEP 3 rejected FP16 only because they weren't registered). - callbacks.rs: parse_monitor_description2 + monitor_query_modes2 now fill IDDCX_MONITOR_MODE2 / IDDCX_TARGET_MODE2 with BitsPerComponent (8|10 bpc RGB); query_target_info already reports IDDCX_TARGET_CAPS_HIGH_COLOR_SPACE; set_default_hdr_metadata + set_gamma_ramp accept (the gamma one is mandatory under FP16). - monitor.rs: wire_bits() (Rgb 8|10, no YCbCr) + target_mode2(). - EDID + INF UNCHANGED (the EDID already carries the CTA-861.3 BT.2020 + ST.2084/PQ block; the INF stays UmdfExtensions=IddCx0102). Built via the ultracode flow (STEP-7 map workflow -> agent-implement -> box build [driver green] -> deploy -> on-glass HDR). OPERATIONAL NOTE: do NOT Disable/Enable the IddCx devnode to reload it — that leaves the adapter STOPPED in the persisted WUDFHost process (ADAPTER OnceLock survives), so monitor-create then fails with 0xc00002b6 (INDIRECT_DISPLAY_DEVICE_STOPPED). Kill the pf_vdisplay WUDFHost process (or reboot) for a clean adapter re-init. This completes the pf-vdisplay rewrite STEP 0-7, all on-glass validated (loads, adapter inits, monitor appears, swap-chain drain, IDD-push frames at ~235fps, and HDR). Remaining: STEP 8 (unsafe- reduction + delete the old vdisplay-driver tree + the vendored SudoVDA driver + unbundle from the installer = the SudoVDA drop). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Packaging punktfunk for Fedora / Bazzite
The punktfunk host is Linux-only and links system FFmpeg (NVENC), PipeWire, Opus and the NVIDIA driver. This directory packages it for the Fedora Atomic / Bazzite world (rpm-ostree + bootc), where most of those deps are already present.
👉 Ubuntu/Debian hosts install via
aptfrom Gitea's package registry — seedebian/README.md(apt update && apt upgradefor new builds).
👉 End-to-end Bazzite setup walkthrough (install → udev/group →
host.env→ service → firewall → verify → troubleshooting):bazzite/README.md. This file is the higher-level packaging rationale.
packaging/
rpm/punktfunk.spec # the RPM (builds punktfunk-host from source with cargo)
bazzite/host.env # gamescope-default config for a Bazzite appliance
bazzite/README.md # step-by-step Bazzite setup guide
bootc/Containerfile # bake punktfunk into a Bazzite-based atomic image
copr/ # COPR build-from-SCM settings
What's needed beyond base Fedora
| Dependency | Where it comes from |
|---|---|
ffmpeg-libs with NVENC |
RPM Fusion nonfree (ffmpeg, not ffmpeg-free) |
NVIDIA driver (libnvidia-encode, libEGL_nvidia) |
Bazzite -nvidia images ship it; plain Fedora: akmod-nvidia + xorg-x11-drv-nvidia-cuda |
| gamescope, PipeWire, wireplumber | Bazzite ships these; plain Fedora: dnf install gamescope pipewire wireplumber |
opus, libei |
Fedora base / updates |
On Bazzite the only genuinely new runtime bits are ffmpeg-libs (RPM Fusion) + opus +
libei — the rest of the stack is already there. The default backend is gamescope
(packaging/bazzite/host.env), which the host spawns headless per session — no desktop login.
Option A — Gitea RPM registry (recommended; per-host, rpm-ostree)
The host's RPM is published to unom's self-hosted Gitea RPM registry (CI builds it on every
push), mirroring the Debian/apt setup. Add one repo file, install, and track
updates with rpm-ostree upgrade — no COPR account needed. Full guide: rpm/README.md.
# GPG-signed pkgs + Gitea-signed metadata → gpgcheck=1, repo_gpgcheck=1 (see rpm/README.md)
sudo tee /etc/yum.repos.d/punktfunk.repo >/dev/null <<'REPO'
[gitea-unom-bazzite]
name=punktfunk (unom, Bazzite)
baseurl=https://git.unom.io/api/packages/unom/rpm/bazzite
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://git.unom.io/api/packages/unom/rpm/repository.key
https://git.unom.io/api/packages/unom/generic/punktfunk-keys/1/RPM-GPG-KEY-punktfunk
REPO
rpm-ostree install punktfunk && systemctl reboot
# updates: rpm-ostree upgrade && systemctl reboot
Option B — COPR (per-host, rpm-ostree install)
- Create a COPR project, enable build-from-SCM pointing at this repo, spec path
packaging/rpm/punktfunk.spec(seecopr/README.md). Under External Repositories add RPM Fusion nonfree soffmpeg-develresolves at build time. - On the Bazzite host:
# RPM Fusion (for the NVENC ffmpeg) — usually already enabled on Bazzite rpm-ostree install \ https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm \ https://mirrors.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$(rpm -E %fedora).noarch.rpm # enable the COPR + install punktfunk sudo wget -O /etc/yum.repos.d/_copr_punktfunk.repo \ https://copr.fedorainfracloud.org/coprs/enricobuehler/punktfunk/repo/fedora-$(rpm -E %fedora)/ rpm-ostree install punktfunk systemctl reboot
Option B — bootc (image-based, atomic)
Layer punktfunk into a Bazzite image once, then rebase any number of hosts onto it — no
per-host drift. See bootc/Containerfile:
podman build -t ghcr.io/<you>/bazzite-punktfunk -f packaging/bootc/Containerfile .
podman push ghcr.io/<you>/bazzite-punktfunk
# on the target:
sudo bootc switch ghcr.io/<you>/bazzite-punktfunk && systemctl reboot
First-run setup (either option)
ujust add-user-to-input-group # virtual gamepads need /dev/uinput (then re-login).
# On Bazzite use ujust, NOT `usermod -aG input` (atomic OS — it won't stick).
mkdir -p ~/.config/punktfunk
cp /usr/share/punktfunk/host.env.bazzite ~/.config/punktfunk/host.env # edit (gamescope app, etc.)
systemctl --user enable --now punktfunk-host
# Management web console (pairing + status) — pulled in by default (the host RPM Recommends it;
# `--no-install-recommends` / headless-only boxes can skip it). Enable it and read the login password:
systemctl --user enable --now punktfunk-web
journalctl --user -u punktfunk-web-init | sed -n 's/.*password generated: //p' # then open http://<host-ip>:3000
Pair a stock Moonlight client (mDNS-discovered), or connect the native punktfunk/1 client — via the
web console at http://<host-ip>:3000 or directly.
⚠️ COPR caveat: COPR's mock chroot has no
bun, so a COPR build produces onlypunktfunk+punktfunk-client— notpunktfunk-web. For the console on a COPR/bootc host, install from the Gitea RPM registry (Option A — it carriespunktfunk-web), which is also whybootc/Containerfileinstalls from there rather than COPR.
Why not Flatpak (for the HOST)?
The host needs unsandboxed access the zero-copy NVENC path, /dev/uinput, the PipeWire
graph and the compositor's privileged protocols — a Flatpak sandbox fights all of these.
An RPM (or the bootc layer) installs into the host system where those just work.
👉 The client is a different story — it IS shipped as a Flatpak (the only viable Steam Deck install path: SteamOS
/usris read-only and lackslibadwaita/libSDL3). Seeflatpak/README.md. The client sandbox only needs the GPU render node, Wayland, PipeWire audio, the network and hidraw — all expressible as finish-args.
Building the SRPM/RPM locally (Fedora only)
git archive --format=tar.gz --prefix=punktfunk-0.0.1/ -o ~/rpmbuild/SOURCES/punktfunk-0.0.1.tar.gz HEAD
rpmbuild -ba packaging/rpm/punktfunk.spec # needs the BuildRequires from the spec
(Not buildable on Debian/Ubuntu — use a Fedora toolbox/container or COPR.)