Skip to content

Commit

Permalink
GUACAMOLE-377: Revert to synchronous flush (asynchronous is slower).
Browse files Browse the repository at this point in the history
The asynchronous flush mechanism leveraging requestAnimationFrame() does
not perform as well as the old synchronous flush. This appears to be due
to delays in when the browser actually allows the frame to proceed,
causing the client to lag behind.

The old synchronous flush mechanism does not suffer from such issues.
  • Loading branch information
mike-jumper committed Jan 13, 2025
1 parent 0460352 commit 1454a5d
Showing 1 changed file with 1 addition and 74 deletions.
75 changes: 1 addition & 74 deletions guacamole-common-js/src/main/webapp/modules/Display.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,19 +185,6 @@ Guacamole.Display = function() {
*/
var frames = [];

/**
* The ID of the animation frame request returned by the last call to
* requestAnimationFrame(). This value will only be set if the browser
* supports requestAnimationFrame(), if a frame render is currently
* pending, and if the current browser tab is currently focused (likely to
* handle requests for animation frames). In all other cases, this will be
* null.
*
* @private
* @type {number}
*/
var inProgressFrame = null;

/**
* Flushes all pending frames synchronously. This function will block until
* all pending frames have rendered. If a frame is currently blocked by an
Expand Down Expand Up @@ -239,45 +226,6 @@ Guacamole.Display = function() {

};

/**
* Flushes all pending frames asynchronously. This function returns
* immediately, relying on requestAnimationFrame() to dictate when each
* frame should be flushed.
*
* @private
*/
var asyncFlush = function asyncFlush() {

var continueFlush = function continueFlush() {

// We're no longer waiting to render a frame
inProgressFrame = null;

// Nothing to do if there are no frames remaining
if (!frames.length)
return;

// Flush the next frame only if it is ready (not awaiting
// completion of some asynchronous operation like an image load)
if (frames[0].isReady()) {
var frame = frames.shift();
frame.flush();
notifyFlushed(frame.localTimestamp, frame.remoteTimestamp, frame.logicalFrames);
}

// Request yet another animation frame if frames remain to be
// flushed
if (frames.length)
inProgressFrame = window.requestAnimationFrame(continueFlush);

};

// Begin flushing frames if not already waiting to render a frame
if (!inProgressFrame)
inProgressFrame = window.requestAnimationFrame(continueFlush);

};

/**
* Recently-gathered display render statistics, as made available by calls
* to notifyFlushed(). The contents of this array will be trimmed to
Expand Down Expand Up @@ -373,33 +321,12 @@ Guacamole.Display = function() {

};

// Switch from asynchronous frame handling to synchronous frame handling if
// requestAnimationFrame() is unlikely to be usable (browsers may not
// invoke the animation frame callback if the relevant tab is not focused)
window.addEventListener('blur', function switchToSyncFlush() {
if (inProgressFrame && !document.hasFocus()) {

// Cancel pending asynchronous processing of frame ...
window.cancelAnimationFrame(inProgressFrame);
inProgressFrame = null;

// ... and instead process it synchronously
syncFlush();

}
}, true);

/**
* Flushes all pending frames.
* @private
*/
function __flush_frames() {

if (window.requestAnimationFrame && document.hasFocus())
asyncFlush();
else
syncFlush();

syncFlush();
}

/**
Expand Down

0 comments on commit 1454a5d

Please sign in to comment.