Create FBOs in LayerCache.

This moves a bit of code away from OpenGLRenderer and matches
what happens with the TextureCache.

Change-Id: I3c67f54f83d7240770daa015861c0e75a1dd8c42
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index 882ad83..bcda45e7 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -18,6 +18,8 @@
 
 #include <GLES2/gl2.h>
 
+#include <utils/Log.h>
+
 #include "LayerCache.h"
 
 namespace android {
@@ -84,11 +86,55 @@
     mCache.setOnEntryRemovedListener(NULL);
 }
 
-Layer* LayerCache::get(LayerSize& size) {
+Layer* LayerCache::get(LayerSize& size, GLuint previousFbo) {
     Layer* layer = mCache.remove(size);
     if (layer) {
+        LAYER_LOGD("Reusing layer");
+
         mSize -= layer->layer.getWidth() * layer->layer.getHeight() * 4;
+    } else {
+        LAYER_LOGD("Creating new layer");
+
+        layer = new Layer;
+        layer->blend = true;
+
+        // Generate the FBO and attach the texture
+        glGenFramebuffers(1, &layer->fbo);
+        glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo);
+
+        // Generate the texture in which the FBO will draw
+        glGenTextures(1, &layer->texture);
+        glBindTexture(GL_TEXTURE_2D, layer->texture);
+
+        // The FBO will not be scaled, so we can use lower quality filtering
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+        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, size.width, size.height, 0,
+                GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+        glBindTexture(GL_TEXTURE_2D, 0);
+
+        // Bind texture to FBO
+        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+                layer->texture, 0);
+
+        GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+        if (status != GL_FRAMEBUFFER_COMPLETE) {
+            LOGE("Framebuffer incomplete (GL error code 0x%x)", status);
+
+            glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
+
+            glDeleteFramebuffers(1, &layer->fbo);
+            glDeleteTextures(1, &layer->texture);
+            delete layer;
+
+            return NULL;
+        }
     }
+
     return layer;
 }
 
diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h
index 519552d..adc6713 100644
--- a/libs/hwui/LayerCache.h
+++ b/libs/hwui/LayerCache.h
@@ -23,6 +23,24 @@
 namespace android {
 namespace uirenderer {
 
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Debug
+#define DEBUG_LAYERS 0
+
+// Debug
+#if DEBUG_LAYERS
+    #define LAYER_LOGD(...) LOGD(__VA_ARGS__)
+#else
+    #define LAYER_LOGD(...)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Cache
+///////////////////////////////////////////////////////////////////////////////
+
 class LayerCache: public OnEntryRemoved<LayerSize, Layer*> {
 public:
     LayerCache(uint32_t maxByteSize);
@@ -35,13 +53,25 @@
     void operator()(LayerSize& bitmap, Layer*& texture);
 
     /**
-     * Returns the layer of specified dimensions, NULL if cannot be found.
+     * Returns the layer of specified dimensions. If not suitable layer
+     * can be found, a new one is created and returned. If creating a new
+     * layer fails, NULL is returned.
+     *
+     * When a layer is obtained from the cache, it is removed and the total
+     * size of the cache goes down.
+     *
+     * @param size The dimensions of the desired layer
+     * @param previousFbo The name of the FBO to bind to if creating a new
+     *        layer fails
      */
-    Layer* get(LayerSize& size);
+    Layer* get(LayerSize& size, GLuint previousFbo);
     /**
      * Adds the layer to the cache. The layer will not be added if there is
      * not enough space available.
      *
+     * @param size The dimensions of the layer
+     * @param layer The layer to add to the cache
+     *
      * @return True if the layer was added, false otherwise.
      */
     bool put(LayerSize& size, Layer* layer);
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index ae7719c..027ed79 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -34,9 +34,6 @@
 // Defines
 ///////////////////////////////////////////////////////////////////////////////
 
-// Debug
-#define DEBUG_LAYERS 0
-
 // These properties are defined in mega-bytes
 #define PROPERTY_TEXTURE_CACHE_SIZE "ro.hwui.texture_cache_size"
 #define PROPERTY_LAYER_CACHE_SIZE "ro.hwui.layer_cache_size"
@@ -51,13 +48,6 @@
 #define SV(x, y) { { x, y } }
 #define FV(x, y, u, v) { { x, y }, { u, v } }
 
-// Debug
-#if DEBUG_LAYERS
-    #define LAYER_LOGD(...) LOGD(__VA_ARGS__)
-#else
-    #define LAYER_LOGD(...)
-#endif
-
 ///////////////////////////////////////////////////////////////////////////////
 // Globals
 ///////////////////////////////////////////////////////////////////////////////
@@ -253,6 +243,8 @@
             layer->texture, layer->alpha, layer->mode, layer->blend, true);
 
     LayerSize size(rect.getWidth(), rect.getHeight());
+    // Failing to add the layer to the cache should happen only if the
+    // layer is too large
     if (!mLayerCache.put(size, layer)) {
         LAYER_LOGD("Deleting layer");
 
@@ -300,63 +292,20 @@
 bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top,
         float right, float bottom, int alpha, SkXfermode::Mode mode,int flags) {
 
-    LayerSize size(right - left, bottom - top);
-    Layer* layer = mLayerCache.get(size);
-
     LAYER_LOGD("Requesting layer %dx%d", size.width, size.height);
     LAYER_LOGD("Layer cache size = %d", mLayerCache.getSize());
 
+    GLuint previousFbo = snapshot->previous.get() ? snapshot->previous->fbo : 0;
+    LayerSize size(right - left, bottom - top);
+
+    // TODO VERY IMPORTANT: Fix TextView to not call saveLayer() all the time
+    Layer* layer = mLayerCache.get(size, previousFbo);
     if (!layer) {
-        LAYER_LOGD("Creating new layer");
-
-        layer = new Layer;
-        layer->blend = true;
-
-        // Generate the FBO and attach the texture
-        glGenFramebuffers(1, &layer->fbo);
-        glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo);
-
-        // Generate the texture in which the FBO will draw
-        glGenTextures(1, &layer->texture);
-        glBindTexture(GL_TEXTURE_2D, layer->texture);
-
-        // The FBO will not be scaled, so we can use lower quality filtering
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
-        // TODO VERY IMPORTANT: Fix TextView to not call saveLayer() all the time
-
-        const GLsizei width = right - left;
-        const GLsizei height = bottom - top;
-
-        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
-        glBindTexture(GL_TEXTURE_2D, 0);
-
-        // Bind texture to FBO
-        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
-                layer->texture, 0);
-
-        GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
-        if (status != GL_FRAMEBUFFER_COMPLETE) {
-            LOGD("Framebuffer incomplete (GL error code 0x%x)", status);
-
-            GLuint previousFbo = snapshot->previous.get() ? snapshot->previous->fbo : 0;
-            glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
-
-            glDeleteFramebuffers(1, &layer->fbo);
-            glDeleteTextures(1, &layer->texture);
-            delete layer;
-
-            return false;
-        }
-    } else {
-        LAYER_LOGD("Reusing layer");
-        glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo);
+        return false;
     }
 
+    glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo);
+
     // Clear the FBO
     glDisable(GL_SCISSOR_TEST);
     glClearColor(0.0f, 0.0f, 0.0f, 0.0f);