]> source.dussan.org Git - tigervnc.git/commitdiff
Render on a temporary surface when needed
authorPierre Ossman <ossman@cendio.se>
Mon, 2 Jan 2017 18:49:52 +0000 (19:49 +0100)
committerPierre Ossman <ossman@cendio.se>
Fri, 10 Feb 2017 16:05:20 +0000 (17:05 +0100)
Some platforms draw directly to the screen, which means that updates
will flicker if we draw multiple layers. Prevent this by first
composing the update on a hidden surface.

tests/fbperf.cxx
vncviewer/DesktopWindow.cxx
vncviewer/DesktopWindow.h
vncviewer/Surface.h
vncviewer/Surface_OSX.cxx
vncviewer/Surface_Win32.cxx
vncviewer/Surface_X11.cxx
vncviewer/Viewport.cxx
vncviewer/Viewport.h

index 8c71d966942d9361a24cf0cbd3b14928aa7ae1dc..7f1e2c1d1abc0381f8353c349fd1fe95bc353901 100644 (file)
@@ -72,6 +72,7 @@ public:
 
 protected:
   Surface* overlay;
+  Surface* offscreen;
 };
 
 TestWindow::TestWindow() :
@@ -202,7 +203,7 @@ void PartialTestWindow::changefb()
 }
 
 OverlayTestWindow::OverlayTestWindow() :
-  overlay(NULL)
+  overlay(NULL), offscreen(NULL)
 {
 }
 
@@ -212,12 +213,21 @@ void OverlayTestWindow::start(int width, int height)
 
   overlay = new Surface(400, 200);
   overlay->clear(0xff, 0x80, 0x00, 0xcc);
+
+  // X11 needs an off screen buffer for compositing to avoid flicker
+#if !defined(WIN32) && !defined(__APPLE__)
+  offscreen = new Surface(w(), h());
+#else
+  offscreen = NULL;
+#endif
 }
 
 void OverlayTestWindow::stop()
 {
   PartialTestWindow::stop();
 
+  delete offscreen;
+  offscreen = NULL;
   delete overlay;
   overlay = NULL;
 }
@@ -227,13 +237,15 @@ void OverlayTestWindow::draw()
   int ox, oy, ow, oh;
   int X, Y, W, H;
 
+  // We cannot update the damage region from inside the draw function,
+  // so delegate this to an idle function
+  Fl::add_idle(timer, this);
+
   // Check what actually needs updating
   fl_clip_box(0, 0, w(), h(), X, Y, W, H);
   if ((W == 0) || (H == 0))
     return;
 
-  PartialTestWindow::draw();
-
   // We might get a redraw before we are fully ready
   if (!overlay)
     return;
@@ -243,16 +255,33 @@ void OverlayTestWindow::draw()
   fl_push_no_clip();
   fl_push_clip(X, Y, W, H);
 
+  if (offscreen)
+    fb->draw(offscreen, X, Y, X, Y, W, H);
+  else
+    fb->draw(X, Y, X, Y, W, H);
+
+  pixels += W*H;
+  frames++;
+
   ox = (w() - overlay->width()) / 2;
   oy = h() / 4 - overlay->height() / 2;
   ow = overlay->width();
   oh = overlay->height();
   fl_clip_box(ox, oy, ow, oh, X, Y, W, H);
-  if ((W != 0) && (H != 0))
-    overlay->draw(X - ox, Y - oy, X, Y, W, H);
+  if ((W != 0) && (H != 0)) {
+    if (offscreen)
+      overlay->draw(offscreen, X - ox, Y - oy, X, Y, W, H);
+    else
+      overlay->draw(X - ox, Y - oy, X, Y, W, H);
+  }
 
   fl_pop_clip();
   fl_pop_clip();
+
+  if (offscreen) {
+    fl_clip_box(0, 0, w(), h(), X, Y, W, H);
+    offscreen->draw(X, Y, X, Y, W, H);
+  }
 }
 
 static void dosubtest(TestWindow* win, int width, int height,
index 707628ee03fd1baac371220a7590d4e679d2ec11..3da250547b3e5636244fa801f06c3d3d114f5530 100644 (file)
@@ -34,6 +34,7 @@
 #include "parameters.h"
 #include "vncviewer.h"
 #include "CConn.h"
+#include "Surface.h"
 #include "Viewport.h"
 
 #include <FL/Fl.H>
@@ -59,7 +60,7 @@ static rfb::LogWriter vlog("DesktopWindow");
 DesktopWindow::DesktopWindow(int w, int h, const char *name,
                              const rfb::PixelFormat& serverPF,
                              CConn* cc_)
-  : Fl_Window(w, h), cc(cc_), firstUpdate(true),
+  : Fl_Window(w, h), cc(cc_), offscreen(NULL), firstUpdate(true),
     delayedFullscreen(false), delayedDesktopSize(false)
 {
   Fl_Group* group;
@@ -183,6 +184,8 @@ DesktopWindow::~DesktopWindow()
 
   OptionsDialog::removeCallback(handleOptions);
 
+  delete offscreen;
+
   // FLTK automatically deletes all child widgets, so we shouldn't touch
   // them ourselves here
 }
@@ -263,7 +266,19 @@ void DesktopWindow::draw()
 {
   bool redraw;
 
-  int W, H;
+  int X, Y, W, H;
+
+  // X11 needs an off screen buffer for compositing to avoid flicker
+#if !defined(WIN32) && !defined(__APPLE__)
+
+  // Adjust offscreen surface dimensions
+  if ((offscreen == NULL) ||
+      (offscreen->width() != w()) || (offscreen->height() != h())) {
+    delete offscreen;
+    offscreen = new Surface(w(), h());
+  }
+
+#endif
 
   // Active area inside scrollbars
   W = w() - (vscroll->visible() ? vscroll->w() : 0);
@@ -274,30 +289,33 @@ void DesktopWindow::draw()
 
   // Redraw background only on full redraws
   if (redraw) {
-    if (viewport->h() < h()) {
-      fl_rectf(0, 0, W, viewport->y(), 40, 40, 40);
-      fl_rectf(0, viewport->y() + viewport->h(), W,
-               h() - (viewport->y() + viewport->h()),
-               40, 40, 40);
-    }
-    if (viewport->w() < w()) {
-      fl_rectf(0, 0, viewport->x(), H, 40, 40, 40);
-      fl_rectf(viewport->x() + viewport->w(), 0,
-               w() - (viewport->x() + viewport->w()),
-               H, 40, 40, 40);
-    }
+    if (offscreen)
+      offscreen->clear(40, 40, 40);
+    else
+      fl_rectf(0, 0, W, H, 40, 40, 40);
   }
 
   // Make sure the viewport isn't trampling on the scrollbars
   fl_push_clip(0, 0, W, H);
 
-  if (redraw)
-    draw_child(*viewport);
-  else
-    update_child(*viewport);
+  if (offscreen) {
+    viewport->draw(offscreen);
+    viewport->clear_damage();
+  } else {
+    if (redraw)
+      draw_child(*viewport);
+    else
+      update_child(*viewport);
+  }
 
   fl_pop_clip();
 
+  // Flush offscreen surface to screen
+  if (offscreen) {
+    fl_clip_box(0, 0, W, H, X, Y, W, H);
+    offscreen->draw(X, Y, X, Y, W, H);
+  }
+
   // Finally the scrollbars
 
   if (redraw) {
index d0e2eae4ca5e4bae4fa9aa64916a17e7017e40e7..073be66128ece5d7c100f94b09b24d41769c0351 100644 (file)
@@ -30,6 +30,7 @@
 namespace rfb { class ModifiablePixelBuffer; }
 
 class CConn;
+class Surface;
 class Viewport;
 
 class Fl_Scrollbar;
@@ -95,6 +96,7 @@ private:
   CConn* cc;
   Fl_Scrollbar *hscroll, *vscroll;
   Viewport *viewport;
+  Surface *offscreen;
 
   bool firstUpdate;
   bool delayedFullscreen;
index 02917e3127c6b2cc9e16734cef8a5df680b5092f..9b1788a5176e33d581ec52f738f83af3fd1a9f94 100644 (file)
@@ -42,6 +42,7 @@ public:
   void clear(unsigned char r, unsigned char g, unsigned char b, unsigned char a=255);
 
   void draw(int src_x, int src_y, int x, int y, int w, int h);
+  void draw(Surface* dst, int src_x, int src_y, int x, int y, int w, int h);
 
 protected:
   void alloc();
index c51e47f5993cbd272e600268ed3423811e689771..dbf0c41819dd505f7be9a2e3971f9c53f29d6812 100644 (file)
 
 #include "Surface.h"
 
+static void render(CGContextRef gc, CGImageRef image,
+                   int src_x, int src_y, int src_w, int src_h,
+                   int x, int y, int w, int h)
+{
+  CGRect rect;
+
+  CGContextSaveGState(gc);
+
+  // We have to use clipping to partially display an image
+  rect.origin.x = x;
+  rect.origin.y = y;
+  rect.size.width = w;
+  rect.size.height = h;
+
+  CGContextClipToRect(gc, rect);
+
+  rect.origin.x = x - src_x;
+  rect.origin.y = y - src_y;
+  rect.size.width = src_w;
+  rect.size.height = src_h;
+
+  CGContextDrawImage(gc, rect, image);
+
+  CGContextRestoreGState(gc);
+}
+
 void Surface::clear(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
 {
   unsigned char* out;
@@ -50,8 +76,6 @@ void Surface::clear(unsigned char r, unsigned char g, unsigned char b, unsigned
 
 void Surface::draw(int src_x, int src_y, int x, int y, int w, int h)
 {
-  CGRect rect;
-
   CGContextSaveGState(fl_gc);
 
   // Reset the transformation matrix back to the default identity
@@ -62,22 +86,37 @@ void Surface::draw(int src_x, int src_y, int x, int y, int w, int h)
   src_y = height() - (src_y + h);
   y = Fl_Window::current()->h() - (y + h);
 
-  // We have to use clipping to partially display an image
-  rect.origin.x = x;
-  rect.origin.y = y;
-  rect.size.width = w;
-  rect.size.height = h;
+  render(fl_gc, image, src_x, src_y, width(), height(), x, y, w, h);
 
-  CGContextClipToRect(fl_gc, rect);
+  CGContextRestoreGState(fl_gc);
+}
 
-  rect.origin.x = x - src_x;
-  rect.origin.y = y - src_y;
-  rect.size.width = width();
-  rect.size.height = height();
+void Surface::draw(Surface* dst, int src_x, int src_y, int x, int y, int w, int h)
+{
+  CGColorSpaceRef lut;
+  CGContextRef bitmap;
 
-  CGContextDrawImage(fl_gc, rect, image);
+  lut = CGDisplayCopyColorSpace(kCGDirectMainDisplay);
+  if (!lut) {
+    lut = CGColorSpaceCreateDeviceRGB();
+    if (!lut)
+      throw rdr::Exception("CGColorSpaceCreateDeviceRGB");
+  }
 
-  CGContextRestoreGState(fl_gc);
+  bitmap = CGBitmapContextCreate(dst->data, dst->width(),
+                                 dst->height(), 8, dst->width()*4, lut,
+                                 kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little);
+  CGColorSpaceRelease(lut);
+  if (!bitmap)
+    throw rdr::Exception("CGBitmapContextCreate");
+
+  // macOS Coordinates are from bottom left, not top left
+  src_y = height() - (src_y + h);
+  y = dst->height() - (y + h);
+
+  render(bitmap, image, src_x, src_y, width(), height(), x, y, w, h);
+
+  CGContextRelease(bitmap);
 }
 
 void Surface::alloc()
index 5a9a654632c9f67a614e692765e619cf487e5ba8..5eea2d1d8d3b9f727873914e9e71726771716e22 100644 (file)
@@ -70,6 +70,25 @@ void Surface::draw(int src_x, int src_y, int x, int y, int w, int h)
   DeleteDC(dc);
 }
 
+void Surface::draw(Surface* dst, int src_x, int src_y, int x, int y, int w, int h)
+{
+  HDC origdc, dstdc;
+
+  dstdc = CreateCompatibleDC(NULL);
+  if (!dstdc)
+    throw rdr::SystemException("CreateCompatibleDC", GetLastError());
+
+  if (!SelectObject(dstdc, dst->bitmap))
+    throw rdr::SystemException("SelectObject", GetLastError());
+
+  origdc = fl_gc;
+  fl_gc = dstdc;
+  draw(src_x, src_y, x, y, w, h);
+  fl_gc = origdc;
+
+  DeleteDC(dstdc);
+}
+
 void Surface::alloc()
 {
   BITMAPINFOHEADER bih;
index c7e3778e22f2828b0dd090b4a24be057059b4985..e761614351506a421c50c3a0be8a3505acdc24e8 100644 (file)
@@ -48,6 +48,12 @@ void Surface::draw(int src_x, int src_y, int x, int y, int w, int h)
   XRenderFreePicture(fl_display, winPict);
 }
 
+void Surface::draw(Surface* dst, int src_x, int src_y, int x, int y, int w, int h)
+{
+  XRenderComposite(fl_display, PictOpSrc, picture, None, dst->picture,
+                   src_x, src_y, 0, 0, x, y, w, h);
+}
+
 void Surface::alloc()
 {
   XRenderPictFormat* format;
index 7b3487c4686169b84a61d7965fc1e4442dca6767..b5c516fa30a86388bce31c8eafc8fc861ef9177c 100644 (file)
@@ -244,6 +244,19 @@ void Viewport::setCursor(int width, int height, const Point& hotspot,
 }
 
 
+void Viewport::draw(Surface* dst)
+{
+  int X, Y, W, H;
+
+  // Check what actually needs updating
+  fl_clip_box(x(), y(), w(), h(), X, Y, W, H);
+  if ((W == 0) || (H == 0))
+    return;
+
+  frameBuffer->draw(dst, X - x(), Y - y(), X, Y, W, H);
+}
+
+
 void Viewport::draw()
 {
   int X, Y, W, H;
index ac1ec338e85383fdc55f9b1d6032795f218ff756..0967fcbbd3289173c840feffb3ae4bc41c065c6a 100644 (file)
@@ -29,6 +29,7 @@ class Fl_RGB_Image;
 
 class CConn;
 class PlatformPixelBuffer;
+class Surface;
 
 class Viewport : public Fl_Widget {
 public:
@@ -46,6 +47,8 @@ public:
   void setCursor(int width, int height, const rfb::Point& hotspot,
                  void* data, void* mask);
 
+  void draw(Surface* dst);
+
   // Fl_Widget callback methods
 
   void draw();