diff --git a/src/VNCFlinger.cpp b/src/VNCFlinger.cpp
index a55b8684311549eb18e8db0e3de8c71b88eb719c..0ca27841df20cbdc474029207bf8ceb78de68618 100644
--- a/src/VNCFlinger.cpp
+++ b/src/VNCFlinger.cpp
@@ -57,10 +57,6 @@ status_t VNCFlinger::setup_l() {
 
     sQueue->addListener(this);
 
-    mVirtualDisplay = new VirtualDisplay();
-
-    mVNCBuf = new uint8_t[mWidth * mHeight * 4];
-
     rfbLog = VNCFlinger::rfbLogger;
     rfbErr = VNCFlinger::rfbLogger;
 
@@ -71,8 +67,9 @@ status_t VNCFlinger::setup_l() {
         return NO_INIT;
     }
 
+    mVNCBuf = new uint8_t[mWidth * mHeight * 4];
+    mVNCScreen->frameBuffer = (char *) mVNCBuf;
     mVNCScreen->desktopName = "VNCFlinger";
-    mVNCScreen->frameBuffer = (char *)mVNCBuf;
     mVNCScreen->alwaysShared = TRUE;
     mVNCScreen->httpDir = NULL;
     mVNCScreen->port = VNC_PORT;
@@ -80,13 +77,16 @@ status_t VNCFlinger::setup_l() {
     mVNCScreen->serverFormat.trueColour = true;
     mVNCScreen->serverFormat.bitsPerPixel = 32;
     mVNCScreen->handleEventsEagerly = true;
-    mVNCScreen->deferUpdateTime = 5;
+    mVNCScreen->deferUpdateTime = 16;
 
     rfbInitServer(mVNCScreen);
 
     /* Mark as dirty since we haven't sent any updates at all yet. */
     rfbMarkRectAsModified(mVNCScreen, 0, 0, mWidth, mHeight);
 
+
+    mVirtualDisplay = new VirtualDisplay(mVNCScreen);
+
     return err;
 }
 
@@ -129,11 +129,21 @@ void VNCFlinger::onEvent(const Event& event) {
             ALOGI("Client disconnected (%zu)", mClientCount);
             break;
 
+        /*
         case EVENT_BUFFER_READY:
+            int64_t startWhenNsec, endWhenNsec;
+            startWhenNsec = systemTime(CLOCK_MONOTONIC);
+            if (event.mData == NULL) {
+                break;
+            }
+            //memcpy(mVNCBuf, (uint8_t *) event.mData, mWidth * mHeight * 4);
             //mVNCScreen->frameBuffer = (char *) event.mData;
-            memcpy(mVNCBuf, (uint8_t *) event.mData, mWidth * mHeight * 4);
             rfbMarkRectAsModified(mVNCScreen, 0, 0, mWidth, mHeight);
+            endWhenNsec = systemTime(CLOCK_MONOTONIC);
+            ALOGV("got pixels (mark=%.3fms)",
+                    (endWhenNsec - startWhenNsec) / 1000000.0);
             break;
+        */
 
         default:
             ALOGE("Unhandled event: %d", event.mId);
diff --git a/src/VirtualDisplay.cpp b/src/VirtualDisplay.cpp
index 369d2719e6d97beb504dbd2d32c1de79396d4156..7a95042d872fb52ff523cb2fd5330122b8b6b339 100644
--- a/src/VirtualDisplay.cpp
+++ b/src/VirtualDisplay.cpp
@@ -23,9 +23,9 @@
 
 #include <gui/SurfaceComposerClient.h>
 
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
 #include <GLES3/gl3.h>
+#include <GLES3/gl3ext.h>
+#include <GLES2/gl2ext.h>
 
 #include <ui/Rect.h>
 
@@ -186,8 +186,9 @@ bool VirtualDisplay::threadLoop() {
         mEventCond.wait(mMutex);
         ALOGD("Awake, frame available");
         void* ptr = processFrame_l();
-        const Event ev(EVENT_BUFFER_READY, ptr);
-        mQueue->enqueue(ev);
+
+        //const Event ev(EVENT_BUFFER_READY, ptr);
+        //mQueue->enqueue(ev);
     }
 
     ALOGV("VDS thread stopping");
@@ -223,7 +224,7 @@ status_t VirtualDisplay::setup_l() {
     }
 
     mBufSize = mWidth * mHeight * kGlBytesPerPixel;
-    
+
     // pixel buffer for image copy
     mPBO = new GLuint[NUM_PBO];
     glGenBuffers(NUM_PBO, mPBO);
@@ -256,6 +257,9 @@ void* VirtualDisplay::processFrame_l() {
     mGlConsumer->updateTexImage();
     mGlConsumer->getTransformMatrix(texMatrix);
 
+    int64_t startWhen, blitWhen, readWhen, mapWhen, memcpyWhen, markWhen;
+    startWhen = systemTime(CLOCK_MONOTONIC);
+
     // The data is in an external texture, so we need to render it to the
     // pbuffer to get access to RGB pixel data.  We also want to flip it
     // upside-down for easy conversion to a bitmap.
@@ -263,6 +267,8 @@ void* VirtualDisplay::processFrame_l() {
     int height = mEglWindow.getHeight();
     mExtTexProgram.blit(mExtTextureName, texMatrix, 0, 0, mWidth, mHeight, true);
 
+    blitWhen = systemTime(CLOCK_MONOTONIC);
+
     GLenum glErr;
     glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO[mIndex]);
     glReadPixels(0, 0, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, 0);
@@ -271,13 +277,28 @@ void* VirtualDisplay::processFrame_l() {
         return NULL;
     }
 
-    glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO[mIndex]);
+    readWhen = systemTime(CLOCK_MONOTONIC);
+
     void* ptr = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, mBufSize, GL_MAP_READ_BIT);
+    mapWhen = systemTime(CLOCK_MONOTONIC);
+    //memcpy(mVNCScreen->frameBuffer, ptr, mBufSize);
+    mVNCScreen->frameBuffer = (char *)ptr;
+    memcpyWhen = systemTime(CLOCK_MONOTONIC);
+    rfbMarkRectAsModified(mVNCScreen, 0, 0, mWidth, mHeight);
+    markWhen = systemTime(CLOCK_MONOTONIC);
+
     glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
     glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
 
-    mIndex = (mIndex + 1) % NUM_PBO; 
-    return ptr;
+    ALOGV("processFrame: blit=%.3fms read=%.3fms map=%.3fms memcpy=%.3fms mark=%.3fms",
+            (blitWhen - startWhen) / 1000000.0,
+            (readWhen - blitWhen) / 1000000.0,
+            (mapWhen - readWhen) / 1000000.0,
+            (memcpyWhen - mapWhen) / 1000000.0,
+            (markWhen - memcpyWhen) / 1000000.0);
+
+    mIndex = (mIndex + 1) % NUM_PBO;
+    return mVNCScreen->frameBuffer;
 }
 
 void VirtualDisplay::release_l() {
diff --git a/src/VirtualDisplay.h b/src/VirtualDisplay.h
index ccfb718877aa9e81fac0f5ca83086c1cdf3a9385..c215395cc7af06242149393b4f6677506e268e87 100644
--- a/src/VirtualDisplay.h
+++ b/src/VirtualDisplay.h
@@ -27,6 +27,7 @@
 #include <ui/DisplayInfo.h>
 #include <utils/Thread.h>
 
+#include <rfb/rfb.h>
 
 #define NUM_PBO 2
 
@@ -37,7 +38,8 @@ namespace android {
  */
 class VirtualDisplay : public GLConsumer::FrameAvailableListener, Thread {
 public:
-    VirtualDisplay() : Thread(false),
+    VirtualDisplay(rfbScreenInfoPtr vncScreen) : Thread(false),
+        mVNCScreen(vncScreen),
         mThreadResult(UNKNOWN_ERROR),
         mState(UNINITIALIZED),
         mIndex(0)
@@ -46,9 +48,9 @@ public:
     // Create an "input surface", similar in purpose to a MediaCodec input
     // surface, that the virtual display can send buffers to.  Also configures
     // EGL with a pbuffer surface on the current thread.
-    status_t start(const DisplayInfo& mainDpyInfo, EventQueue *queue);
+    virtual status_t start(const DisplayInfo& mainDpyInfo, EventQueue *queue);
 
-    status_t stop();
+    virtual status_t stop();
 
     static bool isDeviceRotated(int orientation);
 
@@ -79,6 +81,8 @@ private:
     // Process a frame received from the virtual display.
     void* processFrame_l();
 
+    rfbScreenInfoPtr mVNCScreen;
+
     uint32_t mHeight, mWidth;
     bool mRotate;
 
@@ -119,6 +123,7 @@ private:
 
     // Pixel data buffers.
     size_t mBufSize;
+    uint8_t* mPixelBuf;
     GLuint* mPBO;
     unsigned int mIndex;