forked from unom/punktfunk
The ladder's last rung no longer runs FFmpeg. H.264 decodes through openh264, AV1 through rav1d, and HEVC is refused outright: no permissively licensed software HEVC decoder exists, so an HEVC session that exhausts its hardware rungs now tears down and re-dials advertising HEVC-less caps, and the host picks H.264. The plan calls that a first-class path; it is one. swscale is deleted, and with it the BT.601 default that its correction code existed to undo. Colour on the H.264 lane now comes from the same pf-bitstream planner every hardware rung submits from — openh264 reports no VUI at all — and AV1's comes per-picture from the sequence header. One colour source, one CSC: the old default is unrepresentable rather than merely fixed. Frames reach the presenter as three tightly-packed planes through the planar CSC pass, which had to be un-gated from the pyrowave feature and its device probe, since the last rung must exist on devices that failed that probe. rav1d rather than the dav1d crate, deliberately and against the plan's literal wording: dav1d-sys is system-deps-only, so it would add a system library and a .pc file to every client package — in the milestone family whose excision checklist exists to delete exactly that. rav1d is the same decoder, same licence, statically linked. The cost is honest: no-asm builds on both decoders, and software throughput is still unmeasured. The colour test is the milestone's exit criterion, so it is built to fail. Three fixtures, and a mutation check: hardcoding the swscale default turns the red bar to [255,24,0], and swapping Cb/Cr turns red to blue — a silent error no metadata assertion could catch. Review then disproved the range half of it numerically: with eight saturated bars, decoding the full-range fixture with the wrong range gives max error ZERO, because a mismatch only pushes values outside [0,1] where the shader clamps. A mid-tone was added; the wrong range now costs 11, well past the tolerance. The exit criterion I set was satisfiable by a test that proved nothing. Two blocking defects, both emergent rather than local. Software AV1 on a 10-bit stream never reached its typed refusal: rav1d is built 8-bit-only and returns ENOPROTOOPT, which the send loop turned into a generic error, so the pump's typed downcast missed and every AU failed identically — a permanent freeze on precisely the shipping case, since AV1 is advertised only where hardware AV1 exists and hardware AV1 plus HDR is Main 10. The shape is now read from the sequence header before any byte reaches the decoder, exactly as the H.264 leg reads the active SPS. And the new Reconnecting phase was the first state that is not streaming, not connecting, and still holding a live stream — which opened all three guards that had made a second launch impossible. Pressing A assigned over `stream` where every other site shuts down first, and StreamState has no Drop, so the old pump was detached: a second live session still submitting to a Vulkan device that gets destroyed underneath it. Nothing about the reconnect was wrong in isolation; the defect lived between a new state and three guards nobody re-examined. Start is now defensive and the retry raises the connecting modal, so the UI matches the state and B can cancel. Also closed: retry_caps was computed, tested and never applied, so a shape refusal could end a session reporting no codec available while a working retry existed; the retry inherited force_software sticky-true, landing an HEVC→H.264 fallback on software H.264 with working hardware H.264; it re-dialled with a stale mode; the CPU present arm had no survivable-failure handling where the pyrowave arm — same pass — has it; HEVC is no longer advertised when the decoder is pinned to software; and the software rung now feeds the recovery-point SEI it already had in hand to the re-anchor gate. ⚠ Two host-side gaps found while tracing, neither in scope here: Hello::launch is NOT idempotent (gog:/custom: targets spawn a second copy on a retry; the field is kept verbatim because dropping it orphans the gamescope display whose reuse key includes the command), and a reconnected session can never adopt a game predating its own launch stamp, so it has no game-exit detection. ⚠ OWED: the on-glass software run. ~200 lines of new Vulkan on a path that only runs because the GPU already failed, and no driver has seen it. The review's minimum check is sync validation enabled, a non-multiple-of-16 mode, a mid-session resize and demotion, and both colour matrices. Gates: container clippy -D warnings over four crates, 236 tests, workspace check. pf-vkdecode and pf-bitstream are byte-for-byte untouched, so the hardware rungs' 250/250 stands.
69 lines
3.5 KiB
Rust
69 lines
3.5 KiB
Rust
//! The Vulkan session presenter (punktfunk-planning `linux-client-rearchitecture.md`,
|
|
//! Phase 1): an SDL3 window + ash swapchain that presents the shared session pump's
|
|
//! decoded frames, captures input on the `ui_stream` state-machine contract, and reports
|
|
//! the unified stats window on stdout. No UI toolkit anywhere in the dependency tree.
|
|
//!
|
|
//! Three frame paths: software (`CpuPlanarFrame` — I420 planes staged into three R8
|
|
//! images and converted by the same CICP-driven CSC pass as the hardware lanes; before M8
|
|
//! this lane arrived as swscale RGBA and skipped the pass entirely), Vulkan Video (the
|
|
//! decoder's VkImage on THIS device — plane views + the CICP-driven CSC pass), and on
|
|
//! Linux additionally VAAPI hardware (NV12 dmabuf imported per-plane — `dmabuf.rs`),
|
|
//! all composited by a letterboxed blit. Devices without the import extensions, and any
|
|
//! import/present failure streak, demote the decoder to software via the session pump's
|
|
//! `force_software` contract, same as the GTK presenter.
|
|
//!
|
|
//! Builds on Linux AND Windows; `dmabuf` is Linux-only (DRM-PRIME does not exist on
|
|
//! Windows) and `d3d11` is its Windows counterpart (D3D11VA shared-texture import) —
|
|
//! the decode chain there is Vulkan → D3D11VA → software.
|
|
|
|
// Unsafe-proof program: every `unsafe {}` in this crate carries a `// SAFETY:` proof.
|
|
#![deny(clippy::undocumented_unsafe_blocks)]
|
|
|
|
// THE VULKAN CONTRACT, stated once - most `// SAFETY:` proofs in this crate are an instance of it.
|
|
//
|
|
// Nearly every `unsafe` here is an `ash` call, which is `unsafe` because Vulkan is a C API, not
|
|
// because each call carries its own bespoke obligation. Three shapes recur, and only one of them
|
|
// has a real precondition worth restating per site:
|
|
//
|
|
// * CREATE / ALLOCATE - `create_*`, `allocate_*`. The device is live (this type owns it), and the
|
|
// `vk::*CreateInfo` builders are locals that outlive the synchronous call. The handle returned is
|
|
// owned by the value being constructed and destroyed in its `Drop`.
|
|
// * RECORD - `cmd_*`, `begin/end_command_buffer`, `update_descriptor_sets`. Recorded into a command
|
|
// buffer this code owns and has begun, referencing handles it also owns. Nothing executes until
|
|
// submit, so a recording error is not yet a memory error.
|
|
// * DESTROY - `destroy_*`, `free_*`, `unmap_memory`. THIS is the one with a real obligation: the
|
|
// GPU must not still be using the object. That is established on the path, not by the call - a
|
|
// fence wait, a `queue_wait_idle`, or the swapchain having been retired - and the per-site proofs
|
|
// say so, because getting it wrong is a use-after-free the type system cannot catch.
|
|
//
|
|
// A block doing something OUTSIDE these three shapes gets a real, specific proof; if you add one and
|
|
// find yourself writing "as above", it probably belongs in one of them.
|
|
|
|
#[cfg(any(target_os = "linux", windows))]
|
|
pub mod csc;
|
|
#[cfg(any(target_os = "linux", windows))]
|
|
pub mod cursor;
|
|
#[cfg(windows)]
|
|
pub mod d3d11;
|
|
#[cfg(target_os = "linux")]
|
|
pub mod dmabuf;
|
|
#[cfg(any(target_os = "linux", windows))]
|
|
pub mod input;
|
|
#[cfg(any(target_os = "linux", windows))]
|
|
pub mod keymap_sdl;
|
|
#[cfg(any(target_os = "linux", windows))]
|
|
pub mod overlay;
|
|
#[cfg(any(target_os = "linux", windows))]
|
|
mod present_pace;
|
|
#[cfg(any(target_os = "linux", windows))]
|
|
mod run;
|
|
#[cfg(any(target_os = "linux", windows))]
|
|
pub mod touch;
|
|
#[cfg(any(target_os = "linux", windows))]
|
|
pub mod vk;
|
|
#[cfg(windows)]
|
|
mod win32;
|
|
|
|
#[cfg(any(target_os = "linux", windows))]
|
|
pub use run::{run_browse, run_session, ActionOutcome, Outcome, SessionOpts};
|