-
Notifications
You must be signed in to change notification settings - Fork 301
fix(egfx): stop the desktop from tearing when the server resizes graphics output #1977
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
1a808de
af70359
398ed3b
51e9562
50303b7
e283f3e
77d05a6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1218,6 +1218,17 @@ impl core::fmt::Display for ProgressiveDecodeError { | |
| } | ||
| } | ||
|
|
||
| impl core::error::Error for ProgressiveDecodeError { | ||
| fn source(&self) -> Option<&(dyn core::error::Error + 'static)> { | ||
| match self { | ||
| Self::Pdu(e) => Some(e), | ||
| Self::Rlgr(e) => Some(e), | ||
| Self::Srl(e) => Some(e), | ||
| _ => None, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl From<ironrdp_core::DecodeError> for ProgressiveDecodeError { | ||
| fn from(e: ironrdp_core::DecodeError) -> Self { | ||
| Self::Pdu(e) | ||
|
|
@@ -1547,6 +1558,21 @@ impl ProgressiveDecoder { | |
| self.surface_context_flags.remove(&surface_id); | ||
| } | ||
|
|
||
| /// Total retained difference-tile coefficient buffers across all surfaces. | ||
| #[must_use] | ||
| pub fn total_reference_count(&self) -> usize { | ||
| self.references.len() | ||
| } | ||
|
Comment on lines
+1561
to
+1565
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [skeptical + code-compressor] Public total_reference_count added to a released crate solely for one cross-crate test — low 🟡 — total_reference_count is a #[must_use] pub method on the production ProgressiveDecoder whose only callers are the two assertions in ironrdp-egfx's reset_graphics_drops_tile_references_of_implicitly_destroyed_surfaces test. The decoder-level guarantee can be asserted inside ironrdp-graphics's own tests (which see the private references field), and the egfx test can assert the observable outcome instead — a following difference tile failing with MissingTileReference, the failure class its doc comment already cites. As merged it adds permanent public API surface for test convenience. |
||
|
|
||
| /// Drop every surface's difference-tile coefficient buffers. | ||
| /// | ||
| /// `ResetGraphics` implicitly destroys all surfaces (MS-RDPEGFX 2.2.2.14) | ||
| /// but does not re-negotiate progressive CONTEXT. Tile references belong | ||
| /// to the destroyed surfaces; CONTEXT does not. | ||
| pub fn clear_tile_references(&mut self) { | ||
| self.references.clear(); | ||
| } | ||
|
|
||
| /// Reset codec-context state while retaining surface sub-band references. | ||
| pub fn reset(&mut self) { | ||
| self.contexts.clear(); | ||
|
|
@@ -2075,14 +2101,17 @@ mod tests { | |
| } | ||
|
|
||
| #[test] | ||
| fn upgrade_pass_rejects_truncated_srl() { | ||
| fn upgrade_pass_completes_when_the_srl_stream_ends_early() { | ||
| let mut coefficients = [0i16; COEFFICIENTS_PER_COMPONENT]; | ||
| let mut sign = [SIGN_POSITIVE; COEFFICIENTS_PER_COMPONENT]; | ||
| sign[0] = SIGN_ZERO; | ||
|
|
||
| let mut prev_prog_quant = ComponentCodecQuant::LOSSLESS; | ||
| prev_prog_quant.hl1 = 4; | ||
|
|
||
| // Windows stops emitting once the remaining coefficients are all zero, so the SRL | ||
| // stream is shorter than the band's coefficient count. This used to drop the whole | ||
| // tile update, which on screen is a block that never refreshes. | ||
| assert_eq!( | ||
| decode_upgrade_pass( | ||
| &[0x80, 0x00], | ||
|
|
@@ -2093,7 +2122,7 @@ mod tests { | |
| &mut coefficients, | ||
| &mut sign, | ||
| ), | ||
| Err(SrlError::Truncated) | ||
| Ok(()) | ||
| ); | ||
| } | ||
|
|
||
|
|
@@ -2102,28 +2131,40 @@ mod tests { | |
| let mut tile = TileState::new(); | ||
| let mut prev_prog_quant = ComponentCodecQuant::LOSSLESS; | ||
| prev_prog_quant.hl1 = 4; | ||
| tile.prog_quant = [prev_prog_quant; 3]; | ||
|
|
||
| // The third component's quantization bit width is out of range, so its magnitude | ||
| // decode must fail. On the wire a quant is a 4-bit nibble (see | ||
| // `ComponentCodecQuant::decode`) and cannot reach 16; this goes through a struct | ||
| // literal instead, for the same reason as the `quant_validate` tests in rfx.rs: a | ||
| // literal-constructed value deserves to be rejected before it is used. | ||
| let mut out_of_range_quant = ComponentCodecQuant::LOSSLESS; | ||
| out_of_range_quant.hl1 = 20; | ||
|
|
||
| tile.prog_quant = [prev_prog_quant, prev_prog_quant, out_of_range_quant]; | ||
| tile.pass = 1; | ||
| tile.quality = 50; | ||
| // Each component leaves one SRL entry in HL1, so the first two decode successfully. | ||
| tile.sign[0][0] = SIGN_ZERO; | ||
| tile.sign[1][0] = SIGN_ZERO; | ||
| tile.sign[2][0] = SIGN_ZERO; | ||
|
|
||
| let coefficients = tile.coefficients; | ||
| let sign = tile.sign; | ||
| let prog_quant = tile.prog_quant; | ||
|
|
||
| assert_eq!( | ||
| tile.decode_upgrade( | ||
| [&[0x90, 0x00], &[0x80, 0x00], &[]], | ||
| [&[0x90, 0x00], &[0x80, 0x00], &[0x90, 0x00]], | ||
| [&[], &[], &[]], | ||
| [ComponentCodecQuant::LOSSLESS; 3], | ||
| 75, | ||
| ), | ||
| Err(SrlError::Truncated) | ||
| Err(SrlError::InvalidBitCount(20)) | ||
| ); | ||
|
|
||
| assert_eq!(tile.coefficients, coefficients); | ||
| assert_eq!(tile.sign, sign); | ||
| assert_eq!(tile.prog_quant, [prev_prog_quant; 3]); | ||
| assert_eq!(tile.prog_quant, prog_quant); | ||
| assert_eq!(tile.pass, 1); | ||
| assert_eq!(tile.quality, 50); | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[protocol] Tile references dropped on ResetGraphics rests on a surface-destruction rule the cited sections do not contain — low 🟡 — The new clear_tile_references call cites MS-RDPEGFX 2.2.2.14 and 3.3.5.14 as requiring that ResetGraphics destroys every surface, but in the corpus 3.3.5.14 only mandates resizing the graphics output buffer and 2.2.2.14 describes PDU fields; 3.3.1.3 instead requires sub-band diffing tile contexts to be preserved for the connection or until the associated surface is deleted. A spec-strict server that retains surfaces and reuses a surface id with difference tiles now gets MissingTileReference instead of preserved state. The behavior is safer than the previous silent cross-reset differencing and matches observed Windows, but the normative justification is absent and the protocol-visible error path for conformant-but-different servers is undocumented; the comment should be corrected or the caveat noted.