Commit Graph

8 Commits

Author SHA1 Message Date
enricobuehler 349d16382e fix(stats): decode stage measures GPU completion; Alt+Enter fullscreen alias
The Vulkan path's receive_frame returns at SUBMISSION (~0.1 ms) — the
hardware decodes asynchronously, so the decode stat was truthful but
measured the wrong boundary. The pump now ships the frame to the
presenter FIRST, then waits the frame's timeline fence (vkWaitSemaphores
resolved through the shared device's proc chain) and stamps
received→decode-COMPLETE — true NVDEC time at zero pipeline cost, since
the presenter's own GPU wait is what actually gates sampling. Software/
VAAPI keep their synchronous stamps.

Also: Alt+Enter joins F11 as the fullscreen toggle (some keyboards' Fn
layer sends a media key for plain F11 — observed on glass as
'F11 only works with shift'); the shell's shortcuts panel lists both.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 23:14:08 +02:00
enricobuehler a5bb5ec4d5 fix(input): pointer lock + the missing per-iteration motion flush
Two capture bugs compounding into 'sluggish and not captured':

1. flush_motion was never called from the run loop — the GTK client's
   frame-clock tick flushed pending motion every frame, and the port
   lost it. Pure mouse movement only reached the host when a click/key/
   scroll happened to flush it; the host cursor simply didn't follow.
   Now one coalesced MouseMove per loop iteration.

2. Capture forwarded ABSOLUTE letterboxed coordinates with no pointer
   confinement (GTK parity, the plan's deferred 'stage-2' item): the
   visible local cursor outran the host cursor by the full e2e and
   escaped the window. Capture is now real pointer lock — SDL relative
   mouse mode (hidden, confined, raw deltas) with relative MouseMove on
   the wire, so the host cursor is the only cursor.

Also: focus-gain re-engages after an auto-release (Alt-Tab undoes
itself; the startup focus race can no longer strand the session
uncaptured) while a chord release stays released until the user opts
back in.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 23:14:08 +02:00
enricobuehler c78ddc40cb feat(video): Vulkan Video decode on the presenter's device (NVIDIA hw decode)
FFmpeg's Vulkan Video decoder now runs on the PRESENTER's own VkDevice —
the decoded VkImage feeds the existing CICP CSC pass directly: zero
copy, no interop, and NVIDIA gets hardware decode for the first time
(its VAAPI is unusable by design). One decode architecture for every
vendor going forward; VAAPI-dmabuf and software remain the fallbacks
(auto: vulkan → vaapi → software; PUNKTFUNK_DECODER=vulkan pins it).

Presenter: instance 1.3; probes VK_KHR_video_queue/decode_queue + codec
extensions, a VIDEO_DECODE queue family (+ its codec caps via
QueueFamilyVideoPropertiesKHR), and the samplerYcbcrConversion/
timelineSemaphore/synchronization2 features — all enabled at device
creation when present, exported as a VulkanDecodeDevice handle bundle.

Decoder: AVVulkanDeviceContext built over those handles via pf-ffvk
(features chain, extension lists, deprecated queue indices + the qf[]
map); get_format supplies OUR frames context with MUTABLE_FORMAT so the
presenter's per-plane views are legal; output is DecodedImage::VkFrame
carrying AVVkFrame/frames-ctx pointers plus the lock fns.

Present: R8+R8G8 plane views over the multiplanar image, the live sync
state read under the AVVulkanFramesContext lock, a timeline-semaphore
wait(sem_value)/signal(sem_value+1) folded into the submit, layout/
queue-family/sem_value written back per FFmpeg's contract, and the frame
guard parked in the retire queue until the fence. CSC pass + video
framebuffer are now unconditional (NVIDIA has no dmabuf-import path).

Verified on the RTX 5070 Ti: device creates with decode_qf=3,
caps=DECODE_H264|H265|AV1|VP9; swapchain unaffected. Live stream
validation next.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 23:14:08 +02:00
enricobuehler be09f9f345 feat(session): console game library — Skia coverflow + SkSL aurora, --browse (phase 4b)
The run loop grows a browse mode: the console library idles between
streams in ONE window (no gamescope handoff), overlay actions launch
sessions via run_browse's callback, session end returns to the library.
The Overlay contract gains menu routing (MenuEvent → haptic pulse),
action draining, and session-phase edges.

pf-console-ui ports the GTK launcher wholesale: the coverflow's springs,
cursor arithmetic and recede/tilt constants move verbatim with their
tests (plus new projection tests — focused-card centering, the inner-
edge-recedes corridor); paint order is draw order (the gtk::Fixed
restack hack is gone); the aurora renders as an SkSL runtime shader at
full rate on every box (the 30 Hz CPU-upscale path and its frozen-on-
Deck fallback are deleted — the generated SkSL is compile-tested);
titles/scenes shape through textlayout (CJK-safe). Poster art streams
in as encoded bytes through the shared model and decodes renderer-side.

The session binary wires --browse host[:port] [--mgmt PORT]: KnownHosts
lookup (unpaired renders the pair-first scene), library + art fetch on
threads, PUNKTFUNK_FAKE_LIBRARY dev hook, and a session_params helper
shared with --connect. The minimal build refuses --browse cleanly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 23:13:16 +02:00
enricobuehler 021a2261f6 feat(session): Skia console UI — overlay contract + stats OSD/capture HUD (phase 4a)
The §6.1 presenter↔console-UI contract lands: pf-presenter exposes its
device (SharedDevice) and composites at most one premultiplied-alpha
quad per frame (new overlay.frag + LOAD render pass over the swapchain;
zero cost while the overlay returns None). pf-console-ui implements it
with skia-safe on the shared VkDevice: DirectContext via the ash
dispatch chain, a ring of two offscreen render targets (one-frame-in-
flight safe), damage-driven redraws — the OSD re-renders at 1 Hz, the
hint on capture toggles, nothing per-frame. Skia never touches the
swapchain. The session binary carries it behind the default ui feature:
4.9 MB stripped without, 10 MB with (measured).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 23:13:16 +02:00
enricobuehler 433a23da7a test(presenter): PUNKTFUNK_HW_FAULT=import demotion hook (phase 2 acceptance)
Faults every dmabuf import so the failure-streak → force_software →
software-decode recovery is exercisable on healthy hardware — the plan's
§8 phase-2 acceptance requires demoting via a deliberately faulted
import rather than waiting for a broken driver.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 23:13:16 +02:00
enricobuehler a02d0a2e9f feat(presenter): VAAPI dmabuf → Vulkan zero-copy import + CSC pass (phase 2)
The decoder's NV12 dmabuf imports per-plane (R8 + GR88, explicit DRM
format modifier, dedicated dup'd-fd import, FOREIGN→graphics acquire)
and a fullscreen-triangle render pass converts it into the presenter's
video image with the CICP-driven coefficients ported from video_gl.rs
(same tests, plus a rows-vs-matrix agreement check). SPIR-V is committed
(shaders/build.sh regenerates) so builds and CI need no toolchain. The
import extension set is probed at device creation; unsupported boxes and
3-failure streaks demote the decoder to software via the existing
force_software contract. The session binary now honors the Settings
decoder preference instead of forcing software.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 23:13:16 +02:00
enricobuehler 89d45f2a55 feat(client): Vulkan session binary — SDL3 + ash presenter MVP (phase 1)
punktfunk-session streams one --connect session in an SDL3 window: ash
swapchain with a transfer-only letterboxed blit of the software-decode
path (no graphics pipeline until the phase-2 dmabuf/CSC pass), the
ui_stream input-capture state machine on SDL events (scancode→VK table
cross-checked against the evdev one), gamepads via a new caller-pumped
GamepadService mode (SDL video owns the main thread here), and the
shell↔session stdout contract: {"ready":true}, per-window stats:
lines, JSON error + exit codes 0/2/3/4. Strict trust — no pin, no
connect. Design: punktfunk-planning linux-client-rearchitecture.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 23:13:16 +02:00