config.rs: typed HostConfig parsed ONCE from env (idd_push/encoder_pref/no_helper/force_helper), replacing per-call env::var re-reads (PUNKTFUNK_ENCODER was re-read on EVERY windows_resolved_backend() call; PUNKTFUNK_IDD_PUSH is read 8x across the host — the recompute that lets capture + encode disagree on the backend, plan §2.4). Migrated the two highest-churn dispatch reads onto it (encode::windows_resolved_backend, punktfunk1::should_use_helper). Behavior-identical: the env is constant for the process lifetime (the service loads host.env before launch), so a lazily-parsed global == parsed-once-at-startup. docs/windows-host-goal1-plan.md: the ORDERED, independently-shippable execution plan for Goal-1 (the plan's biggest unstarted goal — a from-scratch layered host architecture). Six behavior-preserving, box-verified stages (HostConfig -> SessionPlan -> SessionContext/SessionFactory -> seam-trait tightenings -> src/windows tree), because the host is live-validated and a monolithic rewrite would strand it broken. Stage 1 done here; stages 3-5 rewire the deployed path and require on-glass re-test. Verified: Linux + box (--features nvenc) cargo check clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4.2 KiB
Goal-1 (clean, layered host architecture) — staged execution plan
The design is in windows-host-rewrite.md §2.2–2.4. This file is the ordered,
independently-shippable execution plan, because the host is live-validated (GameStream + punktfunk/1,
NVENC + IDD-push on-glass) and Goal-1 rewires its session/config/dispatch flow — so every stage must
preserve behavior, compile + box-verify on its own, and be committed before the next starts. The plan's
own §14 makes the §1 preservation checklist a mandatory per-module assert contract; honour it.
Why staged (not one big rewrite)
main is at parity and shipping. A monolithic rewrite would put the validated host in a broken
intermediate state for a long window and make a regression impossible to bisect. Each stage below is a
behaviour-preserving transform with its own verification, so a regression is caught at the stage that
introduced it.
Stages (ordered; each = goal · files · risk · verify)
Stage 1 — HostConfig foundation. ✅ DONE (this commit).
config.rs: typed HostConfig parsed ONCE from env (idd_push/encoder_pref/no_helper/force_helper).
Migrated the two highest-churn dispatch reads onto it (encode::windows_resolved_backend,
punktfunk1::should_use_helper). Risk: low (env constant at runtime → identical behaviour). Verify: box
cargo check --features nvenc.
Stage 2 — finish HostConfig + resolve-once.
Migrate the remaining ~64 env::var sites onto HostConfig fields (esp. PUNKTFUNK_IDD_PUSH ×8,
PUNKTFUNK_RENDER_ADAPTER, PUNKTFUNK_ZEROCOPY, PUNKTFUNK_SECURE_DDA, PUNKTFUNK_IDD_DEPTH,
PUNKTFUNK_NO_WGC, the perf/debug flags). Linux vars (XDG/compositor) included for one config. Risk:
medium (must preserve each var's exact semantics — a flipped bool is a silent regression; assert per the §1
checklist). Verify: Linux + box build; grep env::var should reach ~0 outside config.rs.
Stage 3 — SessionPlan (the single biggest clarity lever, plan §2.4).
Resolve display/capture/topology/encoder/format/hdr/bit_depth ONCE from HostConfig into a typed
SessionPlan; replace the 3-place dispatch (capture_virtual_output, should_use_helper/virtual_stream,
windows_resolved_backend) with reads off the plan. Fixes the capture/encode backend-disagreement bug
class. Risk: medium-high (rewires the deployed decision). Verify: box build + on-glass re-test (NVENC +
IDD-push + a mode switch).
Stage 4 — SessionContext + SessionFactory/Session.
Bundle the 12–13-arg #[allow(too_many_arguments)] signatures into SessionContext; SessionFactory.build()
owns the RAII chain vdm.lease(mode) → open_capturer(vout, fmt) → open_encoder(plan) → spawn pipeline, with
Session::drop the ONLY teardown path. Risk: high (teardown ordering — the §1 RAII asserts are mandatory).
Verify: box build + on-glass (connect/disconnect/reconnect, no leaked monitors/threads).
Stage 5 — seam-trait tightenings (plan §2.3).
Capturer::open_capturer(vout, want: OutputFormat) takes the format IN (kills the
capture → encode::windows_resolved_backend() back-reference recomputed in dxgi.rs); HDR/release become
VirtualLease methods (session glue names no concrete backend, contains no unsafe); optional encoder
features move to EncoderCaps. Risk: medium. Verify: box build + on-glass.
Stage 6 — src/windows/ tree (cfg-sprawl confinement, plan §2.2).
Move the Windows backends under src/windows/ + capture/windows/, encode/windows/, inject/windows/,
audio/windows/, vdisplay/windows.rs behind one #[cfg(windows)] mod windows; seam. Pure file move +
mod/use-path updates — behaviour-identical. Risk: low-but-huge (dozens of files; compile-verify catches all).
Do LAST so the earlier semantic stages don't fight path churn. Verify: Linux + box build.
Guardrails (mandatory, plan §14)
- Each stage is its own commit; box-verify before moving on.
- Stages 3–5 touch the deployed path → on-glass re-test (NVENC + IDD-push, a mode switch, a connect/disconnect cycle) before the next stage.
- Preserve every
PUNKTFUNK_*var's exact semantics; when in doubt, assert old==new at the call site.