Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GUACAMOLE-377: Add libguac "guac_display" API for easy and efficient rendering. #525

Open
wants to merge 39 commits into
base: main
Choose a base branch
from

Conversation

mike-jumper
Copy link
Contributor

This change builds off the internal guac_common_display and guac_common_surface structures to create a new public guac_display structure and surrounding API. The approach within guac_display improves on that of guac_common_surface in several ways:

  • It makes use of a pool of worker threads to parallelize the encoding process. The appropriate number of worker threads is detected automatically when the guac_display is created.
  • Changes due to scrolling, etc. are automatically detected in real time using a combination of a hash table, a hash function designed to avoid inefficient memory traversal, and a 2D variant of Rabin-Karp. Such drawing operations are automatically transformed into copy operations.
  • Changes that can be represented as a simple rect followed by cfill are automatically recognized and sent out as such.
  • Dirty rectangles covering the changes made to the display are automatically broken up and tightened to more efficiently represent the actual changes made, regardless of how large the dirty rectangle submitted to guac_display may have been.
  • Adjustment for client-side processing lag is handled automatically.
  • Encoding of images for a previous frame happens asynchronously while the next frame is being assembled, reducing latency.
  • Any number of additional frames may be assembled while waiting for the previous frame to encode. If a previous frame is still being encoded, further frames are combined together into a pending frame that will be flushed when encoding is complete.

With these changes, you can now throw virtually anything at the display and it will magically get decomposed into an efficient combination of copies, draws, and rectangles, even if the information regarding the nature of those updates is unavailable (ie: RDPGFX and SPICE).

Metrics covering rendering and optimization performance of guac_display are logged at the "trace" level.

I'm opening this as a draft for now, as these changes are currently incomplete:

  • Only VNC has been migrated to the new API, and then only partially.
  • I suspect that the internals of the terminal emulator can be simplified, given that scrolling is now automatically detected.
  • I have not yet added a mechanism for callers to "hint" that a change is due to a copy (this will be necessary for handling RDP's bitmap cache).

Once the above is finished, I'll delete the old guac_common_display, guac_common_surface, etc. and this will be ready.

@mike-jumper
Copy link
Contributor Author

I've been using the following patch to observe the impact these changes:

https://gist.github.com/mike-jumper/0afd41c9fbcbc764f719890fd0dd4e3b

With the above patch in place, you can set Guacamole.Display.DEBUG to true to enable a debug mode in which draw operations to the Guacamole display are highlighted in colored rectangles, where the color varies by the type of operation:

  • RED: Draw of compressed image data (the most expensive operation)
  • BLUE: Copy of existing image data (ie: an optimized scroll, restoring data from a cache - much cheaper than encoding, sending, and decoding image data)
  • GREEN: Solid-color rectangular draw (a combination of rect and cfill - very cheap).

Behavior of guac_common_surface

Here, everything is red. Dirty rects are generally nicely split and tightened around the regions actually changed, but that's about as deep as the processing goes. Without information from the remote desktop server explicitly stating that a particular update is a copy, we can't detect that a copy would be better.

rendering-before-guac-display.mp4

Behavior of guac_display

Now, things get much more colorful. Things that can be represented more efficiently as copies or rectangles are automatically optimized. This includes cases when the nature of those updates would prevent even the remote desktop server from knowing that a copy has occurred.

rendering-after-guac-display.mp4

.gitignore Show resolved Hide resolved
@mike-jumper
Copy link
Contributor Author

In testing this, I'm seeing some visual artifacts where windows dragged around in VNC have bits of the window content spread around, e.g. image

This should now be resolved - it was caused by the GotCopyRect() function having been overridden. Previously, this was no issue because we used our own buffer. Now that we're reusing libvncclient's buffer, the original GotCopyRect() needs to be called or that buffer won't be updated with respect to received CopyRect updates.

@jmuehlner
Copy link
Contributor

In testing this, I'm seeing some visual artifacts where windows dragged around in VNC have bits of the window content spread around, e.g. image

This should now be resolved - it was caused by the GotCopyRect() function having been overridden. Previously, this was no issue because we used our own buffer. Now that we're reusing libvncclient's buffer, the original GotCopyRect() needs to be called or that buffer won't be updated with respect to received CopyRect updates.

Nice - I can confirm that this issue looks to be fixed.

With FreeRDP's GDI being used for ther other aspects of RDP drawing,
using our own implementation of bitmap caching causes the FreeRDP's GDI
to become out-of-sync with Guacamole's representation, producing
graphical artifacts. We can't simply monkey-patch the GDI, as the
functions used are internal and not part of the public FreeRDP API.

There are likely other possible approaches, like manually updating
FreeRDP's GDI surface in addition to Guacamole's surface, but this may
not be worth the effort given that bitmap caching is not commonly used
by modern RDP servers.
…erwise encumbering testing of terminal emulator refactor).
The new guac_display otherwise tends to run out of outbound, client-wide
streams.
…vs. general failure of WaitForMultipleObjects().

The conversion of WAIT_FAILED to a signed int and back may cause the
value of result to not actually match the value of the WAIT_FAILED macro
due to the difference in size and sign extension during conversion.
It is otherwise difficult to guarantee that all operations touching the
pending frame buffer will occur while holding an open raw context,
resulting in unstable behavior.
No longer necessary now that the last and pending frame buffers are not
interleaved.
@mike-jumper mike-jumper force-pushed the guac-display branch 2 times, most recently from bb7e363 to 693f0f2 Compare September 20, 2024 17:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants