Merge origin/main into worktree-native-decode-m0
ci / web (pull_request) Successful in 1m19s
apple / swift (pull_request) Successful in 1m32s
ci / docs-site (pull_request) Successful in 1m23s
apple / screenshots (pull_request) Skipped
ci / bun-nix (pull_request) Successful in 25s
windows / build (aarch64-pc-windows-msvc) (pull_request) Successful in 1m8s
android / android (pull_request) Successful in 3m31s
windows / build (x86_64-pc-windows-msvc) (pull_request) Successful in 3m23s
ci / rust-arm64 (pull_request) Successful in 5m36s
nix / flake (pull_request) Failing after 11m59s
ci / rust (pull_request) Successful in 14m27s
ci / web (pull_request) Successful in 1m19s
apple / swift (pull_request) Successful in 1m32s
ci / docs-site (pull_request) Successful in 1m23s
apple / screenshots (pull_request) Skipped
ci / bun-nix (pull_request) Successful in 25s
windows / build (aarch64-pc-windows-msvc) (pull_request) Successful in 1m8s
android / android (pull_request) Successful in 3m31s
windows / build (x86_64-pc-windows-msvc) (pull_request) Successful in 3m23s
ci / rust-arm64 (pull_request) Successful in 5m36s
nix / flake (pull_request) Failing after 11m59s
ci / rust (pull_request) Successful in 14m27s
main moved 93 commits while this branch ran. Two conflicts, both where main's new
work sat next to M10's excision:
packaging/flatpak/io.unom.Punktfunk.yml — main added the vendored gamescope WSI
layer (the only route to HDR on a Deck) and, before it, a vulkan-headers module.
Took both: this branch predates them and deletes neither. But the headers module's
stated consumer was pf-ffvk's bindgen over FFmpeg's hwcontext_vulkan.h, and M10
deleted pf-ffvk — so it now reads as dead weight to the next person. It is not:
the WSI layer IS a Vulkan layer, compiles against those headers, and builds after
it, so module order is the dependency. Rewrote the rationale to say so, including
why dropping it would be expensive to discover — flatpak.yml has no pull_request:
trigger, so a manifest break reaches main invisibly and a tag then ships no Linux
flatpak. Also recorded that the native decoder needs nothing from there: pf-vkdecode
reaches Vulkan through ash, which is pure Rust bindings, no bindgen, no C headers.
crates/pf-console-ui/src/screens/settings.rs — main restructured the gamepad
settings into TABS, which removed the per-row section headers; this branch had left
Some("Video") untouched from the merge base and added the pre-M10 decoder migration
next to it. Git could not tell those apart. Took main's structure (no header, its
deliberate change) with this branch's migration layered on: a stored `vulkan`,
`vaapi` or `d3d11va` names no preset in the tabbed list and would render as "—",
then silently rewrite the user's preference on the next save.
Gates on the merged tree, Linux container: fmt clean; cargo check --workspace
--all-targets clean; clippy --workspace --all-targets -D warnings clean; tests
green across pf-vkdecode (187), pf-client-core (163), pf-console-ui (58) and
punktfunk-host (447 of 448 — the one failure is the pre-existing
gamestream::stream::tests::sender_delivers_batches, a UDP-loopback EINTR under
qemu that fails identically on a pristine HEAD).
This commit is contained in:
@@ -76,7 +76,14 @@
|
||||
// capability-gated end to end: the wire grows a new datagram tag (0xD1) an old client never
|
||||
// receives (double-gated caps), a new 0xCD kind (0x06, dropped as unknown by old clients) and
|
||||
// arrival flag bits 8/9 sent only toward a capable host, so [`WIRE_VERSION`] is unchanged.
|
||||
#define PUNKTFUNK_ABI_VERSION 16
|
||||
// v17: added `punktfunk_connection_end_reason` + the `PUNKTFUNK_END_REASON_*` vocabulary — asks,
|
||||
// once a session has ended, WHY: this client closed it, the host's launched game exited (its close
|
||||
// carried [`quic::APP_EXITED_CLOSE_CODE`], which the host has sent since long before this bump
|
||||
// with nothing consuming it), the host ended it cleanly, the host reported a failure, or the
|
||||
// connection was simply lost. Purely a read of state the core already had: no new call is required
|
||||
// of an embedder, a client that never calls it is unchanged, and the host sends exactly the same
|
||||
// bytes either way, so [`WIRE_VERSION`] is unchanged.
|
||||
#define PUNKTFUNK_ABI_VERSION 17
|
||||
|
||||
// The punktfunk/1 **wire** version — what `Hello`/`Welcome` carry and hosts equality-check.
|
||||
// Deliberately its own constant: [`ABI_VERSION`] tracks the embeddable **C surface**
|
||||
@@ -1617,6 +1624,63 @@ typedef uint8_t PunktfunkInputKind;
|
||||
#endif // __STDC_VERSION__ >= 202311L
|
||||
#endif // __cplusplus
|
||||
|
||||
#if defined(PUNKTFUNK_FEATURE_QUIC)
|
||||
// Why a session ended — [`NativeClient::end_reason`], and `punktfunk_connection_end_reason` on the
|
||||
// C surface.
|
||||
//
|
||||
// The distinction that matters to a UI is **normal vs alarming**, and it is not a spectrum: a
|
||||
// player quitting their game and a host falling off the network both arrive as "the session
|
||||
// ended", and a client with no way to separate them has to word all of them the same. Every client
|
||||
// worded them as failures.
|
||||
//
|
||||
// Ordered loosely from "the user did this on purpose" to "something went wrong". Values are part
|
||||
// of the C ABI: append only, never renumber.
|
||||
enum PunktfunkEndReason
|
||||
#if defined(__cplusplus) || __STDC_VERSION__ >= 202311L
|
||||
: uint8_t
|
||||
#endif // defined(__cplusplus) || __STDC_VERSION__ >= 202311L
|
||||
{
|
||||
#if defined(PUNKTFUNK_FEATURE_QUIC)
|
||||
// Not ended (or ended before a reason could be observed). Also what an unknown future value
|
||||
// decodes to, so an older client reading a newer core degrades to "no opinion".
|
||||
PUNKTFUNK_END_REASON_NONE = 0,
|
||||
#endif
|
||||
#if defined(PUNKTFUNK_FEATURE_QUIC)
|
||||
// **This client** closed the session — the user pressed stop, or the handle was dropped.
|
||||
// Nothing to report: the UI already knows, it initiated it.
|
||||
PUNKTFUNK_END_REASON_LOCAL = 1,
|
||||
#endif
|
||||
#if defined(PUNKTFUNK_FEATURE_QUIC)
|
||||
// The host's launched game exited ([`crate::quic::APP_EXITED_CLOSE_CODE`]). A normal finish,
|
||||
// and the one reason a launcher client can act on: go back to the library the title was
|
||||
// launched from rather than all the way out to host selection.
|
||||
PUNKTFUNK_END_REASON_GAME_EXITED = 2,
|
||||
#endif
|
||||
#if defined(PUNKTFUNK_FEATURE_QUIC)
|
||||
// The host ended the session cleanly and deliberately — an operator "End" in the console, or
|
||||
// the session simply finishing. Normal; say so plainly or say nothing.
|
||||
PUNKTFUNK_END_REASON_HOST_ENDED = 3,
|
||||
#endif
|
||||
#if defined(PUNKTFUNK_FEATURE_QUIC)
|
||||
// The host closed reporting a failure of its own. Worth showing, and the host's log has the
|
||||
// detail.
|
||||
PUNKTFUNK_END_REASON_HOST_ERROR = 4,
|
||||
#endif
|
||||
#if defined(PUNKTFUNK_FEATURE_QUIC)
|
||||
// The connection died rather than being closed: idle timeout, reset, the network going away.
|
||||
// This — and only this — is the "the host may be asleep, wake it" case.
|
||||
PUNKTFUNK_END_REASON_LOST = 5,
|
||||
#endif
|
||||
};
|
||||
#ifndef __cplusplus
|
||||
#if __STDC_VERSION__ >= 202311L
|
||||
typedef enum PunktfunkEndReason PunktfunkEndReason;
|
||||
#else
|
||||
typedef uint8_t PunktfunkEndReason;
|
||||
#endif // __STDC_VERSION__ >= 202311L
|
||||
#endif // __cplusplus
|
||||
#endif
|
||||
|
||||
#if defined(PUNKTFUNK_FEATURE_QUIC)
|
||||
// Per-session colour signalling (CICP / ITU-T H.273 code points) the host resolved for the
|
||||
// encoded video, carried on [`Welcome`]. A client configures its decoder/presenter from these
|
||||
@@ -2517,6 +2581,28 @@ PunktfunkStatus punktfunk_connection_next_audio(PunktfunkConnection *c,
|
||||
PunktfunkStatus punktfunk_connection_audio_channels(PunktfunkConnection *c, uint8_t *out);
|
||||
#endif
|
||||
|
||||
#if defined(PUNKTFUNK_FEATURE_QUIC)
|
||||
// WHY this session ended: `*out` receives a [`PunktfunkEndReason`] byte
|
||||
// (`PUNKTFUNK_END_REASON_*`). The return status reports only whether the handle was usable.
|
||||
//
|
||||
// Read it once a plane has returned [`PunktfunkStatus::Closed`] (or the embedder's own
|
||||
// end-of-session signal fired); before that it reads `NONE`. It latches, so it is still readable
|
||||
// while the connection is torn down, and a client that never calls it behaves exactly as it did
|
||||
// before this existed.
|
||||
//
|
||||
// **Most endings are not failures.** Before this, a client had no way to tell a player quitting
|
||||
// their game from a host falling off the network, so every client wrote one message for all of
|
||||
// them and every client chose an error. Use `LOCAL`/`GAME_EXITED`/`HOST_ENDED` to stay quiet (and
|
||||
// `GAME_EXITED` to return to the library the title was launched from), and keep the alarming copy
|
||||
// for `HOST_ERROR` and `LOST`.
|
||||
//
|
||||
// Treat an unrecognized value as `NONE` — this crosses an ABI and the core may be newer than you.
|
||||
//
|
||||
// # Safety
|
||||
// `c` is a valid connection handle; `out` is NULL or writable for one `u8`.
|
||||
PunktfunkStatus punktfunk_connection_end_reason(PunktfunkConnection *c, uint8_t *out);
|
||||
#endif
|
||||
|
||||
#if defined(PUNKTFUNK_FEATURE_QUIC)
|
||||
// Pull the next audio frame and **decode it in-core** to interleaved f32 PCM — for embedders
|
||||
// without a multistream-capable Opus decoder (e.g. Apple, whose AudioToolbox Opus path is
|
||||
@@ -2527,6 +2613,13 @@ PunktfunkStatus punktfunk_connection_audio_channels(PunktfunkConnection *c, uint
|
||||
// [`punktfunk_connection_next_audio`] on a given connection, from one dedicated audio thread —
|
||||
// not both (they share the underlying queue).
|
||||
//
|
||||
// **Loss concealment**: packets the wire lost (a gap in the sequence, after the redundant-plane
|
||||
// recovery has had its chance) are synthesized via libopus packet-loss concealment and returned
|
||||
// IN FRONT of the arriving frame in the same buffer — `out->frame_count` then covers the
|
||||
// concealed frames plus the real one (`out->seq`/`out->pts_ns` are the real packet's). The
|
||||
// embedder just writes the whole buffer to its ring, same as any other frame; gaps arrive
|
||||
// pre-healed, exactly as they do on the clients that decode outside core.
|
||||
//
|
||||
// # Safety
|
||||
// `c` is a valid connection handle; `out` is writable. At most one thread pulls audio.
|
||||
PunktfunkStatus punktfunk_connection_next_audio_pcm(PunktfunkConnection *c,
|
||||
|
||||
Reference in New Issue
Block a user