docs(design): trim shipped plans, consolidate cluster, add index
Much of design/ described work that has since shipped. Trim each doc to
its durable rationale + still-open items (the code is the source of truth
for shipped detail; git history holds the full originals).
- Shipped plans -> status stubs: stats-capture, gamestream-host-plan,
apple-stage2-presenter, windows-service.
- Trimmed completed-out / open-kept: implementation-plan, hdr-pipeline,
host-latency, gpu-contention (fixed stale status table), game-library,
linux-setup (fixed m0->spike + stale zero-copy claim),
session-aware-host-followups, windows-client-bootstrap,
windows-dualsense-{scoping,game-detection}, windows-virtual-display,
security-review (per-finding status table; #12 still open),
apollo-comparison (shipped backlog collapsed to one-liners).
- Windows-host cluster consolidated: windows-host.md -> redirect into
windows-host-rewrite.md (whose stale scorecard is corrected -- goal1 is
merged, M4 done); windows-secure-desktop.md archived (now a fallback
behind IDD-push primary).
- Kept evergreen: ci.md, gamescope-multiuser.md, windows-build-and-packaging.md.
- New design/README.md: per-doc status table + consolidated open-items
roll-up so nothing is tracked in only one buried doc.
- Repoint 5 code comments to the archived secure-desktop doc path.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+22
-34
@@ -1,5 +1,10 @@
|
||||
# Linux host setup — NVIDIA GPU VM (pipeline spike + GameStream host)
|
||||
|
||||
> **Status:** Setup guide — still current and in active use (referenced from
|
||||
> `clients/apple/README.md`). The pipeline spike and the GameStream host are **shipped**
|
||||
> (see `design/gamestream-host-plan.md` + `CLAUDE.md`). This doc is trimmed to the bring-up
|
||||
> steps + gotchas; the §4 crate-API walkthrough was folded into `CLAUDE.md` "Pinned crate facts".
|
||||
|
||||
How to bring up the build environment for the punktfunk Linux host on an NVIDIA-GPU Ubuntu VM
|
||||
and run the **pipeline spike** (capture→encode). `punktfunk-core` already builds and is tested
|
||||
cross-platform; this is about the platform backends in `crates/punktfunk-host`.
|
||||
@@ -103,48 +108,26 @@ source /tmp/punktfunk-sway-env.sh
|
||||
swaymsg exec foot # animated content
|
||||
# Live portal capture → NVENC HEVC → playable file, with each AU also round-tripped
|
||||
# through a punktfunk_core host→client Session (FEC + packetize + reassemble) and verified:
|
||||
cargo run -p punktfunk-host -- m0 --source portal --seconds 5 --out /tmp/punktfunk-m0.h265
|
||||
ffprobe /tmp/punktfunk-m0.h265
|
||||
cargo run -p punktfunk-host -- spike --source portal --seconds 5 --out /tmp/punktfunk-spike.h265
|
||||
ffprobe /tmp/punktfunk-spike.h265
|
||||
# No capture session needed (encode + core only): --source synthetic
|
||||
```
|
||||
|
||||
Verified result: `1920x1080` HEVC, ~300 frames in 5s, `punktfunk-core loopback … 0 mismatches`.
|
||||
The portal negotiates packed **`RGB` (24-bit, 3 bpp)** on wlroots; the encoder expands it to
|
||||
`rgb0` (one pad byte/pixel, no colour math) since NVENC accepts `rgb0`/`bgr0` but not
|
||||
`rgb24`. dmabuf zero-copy import is still deferred (plan §9) — this is the CPU-copy path.
|
||||
`rgb24`. **GPU zero-copy is now implemented on all paths** (tiled dmabuf → EGL/GL → CUDA;
|
||||
LINEAR dmabuf → Vulkan bridge → CUDA → NVENC — see `CLAUDE.md`); the `capture` module keeps a
|
||||
`cpu_bytes` fallback for inputs that can't be imported.
|
||||
|
||||
Crate choices, verified current:
|
||||
- **Capture (portal path):** [`ashpd`](https://docs.rs/ashpd) **0.13** with the
|
||||
`screencast` feature (the `pipewire` feature is *not* needed — `open_pipe_wire_remote`
|
||||
is unconditional). Flow (0.13 API, verified against the vendored source): `Screencast::new`
|
||||
→ `create_session(Default)` → `select_sources(&session, SelectSourcesOptions::default()
|
||||
.set_sources(BitFlags::from_flag(SourceType::Monitor))…)` → `start(&session, None,
|
||||
Default)` → `.response()?` → `Stream::pipe_wire_node_id()` + `open_pipe_wire_remote()`.
|
||||
Note 0.13 takes **options structs**, not the old positional args, and defaults to the
|
||||
**tokio** runtime — drive the handshake on a *multi-thread* tokio runtime (a
|
||||
current-thread one starves zbus's reader and the portal reports "Invalid session").
|
||||
Pull frames with [`pipewire`](https://docs.rs/pipewire) **0.9** — it must match the
|
||||
pipewire crate ashpd 0.13 links (the `pipewire-sys` `links` key is unique per build, so
|
||||
`0.10` fails to resolve). 0.9 uses `MainLoopRc`/`ContextRc::connect_fd_rc(OwnedFd)`/
|
||||
`StreamBox`. Only request `SourceType::Monitor` — the wlr backend's
|
||||
`AvailableSourceTypes` is `1` (Monitor only); asking for `Window`/`Virtual` invalidates
|
||||
the session. Set `XDG_CURRENT_DESKTOP=sway` so the wlr portal backend is chosen, and
|
||||
import it into the portal's environment (see "Portal bring-up" below).
|
||||
- **Encode:** [`ffmpeg-next`](https://crates.io/crates/ffmpeg-next) **8.x** (binds the
|
||||
system FFmpeg 8.x via pkg-config; needs `clang`/`libclang`). Select the encoder by
|
||||
name — `encoder::find_by_name("hevc_nvenc")`, *not* by codec id (that's the SW encoder).
|
||||
Low-latency opts: `preset=p1`, `tune=ull`, `rc=cbr`, `bf=0`, `delay=0`, large `g`.
|
||||
If your FFmpeg is in a non-standard prefix, `export FFMPEG_DIR=/that/prefix`.
|
||||
- **Zero-copy is the hard part.** There's no direct dmabuf→CUDA import in FFmpeg.
|
||||
**Start with the CPU-copy fallback** (download frame → `hwupload_cuda` → `hevc_nvenc`)
|
||||
to get an end-to-end stream, then chase true dmabuf zero-copy. The plan flags this
|
||||
(§9) and the `capture` module already has a `cpu_bytes` fallback field.
|
||||
- **Input (GameStream host):** [`reis`](https://crates.io/crates/reis) (pure-Rust libei — no native
|
||||
`libei` needed) with `input-linux`/uinput as the universal fallback.
|
||||
Crate/API details (`ashpd` 0.13 screencast handshake, `pipewire` 0.9 frame pull, `ffmpeg-next`
|
||||
8.x encoder selection, `reis`/uinput input) now live in `CLAUDE.md` "Pinned crate facts" — they
|
||||
are the source of truth, with the FFmpeg-prefix override `export FFMPEG_DIR=/that/prefix` and
|
||||
the bindgen `LIBCLANG_PATH` knob in the troubleshooting table below.
|
||||
|
||||
Then continue toward the **GameStream host**: `serverinfo`/RTSP/pairing enough for a stock Moonlight client
|
||||
to connect, a KWin virtual output created on connect, input via reis/uinput — the
|
||||
shippable milestone.
|
||||
The **GameStream host** built on this spike is shipped — `serverinfo`/RTSP/pairing for a stock
|
||||
Moonlight client, a per-compositor virtual output created on connect, input via reis/uinput.
|
||||
See `design/gamestream-host-plan.md` + `CLAUDE.md`.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
@@ -157,3 +140,8 @@ shippable milestone.
|
||||
| `Cannot load libnvidia-encode.so.1` | NVENC runtime lib missing (driver) or unlicensed vGPU |
|
||||
| `cargo build` can't find FFmpeg | `export FFMPEG_DIR=$(pkg-config --variable=prefix libavcodec)` or point `PKG_CONFIG_PATH` at the custom build |
|
||||
| bindgen: libclang not found | `export LIBCLANG_PATH=$(llvm-config --libdir)` |
|
||||
|
||||
## Open items
|
||||
|
||||
None — the pipeline spike and the GameStream host it seeds are both shipped (see
|
||||
`design/gamestream-host-plan.md` + `CLAUDE.md`). This file remains as the host-box bring-up guide.
|
||||
|
||||
Reference in New Issue
Block a user