fix(host/encode): negotiate the cursor around what the encoder can blend
EncoderCaps::blends_cursor's contract said the HOST must fall back to capturer-side compositing when a cursor-as-metadata session lands on an encoder that can't composite — but that host half was never built: open_video warned and the session streamed WITHOUT a pointer (confirmed on the VAAPI dmabuf and libav-NVENC CUDA paths; latent on vulkan RGB-direct/native-NV12). The negotiation is now caps-aware, ahead of capture, on both planes: * pf-encode grows cursor_blend_capable() — the pre-open dispatch mirror (sibling of linux_native_nv12_ok) answering whether the resolved backend composites frame.cursor; its pure core is test-pinned arm by arm. * Native plane: handshake::cursor_forward grants the cursor channel only where the resolved backend can blend (the capture-mouse flip makes the host draw the pointer on demand); denied sessions keep the pre-channel path — the compositor EMBEDS the pointer, never cursorless, never doubled. The Welcome's HOST_CAP_CURSOR bit is computed once and read back at both session-wiring sites instead of recomputed. SessionPlan::output_format additionally keeps every cursor-blend session off producer-native NV12 (the arm with no CSC to fold a cursor into), and vulkan RGB-direct now yields to a cursor-blend session even when pinned (EFC cannot composite; the open logs the override). Windows plans cursor_blend=false via the new shared cursor_blend_for() rule — the IDD capturer composites the pointer itself, and asking the encoder anyway fired the blends-cursor warn spuriously on every cursor-channel session. * GameStream plane: the hardcoded cursor_blend=true is gone. The portal source asks for cursor-as-metadata only when the resolved backend blends, otherwise negotiates an Embedded pointer (choose_cursor_mode's new ladder); the capturer pool now also keys on that mode. The virtual-output source passes false — its capture embeds the pointer where it can. The per-arm warns in vulkan_video (RGB-direct, native-NV12) are now structurally unreachable and removed. open_video's post-open check stays as the single backstop for what planning cannot see: a Vulkan-open falling back to VAAPI mid-session, and the gamescope residual (no embedded mode exists there, so a never-blending backend — H.264-on-AMD VAAPI, software — still streams cursorless; fixing that needs a compositing stage, deliberately not built in this pass). Zero-copy is preserved throughout — every fallback is a capture-negotiation change, never a readback. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -66,8 +66,14 @@ fn zero_copy_policy(
|
||||
/// Open a live capturer for a client-sized monitor via the xdg ScreenCast portal. `want_hdr`
|
||||
/// offers the GNOME 50+ 10-bit PQ/BT.2020 formats (pass it only when the session negotiated HDR
|
||||
/// AND the mirrored monitor is in HDR mode — see [`pf_capture::gnome_hdr_monitor_active`]).
|
||||
/// `want_metadata_cursor` asks for cursor-as-metadata — pass it only when the session's encode
|
||||
/// backend composites `CapturedFrame::cursor` (`encode::cursor_blend_capable`); otherwise the
|
||||
/// portal embeds the pointer, so no backend × cursor-mode combination streams cursorless.
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn open_portal_monitor(want_hdr: bool) -> Result<Box<dyn Capturer>> {
|
||||
pub fn open_portal_monitor(
|
||||
want_hdr: bool,
|
||||
want_metadata_cursor: bool,
|
||||
) -> Result<Box<dyn Capturer>> {
|
||||
// On RemoteDesktop-capable desktops (KWin/GNOME) anchor ScreenCast to a RemoteDesktop
|
||||
// session so it inherits that grant headlessly; wlroots/Sway has no RemoteDesktop portal,
|
||||
// so use a plain ScreenCast session there.
|
||||
@@ -76,11 +82,19 @@ pub fn open_portal_monitor(want_hdr: bool) -> Result<Box<dyn Capturer>> {
|
||||
// passthrough is virtual-output-only; the global encoder-pref lever still applies inside.
|
||||
// Native NV12 stays off too: the mirror path doesn't resolve the codec here, and the desktop
|
||||
// compositors it mirrors (GNOME/KWin) don't produce NV12 anyway.
|
||||
pf_capture::open_portal_monitor(anchored, want_hdr, zero_copy_policy(false, false))
|
||||
pf_capture::open_portal_monitor(
|
||||
anchored,
|
||||
want_hdr,
|
||||
want_metadata_cursor,
|
||||
zero_copy_policy(false, false),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
pub fn open_portal_monitor(_want_hdr: bool) -> Result<Box<dyn Capturer>> {
|
||||
pub fn open_portal_monitor(
|
||||
_want_hdr: bool,
|
||||
_want_metadata_cursor: bool,
|
||||
) -> Result<Box<dyn Capturer>> {
|
||||
anyhow::bail!("portal capture requires Linux (xdg-desktop-portal + PipeWire)")
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user