Merge 1.6.0 changes to patch.

This commit is contained in:
Virtually Nick 2025-05-01 21:47:35 -04:00
commit 1048cefa4e
4 changed files with 114 additions and 137 deletions

View File

@ -379,11 +379,12 @@ void guac_display_end_multiple_frames(guac_display* display, int frames) {
guac_rwlock_release_lock(&display->last_frame.lock);
/* Not all frames are graphical. If we end up with a frame containing
* nothing but layer property changes, then we must still send a frame
* boundary even though there is no display plan to optimize. */
* nothing but layer property changes, then we must still send at least one
* operation to awaken the workers and flush layer changes, even though
* there is no display plan to optimize. */
if (plan == NULL && frame_nonempty) {
guac_display_plan_operation end_frame_op = {
.type = GUAC_DISPLAY_PLAN_END_FRAME
.type = GUAC_DISPLAY_PLAN_OPERATION_NOP
};
guac_fifo_enqueue(&display->ops, &end_frame_op);
}

View File

@ -305,7 +305,7 @@ guac_display_plan* PFW_LFR_guac_display_plan_create(guac_display* display) {
guac_display_plan* plan = guac_mem_alloc(sizeof(guac_display_plan));
plan->display = display;
plan->frame_end = frame_end;
plan->length = guac_mem_ckd_add_or_die(op_count, 1);
plan->length = op_count;
plan->ops = guac_mem_alloc(plan->length, sizeof(guac_display_plan_operation));
/* Convert the dirty rectangles stored in each layer's cells to individual
@ -359,12 +359,6 @@ guac_display_plan* PFW_LFR_guac_display_plan_create(guac_display* display) {
* predicted quantity */
GUAC_ASSERT(added_ops == op_count);
/* Worker threads must be aware of end-of-frame to know when to send sync,
* etc. Noticing that the operation queue is empty is insufficient, as the
* queue may become empty while a frame is in progress if the worker
* threads happen to be processing things quickly. */
current_op->type = GUAC_DISPLAY_PLAN_END_FRAME;
return plan;
}

View File

@ -141,12 +141,7 @@ typedef enum guac_display_plan_operation_type {
/**
* Draw arbitrary image data to the destination rect.
*/
GUAC_DISPLAY_PLAN_OPERATION_IMG,
/**
* Finish the frame, sending the frame boundary to all connected users.
*/
GUAC_DISPLAY_PLAN_END_FRAME
GUAC_DISPLAY_PLAN_OPERATION_IMG
} guac_display_plan_operation_type;

View File

@ -368,142 +368,129 @@ void* guac_display_worker_thread(void* data) {
case GUAC_DISPLAY_PLAN_OPERATION_COPY:
case GUAC_DISPLAY_PLAN_OPERATION_RECT:
case GUAC_DISPLAY_PLAN_OPERATION_NOP:
guac_client_log(client, GUAC_LOG_DEBUG, "Operation type %i "
"should NOT be present in the set of operations given "
"to guac_display worker thread. All operations except "
"IMG and END_FRAME are handled during the initial, "
"IMG and NOP are handled during the initial, "
"single-threaded flush step. This is likely a bug.",
op.type);
break;
case GUAC_DISPLAY_PLAN_END_FRAME:
guac_fifo_lock(&display->ops);
int other_workers_busy = (display->active_workers > 1);
guac_fifo_unlock(&display->ops);
/* If other workers are still busy, push the frame boundary
* back on the queue so that it's picked up by one of those
* workers */
if (other_workers_busy) {
guac_fifo_enqueue(&display->ops, &op);
}
/* Otherwise, we've reached the end of the frame, and this is
* the worker that will be sending that boundary to connected
* users */
else {
/* Update the mouse cursor if it's been changed since the
* last frame */
guac_display_layer* cursor = display->cursor_buffer;
if (!guac_rect_is_empty(&cursor->last_frame.dirty)) {
guac_protocol_send_cursor(client->socket,
display->last_frame.cursor_hotspot_x,
display->last_frame.cursor_hotspot_y,
cursor->layer, 0, 0,
cursor->last_frame.width,
cursor->last_frame.height);
}
/* Use the amount of time that the client has been waiting
* for a frame vs. the amount of time that it took the
* client to process the most recently acknowledged frame
* to calculate the amount of additional delay required to
* allow the client to catch up. This value is used later,
* after everything else related to the frame has been
* finalized. */
int time_since_last_frame = guac_timestamp_current() - client->last_sent_timestamp;
int processing_lag = guac_client_get_processing_lag(client);
int required_wait = processing_lag - time_since_last_frame;
/* Allow connected clients to move forward with rendering */
guac_client_end_multiple_frames(client, display->last_frame.frames);
/* While connected clients moves forward with rendering,
* commit any changed contents to client-side backing buffer */
guac_display_layer* current = display->last_frame.layers;
while (current != NULL) {
/* Save a copy of the changed region if the layer has
* been modified since the last frame */
guac_rect* dirty = &current->last_frame.dirty;
if (!guac_rect_is_empty(dirty)) {
int x = dirty->left;
int y = dirty->top;
int width = guac_rect_width(dirty);
int height = guac_rect_height(dirty);
/* Ensure destination region is cleared out first if the alpha channel need be considered,
* as GUAC_COMP_OVER is significantly faster than GUAC_COMP_SRC on the browser side */
if (!current->opaque) {
guac_protocol_send_rect(client->socket, current->last_frame_buffer, x, y, width, height);
guac_protocol_send_cfill(client->socket, GUAC_COMP_RATOP, current->last_frame_buffer,
0x00, 0x00, 0x00, 0x00);
}
guac_protocol_send_copy(client->socket,
current->layer, x, y, width, height,
GUAC_COMP_OVER, current->last_frame_buffer, x, y);
}
current = current->last_frame.next;
}
/* This is now absolutely everything for the current frame,
* and it's safe to flush any outstanding data */
guac_socket_flush(client->socket);
/* Notify any watchers of render_state that a frame is no longer in progress */
guac_flag_set_and_lock(&display->render_state, GUAC_DISPLAY_RENDER_STATE_FRAME_NOT_IN_PROGRESS);
guac_flag_clear(&display->render_state, GUAC_DISPLAY_RENDER_STATE_FRAME_IN_PROGRESS);
guac_flag_unlock(&display->render_state);
/* Exclude local, server-side frame processing latency from
* waiting period */
int latency = (int) (guac_timestamp_current() - display->last_frame.timestamp);
if (latency >= 0) {
guac_client_log(display->client, GUAC_LOG_TRACE,
"Rendering latency: %ims (%i:1 frame)\n",
latency, display->last_frame.frames);
required_wait -= latency;
}
/* Ensure we don't wait without bound when compensating for
* client-side processing delays */
if (required_wait > GUAC_DISPLAY_MAX_LAG_COMPENSATION)
required_wait = GUAC_DISPLAY_MAX_LAG_COMPENSATION;
/* Allow connected clients to catch up if they're taking
* longer to process frames than the server is taking to
* generate them */
if (required_wait > 0) {
guac_client_log(display->client, GUAC_LOG_TRACE,
"Waiting %ims to compensate for client-side "
"processing delays.\n", required_wait);
guac_timestamp_msleep(required_wait);
}
guac_fifo_lock(&display->ops);
has_outstanding_frames = display->frame_deferred;
guac_fifo_unlock(&display->ops);
}
case GUAC_DISPLAY_PLAN_OPERATION_NOP:
/* Do nothing */
break;
}
guac_rwlock_release_lock(&display->last_frame.lock);
guac_fifo_lock(&display->ops);
/* If we're the only active worker and there are no further operations
* pending, we've reached the end of the frame, and this is the worker
* that will be sending that boundary to connected users */
if (!(display->ops.state.value & GUAC_FIFO_STATE_NONEMPTY) && display->active_workers == 1) {
/* Update the mouse cursor if it's been changed since the
* last frame */
guac_display_layer* cursor = display->cursor_buffer;
if (!guac_rect_is_empty(&cursor->last_frame.dirty)) {
guac_protocol_send_cursor(client->socket,
display->last_frame.cursor_hotspot_x,
display->last_frame.cursor_hotspot_y,
cursor->layer, 0, 0,
cursor->last_frame.width,
cursor->last_frame.height);
}
/* Use the amount of time that the client has been waiting
* for a frame vs. the amount of time that it took the
* client to process the most recently acknowledged frame
* to calculate the amount of additional delay required to
* allow the client to catch up. This value is used later,
* after everything else related to the frame has been
* finalized. */
int time_since_last_frame = guac_timestamp_current() - client->last_sent_timestamp;
int processing_lag = guac_client_get_processing_lag(client);
int required_wait = processing_lag - time_since_last_frame;
/* Allow connected clients to move forward with rendering */
guac_client_end_multiple_frames(client, display->last_frame.frames);
/* While connected clients moves forward with rendering,
* commit any changed contents to client-side backing buffer */
guac_display_layer* current = display->last_frame.layers;
while (current != NULL) {
/* Save a copy of the changed region if the layer has
* been modified since the last frame */
guac_rect* dirty = &current->last_frame.dirty;
if (!guac_rect_is_empty(dirty)) {
int x = dirty->left;
int y = dirty->top;
int width = guac_rect_width(dirty);
int height = guac_rect_height(dirty);
/* Ensure destination region is cleared out first if the alpha channel need be considered,
* as GUAC_COMP_OVER is significantly faster than GUAC_COMP_SRC on the browser side */
if (!current->opaque) {
guac_protocol_send_rect(client->socket, current->last_frame_buffer, x, y, width, height);
guac_protocol_send_cfill(client->socket, GUAC_COMP_RATOP, current->last_frame_buffer,
0x00, 0x00, 0x00, 0x00);
}
guac_protocol_send_copy(client->socket,
current->layer, x, y, width, height,
GUAC_COMP_OVER, current->last_frame_buffer, x, y);
}
current = current->last_frame.next;
}
/* This is now absolutely everything for the current frame,
* and it's safe to flush any outstanding data */
guac_socket_flush(client->socket);
/* Notify any watchers of render_state that a frame is no longer in progress */
guac_flag_set_and_lock(&display->render_state, GUAC_DISPLAY_RENDER_STATE_FRAME_NOT_IN_PROGRESS);
guac_flag_clear(&display->render_state, GUAC_DISPLAY_RENDER_STATE_FRAME_IN_PROGRESS);
guac_flag_unlock(&display->render_state);
/* Exclude local, server-side frame processing latency from
* waiting period */
int latency = (int) (guac_timestamp_current() - display->last_frame.timestamp);
if (latency >= 0) {
guac_client_log(display->client, GUAC_LOG_TRACE,
"Rendering latency: %ims (%i:1 frame)\n",
latency, display->last_frame.frames);
required_wait -= latency;
}
/* Ensure we don't wait without bound when compensating for
* client-side processing delays */
if (required_wait > GUAC_DISPLAY_MAX_LAG_COMPENSATION)
required_wait = GUAC_DISPLAY_MAX_LAG_COMPENSATION;
/* Allow connected clients to catch up if they're taking
* longer to process frames than the server is taking to
* generate them */
if (required_wait > 0) {
guac_client_log(display->client, GUAC_LOG_TRACE,
"Waiting %ims to compensate for client-side "
"processing delays.\n", required_wait);
guac_timestamp_msleep(required_wait);
}
has_outstanding_frames = display->frame_deferred;
}
display->active_workers--;
guac_fifo_unlock(&display->ops);
guac_rwlock_release_lock(&display->last_frame.lock);
/* Trigger additional flush if frames were completed while we were
* still processing the previous frame */
if (has_outstanding_frames) {