Allocate layers from the layers pool.
Bug #3413433

This change will be beneficial to Launcher to avoid hiccups when
swiping pages of icons. When a layer is discarded, it is kept
in the layers pool instead of being destroyed right away. This
favors memory reuse over allocations.

Change-Id: Ifb6944ba83d6ceb67c331527c0827b26ce648eb1
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index bffab95..ebf7aa0 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -20,6 +20,7 @@
 
 #include "Caches.h"
 #include "Properties.h"
+#include "LayerRenderer.h"
 
 namespace android {
 
@@ -116,12 +117,7 @@
     size_t count = mLayerGarbage.size();
     for (size_t i = 0; i < count; i++) {
         Layer* layer = mLayerGarbage.itemAt(i);
-        if (layer) {
-            if (layer->fbo) glDeleteFramebuffers(1, &layer->fbo);
-            if (layer->texture) glDeleteTextures(1, &layer->texture);
-
-            delete layer;
-        }
+        LayerRenderer::destroyLayer(layer);
     }
     mLayerGarbage.clear();
 }
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index 7667af5..a9710ad 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -128,6 +128,31 @@
     return layer;
 }
 
+bool LayerCache::resize(Layer* layer, const uint32_t width, const uint32_t height) {
+    // TODO: We should be smarter and see if we have a texture of the appropriate
+    //       size already in the cache, and reuse it instead of creating a new one
+
+    LayerEntry entry(width, height);
+    if (entry.mWidth <= layer->width && entry.mHeight <= layer->height) {
+        return true;
+    }
+
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, layer->texture);
+
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, entry.mWidth, entry.mHeight, 0,
+            GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+
+    if (glGetError() != GL_NO_ERROR) {
+        return false;
+    }
+
+    layer->width = entry.mWidth;
+    layer->height = entry.mHeight;
+
+    return true;
+}
+
 bool LayerCache::put(Layer* layer) {
     const uint32_t size = layer->width * layer->height * 4;
     // Don't even try to cache a layer that's bigger than the cache
diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h
index 1333a73..d2d5f39 100644
--- a/libs/hwui/LayerCache.h
+++ b/libs/hwui/LayerCache.h
@@ -76,6 +76,17 @@
      * Clears the cache. This causes all layers to be deleted.
      */
     void clear();
+    /**
+     * Resize the specified layer if needed.
+     *
+     * @param layer The layer to resize
+     * @param width The new width of the layer
+     * @param height The new height of the layer
+     *
+     * @return True if the layer was resized or nothing happened, false if
+     *         a failure occurred during the resizing operation
+     */
+    bool resize(Layer* layer, const uint32_t width, const uint32_t height);
 
     /**
      * Sets the maximum size of the cache in bytes.
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 60ff0bf..24f9739 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -18,6 +18,7 @@
 
 #include <ui/Rect.h>
 
+#include "LayerCache.h"
 #include "LayerRenderer.h"
 #include "Properties.h"
 #include "Rect.h"
@@ -34,21 +35,24 @@
 
     glBindFramebuffer(GL_FRAMEBUFFER, mLayer->fbo);
 
+    const float width = mLayer->layer.getWidth();
+    const float height = mLayer->layer.getHeight();
+
 #if RENDER_LAYERS_AS_REGIONS
     Rect dirty(left, top, right, bottom);
     if (dirty.isEmpty() || (dirty.left <= 0 && dirty.top <= 0 &&
-            dirty.right >= mLayer->width && dirty.bottom >= mLayer->height)) {
+            dirty.right >= width && dirty.bottom >= height)) {
         mLayer->region.clear();
-        dirty.set(0.0f, 0.0f, mLayer->width, mLayer->height);
+        dirty.set(0.0f, 0.0f, width, height);
     } else {
-        dirty.intersect(0.0f, 0.0f, mLayer->width, mLayer->height);
+        dirty.intersect(0.0f, 0.0f, width, height);
         android::Rect r(dirty.left, dirty.top, dirty.right, dirty.bottom);
         mLayer->region.subtractSelf(r);
     }
 
     OpenGLRenderer::prepareDirty(dirty.left, dirty.top, dirty.right, dirty.bottom, opaque);
 #else
-    OpenGLRenderer::prepareDirty(0.0f, 0.0f, mLayer->width, mLayer->height, opaque);
+    OpenGLRenderer::prepareDirty(0.0f, 0.0f, width, height, opaque);
 #endif
 }
 
@@ -162,64 +166,56 @@
 Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque) {
     LAYER_RENDERER_LOGD("Creating new layer %dx%d", width, height);
 
-    Layer* layer = new Layer(width, height);
+    GLuint fbo = Caches::getInstance().fboCache.get();
+    if (!fbo) {
+        LOGW("Could not obtain an FBO");
+        return NULL;
+    }
+
+    glActiveTexture(GL_TEXTURE0);
+    Layer* layer = Caches::getInstance().layerCache.get(width, height);
+    if (!layer) {
+        LOGW("Could not obtain a layer");
+        return NULL;
+    }
+
+    layer->fbo = fbo;
+    layer->layer.set(0.0f, 0.0f, width, height);
+    layer->texCoords.set(0.0f, height / float(layer->height),
+            width / float(layer->width), 0.0f);
+    layer->alpha = 255;
+    layer->mode = SkXfermode::kSrcOver_Mode;
+    layer->blend = !isOpaque;
+    layer->colorFilter = NULL;
+    layer->region.clear();
 
     GLuint previousFbo;
     glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
 
-    glGenFramebuffers(1, &layer->fbo);
     glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo);
-
-    if (glGetError() != GL_NO_ERROR) {
-        glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
-        glDeleteBuffers(1, &layer->fbo);
-        return 0;
-    }
-
-    glActiveTexture(GL_TEXTURE0);
-    glGenTextures(1, &layer->texture);
     glBindTexture(GL_TEXTURE_2D, layer->texture);
 
-    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+    // Initialize the texture if needed
+    if (layer->empty) {
+        layer->empty = false;
+        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, layer->width, layer->height, 0,
+                GL_RGBA, GL_UNSIGNED_BYTE, NULL);
 
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
-    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
-            GL_RGBA, GL_UNSIGNED_BYTE, NULL);
-
-    if (glGetError() != GL_NO_ERROR) {
-        glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
-        glDeleteBuffers(1, &layer->fbo);
-        glDeleteTextures(1, &layer->texture);
-        delete layer;
-        return 0;
+        if (glGetError() != GL_NO_ERROR) {
+            LOGD("Could not allocate texture");
+            glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
+            glDeleteTextures(1, &layer->texture);
+            Caches::getInstance().fboCache.put(fbo);
+            delete layer;
+            return NULL;
+        }
     }
 
     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
             layer->texture, 0);
 
-    if (glGetError() != GL_NO_ERROR) {
-        glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
-        glDeleteBuffers(1, &layer->fbo);
-        glDeleteTextures(1, &layer->texture);
-        delete layer;
-        return 0;
-    }
-
     glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
 
-    layer->layer.set(0.0f, 0.0f, width, height);
-    layer->texCoords.set(0.0f, 1.0f, 1.0f, 0.0f);
-    layer->alpha = 255;
-    layer->mode = SkXfermode::kSrcOver_Mode;
-    layer->blend = !isOpaque;
-    layer->empty = false;
-    layer->colorFilter = NULL;
-
     return layer;
 }
 
@@ -227,27 +223,17 @@
     if (layer) {
         LAYER_RENDERER_LOGD("Resizing layer fbo = %d to %dx%d", layer->fbo, width, height);
 
-        glActiveTexture(GL_TEXTURE0);
-        glBindTexture(GL_TEXTURE_2D, layer->texture);
-
-        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
-                GL_RGBA, GL_UNSIGNED_BYTE, NULL);
-
-        if (glGetError() != GL_NO_ERROR) {
-            glDeleteBuffers(1, &layer->fbo);
-            glDeleteTextures(1, &layer->texture);
-
-            layer->width = 0;
-            layer->height = 0;
-            layer->fbo = 0;
-            layer->texture = 0;
-
+        if (Caches::getInstance().layerCache.resize(layer, width, height)) {
+            layer->layer.set(0.0f, 0.0f, width, height);
+            layer->texCoords.set(0.0f, height / float(layer->height),
+                    width / float(layer->width), 0.0f);
+        } else {
+            if (layer->texture) glDeleteTextures(1, &layer->texture);
+            delete layer;
             return false;
         }
-
-        layer->width = width;
-        layer->height = height;
     }
+
     return true;
 }
 
@@ -255,10 +241,16 @@
     if (layer) {
         LAYER_RENDERER_LOGD("Destroying layer, fbo = %d", layer->fbo);
 
-        if (layer->fbo) glDeleteFramebuffers(1, &layer->fbo);
-        if (layer->texture) glDeleteTextures(1, &layer->texture);
+        if (layer->fbo) {
+            Caches::getInstance().fboCache.put(layer->fbo);
+        }
 
-        delete layer;
+        if (!Caches::getInstance().layerCache.put(layer)) {
+            if (layer->texture) glDeleteTextures(1, &layer->texture);
+            delete layer;
+        } else {
+            layer->region.clear();
+        }
     }
 }
 
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 2f230b5..2d8b6f3 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -64,14 +64,14 @@
 // Converts a number of mega-bytes into bytes
 #define MB(s) s * 1024 * 1024
 
-#define DEFAULT_TEXTURE_CACHE_SIZE 20.0f
-#define DEFAULT_LAYER_CACHE_SIZE 8.0f
+#define DEFAULT_TEXTURE_CACHE_SIZE 24.0f
+#define DEFAULT_LAYER_CACHE_SIZE 24.0f
 #define DEFAULT_PATH_CACHE_SIZE 4.0f
 #define DEFAULT_SHAPE_CACHE_SIZE 1.0f
 #define DEFAULT_PATCH_CACHE_SIZE 512
 #define DEFAULT_GRADIENT_CACHE_SIZE 0.5f
 #define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f
-#define DEFAULT_FBO_CACHE_SIZE 12
+#define DEFAULT_FBO_CACHE_SIZE 16
 
 #define DEFAULT_TEXT_GAMMA 1.4f
 #define DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD 64