Get rid of LayerBase.

The functionality of LayerBase and Layer is folded
into Layer. There wasn't a need for this abstraction
anymore.

Change-Id: I66511c08cc3d89009ba4deabf47e26cd4cfeaefb
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 6245b6b..44ef0b8 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -19,6 +19,7 @@
 #include <stdlib.h>
 #include <stdint.h>
 #include <sys/types.h>
+#include <math.h>
 
 #include <cutils/compiler.h>
 #include <cutils/native_handle.h>
@@ -49,37 +50,39 @@
 
 // ---------------------------------------------------------------------------
 
+int32_t Layer::sSequence = 1;
+
 Layer::Layer(SurfaceFlinger* flinger, const sp<Client>& client)
-    :   LayerBase(flinger, client),
+    :   contentDirty(false),
+        sequence(uint32_t(android_atomic_inc(&sSequence))),
+        mFlinger(flinger),
         mTextureName(-1U),
+        mPremultipliedAlpha(true),
+        mName("unnamed"),
+        mDebug(false),
+        mFormat(PIXEL_FORMAT_NONE),
+        mGLExtensions(GLExtensions::getInstance()),
+        mOpaqueLayer(true),
+        mTransactionFlags(0),
         mQueuedFrames(0),
         mCurrentTransform(0),
         mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
         mCurrentOpacity(true),
         mRefreshPending(false),
         mFrameLatencyNeeded(false),
-        mFormat(PIXEL_FORMAT_NONE),
-        mGLExtensions(GLExtensions::getInstance()),
-        mOpaqueLayer(true),
+        mFiltering(false),
+        mNeedsFiltering(false),
         mSecure(false),
-        mProtectedByApp(false)
+        mProtectedByApp(false),
+        mHasSurface(false),
+        mClientRef(client)
 {
     mCurrentCrop.makeInvalid();
     glGenTextures(1, &mTextureName);
 }
 
-void Layer::onLayerDisplayed(const sp<const DisplayDevice>& hw,
-        HWComposer::HWCLayerInterface* layer) {
-    LayerBase::onLayerDisplayed(hw, layer);
-    if (layer) {
-        mSurfaceFlingerConsumer->setReleaseFence(layer->getAndResetReleaseFenceFd());
-    }
-}
-
 void Layer::onFirstRef()
 {
-    LayerBase::onFirstRef();
-
     // Creates a custom BufferQueue for SurfaceFlingerConsumer to use
     sp<BufferQueue> bq = new SurfaceTextureLayer();
     mSurfaceFlingerConsumer = new SurfaceFlingerConsumer(mTextureName, true,
@@ -102,9 +105,25 @@
 
 Layer::~Layer()
 {
+    sp<Client> c(mClientRef.promote());
+    if (c != 0) {
+        c->detachLayer(this);
+    }
     mFlinger->deleteTextureAsync(mTextureName);
 }
 
+// ---------------------------------------------------------------------------
+// callbacks
+// ---------------------------------------------------------------------------
+
+void Layer::onLayerDisplayed(const sp<const DisplayDevice>& hw,
+        HWComposer::HWCLayerInterface* layer) {
+    if (layer) {
+        layer->onDisplayed();
+        mSurfaceFlingerConsumer->setReleaseFence(layer->getAndResetReleaseFenceFd());
+    }
+}
+
 void Layer::onFrameAvailable() {
     android_atomic_inc(&mQueuedFrames);
     mFlinger->signalLayerUpdate();
@@ -112,56 +131,45 @@
 
 // called with SurfaceFlinger::mStateLock as soon as the layer is entered
 // in the purgatory list
-void Layer::onRemoved()
-{
+void Layer::onRemoved() {
     mSurfaceFlingerConsumer->abandon();
 }
 
+// ---------------------------------------------------------------------------
+// set-up
+// ---------------------------------------------------------------------------
+
 void Layer::setName(const String8& name) {
-    LayerBase::setName(name);
+    mName = name;
     mSurfaceFlingerConsumer->setName(name);
 }
 
-sp<ISurface> Layer::createSurface()
-{
-    /*
-     * This class provides an implementation of BnSurface (the "native" or
-     * "remote" side of the Binder IPC interface ISurface), and mixes in
-     * LayerCleaner to ensure that mFlinger->onLayerDestroyed() is called for
-     * this layer when the BSurface is destroyed.
-     *
-     * The idea is to provide a handle to the Layer through ISurface that
-     * is cleaned up automatically when the last reference to the ISurface
-     * goes away.  (The references will be held on the "proxy" side, while
-     * the Layer exists on the "native" side.)
-     *
-     * The Layer has a reference to an instance of SurfaceFlinger's variant
-     * of GLConsumer, which holds a reference to the BufferQueue.  The
-     * getSurfaceTexture() call returns a Binder interface reference for
-     * the producer interface of the buffer queue associated with the Layer.
-     */
-    class BSurface : public BnSurface, public LayerCleaner {
-        wp<const Layer> mOwner;
-        virtual sp<IGraphicBufferProducer> getSurfaceTexture() const {
-            sp<IGraphicBufferProducer> res;
-            sp<const Layer> that( mOwner.promote() );
-            if (that != NULL) {
-                res = that->mSurfaceFlingerConsumer->getBufferQueue();
-            }
-            return res;
-        }
-    public:
-        BSurface(const sp<SurfaceFlinger>& flinger,
-                const sp<Layer>& layer)
-            : LayerCleaner(flinger, layer), mOwner(layer) { }
-    };
-    sp<ISurface> sur(new BSurface(mFlinger, this));
-    return sur;
+String8 Layer::getName() const {
+    return mName;
 }
 
-wp<IBinder> Layer::getSurfaceTextureBinder() const
+void Layer::initStates(uint32_t w, uint32_t h, uint32_t flags)
 {
-    return mSurfaceFlingerConsumer->getBufferQueue()->asBinder();
+    uint32_t layerFlags = 0;
+    if (flags & ISurfaceComposerClient::eHidden)
+        layerFlags = layer_state_t::eLayerHidden;
+
+    if (flags & ISurfaceComposerClient::eNonPremultiplied)
+        mPremultipliedAlpha = false;
+
+    mCurrentState.active.w = w;
+    mCurrentState.active.h = h;
+    mCurrentState.active.crop.makeInvalid();
+    mCurrentState.z = 0;
+    mCurrentState.alpha = 0xFF;
+    mCurrentState.layerStack = 0;
+    mCurrentState.flags = layerFlags;
+    mCurrentState.sequence = 0;
+    mCurrentState.transform.set(0, 0);
+    mCurrentState.requested = mCurrentState.active;
+
+    // drawing state & current state are identical
+    mDrawingState = mCurrentState;
 }
 
 status_t Layer::setBuffers( uint32_t w, uint32_t h,
@@ -199,6 +207,63 @@
     return NO_ERROR;
 }
 
+sp<ISurface> Layer::createSurface() {
+    /*
+     * This class provides an implementation of BnSurface (the "native" or
+     * "remote" side of the Binder IPC interface ISurface), and mixes in
+     * LayerCleaner to ensure that mFlinger->onLayerDestroyed() is called for
+     * this layer when the BSurface is destroyed.
+     *
+     * The idea is to provide a handle to the Layer through ISurface that
+     * is cleaned up automatically when the last reference to the ISurface
+     * goes away.  (The references will be held on the "proxy" side, while
+     * the Layer exists on the "native" side.)
+     *
+     * The Layer has a reference to an instance of SurfaceFlinger's variant
+     * of GLConsumer, which holds a reference to the BufferQueue.  The
+     * getSurfaceTexture() call returns a Binder interface reference for
+     * the producer interface of the buffer queue associated with the Layer.
+     */
+    class BSurface : public BnSurface, public LayerCleaner {
+        wp<const Layer> mOwner;
+        virtual sp<IGraphicBufferProducer> getSurfaceTexture() const {
+            sp<IGraphicBufferProducer> res;
+            sp<const Layer> that( mOwner.promote() );
+            if (that != NULL) {
+                res = that->mSurfaceFlingerConsumer->getBufferQueue();
+            }
+            return res;
+        }
+    public:
+        BSurface(const sp<SurfaceFlinger>& flinger,
+                const sp<Layer>& layer)
+            : LayerCleaner(flinger, layer), mOwner(layer) { }
+    };
+    sp<ISurface> sur(new BSurface(mFlinger, this));
+    return sur;
+}
+
+wp<IBinder> Layer::getSurfaceTextureBinder() const {
+    return mSurfaceFlingerConsumer->getBufferQueue()->asBinder();
+}
+
+sp<ISurface> Layer::getSurface()
+{
+    sp<ISurface> s;
+    Mutex::Autolock _l(mLock);
+
+    LOG_ALWAYS_FATAL_IF(mHasSurface,
+            "Layer::getSurface() has already been called");
+
+    mHasSurface = true;
+    s = createSurface();
+    return s;
+}
+
+// ---------------------------------------------------------------------------
+// h/w composer set-up
+// ---------------------------------------------------------------------------
+
 Rect Layer::getContentCrop() const {
     // this is the crop rectangle that applies to the buffer
     // itself (as opposed to the window)
@@ -220,11 +285,85 @@
     return mCurrentTransform;
 }
 
+Rect Layer::computeBounds() const {
+    const Layer::State& s(drawingState());
+    Rect win(s.active.w, s.active.h);
+    if (!s.active.crop.isEmpty()) {
+        win.intersect(s.active.crop, &win);
+    }
+    return win;
+}
+
+Rect Layer::computeCrop(const sp<const DisplayDevice>& hw) const {
+    /*
+     * The way we compute the crop (aka. texture coordinates when we have a
+     * Layer) produces a different output from the GL code in
+     * drawWithOpenGL() due to HWC being limited to integers. The difference
+     * can be large if getContentTransform() contains a large scale factor.
+     * See comments in drawWithOpenGL() for more details.
+     */
+
+    // the content crop is the area of the content that gets scaled to the
+    // layer's size.
+    Rect crop(getContentCrop());
+
+    // the active.crop is the area of the window that gets cropped, but not
+    // scaled in any ways.
+    const State& s(drawingState());
+
+    // apply the projection's clipping to the window crop in
+    // layerstack space, and convert-back to layer space.
+    // if there are no window scaling (or content scaling) involved,
+    // this operation will map to full pixels in the buffer.
+    // NOTE: should we revert to GL composition if a scaling is involved
+    // since it cannot be represented in the HWC API?
+    Rect activeCrop(s.transform.transform(s.active.crop));
+    activeCrop.intersect(hw->getViewport(), &activeCrop);
+    activeCrop = s.transform.inverse().transform(activeCrop);
+
+    // paranoia: make sure the window-crop is constrained in the
+    // window's bounds
+    activeCrop.intersect(Rect(s.active.w, s.active.h), &activeCrop);
+
+    if (!activeCrop.isEmpty()) {
+        // Transform the window crop to match the buffer coordinate system,
+        // which means using the inverse of the current transform set on the
+        // SurfaceFlingerConsumer.
+        uint32_t invTransform = getContentTransform();
+        int winWidth = s.active.w;
+        int winHeight = s.active.h;
+        if (invTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+            invTransform ^= NATIVE_WINDOW_TRANSFORM_FLIP_V |
+                    NATIVE_WINDOW_TRANSFORM_FLIP_H;
+            winWidth = s.active.h;
+            winHeight = s.active.w;
+        }
+        const Rect winCrop = activeCrop.transform(
+                invTransform, s.active.w, s.active.h);
+
+        // the code below essentially performs a scaled intersection
+        // of crop and winCrop
+        float xScale = float(crop.width()) / float(winWidth);
+        float yScale = float(crop.height()) / float(winHeight);
+
+        int insetL = int(ceilf( winCrop.left                * xScale));
+        int insetT = int(ceilf( winCrop.top                 * yScale));
+        int insetR = int(ceilf((winWidth  - winCrop.right ) * xScale));
+        int insetB = int(ceilf((winHeight - winCrop.bottom) * yScale));
+
+        crop.left   += insetL;
+        crop.top    += insetT;
+        crop.right  -= insetR;
+        crop.bottom -= insetB;
+    }
+    return crop;
+}
+
 void Layer::setGeometry(
     const sp<const DisplayDevice>& hw,
         HWComposer::HWCLayerInterface& layer)
 {
-    LayerBase::setGeometry(hw, layer);
+    layer.setDefaultState();
 
     // enable this layer
     layer.setSkip(false);
@@ -233,7 +372,21 @@
         layer.setSkip(true);
     }
 
+    // this gives us only the "orientation" component of the transform
     const State& s(drawingState());
+    if (!isOpaque() || s.alpha != 0xFF) {
+        layer.setBlending(mPremultipliedAlpha ?
+                HWC_BLENDING_PREMULT :
+                HWC_BLENDING_COVERAGE);
+    }
+
+    // apply the layer's transform, followed by the display's global transform
+    // here we're guaranteed that the layer's transform preserves rects
+    Rect frame(s.transform.transform(computeBounds()));
+    frame.intersect(hw->getViewport(), &frame);
+    const Transform& tr(hw->getTransform());
+    layer.setFrame(tr.transform(frame));
+    layer.setCrop(computeCrop(hw));
     layer.setPlaneAlpha(s.alpha);
 
     /*
@@ -245,22 +398,29 @@
      */
 
     const Transform bufferOrientation(mCurrentTransform);
-    const Transform tr(hw->getTransform() * s.transform * bufferOrientation);
+    const Transform transform(tr * s.transform * bufferOrientation);
 
     // this gives us only the "orientation" component of the transform
-    const uint32_t finalTransform = tr.getOrientation();
-
-    // we can only handle simple transformation
-    if (finalTransform & Transform::ROT_INVALID) {
+    const uint32_t orientation = transform.getOrientation();
+    if (orientation & Transform::ROT_INVALID) {
+        // we can only handle simple transformation
         layer.setSkip(true);
     } else {
-        layer.setTransform(finalTransform);
+        layer.setTransform(orientation);
     }
 }
 
 void Layer::setPerFrameData(const sp<const DisplayDevice>& hw,
         HWComposer::HWCLayerInterface& layer) {
-    LayerBase::setPerFrameData(hw, layer);
+    // we have to set the visible region on every frame because
+    // we currently free it during onLayerDisplayed(), which is called
+    // after HWComposer::commit() -- every frame.
+    // Apply this display's projection's viewport to the visible region
+    // before giving it to the HWC HAL.
+    const Transform& tr = hw->getTransform();
+    Region visible = tr.transform(visibleRegion.intersect(hw->getViewport()));
+    layer.setVisibleRegionScreen(visible);
+
     // NOTE: buffer can be NULL if the client never drew into this
     // layer yet, or if we ran out of memory
     layer.setBuffer(mActiveBuffer);
@@ -285,6 +445,18 @@
     layer.setAcquireFenceFd(fenceFd);
 }
 
+// ---------------------------------------------------------------------------
+// drawing...
+// ---------------------------------------------------------------------------
+
+void Layer::draw(const sp<const DisplayDevice>& hw, const Region& clip) const {
+    onDraw(hw, clip);
+}
+
+void Layer::draw(const sp<const DisplayDevice>& hw) {
+    onDraw( hw, Region(hw->bounds()) );
+}
+
 void Layer::onDraw(const sp<const DisplayDevice>& hw, const Region& clip) const
 {
     ATRACE_CALL();
@@ -304,8 +476,8 @@
                 mFlinger->mDrawingState.layersSortedByZ);
         const size_t count = drawingLayers.size();
         for (size_t i=0 ; i<count ; ++i) {
-            const sp<LayerBase>& layer(drawingLayers[i]);
-            if (layer.get() == static_cast<LayerBase const*>(this))
+            const sp<Layer>& layer(drawingLayers[i]);
+            if (layer.get() == static_cast<Layer const*>(this))
                 break;
             under.orSelf( hw->getTransform().transform(layer->visibleRegion) );
         }
@@ -365,6 +537,119 @@
     glDisable(GL_TEXTURE_2D);
 }
 
+
+void Layer::clearWithOpenGL(const sp<const DisplayDevice>& hw, const Region& clip,
+        GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) const
+{
+    const uint32_t fbHeight = hw->getHeight();
+    glColor4f(red,green,blue,alpha);
+
+    glDisable(GL_TEXTURE_EXTERNAL_OES);
+    glDisable(GL_TEXTURE_2D);
+    glDisable(GL_BLEND);
+
+    LayerMesh mesh;
+    computeGeometry(hw, &mesh);
+
+    glVertexPointer(2, GL_FLOAT, 0, mesh.getVertices());
+    glDrawArrays(GL_TRIANGLE_FAN, 0, mesh.getVertexCount());
+}
+
+void Layer::clearWithOpenGL(
+        const sp<const DisplayDevice>& hw, const Region& clip) const {
+    clearWithOpenGL(hw, clip, 0,0,0,0);
+}
+
+void Layer::drawWithOpenGL(
+        const sp<const DisplayDevice>& hw, const Region& clip) const {
+    const uint32_t fbHeight = hw->getHeight();
+    const State& s(drawingState());
+
+    GLenum src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA;
+    if (CC_UNLIKELY(s.alpha < 0xFF)) {
+        const GLfloat alpha = s.alpha * (1.0f/255.0f);
+        if (mPremultipliedAlpha) {
+            glColor4f(alpha, alpha, alpha, alpha);
+        } else {
+            glColor4f(1, 1, 1, alpha);
+        }
+        glEnable(GL_BLEND);
+        glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA);
+        glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+    } else {
+        glColor4f(1, 1, 1, 1);
+        glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+        if (!isOpaque()) {
+            glEnable(GL_BLEND);
+            glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA);
+        } else {
+            glDisable(GL_BLEND);
+        }
+    }
+
+    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;
+    };
+
+
+    /*
+     * NOTE: the way we compute the texture coordinates here produces
+     * different results than when we take the HWC path -- in the later case
+     * the "source crop" is rounded to texel boundaries.
+     * This can produce significantly different results when the texture
+     * is scaled by a large amount.
+     *
+     * The GL code below is more logical (imho), and the difference with
+     * HWC is due to a limitation of the HWC API to integers -- a question
+     * is suspend is wether we should ignore this problem or revert to
+     * GL composition when a buffer scaling is applied (maybe with some
+     * minimal value)? Or, we could make GL behave like HWC -- but this feel
+     * like more of a hack.
+     */
+    const Rect win(computeBounds());
+
+    GLfloat left   = GLfloat(win.left)   / GLfloat(s.active.w);
+    GLfloat top    = GLfloat(win.top)    / GLfloat(s.active.h);
+    GLfloat right  = GLfloat(win.right)  / GLfloat(s.active.w);
+    GLfloat bottom = GLfloat(win.bottom) / GLfloat(s.active.h);
+
+    TexCoords texCoords[4];
+    texCoords[0].u = left;
+    texCoords[0].v = top;
+    texCoords[1].u = left;
+    texCoords[1].v = bottom;
+    texCoords[2].u = right;
+    texCoords[2].v = bottom;
+    texCoords[3].u = right;
+    texCoords[3].v = top;
+    for (int i = 0; i < 4; i++) {
+        texCoords[i].v = 1.0f - texCoords[i].v;
+    }
+
+    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+    glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
+    glVertexPointer(2, GL_FLOAT, 0, mesh.getVertices());
+    glDrawArrays(GL_TRIANGLE_FAN, 0, mesh.getVertexCount());
+
+    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+    glDisable(GL_BLEND);
+}
+
+void Layer::setFiltering(bool filtering) {
+    mFiltering = filtering;
+}
+
+bool Layer::getFiltering() const {
+    return mFiltering;
+}
+
 // As documented in libhardware header, formats in the range
 // 0x100 - 0x1FF are specific to the HAL implementation, and
 // are known to have no alpha channel
@@ -383,6 +668,29 @@
     return (err || info.h_alpha <= info.l_alpha);
 }
 
+// ----------------------------------------------------------------------------
+// local state
+// ----------------------------------------------------------------------------
+
+void Layer::computeGeometry(const sp<const DisplayDevice>& hw, LayerMesh* mesh) const
+{
+    const Layer::State& s(drawingState());
+    const Transform tr(hw->getTransform() * s.transform);
+    const uint32_t hw_h = hw->getHeight();
+    Rect win(s.active.w, s.active.h);
+    if (!s.active.crop.isEmpty()) {
+        win.intersect(s.active.crop, &win);
+    }
+    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];
+        }
+    }
+}
 
 bool Layer::isOpaque() const
 {
@@ -404,8 +712,39 @@
             (activeBuffer->getUsage() & GRALLOC_USAGE_PROTECTED);
 }
 
-uint32_t Layer::doTransaction(uint32_t flags)
-{
+bool Layer::isFixedSize() const {
+    return mCurrentScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE;
+}
+
+bool Layer::isCropped() const {
+    return !mCurrentCrop.isEmpty();
+}
+
+bool Layer::needsFiltering(const sp<const DisplayDevice>& hw) const {
+    return mNeedsFiltering || hw->needsFiltering();
+}
+
+void Layer::setVisibleRegion(const Region& visibleRegion) {
+    // always called from main thread
+    this->visibleRegion = visibleRegion;
+}
+
+void Layer::setCoveredRegion(const Region& coveredRegion) {
+    // always called from main thread
+    this->coveredRegion = coveredRegion;
+}
+
+void Layer::setVisibleNonTransparentRegion(const Region&
+        setVisibleNonTransparentRegion) {
+    // always called from main thread
+    this->visibleNonTransparentRegion = setVisibleNonTransparentRegion;
+}
+
+// ----------------------------------------------------------------------------
+// transaction
+// ----------------------------------------------------------------------------
+
+uint32_t Layer::doTransaction(uint32_t flags) {
     ATRACE_CALL();
 
     const Layer::State& front(drawingState());
@@ -464,7 +803,7 @@
                                    (temp.requested.h != temp.active.h);
 
         if (resizePending) {
-            // don't let LayerBase::doTransaction update the drawing state
+            // don't let Layer::doTransaction update the drawing state
             // if we have a pending resize, unless we are in fixed-size mode.
             // the drawing state will be updated only once we receive a buffer
             // with the correct size.
@@ -477,15 +816,117 @@
         }
     }
 
-    return LayerBase::doTransaction(flags);
+    // always set active to requested, unless we're asked not to
+    // this is used by Layer, which special cases resizes.
+    if (flags & eDontUpdateGeometryState)  {
+    } else {
+        Layer::State& editTemp(currentState());
+        editTemp.active = temp.requested;
+    }
+
+    if (front.active != temp.active) {
+        // invalidate and recompute the visible regions if needed
+        flags |= Layer::eVisibleRegion;
+    }
+
+    if (temp.sequence != front.sequence) {
+        // invalidate and recompute the visible regions if needed
+        flags |= eVisibleRegion;
+        this->contentDirty = true;
+
+        // we may use linear filtering, if the matrix scales us
+        const uint8_t type = temp.transform.getType();
+        mNeedsFiltering = (!temp.transform.preserveRects() ||
+                (type >= Transform::SCALE));
+    }
+
+    // Commit the transaction
+    commitTransaction();
+    return flags;
 }
 
-bool Layer::isFixedSize() const {
-    return mCurrentScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE;
+void Layer::commitTransaction() {
+    mDrawingState = mCurrentState;
 }
 
-bool Layer::isCropped() const {
-    return !mCurrentCrop.isEmpty();
+uint32_t Layer::getTransactionFlags(uint32_t flags) {
+    return android_atomic_and(~flags, &mTransactionFlags) & flags;
+}
+
+uint32_t Layer::setTransactionFlags(uint32_t flags) {
+    return android_atomic_or(flags, &mTransactionFlags);
+}
+
+bool Layer::setPosition(float x, float y) {
+    if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y)
+        return false;
+    mCurrentState.sequence++;
+    mCurrentState.transform.set(x, y);
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+bool Layer::setLayer(uint32_t z) {
+    if (mCurrentState.z == z)
+        return false;
+    mCurrentState.sequence++;
+    mCurrentState.z = z;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+bool Layer::setSize(uint32_t w, uint32_t h) {
+    if (mCurrentState.requested.w == w && mCurrentState.requested.h == h)
+        return false;
+    mCurrentState.requested.w = w;
+    mCurrentState.requested.h = h;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+bool Layer::setAlpha(uint8_t alpha) {
+    if (mCurrentState.alpha == alpha)
+        return false;
+    mCurrentState.sequence++;
+    mCurrentState.alpha = alpha;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix) {
+    mCurrentState.sequence++;
+    mCurrentState.transform.set(
+            matrix.dsdx, matrix.dsdy, matrix.dtdx, matrix.dtdy);
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+bool Layer::setTransparentRegionHint(const Region& transparent) {
+    mCurrentState.sequence++;
+    mCurrentState.transparentRegion = transparent;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+bool Layer::setFlags(uint8_t flags, uint8_t mask) {
+    const uint32_t newFlags = (mCurrentState.flags & ~mask) | (flags & mask);
+    if (mCurrentState.flags == newFlags)
+        return false;
+    mCurrentState.sequence++;
+    mCurrentState.flags = newFlags;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+bool Layer::setCrop(const Rect& crop) {
+    if (mCurrentState.requested.crop == crop)
+        return false;
+    mCurrentState.sequence++;
+    mCurrentState.requested.crop = crop;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool Layer::setLayerStack(uint32_t layerStack) {
+    if (mCurrentState.layerStack == layerStack)
+        return false;
+    mCurrentState.sequence++;
+    mCurrentState.layerStack = layerStack;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
 }
 
 // ----------------------------------------------------------------------------
@@ -528,7 +969,9 @@
 }
 
 bool Layer::isVisible() const {
-    return LayerBase::isVisible() && (mActiveBuffer != NULL);
+    const Layer::State& s(mDrawingState);
+    return !(s.flags & layer_state_t::eLayerHidden) && s.alpha
+            && (mActiveBuffer != NULL);
 }
 
 Region Layer::latchBuffer(bool& recomputeVisibleRegions)
@@ -702,44 +1145,6 @@
     return outDirtyRegion;
 }
 
-void Layer::dump(String8& result, char* buffer, size_t SIZE) const
-{
-    LayerBase::dump(result, buffer, SIZE);
-
-    sp<const GraphicBuffer> buf0(mActiveBuffer);
-    uint32_t w0=0, h0=0, s0=0, f0=0;
-    if (buf0 != 0) {
-        w0 = buf0->getWidth();
-        h0 = buf0->getHeight();
-        s0 = buf0->getStride();
-        f0 = buf0->format;
-    }
-    snprintf(buffer, SIZE,
-            "      "
-            "format=%2d, activeBuffer=[%4ux%4u:%4u,%3X],"
-            " queued-frames=%d, mRefreshPending=%d\n",
-            mFormat, w0, h0, s0,f0,
-            mQueuedFrames, mRefreshPending);
-
-    result.append(buffer);
-
-    if (mSurfaceFlingerConsumer != 0) {
-        mSurfaceFlingerConsumer->dump(result, "            ", buffer, SIZE);
-    }
-}
-
-void Layer::dumpStats(String8& result, char* buffer, size_t SIZE) const
-{
-    LayerBase::dumpStats(result, buffer, SIZE);
-    mFrameTracker.dump(result);
-}
-
-void Layer::clearStats()
-{
-    LayerBase::clearStats();
-    mFrameTracker.clear();
-}
-
 uint32_t Layer::getEffectiveUsage(uint32_t usage) const
 {
     // TODO: should we do something special if mSecure is set?
@@ -766,6 +1171,86 @@
     mSurfaceFlingerConsumer->setTransformHint(orientation);
 }
 
+// ----------------------------------------------------------------------------
+// debugging
+// ----------------------------------------------------------------------------
+
+void Layer::dump(String8& result, char* buffer, size_t SIZE) const
+{
+    const Layer::State& s(drawingState());
+
+    snprintf(buffer, SIZE,
+            "+ %s %p (%s)\n",
+            getTypeId(), this, getName().string());
+    result.append(buffer);
+
+    s.transparentRegion.dump(result, "transparentRegion");
+    visibleRegion.dump(result, "visibleRegion");
+    sp<Client> client(mClientRef.promote());
+
+    snprintf(buffer, SIZE,
+            "      "
+            "layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), crop=(%4d,%4d,%4d,%4d), "
+            "isOpaque=%1d, invalidate=%1d, "
+            "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n"
+            "      client=%p\n",
+            s.layerStack, s.z, s.transform.tx(), s.transform.ty(), s.active.w, s.active.h,
+            s.active.crop.left, s.active.crop.top,
+            s.active.crop.right, s.active.crop.bottom,
+            isOpaque(), contentDirty,
+            s.alpha, s.flags,
+            s.transform[0][0], s.transform[0][1],
+            s.transform[1][0], s.transform[1][1],
+            client.get());
+    result.append(buffer);
+
+    sp<const GraphicBuffer> buf0(mActiveBuffer);
+    uint32_t w0=0, h0=0, s0=0, f0=0;
+    if (buf0 != 0) {
+        w0 = buf0->getWidth();
+        h0 = buf0->getHeight();
+        s0 = buf0->getStride();
+        f0 = buf0->format;
+    }
+    snprintf(buffer, SIZE,
+            "      "
+            "format=%2d, activeBuffer=[%4ux%4u:%4u,%3X],"
+            " queued-frames=%d, mRefreshPending=%d\n",
+            mFormat, w0, h0, s0,f0,
+            mQueuedFrames, mRefreshPending);
+
+    result.append(buffer);
+
+    if (mSurfaceFlingerConsumer != 0) {
+        mSurfaceFlingerConsumer->dump(result, "            ", buffer, SIZE);
+    }
+}
+
+
+void Layer::shortDump(String8& result, char* scratch, size_t size) const {
+    Layer::dump(result, scratch, size);
+}
+
+void Layer::dumpStats(String8& result, char* buffer, size_t SIZE) const {
+    mFrameTracker.dump(result);
+}
+
+void Layer::clearStats() {
+    mFrameTracker.clear();
+}
+
+// ---------------------------------------------------------------------------
+
+Layer::LayerCleaner::LayerCleaner(const sp<SurfaceFlinger>& flinger,
+        const sp<Layer>& layer)
+    : mFlinger(flinger), mLayer(layer) {
+}
+
+Layer::LayerCleaner::~LayerCleaner() {
+    // destroy client resources
+    mFlinger->onLayerDestroyed(mLayer);
+}
+
 // ---------------------------------------------------------------------------