Skip to content

Commit

Permalink
GUACAMOLE-377: Migrate RDP to default render thread.
Browse files Browse the repository at this point in the history
  • Loading branch information
mike-jumper committed Sep 28, 2024
1 parent 5407c0b commit 7b829b4
Show file tree
Hide file tree
Showing 6 changed files with 35 additions and 90 deletions.
29 changes: 7 additions & 22 deletions src/protocols/rdp/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,29 +23,14 @@
#include <guacamole/client.h>

/**
* The maximum duration of a frame in milliseconds. This ensures we at least
* meet a reasonable minimum framerate in the case that the RDP server provides
* no frame boundaries and streams data continuously enough that frame
* boundaries are not discernable through timing.
* The amount of time to wait for new messages from the RDP server before
* moving on to internal matters, in milliseconds. This value must be kept
* reasonably small such that a slow RDP server will not prevent external
* events from being handled (such as the stop signal from guac_client_stop()),
* but large enough that the message handling loop does not eat up CPU
* spinning.
*/
#define GUAC_RDP_MAX_FRAME_DURATION 33

/**
* The minimum duration of a frame in milliseconds. This ensures we don't start
* flushing a ton of tiny frames if an RDP server provides no frame boundaries
* and streams data inconsistently enough that timing would suggest frame
* boundaries in the middle of a frame.
*/
#define GUAC_RDP_MIN_FRAME_DURATION 10

/**
* The amount of time to wait for a new message from the RDP server when
* beginning a new frame, in milliseconds. This value must be kept reasonably
* small such that a slow RDP server will not prevent external events from
* being handled (such as the stop signal from guac_client_stop()), but large
* enough that the message handling loop does not eat up CPU spinning.
*/
#define GUAC_RDP_FRAME_START_TIMEOUT 250
#define GUAC_RDP_MESSAGE_CHECK_INTERVAL 1000

/**
* The native resolution of most RDP connections. As Windows and other systems
Expand Down
12 changes: 4 additions & 8 deletions src/protocols/rdp/gdi.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,9 @@ void guac_rdp_gdi_mark_frame(rdpContext* context, int starting) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;

/* A new frame is beginning */
if (starting) {
rdp_client->in_frame = 1;
return;
}

/* A new frame has been received from the RDP server and processed */
rdp_client->in_frame = 0;
rdp_client->frames_received++;
if (!starting)
guac_display_render_thread_notify_frame(rdp_client->render_thread);

}

Expand Down Expand Up @@ -133,6 +127,8 @@ BOOL guac_rdp_gdi_end_paint(rdpContext* context) {
guac_rect_constrain(&dst_rect, &current_context->bounds);
guac_rect_extend(&current_context->dirty, &dst_rect);

guac_display_render_thread_notify_modified(rdp_client->render_thread);

paint_complete:

/* There will be no further drawing operations */
Expand Down
6 changes: 3 additions & 3 deletions src/protocols/rdp/pointer.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ BOOL guac_rdp_pointer_set(rdpContext* context, POINTER_SET_CONST rdpPointer* poi
guac_display_layer_close_raw(cursor_layer, dst_context);
guac_display_layer_close_raw(src_layer, src_context);

guac_display_end_mouse_frame(rdp_client->display);
guac_display_render_thread_notify_modified(rdp_client->render_thread);
return TRUE;

}
Expand All @@ -128,7 +128,7 @@ BOOL guac_rdp_pointer_set_null(rdpContext* context) {
/* Set cursor to empty/blank graphic */
guac_display_set_cursor(rdp_client->display, GUAC_DISPLAY_CURSOR_NONE);

guac_display_end_mouse_frame(rdp_client->display);
guac_display_render_thread_notify_modified(rdp_client->render_thread);
return TRUE;

}
Expand All @@ -141,6 +141,6 @@ BOOL guac_rdp_pointer_set_default(rdpContext* context) {
/* Set cursor to embedded pointer */
guac_display_set_cursor(rdp_client->display, GUAC_DISPLAY_CURSOR_POINTER);

guac_display_end_mouse_frame(rdp_client->display);
guac_display_render_thread_notify_modified(rdp_client->render_thread);
return TRUE;
}
61 changes: 14 additions & 47 deletions src/protocols/rdp/rdp.c
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,8 @@ static int guac_rdp_handle_connection(guac_client* client) {

guac_rwlock_release_lock(&(rdp_client->lock));

rdp_client->render_thread = guac_display_render_thread_create(rdp_client->display);

/* Handle messages from RDP server while client is running */
while (client->state == GUAC_CLIENT_RUNNING
&& !guac_rdp_disp_reconnect_needed(rdp_client->disp)) {
Expand All @@ -605,48 +607,16 @@ static int guac_rdp_handle_connection(guac_client* client) {
guac_rdp_disp_update_size(rdp_client->disp, settings, rdp_inst);

/* Wait for data and construct a reasonable frame */
int wait_result = rdp_guac_client_wait_for_messages(client, GUAC_RDP_FRAME_START_TIMEOUT);
if (wait_result > 0) {

/* Read server messages until frame is built */
guac_timestamp frame_start = guac_timestamp_current();
int frames_at_start = rdp_client->frames_received;
do {

/* Handle any queued FreeRDP events (this may result in RDP
* messages being sent), aborting if FreeRDP event handling
* fails */
if (!guac_rdp_handle_events(rdp_client)) {
wait_result = -1;
break;
}

int frame_duration = guac_timestamp_current() - frame_start;

if (!rdp_client->in_frame) {

/* Flush frame if at least one frame has been produced */
if (rdp_client->frames_received > frames_at_start)
break;

/* Continue processing messages for up to a reasonable
* minimum framerate without an explicit frame boundary
* indicating that the frame is not yet complete */
if (frame_duration > GUAC_RDP_MAX_FRAME_DURATION)
break;

}

/* Do not exceed a reasonable maximum framerate without an
* explicit frame boundary terminating the frame early */
int allowed_wait = GUAC_RDP_MIN_FRAME_DURATION - frame_duration;
if (allowed_wait < 0)
allowed_wait = 0;

wait_result = rdp_guac_client_wait_for_messages(client, allowed_wait);

} while (wait_result > 0);
int wait_result = rdp_guac_client_wait_for_messages(client, GUAC_RDP_MESSAGE_CHECK_INTERVAL);
if (wait_result < 0)
break;

/* Handle any queued FreeRDP events (this may result in RDP messages
* being sent), aborting if FreeRDP event handling fails */
if (!guac_rdp_handle_events(rdp_client)) {
wait_result = -1;
break;
}

/* Test whether the RDP server is closing the connection */
Expand All @@ -666,13 +636,6 @@ static int guac_rdp_handle_connection(guac_client* client) {
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_UNAVAILABLE,
"Connection closed.");

/* Flush frame only if successful and an RDP frame is not known to be
* in progress */
else if (!rdp_client->in_frame) {
guac_display_end_multiple_frames(rdp_client->display, rdp_client->frames_received);
rdp_client->frames_received = 0;
}

}

guac_rwlock_acquire_write_lock(&(rdp_client->lock));
Expand All @@ -688,6 +651,10 @@ static int guac_rdp_handle_connection(guac_client* client) {
freerdp_disconnect(rdp_inst);
pthread_mutex_unlock(&(rdp_client->message_lock));

/* Stop render loop */
guac_display_render_thread_destroy(rdp_client->render_thread);
rdp_client->render_thread = NULL;

/* Remove reference to FreeRDP's GDI buffer so that it can be safely freed
* prior to freeing the guac_display */
guac_display_layer_raw_context* context = guac_display_layer_open_raw(default_layer);
Expand Down
13 changes: 3 additions & 10 deletions src/protocols/rdp/rdp.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,17 +115,10 @@ typedef struct guac_rdp_client {
guac_display_layer_raw_context* current_context;

/**
* Whether the RDP server has reported that a new frame is in progress, and
* we are now receiving updates relevant to that frame.
* The current instance of the guac_display render thread. If the thread
* has not yet been started, this will be NULL.
*/
int in_frame;

/**
* The number of distinct frames received from the RDP server since last
* flush, if the RDP server supports reporting frame boundaries. If the RDP
* server does not support tracking frames, this will be zero.
*/
int frames_received;
guac_display_render_thread* render_thread;

/**
* The current state of the keyboard with respect to the RDP session.
Expand Down
4 changes: 4 additions & 0 deletions src/protocols/rdp/settings.c
Original file line number Diff line number Diff line change
Expand Up @@ -1774,6 +1774,10 @@ void guac_rdp_push_settings(guac_client* client,
rdp_settings->FrameMarkerCommandEnabled = TRUE;
rdp_settings->SurfaceFrameMarkerEnabled = TRUE;

/* Always handle input events asynchronously (rather than synchronously
* with the rest of FreeRDP's event loop, including graphics) */
rdp_settings->AsyncInput = TRUE;

/* Enable RemoteFX / Graphics Pipeline */
if (guac_settings->enable_gfx) {

Expand Down

0 comments on commit 7b829b4

Please sign in to comment.