aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPierre Ossman <ossman@cendio.se>2017-01-02 19:49:52 +0100
committerPierre Ossman <ossman@cendio.se>2017-02-10 17:05:20 +0100
commit3d74d88806d09f3a899be5d8f1df8ec20ed9a14d (patch)
treea2a86607aa8dda47559c5be82fadad9247844193
parent403ac27d2377df4fed8bf5039125f6d89aaa6955 (diff)
downloadtigervnc-3d74d88806d09f3a899be5d8f1df8ec20ed9a14d.tar.gz
tigervnc-3d74d88806d09f3a899be5d8f1df8ec20ed9a14d.zip
Render on a temporary surface when needed
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.
-rw-r--r--tests/fbperf.cxx39
-rw-r--r--vncviewer/DesktopWindow.cxx54
-rw-r--r--vncviewer/DesktopWindow.h2
-rw-r--r--vncviewer/Surface.h1
-rw-r--r--vncviewer/Surface_OSX.cxx67
-rw-r--r--vncviewer/Surface_Win32.cxx19
-rw-r--r--vncviewer/Surface_X11.cxx6
-rw-r--r--vncviewer/Viewport.cxx13
-rw-r--r--vncviewer/Viewport.h3
9 files changed, 167 insertions, 37 deletions
diff --git a/tests/fbperf.cxx b/tests/fbperf.cxx
index 8c71d966..7f1e2c1d 100644
--- a/tests/fbperf.cxx
+++ b/tests/fbperf.cxx
@@ -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,
diff --git a/vncviewer/DesktopWindow.cxx b/vncviewer/DesktopWindow.cxx
index 707628ee..3da25054 100644
--- a/vncviewer/DesktopWindow.cxx
+++ b/vncviewer/DesktopWindow.cxx
@@ -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) {
diff --git a/vncviewer/DesktopWindow.h b/vncviewer/DesktopWindow.h
index d0e2eae4..073be661 100644
--- a/vncviewer/DesktopWindow.h
+++ b/vncviewer/DesktopWindow.h
@@ -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;
diff --git a/vncviewer/Surface.h b/vncviewer/Surface.h
index 02917e31..9b1788a5 100644
--- a/vncviewer/Surface.h
+++ b/vncviewer/Surface.h
@@ -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();
diff --git a/vncviewer/Surface_OSX.cxx b/vncviewer/Surface_OSX.cxx
index c51e47f5..dbf0c418 100644
--- a/vncviewer/Surface_OSX.cxx
+++ b/vncviewer/Surface_OSX.cxx
@@ -28,6 +28,32 @@
#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()
diff --git a/vncviewer/Surface_Win32.cxx b/vncviewer/Surface_Win32.cxx
index 5a9a6546..5eea2d1d 100644
--- a/vncviewer/Surface_Win32.cxx
+++ b/vncviewer/Surface_Win32.cxx
@@ -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;
diff --git a/vncviewer/Surface_X11.cxx b/vncviewer/Surface_X11.cxx
index c7e3778e..e7616143 100644
--- a/vncviewer/Surface_X11.cxx
+++ b/vncviewer/Surface_X11.cxx
@@ -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;
diff --git a/vncviewer/Viewport.cxx b/vncviewer/Viewport.cxx
index 7b3487c4..b5c516fa 100644
--- a/vncviewer/Viewport.cxx
+++ b/vncviewer/Viewport.cxx
@@ -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;
diff --git a/vncviewer/Viewport.h b/vncviewer/Viewport.h
index ac1ec338..0967fcbb 100644
--- a/vncviewer/Viewport.h
+++ b/vncviewer/Viewport.h
@@ -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();