The safety half of the rust-safety programme's §8.4: `std::env::set_var`/`remove_var` are
`unsafe fn` in edition 2024, converting the class of bug the programme found the hard way
(the 972af299 environ data race lived in a file with ZERO occurrences of the word
`unsafe`) from invisible to counted and compiler-enforced.
Manifests: [workspace.package] edition 2021→2024, rust-version 1.82→1.85 (the pinned
toolchain is 1.96.0, so no toolchain bump — only the declared floor rises); the 13 crates
pinning `edition = "2021"` literally now inherit it (Trap 1: the root bump alone reaches
only `edition.workspace = true` crates and would have left pf-encode/pf-capture/pf-inject
et al. on 2021 while reading as complete); pf-driver-proto's stale rust-version 1.82 pin
now inherits; pf-vkhdr-layer (a separate workspace, inherits nothing) bumped to 2024. The
four vendored crates (fec-rs, cros-codecs, usbip-sim, the patched ndk) stay on 2021
deliberately — upstream code stays pristine. The excluded usbip-poc standalone PoC is
untouched.
Mechanical, done textually across ALL cfg branches so no platform's half is left behind
(Trap 3 — 44% of the host's unsafe is Windows-only and a one-platform `cargo fix` misses
it): 148 `#[no_mangle]` → `#[unsafe(no_mangle)]` (83 in abi.rs); 12 bare extern blocks →
`unsafe extern`; `gen` is a reserved keyword, so pf-vdisplay's generation stamps
(registry.rs, windows/manager.rs) and the WinUI shell's animation counters rename
gen → generation (internal identifiers only, no serde/wire surface); two
match-ergonomics patterns take the compiler's suggested reference form.
env mutation: every `set_var`/`remove_var` site (20 files) now sits in an `unsafe` block
whose SAFETY comment states the real serialization argument (pf-vdisplay's ENV_LOCK,
CONFIG_DIR_TEST_LOCK, ART_ROOTS_LOCK, vkdecode's gpu_lock, the `--test-threads=1`
contracts of the hardware spikes, or single-threaded startup). Two genuine hazards
surfaced en route — exactly the WP3b-class finds this migration exists to make visible —
and are fixed here:
- windows/service.rs spawned the network-profile warner thread BEFORE `load_host_env()`,
so a child-spawning thread (child spawn snapshots the env block) was live while
`set_var` ran in a loop; the load now precedes the spawn.
- pf-console-ui's `fake_home()` re-set HOME outside its OnceLock on EVERY call, so two
parallel tests could race the write; the set now happens exactly once inside
`get_or_init`.
cbindgen (Trap 2): 0.29.4 parses `#[unsafe(no_mangle)]` — verified empirically; the
header regenerates byte-identical. The ci.yml drift check could never catch "failed to
regenerate" (build.rs demotes a cbindgen failure to a warning and writes nothing, leaving
the checked-in header untouched and the diff clean), so the step now first asserts the
"punktfunk-core: wrote" line and the absence of "cbindgen failed" (sh -e safe: no `!`
pipeline, no tee-masked exit).
rustfmt: style_edition pinned to 2021 at the root — edition 2024 would otherwise flip the
style edition and reformat ~370 untouched files inside this same commit, burying the
migration diff. The drivers workspace pins its already-current 2024 style. Adopting the
2024 style tree-wide is its own future one-line-plus-reformat commit.
Census: the primary metric moves UP BY DESIGN — 2435 → 2453 operations, unsafe blocks
1534 → 1577, and env_set_var is now a counted category (45 ops). The newly counted env
sites are a truer number, not a regression; baseline snapshot saved as punktfunk-planning
design/rust-safety-census-baseline-2026-08-12-edition-2024.txt. Gate C's env ratchet is
now compiler-enforced (the hygiene-script header says so); the two shrunk file counts
(nvenc_cuda 49→2 via the test helpers, shell/tests 2→1) are lowered in the same commit
per the gate's own rule.
Drop order (the semantic change most likely to bite this codebase): the migration lint
`-W tail-expr-drop-order` reports zero findings on the macOS-visible halves of
pf-encode / pf-zerocopy / pf-capture / pf-frame; the Linux and Windows halves run the
same lint on the gate boxes. The four #[ignore]d alloc/drop-cycle tests on the hardware
boxes remain owed, as before this change.
pf-vkhdr-layer — HDR Vulkan layer for the virtual display
A tiny Vulkan implicit layer (VK_LAYER_PUNKTFUNK_hdr_inject) that lets Vulkan games enable
HDR while streaming over the punktfunk virtual display.
The problem it solves
On Windows, NVIDIA/AMD Vulkan ICDs do not advertise any HDR color space
(VK_COLOR_SPACE_HDR10_ST2084_EXT, VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT) for a surface that
lives on an IddCx indirect / virtual display — even when Windows "Use HDR" is on and the desktop
is composited at 10-bit. So Vulkan games (Doom: The Dark Ages and the rest of id Tech, Indiana Jones
and the Great Circle, …) query vkGetPhysicalDeviceSurfaceFormatsKHR, find no HDR color space, and
refuse HDR ("This device does not support HDR"). D3D11/D3D12 HDR works on the very same display
because the OS compositor drives it — only the Vulkan WSI enumeration is gated.
This was long believed unfixable from outside the GPU driver (the Apollo/Sunshine/Virtual-Display-
Driver communities all concluded "it's in Windows's kernel"). It isn't: an on-box experiment proved
the ICD happily accepts and presents a forced HDR swapchain on that exact virtual-display
surface (vkCreateSwapchainKHR + vkSetHdrMetadataEXT + present all succeed) — it simply won't
advertise the format. So the whole fix is to add the HDR surface formats to the enumeration the
game queries; once the game requests that swapchain, the ICD honors it. Validated live: Doom: The
Dark Ages enables HDR over the virtual display with this layer.
What it does
- Intercepts
vkGetPhysicalDeviceSurfaceFormatsKHR/...2KHR, calls down to the ICD, and appends{A2B10G10R10_UNORM_PACK32, HDR10_ST2084_EXT}+{R16G16B16A16_SFLOAT, EXTENDED_SRGB_LINEAR_EXT}(deduped — a no-op on real HDR monitors that already list them). - Self-gates: it only injects when the surface's monitor actually has Windows advanced-color
(HDR) enabled right now (checked via
DisplayConfigGetDeviceInfo/GET_ADVANCED_COLOR_INFO). So it does nothing on SDR sessions/displays — no washed-out "SDR-in-HDR". It tracksVkSurfaceKHR → HWNDby interceptingvkCreateWin32SurfaceKHR. - Everything else is pass-through dispatch chaining (instance + device).
It is shipped as an always-on implicit layer (loads via the registry, so it works regardless of how a game is launched — including via an already-running Steam, which env-based scoping can't guarantee). Because of the self-gate it is inert outside HDR streaming.
Controls
| Variable | Effect |
|---|---|
DISABLE_PF_VKHDR=1 |
Loader-standard off-switch — disables the whole layer for that process. |
PF_VKHDR_EXCLUDE=foo.exe,bar.exe |
Extra exe basenames to skip (in addition to a small built-in kernel-anti-cheat default list: cs2.exe, rainbowsix.exe, …). |
PF_VKHDR_LOG=1 |
Write a debug log to %TEMP%\pf_vkhdr_layer.log. |
Build / install
Standalone crate (own [workspace]), Windows-only cdylib:
cargo build --release # -> target/release/pf_vkhdr_layer.dll
The host installer (packaging/windows/pack-host-installer.ps1 → punktfunk-host.iss) builds it,
lays pf_vkhdr_layer.dll + pf_vkhdr_layer.json into {app}\vklayer, and registers it under
HKLM64\SOFTWARE\Khronos\Vulkan\ImplicitLayers (opt-out task "Install the HDR Vulkan layer").
Manual dev install: drop the DLL + JSON in one directory and add a REG_DWORD value named after the
JSON's full path (data 0) under HKLM\SOFTWARE\Khronos\Vulkan\ImplicitLayers (or HKCU\...).
Confirm with vulkaninfo (Surface section) — HDR10_ST2084_EXT should appear when the display has
HDR enabled.
Notes
- x64 only (the Windows host is x64 only).
- Anti-cheat: the layer is benign (it only adds surface formats; it never touches rendering,
memory, or input) and is signed, but because it's always-on it is present in every Vulkan
process. The built-in exclude list +
PF_VKHDR_EXCLUDE+DISABLE_PF_VKHDRcover kernel-anti- cheat titles you'd rather it stay out of.