GUACAMOLE-377: Flush frames asynchronously with requestAnimationFrame() if possible.

This commit is contained in:
Michael Jumper
2021-07-23 23:45:12 -07:00
parent 7f707cdb04
commit d6db8fac7e

View File

@@ -163,10 +163,28 @@ Guacamole.Display = function() {
var frames = [];
/**
* Flushes all pending 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
* asynchronous operation like an image load, this function will return
* after reaching that operation and the flush operation will
* automamtically resume after that operation completes.
*
* @private
*/
function __flush_frames() {
var syncFlush = function syncFlush() {
var rendered_frames = 0;
@@ -185,6 +203,71 @@ Guacamole.Display = function() {
// Remove rendered frames from array
frames.splice(0, rendered_frames);
};
/**
* 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())
frames.shift().flush();
// 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);
};
// 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();
}
/**