From 31cfbb309df83d53560a79be8e4d32a5cfed25db Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 24 Aug 2024 16:35:15 -0700 Subject: [PATCH] GUACAMOLE-377: Prefer GUAC_COMP_OVER to GUAC_COMP_SRC for performance-critical operations (~3x faster). --- src/libguac/display-worker.c | 29 +++++++++++++++++++++++++---- src/libguac/display.c | 2 +- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/libguac/display-worker.c b/src/libguac/display-worker.c index c6b18bdf6..e39513bbc 100644 --- a/src/libguac/display-worker.c +++ b/src/libguac/display-worker.c @@ -23,6 +23,7 @@ #include "guacamole/display.h" #include "guacamole/fifo.h" #include "guacamole/layer.h" +#include "guacamole/protocol-types.h" #include "guacamole/protocol.h" #include "guacamole/rect.h" #include "guacamole/rwlock.h" @@ -446,7 +447,8 @@ void* guac_display_worker_thread(void* data) { /* Allow connected clients to move forward with rendering */ guac_client_end_multiple_frames(client, display->last_frame.frames); - /* Commit any changed contents to client-side backing buffer */ + /* 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) { @@ -454,15 +456,34 @@ void* guac_display_worker_thread(void* data) { * been modified since the last frame */ guac_rect* dirty = ¤t->last_frame.dirty; if (!guac_rect_is_empty(dirty)) { - guac_protocol_send_copy(client->socket, current->layer, - 0, 0, current->last_frame.width, current->last_frame.height, - GUAC_COMP_SRC, current->last_frame_buffer, 0, 0); + + 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; } + /* Include an additional frame boundary to allow the client to also move forward with committing + * changes to the backing buffer while the server is receiving and preparing the next frame */ + guac_client_end_multiple_frames(client, 0); + /* This is now absolutely everything for the current frame, * and it's safe to flush any outstanding data */ guac_socket_flush(client->socket); diff --git a/src/libguac/display.c b/src/libguac/display.c index 2d627d4dd..731a44632 100644 --- a/src/libguac/display.c +++ b/src/libguac/display.c @@ -236,7 +236,7 @@ void guac_display_dup(guac_display* display, guac_socket* socket) { /* Resync copy of previous frame */ guac_protocol_send_copy(socket, layer, 0, 0, width, height, - GUAC_COMP_SRC, current->last_frame_buffer, 0, 0); + GUAC_COMP_OVER, current->last_frame_buffer, 0, 0); /* Resync any properties that are specific to non-buffer layers */ if (current->layer->index > 0) {