https://bugs.kde.org/show_bug.cgi?id=520260

Max Quinn <[email protected]> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |[email protected]

--- Comment #4 from Max Quinn <[email protected]> ---
I was running into this issue and after I failed to find anything myself I had
Claude try to fix it. This report was written by Claude. I am sorry to whoever
is reading through this and I am very sorry if it turns out to be useless but I
do not have quite the level of technical knowledge to be able to fix this
myself. I will try my best to answer any questions or provide any more
information, but honestly I can't promise that I'll be of much help. Best of
luck and thank you to whatever volunteer is going through this.


I think I have the mechanism for this. Short version: the RDPGFX in-flight
frame window is drained only by client frame acknowledgements and has no
timeout, so a client that stops acknowledging wedges the video stream
permanently while input keeps working.

Environment
  krdp 6.7.4, FreeRDP 3.31.0, Fedora 44 (Nobara), kernel 7.2.8
  Plasma 6.7.4 Wayland
  Client: Microsoft "Windows App" for Android, running on ChromeOS
          [app version: 11.0.26071.13915] [ChromeOS version: 154.0.8037.96]
  Host GPUs: NVIDIA RTX 3050 + AMD Granite Ridge iGPU, one monitor on each

Symptom
  Connection succeeds, the first frame renders correctly, then video never
  updates again. Input continues to work for the whole "frozen" period --
  typing and clicks land on the host and are visible on the physical
  monitors. Disconnect/reconnect gives one fresh frame, then freezes again.

This is not GPU- or DMA-BUF-related
  Worth stating because this thread has been looking at Intel/Arc specifics
  and "Failed to create surface from DRM object". I reproduced the freeze
  identically with --virtual-monitor 1920x1080@1, i.e. a single output, a
  small surface, and no multi-GPU involvement. Same freeze, same timing.
  Whatever the DRM/encoder errors other reporters are seeing, the freeze
  itself reproduces without them.

Measurements taken during a live frozen session
  Capture side is healthy:
    - both PipeWire nodes 'running', and the link between them 'active'
    - injecting pointer motion through the portal delivered ~40 frames on
      the capture node
    - an injected rubber-band drag produced real content damage, not just
      cursor movement
  Send side is completely idle:
    - ss -ti on the RDP socket: bytes_sent frozen at 9911521 across 8s and
      12s samples (full-desktop config), and at 2633169 across 5s and
      across the whole drag test (virtual-monitor config)
    - zero bytes. KRdp is not sending anything at all, not sending
      corrupt or undecodable frames.
  So frames are arriving from KWin and nothing is leaving for the client.

  Suppress Output is ruled out: that path goes through setActive(false),
  which would deactivate the PipeWire stream. The stream stayed active
  throughout.

Where it blocks (line numbers from Plasma/6.7)
  VideoStream.cpp:40   constexpr qsizetype MaximumInFlightFrames = 2;
  VideoStream.cpp:795  hasInFlightCapacity() -> pendingFrames.size() < 2
  VideoStream.cpp:356  submission thread's first gate is !hasInFlightCapacity()
  VideoStream.cpp:845  pendingFrames.insert(frameId) on send
  VideoStream.cpp:635  pendingFrames.erase(itr) in onFrameAcknowledge()

  pendingFrames is erased in exactly one place: onFrameAcknowledge(). The
  only other things that touch it are clear() on an encoding-mode switch
  (:396) and on GFX channel re-advertisement (:531). There is no timeout
  anywhere. Once two frames are outstanding and unacknowledged, the
  submission thread spins on a 1ms sleep forever and the stream is dead
  for the life of the connection.

  Input is unaffected because it travels an entirely separate path through
  the portal, which is exactly why this presents as a client/input bug and
  why there are several open reports describing "black screen but input
  works" (see below).

  This client negotiates RDPGFX_CAPS_FLAG_AVC_DISABLED (0x20), so
  onCapsAdvertise() selects EncodingMode::Progressive. In that mode
  queueFrame() keeps only the newest frame and accumulates damage into it,
  so there is no memory growth -- it just silently stops sending.

Still present in master
  master makes the window RTT-adaptive (maxInFlight recomputed in
  updateInFlightWindow() on rttChanged) but MaximumInFlightFrames = 2 is
  now the *floor*, the drain is still acknowledgement-only, and the
  submission loop is byte-for-byte equivalent. Updating will not fix this.

The one existing recovery path
  onCapsAdvertise() clears pendingFrames when a client re-advertises caps
  (treated as a GFX channel reset). mstsc does this; it may be why desktop
  Windows clients mostly recover. This Android client does not appear to,
  so there is no escape.

What I have NOT confirmed
  That the client actually stops sending RDPGFX_FRAME_ACKNOWLEDGE. I
  inferred it: the pipeline demonstrably worked once (first frame
  rendered), capture stayed healthy, zero bytes left the socke
  in-flight window is the only non-recoverable gate on that path. I did
  not capture the wire or run an instrumented build. Confirmin
  cheap -- a qCDebug of frameId in onFrameAcknowledge() plus
  pendingFrames.size() in the submission loop would settle it
  connection. Happy to run that if someone points me at a build.

Suggested fix
  Store a send timestamp alongside each pending frame id. If t
  outstanding frame exceeds some bound (a few seconds, or N * RTT from
  NetworkDetection), drop it from pendingFrames, warn once, an
  fresh full-damage frame. That turns a permanent freeze into a brief
  stutter and costs nothing for well-behaved clients. A strict
  would also recreate the surface / send RDPGFX_RESET_GRAPHICS.

Possibly the same root cause
  Bug 525818 -- freeze on client minimise/lock, input still ac
             Same signature, different trigger.
  Bug 494715 -- black screen, keyboard and mouse still registe
             server device.
  Bug 507949 -- OOM when a client stops receiving data. In H26
             queueFrame() appends unbounded and only clears on a
             keyframe, so a stuck in-flight window would grow
             without limit. Plausibly this defect with a different
             consequence depending on encoding mode.

Unrelated minor thing noticed while reading logs: capVersionTo
no cases for RDPGFX_CAPVERSION_105/106/107, so modern clients log
"Selected caps: UNKNOWN_VERSION", which is misleading when debugging.

-- 
You are receiving this mail because:
You are watching all bug changes.

Reply via email to