Update layers in a single batch at the beginning of a frame
Bug #7186819

Change-Id: Ice5926dfedfb3be3a3064e65008dafa2852407da
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 5d59a4c..bb004c0 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -78,6 +78,10 @@
     return mLayer->getFbo();
 }
 
+bool LayerRenderer::suppressErrorChecks() {
+    return true;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // Dirty region tracking
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h
index 8d42f7f..392f863 100644
--- a/libs/hwui/LayerRenderer.h
+++ b/libs/hwui/LayerRenderer.h
@@ -51,10 +51,6 @@
     virtual int prepareDirty(float left, float top, float right, float bottom, bool opaque);
     virtual void finish();
 
-    virtual bool hasLayer();
-    virtual Region* getRegion();
-    virtual GLint getTargetFbo();
-
     ANDROID_API static Layer* createTextureLayer(bool isOpaque);
     ANDROID_API static Layer* createLayer(uint32_t width, uint32_t height, bool isOpaque = false);
     ANDROID_API static bool resizeLayer(Layer* layer, uint32_t width, uint32_t height);
@@ -64,6 +60,12 @@
     ANDROID_API static void destroyLayerDeferred(Layer* layer);
     ANDROID_API static bool copyLayer(Layer* layer, SkBitmap* bitmap);
 
+protected:
+    virtual bool hasLayer();
+    virtual Region* getRegion();
+    virtual GLint getTargetFbo();
+    virtual bool suppressErrorChecks();
+
 private:
     void generateMesh();
 
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index da0900a..c475f20 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -177,6 +177,8 @@
     mSnapshot->setClip(left, top, right, bottom);
     mDirtyClip = opaque;
 
+    updateLayers();
+
     // If we know that we are going to redraw the entire framebuffer,
     // perform a discard to let the driver know we don't need to preserve
     // the back buffer for this frame.
@@ -230,33 +232,36 @@
 void OpenGLRenderer::finish() {
     endTiling();
 
+    if (!suppressErrorChecks()) {
 #if DEBUG_OPENGL
-    GLenum status = GL_NO_ERROR;
-    while ((status = glGetError()) != GL_NO_ERROR) {
-        ALOGD("GL error from OpenGLRenderer: 0x%x", status);
-        switch (status) {
-            case GL_INVALID_ENUM:
-                ALOGE("  GL_INVALID_ENUM");
-                break;
-            case GL_INVALID_VALUE:
-                ALOGE("  GL_INVALID_VALUE");
-                break;
-            case GL_INVALID_OPERATION:
-                ALOGE("  GL_INVALID_OPERATION");
-                break;
-            case GL_OUT_OF_MEMORY:
-                ALOGE("  Out of memory!");
-                break;
+        GLenum status = GL_NO_ERROR;
+        while ((status = glGetError()) != GL_NO_ERROR) {
+            ALOGD("GL error from OpenGLRenderer: 0x%x", status);
+            switch (status) {
+                case GL_INVALID_ENUM:
+                    ALOGE("  GL_INVALID_ENUM");
+                    break;
+                case GL_INVALID_VALUE:
+                    ALOGE("  GL_INVALID_VALUE");
+                    break;
+                case GL_INVALID_OPERATION:
+                    ALOGE("  GL_INVALID_OPERATION");
+                    break;
+                case GL_OUT_OF_MEMORY:
+                    ALOGE("  Out of memory!");
+                    break;
+            }
         }
-    }
 #endif
+
 #if DEBUG_MEMORY_USAGE
-    mCaches.dumpMemoryUsage();
-#else
-    if (mCaches.getDebugLevel() & kDebugMemory) {
         mCaches.dumpMemoryUsage();
-    }
+#else
+        if (mCaches.getDebugLevel() & kDebugMemory) {
+            mCaches.dumpMemoryUsage();
+        }
 #endif
+    }
 }
 
 void OpenGLRenderer::interrupt() {
@@ -393,6 +398,75 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
+// Layers
+///////////////////////////////////////////////////////////////////////////////
+
+bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) {
+    if (layer->deferredUpdateScheduled && layer->renderer && layer->displayList) {
+        OpenGLRenderer* renderer = layer->renderer;
+        Rect& dirty = layer->dirtyRect;
+
+        if (inFrame) endTiling();
+
+        renderer->setViewport(layer->layer.getWidth(), layer->layer.getHeight());
+        renderer->prepareDirty(dirty.left, dirty.top, dirty.right, dirty.bottom, !layer->isBlend());
+        renderer->drawDisplayList(layer->displayList, dirty, DisplayList::kReplayFlag_ClipChildren);
+        renderer->finish();
+
+        if (inFrame) {
+            resumeAfterLayer();
+            startTiling(mSnapshot);
+        }
+
+        dirty.setEmpty();
+        layer->deferredUpdateScheduled = false;
+        layer->renderer = NULL;
+        layer->displayList = NULL;
+
+        return true;
+    }
+
+    return false;
+}
+
+void OpenGLRenderer::updateLayers() {
+    int count = mLayerUpdates.size();
+    if (count > 0) {
+        startMark("Layer Updates");
+
+        // Note: it is very important to update the layers in reverse order
+        for (int i = count - 1; i >= 0; i--) {
+            Layer* layer = mLayerUpdates.itemAt(i);
+            updateLayer(layer, false);
+            mCaches.resourceCache.decrementRefcount(layer);
+        }
+        mLayerUpdates.clear();
+
+        glBindFramebuffer(GL_FRAMEBUFFER, getTargetFbo());
+        endMark();
+    }
+}
+
+void OpenGLRenderer::pushLayerUpdate(Layer* layer) {
+    if (layer) {
+        mLayerUpdates.push_back(layer);
+        mCaches.resourceCache.incrementRefcount(layer);
+    }
+}
+
+void OpenGLRenderer::clearLayerUpdates() {
+    size_t count = mLayerUpdates.size();
+    if (count > 0) {
+        mCaches.resourceCache.lock();
+        for (size_t i = 0; i < count; i++) {
+            mCaches.resourceCache.decrementRefcountLocked(mLayerUpdates.itemAt(i));
+        }
+        mCaches.resourceCache.unlock();
+        mLayerUpdates.clear();
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
 // State management
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -2629,25 +2703,7 @@
     }
 
     bool debugLayerUpdate = false;
-    if (layer->deferredUpdateScheduled && layer->renderer && layer->displayList) {
-        OpenGLRenderer* renderer = layer->renderer;
-        Rect& dirty = layer->dirtyRect;
-
-        endTiling();
-
-        renderer->setViewport(layer->layer.getWidth(), layer->layer.getHeight());
-        renderer->prepareDirty(dirty.left, dirty.top, dirty.right, dirty.bottom, !layer->isBlend());
-        renderer->drawDisplayList(layer->displayList, dirty, DisplayList::kReplayFlag_ClipChildren);
-        renderer->finish();
-
-        resumeAfterLayer();
-        startTiling(mSnapshot);
-
-        dirty.setEmpty();
-        layer->deferredUpdateScheduled = false;
-        layer->renderer = NULL;
-        layer->displayList = NULL;
-
+    if (updateLayer(layer, true)) {
         debugLayerUpdate = mCaches.debugLayersUpdates;
     }
 
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 4bbdde1..3e34336 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -132,6 +132,9 @@
     ANDROID_API void attachFunctor(Functor* functor);
     virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty);
 
+    ANDROID_API void pushLayerUpdate(Layer* layer);
+    ANDROID_API void clearLayerUpdates();
+
     ANDROID_API int getSaveCount() const;
     virtual int save(int flags);
     virtual void restore();
@@ -339,6 +342,13 @@
         return resultMode;
     }
 
+    /**
+     * Set to true to suppress error checks at the end of a frame.
+     */
+    virtual bool suppressErrorChecks() {
+        return false;
+    }
+
 private:
     /**
      * Ensures the state of the renderer is the same as the state of
@@ -720,6 +730,9 @@
     void finishDrawTexture();
     void accountForClear(SkXfermode::Mode mode);
 
+    bool updateLayer(Layer* layer, bool inFrame);
+    void updateLayers();
+
     /**
      * Renders the specified region as a series of rectangles. This method
      * is used for debugging only.
@@ -780,6 +793,8 @@
     Vector<Rect*> mLayers;
     // List of functors to invoke after a frame is drawn
     SortedVector<Functor*> mFunctors;
+    // List of layers to update at the beginning of a frame
+    Vector<Layer*> mLayerUpdates;
 
     // Indentity matrix
     const mat4 mIdentity;