fix(egfx): advertise only capability sets the client can decode - #1564
Merged
Benoît Cortier (CBenoit) merged 1 commit intoAug 7, 2026
Conversation
The default `GraphicsPipelineHandler::capabilities()` advertised V10.7, which signals AVC444 support unless `AVC_DISABLED` is set. The client has no AVC444 decoder: `handle_wire_to_surface1` routes `Avc444` and `Avc444v2` to `on_unhandled_pdu`. Since the server picks one of the advertised sets and prefers the most capable, a host that supports AVC444 sent every frame in a codec the client discards, leaving a blank screen with no error and nothing reaching `on_bitmap_updated`. Drop V10.7 from the default. V8.1 keeps AVC420, which does decode, so no H.264 capability is lost, and V8 remains the no-AVC fallback. V10.7 can return once AVC444 decodes. Reproduced against Windows 11 Pro 25H2 (build 26200.8875): server-side `RemoteFX Graphics` counters read 0.00 output frames/second while FreeRDP on the same host and link sustained 9.31 with comparable encoding time. Adds two unit tests asserting the advertised sets imply no AVC444 and still enable AVC420, and updates the workspace testsuite expectation. closes Devolutions#1563
Anton Isaiev (totoshko88)
temporarily deployed
to
llm-providers
August 7, 2026 07:00 — with
GitHub Actions
Inactive
Benoît Cortier (CBenoit)
added a commit
that referenced
this pull request
Aug 7, 2026
A valid classifier result for PR #1564 was discarded because structured output represented an empty string as the literal text `""`. That left the PR at `risk/unknown` and hid the concrete validation failure from maintainers. This change canonicalizes that exact empty-string artifact in the shared text normalizer used by classifier, protocol-analysis, and reviewer outputs. Required prose still fails closed, while optional empty fields normalize consistently. Classification failures now publish a neutral SHA-bound `AI classification` check containing the precise validator or prerequisite reason, without opening the automated review gate. Regression coverage includes the PR #1564 payload, reviewer and protocol text behavior, and neutral diagnostic check publication. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
GraphicsPipelineHandler::capabilities()defaulted to advertisingCapabilitySet::V10_7, whichtells the server AVC444 is available unless
AVC_DISABLEDis set.GraphicsPipelineClienthas noAVC444 decoder —
handle_wire_to_surface1routesCodec1Type::Avc444andAvc444v2toon_unhandled_pdu. The server picks one of the advertised sets and prefers the most capable, so ona host that supports AVC444 the client asked for a codec it then discarded: every frame of the
desktop was lost, the session looked frozen, and nothing reached
on_bitmap_updatedat all.This drops V10.7 from the default. V8.1 keeps
AVC420_ENABLED, which does decode, so no H.264capability is lost, and V8 remains the no-AVC fallback. V10.7 can come back the moment AVC444
decodes — the doc comment and the new test both say so.
Closes #1563. This is item 5 of the #1464 tracking list ("advertise only what we can decode", which
already names AVC444 as the example), addressed for the AVC444 case only.
Why the smaller set is not a downgrade
AVC420_ENABLEDdecode_avc420An alternative that keeps V10.x is to advertise it with
AVC_DISABLEDset, but perCodecCapabilities::from_capability_setthat flag clearsavc420as well, so it gives up H.264entirely. V8.1 is the better default while AVC444 is missing.
Changes
crates/ironrdp-egfx/src/client.rs— removeV10_7from the defaultcapabilities(); documentwhy V10.x is absent and what has to happen before it returns.
against
CodecCapabilities::from_capability_setrather than naming versions, so they keep holdingif the set changes shape.
crates/ironrdp-testsuite-core/tests/egfx/client.rs—client_keeps_avc_caps_with_decodernowexpects two sets instead of three.
Validation
Reproduced and measured against Windows 11 Pro 25H2 (build 26200.8875), no GPU (WARP software
rendering), same LAN,
base tcp rtt5 ms. Server-side counters sampled withGet-Counter "\RemoteFX Graphics(*)\*" -SampleInterval 2 -MaxSamples 12while interacting:sdl-freerdp, same host, minutes apartframes skipped/second — insufficient server resourcesand— insufficient client resourceswereboth 0.00 and guest CPU stayed at 1–3%: the server was encoding and not throttling. FreeRDP's
near-identical encoding time on the same pipeline confirms the server side was healthy, which is
what places the fault in the client's advertisement.
Local checks, all clean:
cargo xtask check fmt -vcargo xtask check lints -vcargo xtask check tests -v— 22 suites, 0 failedcargo clippy -p ironrdp-egfx --all-targets --all-features— 0 warningscargo doc -p ironrdp-egfx --no-deps— the two remaining warnings (THIRD_PARTY_NOTICES,compositor→ privateCompositor) predate this branchcargo xtask check typos -vwas skipped:typos-cliis not installed in this environment.Deliberately not in this PR
H264Decoder/DecodedFramedo not expose —the auxiliary stream is a packed chroma carrier, not an image, so it cannot be reconstructed from
two RGBA buffers — plus a second decoder context, since each subframe is its own H.264 sequence.
That is a public-API decision for the maintainers; I sketched a non-breaking shape
(
fn decode_avc444(..) -> DecoderResult<DecodedFrame>defaulting to an error, implemented forOpenH264Decoder) in egfx client advertises AVC444 it cannot decode, so frames are dropped #1563 and would rather agree on it before writing the reconstruction.I can validate a branch against the 25H2 host above; the counter harness is scripted.
debug!toa first-occurrence
warn!and adding a typed callback for undecodable content. Both are usefuland both are separate from this fix, so I left them out rather than mix concerns.
h264_decoder: Nonethe filter leaves V8 only and a Windows hostanswers with RFX Progressive, which
on_wire_to_surface2drops by default — the same blank screenby another route until feat(egfx): wire RemoteFX Progressive decode into WireToSurface2 dispatch #1443 lands. Out of scope here, noted for [tracking] Client-side Graphics Pipeline (EGFX): wire the existing decode core into the connector and client stack #1464 item 4.