fix(client-linux,host): Deck video defaults to software decode + input-interception diagnostics
apple / swift (push) Successful in 1m8s
apple / screenshots (push) Successful in 5m38s
windows-host / package (push) Successful in 7m12s
android / android (push) Successful in 3m36s
ci / rust (push) Successful in 1m31s
ci / web (push) Successful in 49s
ci / docs-site (push) Successful in 57s
ci / bench (push) Successful in 4m56s
decky / build-publish (push) Successful in 14s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 5s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 4s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 3s
deb / build-publish (push) Successful in 4m38s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 8s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 5s
flatpak / build-publish (push) Successful in 4m55s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 9m57s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 9m21s
docker / deploy-docs (push) Successful in 17s
apple / swift (push) Successful in 1m8s
apple / screenshots (push) Successful in 5m38s
windows-host / package (push) Successful in 7m12s
android / android (push) Successful in 3m36s
ci / rust (push) Successful in 1m31s
ci / web (push) Successful in 49s
ci / docs-site (push) Successful in 57s
ci / bench (push) Successful in 4m56s
decky / build-publish (push) Successful in 14s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 5s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 4s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 3s
deb / build-publish (push) Successful in 4m38s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 8s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 5s
flatpak / build-publish (push) Successful in 4m55s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 9m57s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 9m21s
docker / deploy-docs (push) Successful in 17s
Video (Deck): the VAAPI zero-copy path renders corrupt/gray/washed-out on the Deck — root-caused to Mesa >= 25.1 exporting radeonsi VCN decode surfaces TILED (the Flatpak runtime's Mesa 26 drives both the decoder and GTK's GL, and GTK's tiled-NV12 dmabuf import mishandles it; desktop Tier-1 validations ran distro Mesa with linear export). `auto` now resolves to software on a Deck (clean, correct-colour, easily handles 1280x800 HEVC); PUNKTFUNK_DECODER=vaapi still forces the hw path, with the descriptor modifier dump + GSK_RENDERER as the bisect levers. Also reserve extra_hw_frames=4 on the VAAPI decoder: the presenter pins mapped surfaces past receive_frame, and the fixed pool recycling a surface the renderer still samples is intermittent block corruption anywhere. Input (Deck): with Steam Input ON for Punktfunk, SDL sees only Steam's virtual X360 pad — the right trackpad arrives as a plain right stick and the left trackpad/paddles/gyro not at all, silently. The client now checks once the post-attach enumeration settles and raises a toast + warn naming the fix (disable Steam Input for the shortcut). The host logs a one-shot warning when InputPlumber is running (Bazzite default) since it can grab the virtual Deck pad and re-emit it under a different identity. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -280,6 +280,35 @@ impl SessionUi {
|
|||||||
if self.app.fullscreen || self.app.settings.borrow().fullscreen_on_stream {
|
if self.app.fullscreen || self.app.settings.borrow().fullscreen_on_stream {
|
||||||
self.app.window.fullscreen();
|
self.app.window.fullscreen();
|
||||||
}
|
}
|
||||||
|
// Steam Input owning the Deck's built-in controller is invisible degradation: SDL
|
||||||
|
// then sees only Steam's virtual X360 pad, so the right trackpad arrives at the
|
||||||
|
// host as a plain right stick (Steam's default template) and the left trackpad,
|
||||||
|
// paddles and gyro not at all. The real 28DE:1205 identity enumerates shortly
|
||||||
|
// after attach (the Valve HIDAPI drivers are session-scoped) — check once that
|
||||||
|
// settles and say so, instead of streaming silently degraded.
|
||||||
|
if crate::gamepad::is_steam_deck() {
|
||||||
|
let app = self.app.clone();
|
||||||
|
let stop = self.stop.clone();
|
||||||
|
glib::timeout_add_seconds_local_once(4, move || {
|
||||||
|
if stop.load(std::sync::atomic::Ordering::Relaxed) {
|
||||||
|
return; // session already over
|
||||||
|
}
|
||||||
|
if app.gamepad.active().is_none_or(|pad| pad.steam_virtual) {
|
||||||
|
tracing::warn!(
|
||||||
|
"Steam Input is holding the built-in controller — trackpads, \
|
||||||
|
paddles and gyro won't reach the host (right pad degrades to a \
|
||||||
|
right stick). Disable Steam Input for Punktfunk to fix this."
|
||||||
|
);
|
||||||
|
let toast = adw::Toast::new(
|
||||||
|
"Steam Input is holding the Deck's controls — trackpads, paddles \
|
||||||
|
and gyro won't reach the game. Fix: Punktfunk → controller \
|
||||||
|
settings → Disable Steam Input.",
|
||||||
|
);
|
||||||
|
toast.set_timeout(12);
|
||||||
|
app.toasts.add_toast(toast);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
self.page = Some(p);
|
self.page = Some(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -187,6 +187,25 @@ impl Decoder {
|
|||||||
.ok()
|
.ok()
|
||||||
.filter(|v| !v.is_empty())
|
.filter(|v| !v.is_empty())
|
||||||
.unwrap_or_else(|| pref.to_string());
|
.unwrap_or_else(|| pref.to_string());
|
||||||
|
// The Steam Deck's VAAPI zero-copy path renders corrupt/gray/washed-out — validated live;
|
||||||
|
// software decode is clean, correct-colour, and the Deck's APU handles 1280×800 HEVC
|
||||||
|
// easily. Likely cause: since Mesa 25.1 radeonsi exports VCN decode surfaces TILED (with
|
||||||
|
// AMD modifiers) instead of linear, and inside the Flatpak both the VAAPI driver and GTK's
|
||||||
|
// GL come from the runtime's Mesa 26.x — GTK's tiled-NV12 dmabuf import mishandles the new
|
||||||
|
// layout (desktop AMD/Intel boxes validated Tier-1 ran distro Mesa with linear export).
|
||||||
|
// So `auto` resolves to software on a Deck; an explicit `vaapi` (Settings or
|
||||||
|
// PUNKTFUNK_DECODER=vaapi) still forces the hw path for testing — the first-frame
|
||||||
|
// descriptor dump logs the modifier (LINEAR = 0x0), and GSK_RENDERER=ngl|vulkan bisects
|
||||||
|
// the import side.
|
||||||
|
let choice = if (choice == "auto" || choice.is_empty()) && crate::gamepad::is_steam_deck() {
|
||||||
|
tracing::info!(
|
||||||
|
"Steam Deck — defaulting to software decode (AMD VAAPI dmabuf is broken on this \
|
||||||
|
SteamOS+Mesa combo); set the decoder to `vaapi` to override"
|
||||||
|
);
|
||||||
|
"software".to_string()
|
||||||
|
} else {
|
||||||
|
choice
|
||||||
|
};
|
||||||
if choice != "software" {
|
if choice != "software" {
|
||||||
match VaapiDecoder::new(codec_id) {
|
match VaapiDecoder::new(codec_id) {
|
||||||
Ok(v) => {
|
Ok(v) => {
|
||||||
@@ -456,6 +475,14 @@ impl VaapiDecoder {
|
|||||||
(*ctx).get_format = Some(pick_vaapi);
|
(*ctx).get_format = Some(pick_vaapi);
|
||||||
(*ctx).flags |= ffi::AV_CODEC_FLAG_LOW_DELAY as i32;
|
(*ctx).flags |= ffi::AV_CODEC_FLAG_LOW_DELAY as i32;
|
||||||
(*ctx).thread_count = 1; // hwaccel: threads only add latency
|
(*ctx).thread_count = 1; // hwaccel: threads only add latency
|
||||||
|
|
||||||
|
// The presenter holds mapped surfaces PAST receive_frame (the paintable's
|
||||||
|
// current texture + the newest frame in flight each pin one until GDK's
|
||||||
|
// release func) — surfaces libavcodec doesn't know are missing from its
|
||||||
|
// fixed-size VAAPI pool. Without headroom the decoder can recycle a surface
|
||||||
|
// the renderer is still sampling (intermittent block corruption) or fail
|
||||||
|
// allocation under scheduling jitter.
|
||||||
|
(*ctx).extra_hw_frames = 4;
|
||||||
let r = ffi::avcodec_open2(ctx, codec, ptr::null_mut());
|
let r = ffi::avcodec_open2(ctx, codec, ptr::null_mut());
|
||||||
if r < 0 {
|
if r < 0 {
|
||||||
let mut ctx = ctx;
|
let mut ctx = ctx;
|
||||||
|
|||||||
@@ -276,12 +276,43 @@ impl DeckTransport {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// One-shot diagnostic: InputPlumber (shipped and enabled by default on Bazzite) hidraw-grabs
|
||||||
|
/// controllers it decides to manage and re-emits them under a different identity — historically
|
||||||
|
/// the Deck config re-emitted an Xbox Elite pad with the trackpads routed to a mouse target. If
|
||||||
|
/// it grabs our virtual Deck, everything downstream of hid-steam looks wrong (trackpads surface
|
||||||
|
/// as a stick/mouse, gyro vanishes) while punktfunk's own logs stay clean — so name the suspect
|
||||||
|
/// up front. Best-effort process-name scan; no dependency on its D-Bus API.
|
||||||
|
fn warn_if_inputplumber() {
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
static ONCE: AtomicBool = AtomicBool::new(true);
|
||||||
|
if !ONCE.swap(false, Ordering::Relaxed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let running = std::fs::read_dir("/proc")
|
||||||
|
.ok()
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.flatten()
|
||||||
|
.any(|e| {
|
||||||
|
std::fs::read_to_string(e.path().join("comm")).is_ok_and(|c| c.trim() == "inputplumber")
|
||||||
|
});
|
||||||
|
if running {
|
||||||
|
tracing::warn!(
|
||||||
|
"InputPlumber is running on this host — if it manages the virtual Steam Deck pad, \
|
||||||
|
games see InputPlumber's re-emitted device instead (trackpads may arrive as a \
|
||||||
|
stick/mouse, gyro may vanish). Check `inputplumber devices` and exclude the \
|
||||||
|
virtual pad from management if inputs look remapped."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Open the best Steam-Input-promotable Deck transport available, in preference order:
|
/// Open the best Steam-Input-promotable Deck transport available, in preference order:
|
||||||
/// **`raw_gadget` (SteamOS validated fast-path) → `usbip`/`vhci_hcd` (universal, Secure-Boot-clean)
|
/// **`raw_gadget` (SteamOS validated fast-path) → `usbip`/`vhci_hcd` (universal, Secure-Boot-clean)
|
||||||
/// → UHID (universal, but `Interface: -1` so Steam Input won't promote it).** Each rung degrades to
|
/// → UHID (universal, but `Interface: -1` so Steam Input won't promote it).** Each rung degrades to
|
||||||
/// the next on failure, so a host lacking the gadget modules still gets a *promotable* Deck via
|
/// the next on failure, so a host lacking the gadget modules still gets a *promotable* Deck via
|
||||||
/// usbip, and one lacking both still gets a working (if non-promoted) UHID pad.
|
/// usbip, and one lacking both still gets a working (if non-promoted) UHID pad.
|
||||||
fn open_transport(idx: u8) -> Result<DeckTransport> {
|
fn open_transport(idx: u8) -> Result<DeckTransport> {
|
||||||
|
warn_if_inputplumber();
|
||||||
use crate::inject::{steam_gadget, steam_usbip};
|
use crate::inject::{steam_gadget, steam_usbip};
|
||||||
// 1. raw_gadget — the validated SteamOS fast-path (default on there).
|
// 1. raw_gadget — the validated SteamOS fast-path (default on there).
|
||||||
if steam_gadget::gadget_preferred() {
|
if steam_gadget::gadget_preferred() {
|
||||||
|
|||||||
Reference in New Issue
Block a user