Skip to content

feat(egfx): add QoE statistics accumulation on GraphicsPipelineServer#1167

Merged
Benoît Cortier (CBenoit) merged 1 commit into
Devolutions:masterfrom
glamberson:feat/qoe-collector
Mar 16, 2026
Merged

feat(egfx): add QoE statistics accumulation on GraphicsPipelineServer#1167
Benoît Cortier (CBenoit) merged 1 commit into
Devolutions:masterfrom
glamberson:feat/qoe-collector

Conversation

@glamberson
Copy link
Copy Markdown
Contributor

@glamberson Greg Lamberson (glamberson) commented Mar 15, 2026

Part of #1158 (Section 11: Session Health Monitoring & Stream Observability)

Summary

  • Add QoeCollector (private) and QoeSnapshot (public) to GraphicsPipelineServer for accumulated QoE statistics
  • Wire record_rtt() into handle_frame_acknowledge() to measure frame round-trip latency from FrameInfo.sent_at
  • Wire record_qoe() into handle_qoe_frame_acknowledge() to accumulate client decode+render timing
  • Add qoe_snapshot() and reset_qoe() public methods

Motivation

The server currently processes QoE data and discards it. Ironrdp-server logs it at warn! level. Downstream consumers reimplement accumulation in their handler callbacks. This puts the accumulation where the data naturally flows (the server already processes both PDU types and owns FrameInfo.sent_at for RTT correlation).

On Linux server deployments, session health monitoring is critical: Headless RDP servers running behind tunnels, in Proxmox clusters, or as systemd services need queryable performance data for health checks and alerting. Currently there is no way to answer "what is the client decode latency?" or "what is the frame round-trip time?" without building custom accumulation on top of the handler callbacks.

Design

QoeCollector uses exponential moving average (alpha=0.1, approximately 10-sample effective window) for both decode+render time and RTT, with min/max tracking. It mirrors the FrameTracker pattern: private accumulator owned by the server, exposed via public read-only accessors.

QoeSnapshot is Clone + Debug (not Serialize) and follows the convention of existing IronRDP data types. Consumers who need serialization can trivially project from the snapshot fields.

No changes to GraphicsPipelineHandler. The existing on_frame_ack() and on_qoe_metrics() callbacks continue to work exactly as before.

Test plan

  • test_qoe_snapshot_none_before_data .. None before any QoE/ack data
  • test_qoe_snapshot_after_frame_ack .. RTT accumulation from frame acknowledgment
  • test_qoe_snapshot_after_qoe_report .. client decode+render time accumulation from QoE PDU
  • test_qoe_reset .. reset_qoe() clears all accumulated statistics
  • All 604 existing tests pass unchanged
  • cargo xtask check fmt/lints/tests/typos/locks all pass

The server currently processes QoE Frame Acknowledge PDUs and frame
acknowledgments but discards all performance data after forwarding
to handler callbacks. This adds a QoeCollector that automatically
accumulates client decode/render timing and frame round-trip latency,
exposed via qoe_snapshot() for health monitoring consumers.

QoeCollector tracks:
- Client decode+render time from QoE PDUs (EMA, min/max, latest)
- Frame round-trip latency from FrameInfo.sent_at correlation (EMA, min/max)

New public API:
- QoeSnapshot struct (Clone + Debug)
- GraphicsPipelineServer::qoe_snapshot() -> Option<QoeSnapshot>
- GraphicsPipelineServer::reset_qoe()

No changes to GraphicsPipelineHandler trait. Zero breaking changes.

Part of Devolutions#1158 (Section 11: Session Health Monitoring)
Copy link
Copy Markdown
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.

LGTM!

@CBenoit Benoît Cortier (CBenoit) merged commit bf694c8 into Devolutions:master Mar 16, 2026
10 checks passed
@glamberson Greg Lamberson (glamberson) deleted the feat/qoe-collector branch March 17, 2026 12:13
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