diff mbox series

[4/4] ui: honour the actual guest display dimensions without rounding

Message ID 20210311182957.486939-5-berrange@redhat.com
State New
Headers show
Series ui: improve precision of VNC desktop resizing | expand

Commit Message

Daniel P. Berrangé March 11, 2021, 6:29 p.m. UTC
A long time ago the VNC server code had some memory corruption
fixes done in:

  commit bea60dd7679364493a0d7f5b54316c767cf894ef
  Author: Peter Lieven <pl@kamp.de>
  Date:   Mon Jun 30 10:57:51 2014 +0200

    ui/vnc: fix potential memory corruption issues

One of the implications of the fix was that the VNC server would have a
thin black bad down the right hand side if the guest desktop width was
not a multiple of 16. In practice this was a non-issue since the VNC
server was always honouring a guest specified resolution and guests
essentially always pick from a small set of sane resolutions likely in
real world hardware.

We recently introduced support for the extended desktop resize extension
and as a result the VNC client has ability to specify an arbitrary
desktop size and the guest OS may well honour it exactly. As a result we
no longer have any guarantee that the width will be a multiple of 16,
and so when resizing the desktop we have a 93% chance of getting the
black bar on the right hand size.

The VNC server maintains three different desktop dimensions

 1. The guest surface
 2. The server surface
 3. The client desktop

The requirement for the width to be a multiple of 16 only applies to
item 2, the server surface, for the purpose of doing dirty bitmap
tracking.

Normally we will set the client desktop size to always match the server
surface size, but that's not a strict requirement. In order to cope with
clients that don't support the desktop size encoding, we already allow
for the client desktop to be a different size that the server surface.

Thus we can trivially eliminate the black bar, but setting the client
desktop size to be the un-rounded server surface size - the so called
"true width".

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
 ui/trace-events |  2 ++
 ui/vnc.c        | 23 +++++++++++++++++++----
 ui/vnc.h        |  1 +
 3 files changed, 22 insertions(+), 4 deletions(-)

Comments

Marc-André Lureau March 12, 2021, 6:48 a.m. UTC | #1
On Thu, Mar 11, 2021 at 10:38 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:

> A long time ago the VNC server code had some memory corruption
> fixes done in:
>
>   commit bea60dd7679364493a0d7f5b54316c767cf894ef
>   Author: Peter Lieven <pl@kamp.de>
>   Date:   Mon Jun 30 10:57:51 2014 +0200
>
>     ui/vnc: fix potential memory corruption issues
>
> One of the implications of the fix was that the VNC server would have a
> thin black bad down the right hand side if the guest desktop width was
> not a multiple of 16. In practice this was a non-issue since the VNC
> server was always honouring a guest specified resolution and guests
> essentially always pick from a small set of sane resolutions likely in
> real world hardware.
>
> We recently introduced support for the extended desktop resize extension
> and as a result the VNC client has ability to specify an arbitrary
> desktop size and the guest OS may well honour it exactly. As a result we
> no longer have any guarantee that the width will be a multiple of 16,
> and so when resizing the desktop we have a 93% chance of getting the
> black bar on the right hand size.
>
> The VNC server maintains three different desktop dimensions
>
>  1. The guest surface
>  2. The server surface
>  3. The client desktop
>
> The requirement for the width to be a multiple of 16 only applies to
> item 2, the server surface, for the purpose of doing dirty bitmap
> tracking.
>
> Normally we will set the client desktop size to always match the server
> surface size, but that's not a strict requirement. In order to cope with
> clients that don't support the desktop size encoding, we already allow
> for the client desktop to be a different size that the server surface.
>
> Thus we can trivially eliminate the black bar, but setting the client
> desktop size to be the un-rounded server surface size - the so called
> "true width".
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
>  ui/trace-events |  2 ++
>  ui/vnc.c        | 23 +++++++++++++++++++----
>  ui/vnc.h        |  1 +
>  3 files changed, 22 insertions(+), 4 deletions(-)
>
> diff --git a/ui/trace-events b/ui/trace-events
> index 3838ae2d84..5d1da6f236 100644
> --- a/ui/trace-events
> +++ b/ui/trace-events
> @@ -59,6 +59,8 @@ vnc_client_throttle_audio(void *state, void *ioc, size_t
> offset) "VNC client thr
>  vnc_client_unthrottle_forced(void *state, void *ioc) "VNC client
> unthrottle forced offset state=%p ioc=%p"
>  vnc_client_unthrottle_incremental(void *state, void *ioc, size_t offset)
> "VNC client unthrottle incremental state=%p ioc=%p offset=%zu"
>  vnc_client_output_limit(void *state, void *ioc, size_t offset, size_t
> threshold) "VNC client output limit state=%p ioc=%p offset=%zu
> threshold=%zu"
> +vnc_server_dpy_pageflip(void *dpy, int w, int h, int fmt) "VNC server dpy
> pageflip dpy=%p size=%dx%d fmt=%d"
> +vnc_server_dpy_recreate(void *dpy, int w, int h, int fmt) "VNC server dpy
> recreate dpy=%p size=%dx%d fmt=%d"
>  vnc_job_add_rect(void *state, void *job, int x, int y, int w, int h) "VNC
> add rect state=%p job=%p offset=%d,%d size=%dx%d"
>  vnc_job_discard_rect(void *state, void *job, int x, int y, int w, int h)
> "VNC job discard rect state=%p job=%p offset=%d,%d size=%dx%d"
>  vnc_job_clamp_rect(void *state, void *job, int x, int y, int w, int h)
> "VNC job clamp rect state=%p job=%p offset=%d,%d size=%dx%d"
> diff --git a/ui/vnc.c b/ui/vnc.c
> index 8c9890b3cd..9c004a11f4 100644
> --- a/ui/vnc.c
> +++ b/ui/vnc.c
> @@ -608,6 +608,11 @@ static int vnc_width(VncDisplay *vd)
>                                         VNC_DIRTY_PIXELS_PER_BIT));
>  }
>
> +static int vnc_true_width(VncDisplay *vd)
> +{
> +    return MIN(VNC_MAX_WIDTH, surface_width(vd->ds));
> +}
> +
>  static int vnc_height(VncDisplay *vd)
>  {
>      return MIN(VNC_MAX_HEIGHT, surface_height(vd->ds));
> @@ -691,16 +696,16 @@ static void vnc_desktop_resize(VncState *vs)
>                              !vnc_has_feature(vs,
> VNC_FEATURE_RESIZE_EXT))) {
>          return;
>      }
> -    if (vs->client_width == pixman_image_get_width(vs->vd->server) &&
> +    if (vs->client_width == vs->vd->true_width &&
>          vs->client_height == pixman_image_get_height(vs->vd->server)) {
>          return;
>      }
>
> -    assert(pixman_image_get_width(vs->vd->server) < 65536 &&
> -           pixman_image_get_width(vs->vd->server) >= 0);
> +    assert(vs->vd->true_width < 65536 &&
> +           vs->vd->true_width >= 0);
>      assert(pixman_image_get_height(vs->vd->server) < 65536 &&
>             pixman_image_get_height(vs->vd->server) >= 0);
> -    vs->client_width = pixman_image_get_width(vs->vd->server);
> +    vs->client_width = vs->vd->true_width;
>      vs->client_height = pixman_image_get_height(vs->vd->server);
>
>      if (vnc_has_feature(vs, VNC_FEATURE_RESIZE_EXT)) {
> @@ -774,6 +779,7 @@ static void vnc_update_server_surface(VncDisplay *vd)
>
>      width = vnc_width(vd);
>      height = vnc_height(vd);
> +    vd->true_width = vnc_true_width(vd);
>      vd->server = pixman_image_create_bits(VNC_SERVER_FB_FORMAT,
>                                            width, height,
>                                            NULL, 0);
> @@ -809,13 +815,22 @@ static void vnc_dpy_switch(DisplayChangeListener
> *dcl,
>      vd->guest.fb = pixman_image_ref(surface->image);
>      vd->guest.format = surface->format;
>
> +
>      if (pageflip) {
> +        trace_vnc_server_dpy_pageflip(vd,
> +                                      surface_width(surface),
> +                                      surface_height(surface),
> +                                      surface_format(surface));
>          vnc_set_area_dirty(vd->guest.dirty, vd, 0, 0,
>                             surface_width(surface),
>                             surface_height(surface));
>          return;
>      }
>
> +    trace_vnc_server_dpy_recreate(vd,
> +                                  surface_width(surface),
> +                                  surface_height(surface),
> +                                  surface_format(surface));
>      /* server surface */
>      vnc_update_server_surface(vd);
>
> diff --git a/ui/vnc.h b/ui/vnc.h
> index 116463d5f0..d4f3e15558 100644
> --- a/ui/vnc.h
> +++ b/ui/vnc.h
> @@ -164,6 +164,7 @@ struct VncDisplay
>
>      struct VncSurface guest;   /* guest visible surface (aka ds->surface)
> */
>      pixman_image_t *server;    /* vnc server surface */
> +    int true_width; /* server surface width before rounding up */
>
>      const char *id;
>      QTAILQ_ENTRY(VncDisplay) next;
> --
> 2.29.2
>
>
>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
diff mbox series

Patch

diff --git a/ui/trace-events b/ui/trace-events
index 3838ae2d84..5d1da6f236 100644
--- a/ui/trace-events
+++ b/ui/trace-events
@@ -59,6 +59,8 @@  vnc_client_throttle_audio(void *state, void *ioc, size_t offset) "VNC client thr
 vnc_client_unthrottle_forced(void *state, void *ioc) "VNC client unthrottle forced offset state=%p ioc=%p"
 vnc_client_unthrottle_incremental(void *state, void *ioc, size_t offset) "VNC client unthrottle incremental state=%p ioc=%p offset=%zu"
 vnc_client_output_limit(void *state, void *ioc, size_t offset, size_t threshold) "VNC client output limit state=%p ioc=%p offset=%zu threshold=%zu"
+vnc_server_dpy_pageflip(void *dpy, int w, int h, int fmt) "VNC server dpy pageflip dpy=%p size=%dx%d fmt=%d"
+vnc_server_dpy_recreate(void *dpy, int w, int h, int fmt) "VNC server dpy recreate dpy=%p size=%dx%d fmt=%d"
 vnc_job_add_rect(void *state, void *job, int x, int y, int w, int h) "VNC add rect state=%p job=%p offset=%d,%d size=%dx%d"
 vnc_job_discard_rect(void *state, void *job, int x, int y, int w, int h) "VNC job discard rect state=%p job=%p offset=%d,%d size=%dx%d"
 vnc_job_clamp_rect(void *state, void *job, int x, int y, int w, int h) "VNC job clamp rect state=%p job=%p offset=%d,%d size=%dx%d"
diff --git a/ui/vnc.c b/ui/vnc.c
index 8c9890b3cd..9c004a11f4 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -608,6 +608,11 @@  static int vnc_width(VncDisplay *vd)
                                        VNC_DIRTY_PIXELS_PER_BIT));
 }
 
+static int vnc_true_width(VncDisplay *vd)
+{
+    return MIN(VNC_MAX_WIDTH, surface_width(vd->ds));
+}
+
 static int vnc_height(VncDisplay *vd)
 {
     return MIN(VNC_MAX_HEIGHT, surface_height(vd->ds));
@@ -691,16 +696,16 @@  static void vnc_desktop_resize(VncState *vs)
                             !vnc_has_feature(vs, VNC_FEATURE_RESIZE_EXT))) {
         return;
     }
-    if (vs->client_width == pixman_image_get_width(vs->vd->server) &&
+    if (vs->client_width == vs->vd->true_width &&
         vs->client_height == pixman_image_get_height(vs->vd->server)) {
         return;
     }
 
-    assert(pixman_image_get_width(vs->vd->server) < 65536 &&
-           pixman_image_get_width(vs->vd->server) >= 0);
+    assert(vs->vd->true_width < 65536 &&
+           vs->vd->true_width >= 0);
     assert(pixman_image_get_height(vs->vd->server) < 65536 &&
            pixman_image_get_height(vs->vd->server) >= 0);
-    vs->client_width = pixman_image_get_width(vs->vd->server);
+    vs->client_width = vs->vd->true_width;
     vs->client_height = pixman_image_get_height(vs->vd->server);
 
     if (vnc_has_feature(vs, VNC_FEATURE_RESIZE_EXT)) {
@@ -774,6 +779,7 @@  static void vnc_update_server_surface(VncDisplay *vd)
 
     width = vnc_width(vd);
     height = vnc_height(vd);
+    vd->true_width = vnc_true_width(vd);
     vd->server = pixman_image_create_bits(VNC_SERVER_FB_FORMAT,
                                           width, height,
                                           NULL, 0);
@@ -809,13 +815,22 @@  static void vnc_dpy_switch(DisplayChangeListener *dcl,
     vd->guest.fb = pixman_image_ref(surface->image);
     vd->guest.format = surface->format;
 
+
     if (pageflip) {
+        trace_vnc_server_dpy_pageflip(vd,
+                                      surface_width(surface),
+                                      surface_height(surface),
+                                      surface_format(surface));
         vnc_set_area_dirty(vd->guest.dirty, vd, 0, 0,
                            surface_width(surface),
                            surface_height(surface));
         return;
     }
 
+    trace_vnc_server_dpy_recreate(vd,
+                                  surface_width(surface),
+                                  surface_height(surface),
+                                  surface_format(surface));
     /* server surface */
     vnc_update_server_surface(vd);
 
diff --git a/ui/vnc.h b/ui/vnc.h
index 116463d5f0..d4f3e15558 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -164,6 +164,7 @@  struct VncDisplay
 
     struct VncSurface guest;   /* guest visible surface (aka ds->surface) */
     pixman_image_t *server;    /* vnc server surface */
+    int true_width; /* server surface width before rounding up */
 
     const char *id;
     QTAILQ_ENTRY(VncDisplay) next;