one more step towards multiple display support

- remove dependency on cached state in validateVisibility
- get rid of mVertices and mTransformedBounds
- get rid of validateVisibility
- get rid of unlockPageFlip
- handleTransaction now returns a dirty region
- computevisibileregion now uses window-manager space
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 0d1cb45..a09b5c7 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -145,14 +145,6 @@
     mSurfaceTexture->setName(name);
 }
 
-void Layer::validateVisibility(const Transform& globalTransform, const DisplayHardware& hw) {
-    LayerBase::validateVisibility(globalTransform, hw);
-
-    // This optimization allows the SurfaceTexture to bake in
-    // the rotation so hardware overlays can be used
-    mSurfaceTexture->setTransformHint(getTransformHint());
-}
-
 sp<ISurface> Layer::createSurface()
 {
     class BSurface : public BnSurface, public LayerCleaner {
@@ -225,7 +217,8 @@
     } else  if (mActiveBuffer != NULL){
         crop = Rect(mActiveBuffer->getWidth(), mActiveBuffer->getHeight());
     } else {
-        crop = Rect(mTransformedBounds.width(), mTransformedBounds.height());
+        crop.makeInvalid();
+        return crop;
     }
 
     // ... then reduce that in the same proportions as the window crop reduces
@@ -258,9 +251,11 @@
     return crop;
 }
 
-void Layer::setGeometry(HWComposer::HWCLayerInterface& layer)
+void Layer::setGeometry(
+        const DisplayHardware& hw,
+        HWComposer::HWCLayerInterface& layer)
 {
-    LayerBaseClient::setGeometry(layer);
+    LayerBaseClient::setGeometry(hw, layer);
 
     // enable this layer
     layer.setSkip(false);
@@ -276,12 +271,11 @@
      * 1) buffer orientation/flip/mirror
      * 2) state transformation (window manager)
      * 3) layer orientation (screen orientation)
-     * mTransform is already the composition of (2) and (3)
      * (NOTE: the matrices are multiplied in reverse order)
      */
 
     const Transform bufferOrientation(mCurrentTransform);
-    const Transform tr(mTransform * bufferOrientation);
+    const Transform tr(hw.getTransform() * s.transform * bufferOrientation);
 
     // this gives us only the "orientation" component of the transform
     const uint32_t finalTransform = tr.getOrientation();
@@ -339,7 +333,7 @@
             const sp<LayerBase>& layer(drawingLayers[i]);
             if (layer.get() == static_cast<LayerBase const*>(this))
                 break;
-            under.orSelf(layer->visibleRegionScreen);
+            under.orSelf( hw.getTransform().transform(layer->visibleRegion) );
         }
         // if not everything below us is covered, we plug the holes!
         Region holes(clip.subtract(under));
@@ -527,10 +521,11 @@
     return mQueuedFrames > 0;
 }
 
-void Layer::lockPageFlip(bool& recomputeVisibleRegions)
+Region Layer::latchBuffer(bool& recomputeVisibleRegions)
 {
     ATRACE_CALL();
 
+    Region outDirtyRegion;
     if (mQueuedFrames > 0) {
 
         // if we've already called updateTexImage() without going through
@@ -539,8 +534,7 @@
         // compositionComplete() call.
         // we'll trigger an update in onPreComposition().
         if (mRefreshPending) {
-            mPostedDirtyRegion.clear();
-            return;
+            return outDirtyRegion;
         }
 
         // Capture the old state of the layer for comparisons later
@@ -637,17 +631,21 @@
 
         Reject r(mDrawingState, currentState(), recomputeVisibleRegions);
 
+        // XXX: not sure if setTransformHint belongs here
+        // it should only be needed when the main screen orientation changes
+        mSurfaceTexture->setTransformHint(getTransformHint());
+
         if (mSurfaceTexture->updateTexImage(&r) < NO_ERROR) {
             // something happened!
             recomputeVisibleRegions = true;
-            return;
+            return outDirtyRegion;
         }
 
         // update the active buffer
         mActiveBuffer = mSurfaceTexture->getCurrentBuffer();
         if (mActiveBuffer == NULL) {
             // this can only happen if the very first buffer was rejected.
-            return;
+            return outDirtyRegion;
         }
 
         mRefreshPending = true;
@@ -686,38 +684,17 @@
             recomputeVisibleRegions = true;
         }
 
-        // FIXME: mPostedDirtyRegion = dirty & bounds
-        const Layer::State& front(drawingState());
-        mPostedDirtyRegion.set(front.active.w, front.active.h);
-
         glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
         glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+        // FIXME: postedRegion should be dirty & bounds
+        const Layer::State& front(drawingState());
+        Region dirtyRegion(Rect(front.active.w, front.active.h));
+
+        // transform the dirty region to window-manager space
+        outDirtyRegion = (front.transform.transform(dirtyRegion));
     }
-}
-
-void Layer::unlockPageFlip(
-        const Transform& planeTransform, Region& outDirtyRegion)
-{
-    ATRACE_CALL();
-
-    Region postedRegion(mPostedDirtyRegion);
-    if (!postedRegion.isEmpty()) {
-        mPostedDirtyRegion.clear();
-        if (!visibleRegionScreen.isEmpty()) {
-            // The dirty region is given in the layer's coordinate space
-            // transform the dirty region by the surface's transformation
-            // and the global transformation.
-            const Layer::State& s(drawingState());
-            const Transform tr(planeTransform * s.transform);
-            postedRegion = tr.transform(postedRegion);
-
-            // At this point, the dirty region is in screen space.
-            // Make sure it's constrained by the visible region (which
-            // is in screen space as well).
-            postedRegion.andSelf(visibleRegionScreen);
-            outDirtyRegion.orSelf(postedRegion);
-        }
-    }
+    return outDirtyRegion;
 }
 
 void Layer::dump(String8& result, char* buffer, size_t SIZE) const
@@ -786,7 +763,14 @@
 uint32_t Layer::getTransformHint() const {
     uint32_t orientation = 0;
     if (!mFlinger->mDebugDisableTransformHint) {
-        orientation = getPlaneOrientation();
+        // The transform hint is used to improve performance on the main
+        // display -- we can only have a single transform hint, it cannot
+        // apply to all displays.
+        // This is why we use the default display here. This is not an
+        // oversight.
+        const DisplayHardware& hw(mFlinger->getDefaultDisplayHardware());
+        const Transform& planeTransform(hw.getTransform());
+        orientation = planeTransform.getOrientation();
         if (orientation & Transform::ROT_INVALID) {
             orientation = 0;
         }
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index b6ae0ae..239250d 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -64,20 +64,20 @@
     bool isFixedSize() const;
 
     // LayerBase interface
-    virtual void setGeometry(HWComposer::HWCLayerInterface& layer);
+    virtual void setGeometry(const DisplayHardware& hw,
+            HWComposer::HWCLayerInterface& layer);
     virtual void setPerFrameData(HWComposer::HWCLayerInterface& layer);
     virtual void setAcquireFence(HWComposer::HWCLayerInterface& layer);
+
     virtual void onDraw(const DisplayHardware& hw, const Region& clip) const;
     virtual uint32_t doTransaction(uint32_t transactionFlags);
-    virtual void lockPageFlip(bool& recomputeVisibleRegions);
-    virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion);
+    virtual Region latchBuffer(bool& recomputeVisibleRegions);
     virtual bool isOpaque() const;
     virtual bool isSecure() const           { return mSecure; }
     virtual bool isProtected() const;
     virtual void onRemoved();
     virtual sp<Layer> getLayer() const { return const_cast<Layer*>(this); }
     virtual void setName(const String8& name);
-    virtual void validateVisibility(const Transform& globalTransform, const DisplayHardware& hw);
 
     // LayerBaseClient interface
     virtual wp<IBinder> getSurfaceTextureBinder() const;
@@ -142,7 +142,6 @@
     // page-flip thread (currently main thread)
     bool mSecure;         // no screenshots
     bool mProtectedByApp; // application requires protected path to external sink
-    Region mPostedDirtyRegion;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
index 8350d27..2070cb9 100644
--- a/services/surfaceflinger/LayerBase.cpp
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -45,8 +45,6 @@
       sequence(uint32_t(android_atomic_inc(&sSequence))),
       mFlinger(flinger), mFiltering(false),
       mNeedsFiltering(false),
-      mOrientation(0),
-      mPlaneOrientation(0),
       mTransactionFlags(0),
       mPremultipliedAlpha(true), mName("unnamed"), mDebug(false)
 {
@@ -170,19 +168,14 @@
     return true;
 }
 
-Rect LayerBase::visibleBounds() const
-{
-    return mTransformedBounds;
-}      
-
 void LayerBase::setVisibleRegion(const Region& visibleRegion) {
     // always called from main thread
-    visibleRegionScreen = visibleRegion;
+    this->visibleRegion = visibleRegion;
 }
 
 void LayerBase::setCoveredRegion(const Region& coveredRegion) {
     // always called from main thread
-    coveredRegionScreen = coveredRegion;
+    this->coveredRegion = coveredRegion;
 }
 
 uint32_t LayerBase::doTransaction(uint32_t flags)
@@ -219,57 +212,45 @@
     return flags;
 }
 
-void LayerBase::validateVisibility(const Transform& planeTransform, const DisplayHardware& hw)
+void LayerBase::computeGeometry(const DisplayHardware& hw, LayerMesh* mesh) const
 {
     const Layer::State& s(drawingState());
-    const Transform tr(planeTransform * s.transform);
-    const bool transformed = tr.transformed();
+    const Transform tr(hw.getTransform() * s.transform);
     const uint32_t hw_h = hw.getHeight();
     const Rect& crop(s.active.crop);
-
     Rect win(s.active.w, s.active.h);
     if (!crop.isEmpty()) {
         win.intersect(crop, &win);
     }
-
-    mNumVertices = 4;
-    tr.transform(mVertices[0], win.left,  win.top);
-    tr.transform(mVertices[1], win.left,  win.bottom);
-    tr.transform(mVertices[2], win.right, win.bottom);
-    tr.transform(mVertices[3], win.right, win.top);
-    for (size_t i=0 ; i<4 ; i++)
-        mVertices[i][1] = hw_h - mVertices[i][1];
-
-    if (CC_UNLIKELY(transformed)) {
-        // NOTE: here we could also punt if we have too many rectangles
-        // in the transparent region
-        if (tr.preserveRects()) {
-            // transform the transparent region
-            transparentRegionScreen = tr.transform(s.transparentRegion);
-        } else {
-            // transformation too complex, can't do the transparent region
-            // optimization.
-            transparentRegionScreen.clear();
+    if (mesh) {
+        tr.transform(mesh->mVertices[0], win.left,  win.top);
+        tr.transform(mesh->mVertices[1], win.left,  win.bottom);
+        tr.transform(mesh->mVertices[2], win.right, win.bottom);
+        tr.transform(mesh->mVertices[3], win.right, win.top);
+        for (size_t i=0 ; i<4 ; i++) {
+            mesh->mVertices[i][1] = hw_h - mesh->mVertices[i][1];
         }
-    } else {
-        transparentRegionScreen = s.transparentRegion;
     }
-
-    // cache a few things...
-    mOrientation = tr.getOrientation();
-    mPlaneOrientation = planeTransform.getOrientation();
-    mTransform = tr;
-    mTransformedBounds = tr.transform(win);
 }
 
-void LayerBase::lockPageFlip(bool& recomputeVisibleRegions) {
+Rect LayerBase::computeBounds() const {
+    const Layer::State& s(drawingState());
+    const Rect& crop(s.active.crop);
+    Rect win(s.active.w, s.active.h);
+    if (!crop.isEmpty()) {
+        win.intersect(crop, &win);
+    }
+    return s.transform.transform(win);
 }
 
-void LayerBase::unlockPageFlip(
-        const Transform& planeTransform, Region& outDirtyRegion) {
+Region LayerBase::latchBuffer(bool& recomputeVisibleRegions) {
+    Region result;
+    return result;
 }
 
-void LayerBase::setGeometry(HWComposer::HWCLayerInterface& layer)
+void LayerBase::setGeometry(
+        const DisplayHardware& hw,
+        HWComposer::HWCLayerInterface& layer)
 {
     layer.setDefaultState();
 
@@ -289,10 +270,14 @@
                 HWC_BLENDING_COVERAGE);
     }
 
-    // scaling is already applied in mTransformedBounds
-    layer.setFrame(mTransformedBounds);
-    layer.setVisibleRegionScreen(visibleRegionScreen);
-    layer.setCrop(mTransformedBounds.getBounds());
+    const Transform& tr = hw.getTransform();
+    Rect transformedBounds(computeBounds());
+    transformedBounds = tr.transform(transformedBounds);
+
+    // scaling is already applied in transformedBounds
+    layer.setFrame(transformedBounds);
+    layer.setCrop(transformedBounds.getBounds());
+    layer.setVisibleRegionScreen(tr.transform(visibleRegion));
 }
 
 void LayerBase::setPerFrameData(HWComposer::HWCLayerInterface& layer) {
@@ -335,8 +320,11 @@
     glDisable(GL_TEXTURE_2D);
     glDisable(GL_BLEND);
 
-    glVertexPointer(2, GL_FLOAT, 0, mVertices);
-    glDrawArrays(GL_TRIANGLE_FAN, 0, mNumVertices);
+    LayerMesh mesh;
+    computeGeometry(hw, &mesh);
+
+    glVertexPointer(2, GL_FLOAT, 0, mesh.getVertices());
+    glDrawArrays(GL_TRIANGLE_FAN, 0, mesh.getVertexCount());
 }
 
 void LayerBase::clearWithOpenGL(const DisplayHardware& hw, const Region& clip) const
@@ -371,6 +359,12 @@
         }
     }
 
+    LayerMesh mesh;
+    computeGeometry(hw, &mesh);
+
+    // TODO: we probably want to generate the texture coords with the mesh
+    // here we assume that we only have 4 vertices
+
     struct TexCoords {
         GLfloat u;
         GLfloat v;
@@ -380,9 +374,9 @@
     if (!s.active.crop.isEmpty()) {
         crop = s.active.crop;
     }
-    GLfloat left = GLfloat(crop.left) / GLfloat(s.active.w);
-    GLfloat top = GLfloat(crop.top) / GLfloat(s.active.h);
-    GLfloat right = GLfloat(crop.right) / GLfloat(s.active.w);
+    GLfloat left   = GLfloat(crop.left)   / GLfloat(s.active.w);
+    GLfloat top    = GLfloat(crop.top)    / GLfloat(s.active.h);
+    GLfloat right  = GLfloat(crop.right)  / GLfloat(s.active.w);
     GLfloat bottom = GLfloat(crop.bottom) / GLfloat(s.active.h);
 
     TexCoords texCoords[4];
@@ -399,9 +393,9 @@
     }
 
     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-    glVertexPointer(2, GL_FLOAT, 0, mVertices);
     glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
-    glDrawArrays(GL_TRIANGLE_FAN, 0, mNumVertices);
+    glVertexPointer(2, GL_FLOAT, 0, mesh.getVertices());
+    glDrawArrays(GL_TRIANGLE_FAN, 0, mesh.getVertexCount());
 
     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
     glDisable(GL_BLEND);
@@ -417,8 +411,7 @@
     result.append(buffer);
 
     s.transparentRegion.dump(result, "transparentRegion");
-    transparentRegionScreen.dump(result, "transparentRegionScreen");
-    visibleRegionScreen.dump(result, "visibleRegionScreen");
+    visibleRegion.dump(result, "visibleRegion");
 
     snprintf(buffer, SIZE,
             "      "
diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h
index 83e871c..d227b2d 100644
--- a/services/surfaceflinger/LayerBase.h
+++ b/services/surfaceflinger/LayerBase.h
@@ -57,9 +57,9 @@
 
     DisplayID           dpy;
     mutable bool        contentDirty;
-            Region      visibleRegionScreen;
-            Region      transparentRegionScreen;
-            Region      coveredRegionScreen;
+            // regions below are in window-manager space
+            Region      visibleRegion;
+            Region      coveredRegion;
             int32_t     sequence;
             
             struct Geometry {
@@ -86,6 +86,20 @@
                 Region          transparentRegion;
             };
 
+            class LayerMesh {
+                friend class LayerBase;
+                GLfloat mVertices[4][2];
+                size_t mNumVertices;
+            public:
+                LayerMesh() : mNumVertices(4) { }
+                GLfloat const* getVertices() const {
+                    return &mVertices[0][0];
+                }
+                size_t getVertexCount() const {
+                    return mNumVertices;
+                }
+            };
+
     virtual void setName(const String8& name);
             String8 getName() const;
 
@@ -105,15 +119,18 @@
             
             uint32_t getTransactionFlags(uint32_t flags);
             uint32_t setTransactionFlags(uint32_t flags);
-            
-            Rect visibleBounds() const;
+
+            void computeGeometry(const DisplayHardware& hw, LayerMesh* mesh) const;
+            Rect computeBounds() const;
+
 
     virtual sp<LayerBaseClient> getLayerBaseClient() const { return 0; }
     virtual sp<Layer> getLayer() const { return 0; }
 
     virtual const char* getTypeId() const { return "LayerBase"; }
 
-    virtual void setGeometry(HWComposer::HWCLayerInterface& layer);
+    virtual void setGeometry(const DisplayHardware& hw,
+            HWComposer::HWCLayerInterface& layer);
     virtual void setPerFrameData(HWComposer::HWCLayerInterface& layer);
     virtual void setAcquireFence(HWComposer::HWCLayerInterface& layer);
 
@@ -156,26 +173,13 @@
     virtual void setCoveredRegion(const Region& coveredRegion);
 
     /**
-     * validateVisibility - cache a bunch of things
-     */
-    virtual void validateVisibility(const Transform& globalTransform, const DisplayHardware& hw);
-
-    /**
-     * lockPageFlip - called each time the screen is redrawn and returns whether
+     * latchBuffer - called each time the screen is redrawn and returns whether
      * the visible regions need to be recomputed (this is a fairly heavy
      * operation, so this should be set only if needed). Typically this is used
      * to figure out if the content or size of a surface has changed.
      */
-    virtual void lockPageFlip(bool& recomputeVisibleRegions);
-    
-    /**
-     * unlockPageFlip - called each time the screen is redrawn. updates the
-     * final dirty region wrt the planeTransform.
-     * At this point, all visible regions, surface position and size, etc... are
-     * correct.
-     */
-    virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion);
-    
+    virtual Region latchBuffer(bool& recomputeVisibleRegions);
+
     /**
      * isOpaque - true if this surface is opaque
      */
@@ -233,9 +237,6 @@
     inline  const State&    currentState() const    { return mCurrentState; }
     inline  State&          currentState()          { return mCurrentState; }
 
-    int32_t  getOrientation() const { return mOrientation; }
-    int32_t  getPlaneOrientation() const { return mPlaneOrientation; }
-
     void clearWithOpenGL(const DisplayHardware& hw, const Region& clip) const;
 
 protected:
@@ -253,19 +254,10 @@
                 // Whether filtering is forced on or not
                 bool            mFiltering;
 
-                // cached during validateVisibility()
                 // Whether filtering is needed b/c of the drawingstate
                 bool            mNeedsFiltering;
 
 protected:
-                // cached during validateVisibility()
-                int32_t         mOrientation;
-                int32_t         mPlaneOrientation;
-                Transform       mTransform;
-                GLfloat         mVertices[4][2];
-                size_t          mNumVertices;
-                Rect            mTransformedBounds;
-            
                 // these are protected by an external lock
                 State           mCurrentState;
                 State           mDrawingState;
diff --git a/services/surfaceflinger/LayerDim.cpp b/services/surfaceflinger/LayerDim.cpp
index ceead45..5c37d01 100644
--- a/services/surfaceflinger/LayerDim.cpp
+++ b/services/surfaceflinger/LayerDim.cpp
@@ -58,8 +58,11 @@
 
         glColor4f(0, 0, 0, alpha);
 
-        glVertexPointer(2, GL_FLOAT, 0, mVertices);
-        glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+        LayerMesh mesh;
+        computeGeometry(hw, &mesh);
+
+        glVertexPointer(2, GL_FLOAT, 0, mesh.getVertices());
+        glDrawArrays(GL_TRIANGLE_FAN, 0, mesh.getVertexCount());
 
         glDisable(GL_BLEND);
         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
diff --git a/services/surfaceflinger/LayerScreenshot.cpp b/services/surfaceflinger/LayerScreenshot.cpp
index d046879..f2bf19d 100644
--- a/services/surfaceflinger/LayerScreenshot.cpp
+++ b/services/surfaceflinger/LayerScreenshot.cpp
@@ -120,6 +120,9 @@
             glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
         }
 
+        LayerMesh mesh;
+        computeGeometry(hw, &mesh);
+
         glColor4f(0, 0, 0, alpha);
 
         glDisable(GL_TEXTURE_EXTERNAL_OES);
@@ -133,8 +136,8 @@
 
         glEnableClientState(GL_TEXTURE_COORD_ARRAY);
         glTexCoordPointer(2, GL_FLOAT, 0, mTexCoords);
-        glVertexPointer(2, GL_FLOAT, 0, mVertices);
-        glDrawArrays(GL_TRIANGLE_FAN, 0, mNumVertices);
+        glVertexPointer(2, GL_FLOAT, 0, mesh.getVertices());
+        glDrawArrays(GL_TRIANGLE_FAN, 0, mesh.getVertexCount());
 
         glDisable(GL_BLEND);
         glDisable(GL_TEXTURE_2D);
diff --git a/services/surfaceflinger/MessageQueue.cpp b/services/surfaceflinger/MessageQueue.cpp
index 290fff4..3f77f74 100644
--- a/services/surfaceflinger/MessageQueue.cpp
+++ b/services/surfaceflinger/MessageQueue.cpp
@@ -49,13 +49,13 @@
 
 // ---------------------------------------------------------------------------
 
-void MessageQueue::Handler::signalRefresh() {
+void MessageQueue::Handler::dispatchRefresh() {
     if ((android_atomic_or(eventMaskRefresh, &mEventMask) & eventMaskRefresh) == 0) {
         mQueue.mLooper->sendMessage(this, Message(MessageQueue::REFRESH));
     }
 }
 
-void MessageQueue::Handler::signalInvalidate() {
+void MessageQueue::Handler::dispatchInvalidate() {
     if ((android_atomic_or(eventMaskInvalidate, &mEventMask) & eventMaskInvalidate) == 0) {
         mQueue.mLooper->sendMessage(this, Message(MessageQueue::INVALIDATE));
     }
@@ -132,13 +132,31 @@
     return NO_ERROR;
 }
 
+/* when INVALIDATE_ON_VSYNC is set SF only processes
+ * buffer updates on VSYNC and performs a refresh immediately
+ * after.
+ *
+ * when INVALIDATE_ON_VSYNC is set to false, SF will instead
+ * perform the buffer updates immediately, but the refresh only
+ * at the next VSYNC.
+ * THIS MODE IS BUGGY ON GALAXY NEXUS AND WILL CAUSE HANGS
+ */
+#define INVALIDATE_ON_VSYNC 1
+
 void MessageQueue::invalidate() {
-//    mHandler->signalInvalidate();
+#if INVALIDATE_ON_VSYNC
     mEvents->requestNextVsync();
+#else
+    mHandler->dispatchInvalidate();
+#endif
 }
 
 void MessageQueue::refresh() {
+#if INVALIDATE_ON_VSYNC
+    mHandler->dispatchRefresh();
+#else
     mEvents->requestNextVsync();
+#endif
 }
 
 int MessageQueue::cb_eventReceiver(int fd, int events, void* data) {
@@ -152,7 +170,11 @@
     while ((n = DisplayEventReceiver::getEvents(mEventTube, buffer, 8)) > 0) {
         for (int i=0 ; i<n ; i++) {
             if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
-                mHandler->signalRefresh();
+#if INVALIDATE_ON_VSYNC
+                mHandler->dispatchInvalidate();
+#else
+                mHandler->dispatchRefresh();
+#endif
                 break;
             }
         }
diff --git a/services/surfaceflinger/MessageQueue.h b/services/surfaceflinger/MessageQueue.h
index ea29e7e..710b2c2 100644
--- a/services/surfaceflinger/MessageQueue.h
+++ b/services/surfaceflinger/MessageQueue.h
@@ -70,8 +70,8 @@
     public:
         Handler(MessageQueue& queue) : mQueue(queue), mEventMask(0) { }
         virtual void handleMessage(const Message& message);
-        void signalRefresh();
-        void signalInvalidate();
+        void dispatchRefresh();
+        void dispatchInvalidate();
     };
 
     friend class Handler;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 3b2bf00..6c900be 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -425,100 +425,117 @@
     return res;
 }
 
-bool SurfaceFlinger::threadLoop()
-{
+bool SurfaceFlinger::threadLoop() {
     waitForEvent();
     return true;
 }
 
-void SurfaceFlinger::onMessageReceived(int32_t what)
-{
+void SurfaceFlinger::onMessageReceived(int32_t what) {
     ATRACE_CALL();
     switch (what) {
-        case MessageQueue::REFRESH: {
-//        case MessageQueue::INVALIDATE: {
-            // if we're in a global transaction, don't do anything.
-            const uint32_t mask = eTransactionNeeded | eTraversalNeeded;
-            uint32_t transactionFlags = peekTransactionFlags(mask);
-            if (CC_UNLIKELY(transactionFlags)) {
-                handleTransaction(transactionFlags);
-            }
-
-            // post surfaces (if needed)
-            handlePageFlip();
-
-//            signalRefresh();
-//
-//        } break;
-//
-//        case MessageQueue::REFRESH: {
-
-            handleRefresh();
-
-            // TODO: iterate through all displays
-            const DisplayHardware& hw(getDisplayHardware(0));
-
-//            if (mDirtyRegion.isEmpty()) {
-//                return;
-//            }
-
-            if (CC_UNLIKELY(mHwWorkListDirty)) {
-                // build the h/w work list
-                handleWorkList(hw);
-            }
-
-            if (CC_LIKELY(hw.canDraw())) {
-                // repaint the framebuffer (if needed)
-                handleRepaint(hw);
-                // inform the h/w that we're done compositing
-                hw.compositionComplete();
-                postFramebuffer();
-            } else {
-                // pretend we did the post
-                hw.compositionComplete();
-            }
-
-            // render to the external display if we have one
-            EGLSurface externalDisplaySurface = getExternalDisplaySurface();
-            if (externalDisplaySurface != EGL_NO_SURFACE) {
-                EGLSurface cur = eglGetCurrentSurface(EGL_DRAW);
-                EGLBoolean success = eglMakeCurrent(eglGetCurrentDisplay(),
-                        externalDisplaySurface, externalDisplaySurface,
-                        eglGetCurrentContext());
-
-                ALOGE_IF(!success, "eglMakeCurrent -> external failed");
-
-                if (success) {
-                    // redraw the screen entirely...
-                    glDisable(GL_TEXTURE_EXTERNAL_OES);
-                    glDisable(GL_TEXTURE_2D);
-                    glClearColor(0,0,0,1);
-                    glClear(GL_COLOR_BUFFER_BIT);
-                    glMatrixMode(GL_MODELVIEW);
-                    glLoadIdentity();
-                    const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);
-                    const size_t count = layers.size();
-                    for (size_t i=0 ; i<count ; ++i) {
-                        const sp<LayerBase>& layer(layers[i]);
-                        layer->drawForSreenShot(hw);
-                    }
-
-                    success = eglSwapBuffers(eglGetCurrentDisplay(), externalDisplaySurface);
-                    ALOGE_IF(!success, "external display eglSwapBuffers failed");
-
-                    hw.compositionComplete();
-                }
-
-                success = eglMakeCurrent(eglGetCurrentDisplay(),
-                        cur, cur, eglGetCurrentContext());
-
-                ALOGE_IF(!success, "eglMakeCurrent -> internal failed");
-            }
-
-        } break;
+    case MessageQueue::INVALIDATE:
+        handleMessageTransaction();
+        handleMessageInvalidate();
+        signalRefresh();
+        break;
+    case MessageQueue::REFRESH:
+        handleMessageRefresh();
+        break;
     }
 }
 
+void SurfaceFlinger::handleMessageTransaction() {
+    const uint32_t mask = eTransactionNeeded | eTraversalNeeded;
+    uint32_t transactionFlags = peekTransactionFlags(mask);
+    if (transactionFlags) {
+        Region dirtyRegion;
+        dirtyRegion = handleTransaction(transactionFlags);
+        // XXX: dirtyRegion should be per screen
+        mDirtyRegion |= dirtyRegion;
+    }
+}
+
+void SurfaceFlinger::handleMessageInvalidate() {
+    Region dirtyRegion;
+    dirtyRegion = handlePageFlip();
+    // XXX: dirtyRegion should be per screen
+    mDirtyRegion |= dirtyRegion;
+}
+
+void SurfaceFlinger::handleMessageRefresh() {
+    handleRefresh();
+
+    // XXX: dirtyRegion should be per screen, we should check all of them
+    if (mDirtyRegion.isEmpty()) {
+        return;
+    }
+
+    // TODO: iterate through all displays
+    const DisplayHardware& hw(getDisplayHardware(0));
+
+    // XXX: dirtyRegion should be per screen
+    // transform the dirty region into this screen's coordinate space
+    const Transform& planeTransform(hw.getTransform());
+    mDirtyRegion = planeTransform.transform(mDirtyRegion);
+    mDirtyRegion.orSelf(getAndClearInvalidateRegion());
+    mDirtyRegion.andSelf(hw.bounds());
+
+
+    if (CC_UNLIKELY(mHwWorkListDirty)) {
+        // build the h/w work list
+        handleWorkList(hw);
+    }
+
+    if (CC_LIKELY(hw.canDraw())) {
+        // repaint the framebuffer (if needed)
+        handleRepaint(hw);
+        // inform the h/w that we're done compositing
+        hw.compositionComplete();
+        postFramebuffer();
+    } else {
+        // pretend we did the post
+        hw.compositionComplete();
+    }
+
+    // render to the external display if we have one
+    EGLSurface externalDisplaySurface = getExternalDisplaySurface();
+    if (externalDisplaySurface != EGL_NO_SURFACE) {
+        EGLSurface cur = eglGetCurrentSurface(EGL_DRAW);
+        EGLBoolean success = eglMakeCurrent(eglGetCurrentDisplay(),
+                externalDisplaySurface, externalDisplaySurface,
+                eglGetCurrentContext());
+
+        ALOGE_IF(!success, "eglMakeCurrent -> external failed");
+
+        if (success) {
+            // redraw the screen entirely...
+            glDisable(GL_TEXTURE_EXTERNAL_OES);
+            glDisable(GL_TEXTURE_2D);
+            glClearColor(0,0,0,1);
+            glClear(GL_COLOR_BUFFER_BIT);
+            glMatrixMode(GL_MODELVIEW);
+            glLoadIdentity();
+            const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);
+            const size_t count = layers.size();
+            for (size_t i=0 ; i<count ; ++i) {
+                const sp<LayerBase>& layer(layers[i]);
+                layer->drawForSreenShot(hw);
+            }
+
+            success = eglSwapBuffers(eglGetCurrentDisplay(), externalDisplaySurface);
+            ALOGE_IF(!success, "external display eglSwapBuffers failed");
+
+            hw.compositionComplete();
+        }
+
+        success = eglMakeCurrent(eglGetCurrentDisplay(),
+                cur, cur, eglGetCurrentContext());
+
+        ALOGE_IF(!success, "eglMakeCurrent -> internal failed");
+    }
+
+}
+
 void SurfaceFlinger::postFramebuffer()
 {
     ATRACE_CALL();
@@ -564,10 +581,12 @@
     mSwapRegion.clear();
 }
 
-void SurfaceFlinger::handleTransaction(uint32_t transactionFlags)
+Region SurfaceFlinger::handleTransaction(uint32_t transactionFlags)
 {
     ATRACE_CALL();
 
+    Region dirtyRegion;
+
     Mutex::Autolock _l(mStateLock);
     const nsecs_t now = systemTime();
     mDebugInTransaction = now;
@@ -580,16 +599,19 @@
 
     const uint32_t mask = eTransactionNeeded | eTraversalNeeded;
     transactionFlags = getTransactionFlags(mask);
-    handleTransactionLocked(transactionFlags);
+    dirtyRegion = handleTransactionLocked(transactionFlags);
 
     mLastTransactionTime = systemTime() - now;
     mDebugInTransaction = 0;
     invalidateHwcGeometry();
     // here the transaction has been committed
+
+    return dirtyRegion;
 }
 
-void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags)
+Region SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags)
 {
+    Region dirtyRegion;
     const LayerVector& currentLayers(mCurrentState.layersSortedByZ);
     const size_t count = currentLayers.size();
 
@@ -652,13 +674,34 @@
                 const sp<LayerBase>& layer(previousLayers[i]);
                 if (currentLayers.indexOf( layer ) < 0) {
                     // this layer is not visible anymore
-                    mDirtyRegionRemovedLayer.orSelf(layer->visibleRegionScreen);
+                    // TODO: we could traverse the tree from front to back and compute the actual visible region
+                    // TODO: we could cache the transformed region
+                    Layer::State front(layer->drawingState());
+                    Region visibleReg = front.transform.transform(
+                            Region(Rect(front.active.w, front.active.h)));
+                    dirtyRegion.orSelf(visibleReg);
                 }
             }
         }
     }
 
     commitTransaction();
+    return dirtyRegion;
+}
+
+void SurfaceFlinger::commitTransaction()
+{
+    if (!mLayersPendingRemoval.isEmpty()) {
+        // Notify removed layers now that they can't be drawn from
+        for (size_t i = 0; i < mLayersPendingRemoval.size(); i++) {
+            mLayersPendingRemoval[i]->onRemoved();
+        }
+        mLayersPendingRemoval.clear();
+    }
+
+    mDrawingState = mCurrentState;
+    mTransationPending = false;
+    mTransactionCV.broadcast();
 }
 
 void SurfaceFlinger::computeVisibleRegions(
@@ -666,10 +709,6 @@
 {
     ATRACE_CALL();
 
-    const DisplayHardware& hw(getDefaultDisplayHardware()); // FIXME: we shouldn't rely on DisplayHardware here
-    const Transform& planeTransform(hw.getTransform());
-    const Region screenRegion(hw.bounds());
-
     Region aboveOpaqueLayers;
     Region aboveCoveredLayers;
     Region dirty;
@@ -679,7 +718,6 @@
     size_t i = currentLayers.size();
     while (i--) {
         const sp<LayerBase>& layer = currentLayers[i];
-        layer->validateVisibility(planeTransform, hw);
 
         // start with the whole surface at its current location
         const Layer::State& s(layer->drawingState());
@@ -707,17 +745,30 @@
         // handle hidden surfaces by setting the visible region to empty
         if (CC_LIKELY(!(s.flags & ISurfaceComposer::eLayerHidden) && s.alpha)) {
             const bool translucent = !layer->isOpaque();
-            const Rect bounds(layer->visibleBounds());
+            Rect bounds(layer->computeBounds());
             visibleRegion.set(bounds);
-            visibleRegion.andSelf(screenRegion);
             if (!visibleRegion.isEmpty()) {
                 // Remove the transparent area from the visible region
                 if (translucent) {
-                    visibleRegion.subtractSelf(layer->transparentRegionScreen);
+                    Region transparentRegionScreen;
+                    const Transform tr(s.transform);
+                    if (tr.transformed()) {
+                        if (tr.preserveRects()) {
+                            // transform the transparent region
+                            transparentRegionScreen = tr.transform(s.transparentRegion);
+                        } else {
+                            // transformation too complex, can't do the
+                            // transparent region optimization.
+                            transparentRegionScreen.clear();
+                        }
+                    } else {
+                        transparentRegionScreen = s.transparentRegion;
+                    }
+                    visibleRegion.subtractSelf(transparentRegionScreen);
                 }
 
                 // compute the opaque region
-                const int32_t layerOrientation = layer->getOrientation();
+                const int32_t layerOrientation = s.transform.getOrientation();
                 if (s.alpha==255 && !translucent &&
                         ((layerOrientation & Transform::ROT_INVALID) == false)) {
                     // the opaque region is the layer's footprint
@@ -740,7 +791,7 @@
             // we need to invalidate the whole region
             dirty = visibleRegion;
             // as well, as the old visible region
-            dirty.orSelf(layer->visibleRegionScreen);
+            dirty.orSelf(layer->visibleRegion);
             layer->contentDirty = false;
         } else {
             /* compute the exposed region:
@@ -756,8 +807,8 @@
              * exposed because of a resize.
              */
             const Region newExposed = visibleRegion - coveredRegion;
-            const Region oldVisibleRegion = layer->visibleRegionScreen;
-            const Region oldCoveredRegion = layer->coveredRegionScreen;
+            const Region oldVisibleRegion = layer->visibleRegion;
+            const Region oldCoveredRegion = layer->coveredRegion;
             const Region oldExposed = oldVisibleRegion - oldCoveredRegion;
             dirty = (visibleRegion&oldCoveredRegion) | (newExposed-oldExposed);
         }
@@ -779,63 +830,51 @@
         }
     }
 
-    // invalidate the areas where a layer was removed
-    dirtyRegion.orSelf(mDirtyRegionRemovedLayer);
-    mDirtyRegionRemovedLayer.clear();
-
     mSecureFrameBuffer = secureFrameBuffer;
     opaqueRegion = aboveOpaqueLayers;
 }
 
-
-void SurfaceFlinger::commitTransaction()
-{
-    if (!mLayersPendingRemoval.isEmpty()) {
-        // Notify removed layers now that they can't be drawn from
-        for (size_t i = 0; i < mLayersPendingRemoval.size(); i++) {
-            mLayersPendingRemoval[i]->onRemoved();
-        }
-        mLayersPendingRemoval.clear();
-    }
-
-    mDrawingState = mCurrentState;
-    mTransationPending = false;
-    mTransactionCV.broadcast();
-}
-
-void SurfaceFlinger::handlePageFlip()
+Region SurfaceFlinger::handlePageFlip()
 {
     ATRACE_CALL();
-    const DisplayHardware& hw(getDefaultDisplayHardware()); // FIXME: it's a problem we need DisplayHardware here
-    const Region screenRegion(hw.bounds());
+    Region dirtyRegion;
 
     const LayerVector& currentLayers(mDrawingState.layersSortedByZ);
-    const bool visibleRegions = lockPageFlip(currentLayers);
 
-        if (visibleRegions || mVisibleRegionsDirty) {
-            Region opaqueRegion;
-            computeVisibleRegions(currentLayers, mDirtyRegion, opaqueRegion);
+    bool visibleRegions = false;
+    const size_t count = currentLayers.size();
+    sp<LayerBase> const* layers = currentLayers.array();
+    for (size_t i=0 ; i<count ; i++) {
+        const sp<LayerBase>& layer(layers[i]);
+        dirtyRegion.orSelf( layer->latchBuffer(visibleRegions) );
+    }
 
-            /*
-             *  rebuild the visible layer list
-             */
-            const size_t count = currentLayers.size();
-            mVisibleLayersSortedByZ.clear();
-            mVisibleLayersSortedByZ.setCapacity(count);
-            for (size_t i=0 ; i<count ; i++) {
-                if (!currentLayers[i]->visibleRegionScreen.isEmpty())
-                    mVisibleLayersSortedByZ.add(currentLayers[i]);
-            }
+    if (visibleRegions || mVisibleRegionsDirty) {
+        Region opaqueRegion;
+        computeVisibleRegions(currentLayers, dirtyRegion, opaqueRegion);
 
-            mWormholeRegion = screenRegion.subtract(opaqueRegion);
-            mVisibleRegionsDirty = false;
-            invalidateHwcGeometry();
+        /*
+         *  rebuild the visible layer list
+         */
+
+        // XXX: mVisibleLayersSortedByZ should be per-screen
+        const size_t count = currentLayers.size();
+        mVisibleLayersSortedByZ.clear();
+        mVisibleLayersSortedByZ.setCapacity(count);
+        for (size_t i=0 ; i<count ; i++) {
+            if (!currentLayers[i]->visibleRegion.isEmpty())
+                mVisibleLayersSortedByZ.add(currentLayers[i]);
         }
 
-    unlockPageFlip(currentLayers);
+        // FIXME: mWormholeRegion needs to be calculated per screen
+        const DisplayHardware& hw(getDefaultDisplayHardware()); // XXX: we can't keep that here
+        mWormholeRegion = Region(hw.getBounds()).subtract(
+                hw.getTransform().transform(opaqueRegion) );
+        mVisibleRegionsDirty = false;
+        invalidateHwcGeometry();
+    }
 
-    mDirtyRegion.orSelf(getAndClearInvalidateRegion());
-    mDirtyRegion.andSelf(screenRegion);
+    return dirtyRegion;
 }
 
 void SurfaceFlinger::invalidateHwcGeometry()
@@ -843,30 +882,6 @@
     mHwWorkListDirty = true;
 }
 
-bool SurfaceFlinger::lockPageFlip(const LayerVector& currentLayers)
-{
-    bool recomputeVisibleRegions = false;
-    size_t count = currentLayers.size();
-    sp<LayerBase> const* layers = currentLayers.array();
-    for (size_t i=0 ; i<count ; i++) {
-        const sp<LayerBase>& layer(layers[i]);
-        layer->lockPageFlip(recomputeVisibleRegions);
-    }
-    return recomputeVisibleRegions;
-}
-
-void SurfaceFlinger::unlockPageFlip(const LayerVector& currentLayers)
-{
-    const DisplayHardware& hw(getDefaultDisplayHardware()); // FIXME: it's a problem we need DisplayHardware here
-    const Transform& planeTransform(hw.getTransform());
-    const size_t count = currentLayers.size();
-    sp<LayerBase> const* layers = currentLayers.array();
-    for (size_t i=0 ; i<count ; i++) {
-        const sp<LayerBase>& layer(layers[i]);
-        layer->unlockPageFlip(planeTransform, mDirtyRegion);
-    }
-}
-
 void SurfaceFlinger::handleRefresh()
 {
     bool needInvalidate = false;
@@ -896,7 +911,7 @@
         HWComposer::LayerListIterator cur = hwc.begin();
         const HWComposer::LayerListIterator end = hwc.end();
         for (size_t i=0 ; cur!=end && i<count ; ++i, ++cur) {
-            currentLayers[i]->setGeometry(*cur);
+            currentLayers[i]->setGeometry(hw, *cur);
             if (mDebugDisableHWC || mDebugRegion) {
                 cur->setSkip(true);
             }
@@ -1012,9 +1027,10 @@
 
         const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);
         const size_t count = layers.size();
+        const Transform& tr = hw.getTransform();
         for (size_t i=0 ; cur!=end && i<count ; ++i, ++cur) {
             const sp<LayerBase>& layer(layers[i]);
-            const Region clip(dirty.intersect(layer->visibleRegionScreen));
+            const Region clip(dirty.intersect(tr.transform(layer->visibleRegion)));
             if (!clip.isEmpty()) {
                 if (cur->getCompositionType() == HWC_OVERLAY) {
                     if (i && (cur->getHints() & HWC_HINT_CLEAR_FB)
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 3e27564..91cc6a5 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -129,6 +129,9 @@
                     GLuint* textureName, GLfloat* uOut, GLfloat* vOut);
 
             void onMessageReceived(int32_t what);
+            void handleMessageTransaction();
+            void handleMessageInvalidate();
+            void handleMessageRefresh();
 
             status_t postMessageAsync(const sp<MessageBase>& msg,
                     nsecs_t reltime=0, uint32_t flags = 0);
@@ -237,17 +240,23 @@
 
 private:
             void        waitForEvent();
-            void        handleTransaction(uint32_t transactionFlags);
-            void        handleTransactionLocked(uint32_t transactionFlags);
+            Region      handleTransaction(uint32_t transactionFlags);
+            Region      handleTransactionLocked(uint32_t transactionFlags);
 
             void        computeVisibleRegions(
                             const LayerVector& currentLayers,
                             Region& dirtyRegion,
                             Region& wormholeRegion);
 
-            void        handlePageFlip();
-            bool        lockPageFlip(const LayerVector& currentLayers);
-            void        unlockPageFlip(const LayerVector& currentLayers);
+            /* handlePageFilp: this is were we latch a new buffer
+             * if available and compute the dirty region.
+             * The return value is the dirty region expressed in the
+             * window manager's coordinate space (or the layer's state
+             * space, which is the same thing), in particular the dirty
+             * region is independent from a specific display's orientation.
+             */
+            Region      handlePageFlip();
+
             void        handleRefresh();
             void        handleWorkList(const DisplayHardware& hw);
             void        handleRepaint(const DisplayHardware& hw);