Skip to content

feat(server): add EGFX server integration with DVC bridge#1099

Merged
Benoît Cortier (CBenoit) merged 3 commits intoDevolutions:masterfrom
glamberson:egfx-server-integration
Feb 17, 2026
Merged

feat(server): add EGFX server integration with DVC bridge#1099
Benoît Cortier (CBenoit) merged 3 commits intoDevolutions:masterfrom
glamberson:egfx-server-integration

Conversation

@glamberson
Copy link
Contributor

Summary

Wire ironrdp-egfx's GraphicsPipelineServer into ironrdp-server, enabling H.264 AVC420/AVC444 video streaming to RDP clients via the EGFX dynamic virtual channel.

This builds on #1057 (merged) which added the EGFX PDU types and server state machine. This PR adds the integration layer that connects it to the server connection lifecycle.

Changes to ironrdp-egfx

  • Make CHANNEL_NAME public for cross-crate DVC registration
  • Add ZGFX uncompressed segment wrapping for DVC wire format (MS-RDPEGFX 2.2.5)
  • Auto-send ResetGraphics before first CreateSurface per MS-RDPEGFX requirement
  • Track DVC channel_id from start() for proactive frame encoding
  • Rewrite drain_output() to ZGFX-wrap PDUs for DVC transmission

New ironrdp-server gfx module (behind egfx feature)

  • GfxServerFactory trait following the CliprdrServerFactory / SoundServerFactory pattern
  • GfxDvcBridge: Arc<Mutex> wrapper enabling shared access between DVC message processing and proactive frame submission from display handlers
  • ServerEvent::Egfx variant for routing EGFX PDUs to the wire
  • Builder integration with with_gfx_factory()

Design notes

  • Uses std::sync::Mutex (not tokio) because DvcProcessor trait methods are synchronous
  • ZGFX wrapping is uncompressed-only for now; compressed mode can be added as a follow-up when feat(zgfx): add segment wrapping utilities #1076 merges
  • Capability negotiation intersects client and server flags per protocol spec
  • The egfx feature is optional and off by default, so no impact on existing users

Test plan

  • cargo fmt --all -- --check: clean
  • cargo clippy --workspace --all-targets --features helper,__bench -- -D warnings: clean
  • cargo clippy -p ironrdp-egfx -p ironrdp-server --features egfx -- -D warnings: clean
  • cargo test --workspace: 1058 passed, 0 failed
  • Updated test_surface_lifecycle to account for auto-ResetGraphics PDU

@glamberson
Copy link
Contributor Author

I nearly forgot about thsi one...

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR integrates the EGFX (Graphics Pipeline Extension) server from ironrdp-egfx into ironrdp-server, enabling H.264 AVC420/AVC444 video streaming to RDP clients via the EGFX dynamic virtual channel. The integration builds on PR #1057 which added the EGFX PDU types and server state machine.

Changes:

  • Added ZGFX uncompressed segment wrapping for DVC wire format compliance with MS-RDPEGFX 2.2.5
  • Implemented auto-send of ResetGraphics PDU before first CreateSurface per MS-RDPEGFX requirements
  • Created GfxServerFactory trait and GfxDvcBridge for integrating EGFX with the server DVC infrastructure

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
crates/ironrdp-egfx/src/lib.rs Made CHANNEL_NAME public for cross-crate DVC registration
crates/ironrdp-egfx/src/server.rs Added ZGFX wrapping, auto-ResetGraphics, channel_id tracking, and rewrote drain_output()
crates/ironrdp-server/src/lib.rs Exposed gfx module under egfx feature flag
crates/ironrdp-server/src/gfx.rs New module with GfxServerFactory trait and GfxDvcBridge DVC processor
crates/ironrdp-server/src/builder.rs Added with_gfx_factory() builder method
crates/ironrdp-server/src/server.rs Integrated GfxServerFactory, added ServerEvent::Egfx variant, wired DVC bridge
crates/ironrdp-server/Cargo.toml Added egfx optional feature and ironrdp-egfx dependency
crates/ironrdp-testsuite-core/tests/egfx/server.rs Updated test to expect auto-sent ResetGraphics PDU
Cargo.lock Added ironrdp-egfx dependency to ironrdp-server

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Wire ironrdp-egfx's GraphicsPipelineServer into ironrdp-server,
enabling H.264 AVC420/AVC444 video streaming to RDP clients.

Changes to ironrdp-egfx:
- Make CHANNEL_NAME public for cross-crate DVC registration
- Add ZGFX uncompressed segment wrapping for DVC wire format
- Auto-send ResetGraphics before first CreateSurface (MS-RDPEGFX)
- Track DVC channel_id from start() for proactive frame encoding
- Rewrite drain_output() to ZGFX-wrap PDUs for DVC transmission

New ironrdp-server gfx module (behind "egfx" feature):
- GfxServerFactory trait following CliprdrServerFactory pattern
- GfxDvcBridge: Arc<Mutex> wrapper enabling shared access between
  DVC message processing and proactive frame submission
- ServerEvent::Egfx variant for routing EGFX PDUs to the wire
- Builder integration with with_gfx_factory()
- Remove unused channel_id from EgfxServerMessage::SendMessages
- Make GfxServerFactory extend ServerEventSender for event loop signaling
- Store GfxServerHandle in RdpServer instead of discarding it
- Replace local wrap_zgfx_uncompressed with ironrdp_graphics::zgfx::wrap_uncompressed
@glamberson glamberson force-pushed the egfx-server-integration branch from 797ee1f to 96a8c7d Compare February 13, 2026 13:49
@glamberson
Copy link
Contributor Author

Great. issues resolved and rebased. Thanks.

@CBenoit
Copy link
Member

Benoît Cortier (CBenoit) commented Feb 16, 2026

I’m okay with adding the egfx feature flag as a way of avoiding breaking changes in the following releases, but I think there is a better way of handling optional features and smoother API extensions:

  • Builder pattern with optional methods so we don’t need to have a big new function with many parameters that we expend over time
  • Separate traits and implementation in two different crates: ironrdp-server depends on the crate providing the traits, but the implementation crate can be imported by the end user and we can do dependency injection to feed the behaviors back into ironrdp-server

How does that sound? I think it could be good to move towards this kind of patterns in the future and remove the egfx feature flag by then.

Replace .ok_or_else(|| anyhow!(...)) with .context() for rdpsnd,
clipboard, and EGFX channel lookups. All three use static strings
so .context() is cleaner and consistent with the rest of the file.
@glamberson
Copy link
Contributor Author

I think there is a better way of handling optional features and smoother API extensions:

  • Builder pattern with optional methods so we don’t need to have a big new function with many parameters that we expend over time
  • Separate traits and implementation in two different crates: ironrdp-server depends on the crate providing the traits, but the implementation crate can be imported by the end user and we can do dependency injection to feed the behaviors back into ironrdp-server

Yes I completely agree. Sounds good. I'm happy to help refactor once we get a little further along and have a better feel for the right trait boundaries.

Copy link
Member

@CBenoit Benoît Cortier (CBenoit) left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! Merging as-is for now

@CBenoit Benoît Cortier (CBenoit) merged commit 4ba696c into Devolutions:master Feb 17, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments