drm_hwcomposer: cache the framebuffers given to the GLWorker

This saves some work in EGL/GL that would normally get repeated every frame.
Because the same framebuffers are generally used every frame, this CL adds a
cache containing all the EGL/GL objects that correspond to the given
GraphicBuffer. The cache initially holds a strong reference while rendering,
but once glFinish is called, only a weak reference is held until rendering
to that same GraphicBuffer starts again. The weak reference is used to check
if that GraphicBuffer has been destroyed, which means the corresponding EGL/GL
resources should be destroyed.

The cache can be disabled with the following command:
$ adb shell setprop hwc.drm.use_framebuffer_cache 0

Change-Id: Iddee1efc5bad67f6c4479e7f402354e229c3b0a2
diff --git a/drmdisplaycompositor.cpp b/drmdisplaycompositor.cpp
index 7259d07..9130085 100644
--- a/drmdisplaycompositor.cpp
+++ b/drmdisplaycompositor.cpp
@@ -184,8 +184,9 @@
     }
   }
 
-  ret = pre_compositor_->CompositeAndFinish(
-      pre_comp_layers.data(), pre_comp_layers.size(), fb.buffer());
+  ret = pre_compositor_->Composite(pre_comp_layers.data(),
+                                   pre_comp_layers.size(), fb.buffer());
+  pre_compositor_->Finish();
 
   for (auto &pre_comp_layer : pre_comp_layers) {
     if (pre_comp_layer.acquireFenceFd >= 0) {
diff --git a/glworker.cpp b/glworker.cpp
index 10e895f..9346d27 100644
--- a/glworker.cpp
+++ b/glworker.cpp
@@ -23,6 +23,8 @@
 
 #include <sys/resource.h>
 
+#include <cutils/properties.h>
+
 #include <hardware/hardware.h>
 #include <hardware/hwcomposer.h>
 
@@ -552,7 +554,7 @@
 }
 
 int GLWorkerCompositor::Composite(hwc_layer_1 *layers, size_t num_layers,
-                                  sp<GraphicBuffer> framebuffer) {
+                                  const sp<GraphicBuffer> &framebuffer) {
   ATRACE_CALL();
   int ret = 0;
   size_t i;
@@ -565,37 +567,10 @@
 
   GLint frame_width = framebuffer->getWidth();
   GLint frame_height = framebuffer->getHeight();
-  EGLSyncKHR finished_sync;
-
-  AutoEGLDisplayImage egl_fb_image(
-      egl_display_,
-      eglCreateImageKHR(egl_display_, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
-                        (EGLClientBuffer)framebuffer->getNativeBuffer(),
-                        NULL /* no attribs */));
-
-  if (egl_fb_image.image() == EGL_NO_IMAGE_KHR) {
-    ALOGE("Failed to make image from target buffer: %s", GetEGLError());
-    return -EINVAL;
-  }
-
-  GLuint gl_fb_tex;
-  glGenTextures(1, &gl_fb_tex);
-  AutoGLTexture gl_fb_tex_auto(gl_fb_tex);
-  glBindTexture(GL_TEXTURE_2D, gl_fb_tex);
-  glEGLImageTargetTexture2DOES(GL_TEXTURE_2D,
-                               (GLeglImageOES)egl_fb_image.image());
-  glBindTexture(GL_TEXTURE_2D, 0);
-
-  GLuint gl_fb;
-  glGenFramebuffers(1, &gl_fb);
-  AutoGLFramebuffer gl_fb_auto(gl_fb);
-  glBindFramebuffer(GL_FRAMEBUFFER, gl_fb);
-  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
-                         gl_fb_tex, 0);
-
-  if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
-    ALOGE("Failed framebuffer check for created target buffer: %s",
-          GetGLFramebufferError());
+  CachedFramebuffer *cached_framebuffer =
+      PrepareAndCacheFramebuffer(framebuffer);
+  if (cached_framebuffer == NULL) {
+    ALOGE("Composite failed because of failed framebuffer");
     return -EINVAL;
   }
 
@@ -703,12 +678,103 @@
   return ret;
 }
 
-int GLWorkerCompositor::CompositeAndFinish(hwc_layer_1 *layers,
-                                           size_t num_layers,
-                                           sp<GraphicBuffer> framebuffer) {
-  int ret = Composite(layers, num_layers, framebuffer);
+void GLWorkerCompositor::Finish() {
+  ATRACE_CALL();
   glFinish();
-  return ret;
+
+  char use_framebuffer_cache_opt[PROPERTY_VALUE_MAX];
+  property_get("hwc.drm.use_framebuffer_cache", use_framebuffer_cache_opt, "1");
+  bool use_framebuffer_cache = atoi(use_framebuffer_cache_opt);
+
+  if (use_framebuffer_cache) {
+    for (auto &fb : cached_framebuffers_)
+      fb.strong_framebuffer.clear();
+  } else {
+    cached_framebuffers_.clear();
+  }
+}
+
+GLWorkerCompositor::CachedFramebuffer::CachedFramebuffer(
+    const sp<GraphicBuffer> &gb, AutoEGLDisplayImage &&image,
+    AutoGLTexture &&tex, AutoGLFramebuffer &&fb)
+    : strong_framebuffer(gb),
+      weak_framebuffer(gb),
+      egl_fb_image(std::move(image)),
+      gl_fb_tex(std::move(tex)),
+      gl_fb(std::move(fb)) {
+}
+
+bool GLWorkerCompositor::CachedFramebuffer::Promote() {
+  if (strong_framebuffer.get() != NULL)
+    return true;
+  strong_framebuffer = weak_framebuffer.promote();
+  return strong_framebuffer.get() != NULL;
+}
+
+GLWorkerCompositor::CachedFramebuffer *
+GLWorkerCompositor::FindCachedFramebuffer(
+    const sp<GraphicBuffer> &framebuffer) {
+  for (auto &fb : cached_framebuffers_)
+    if (fb.weak_framebuffer == framebuffer)
+      return &fb;
+  return NULL;
+}
+
+GLWorkerCompositor::CachedFramebuffer *
+GLWorkerCompositor::PrepareAndCacheFramebuffer(
+    const sp<GraphicBuffer> &framebuffer) {
+  CachedFramebuffer *cached_framebuffer = FindCachedFramebuffer(framebuffer);
+  if (cached_framebuffer != NULL) {
+    if (cached_framebuffer->Promote()) {
+      glBindFramebuffer(GL_FRAMEBUFFER, cached_framebuffer->gl_fb.get());
+      return cached_framebuffer;
+    }
+
+    for (auto it = cached_framebuffers_.begin();
+         it != cached_framebuffers_.end(); ++it) {
+      if (it->weak_framebuffer == framebuffer) {
+        cached_framebuffers_.erase(it);
+        break;
+      }
+    }
+  }
+
+  AutoEGLDisplayImage egl_fb_image(
+      egl_display_,
+      eglCreateImageKHR(egl_display_, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+                        (EGLClientBuffer)framebuffer->getNativeBuffer(),
+                        NULL /* no attribs */));
+
+  if (egl_fb_image.image() == EGL_NO_IMAGE_KHR) {
+    ALOGE("Failed to make image from target buffer: %s", GetEGLError());
+    return NULL;
+  }
+
+  GLuint gl_fb_tex;
+  glGenTextures(1, &gl_fb_tex);
+  AutoGLTexture gl_fb_tex_auto(gl_fb_tex);
+  glBindTexture(GL_TEXTURE_2D, gl_fb_tex);
+  glEGLImageTargetTexture2DOES(GL_TEXTURE_2D,
+                               (GLeglImageOES)egl_fb_image.image());
+  glBindTexture(GL_TEXTURE_2D, 0);
+
+  GLuint gl_fb;
+  glGenFramebuffers(1, &gl_fb);
+  AutoGLFramebuffer gl_fb_auto(gl_fb);
+  glBindFramebuffer(GL_FRAMEBUFFER, gl_fb);
+  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+                         gl_fb_tex, 0);
+
+  if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+    ALOGE("Failed framebuffer check for created target buffer: %s",
+          GetGLFramebufferError());
+    return NULL;
+  }
+
+  cached_framebuffers_.emplace_back(framebuffer, std::move(egl_fb_image),
+                                    std::move(gl_fb_tex_auto),
+                                    std::move(gl_fb_auto));
+  return &cached_framebuffers_.back();
 }
 
 }  // namespace android
diff --git a/glworker.h b/glworker.h
index 11f4bd9..ce41444 100644
--- a/glworker.h
+++ b/glworker.h
@@ -39,18 +39,39 @@
   ~GLWorkerCompositor();
 
   int Init();
-
   int Composite(hwc_layer_1 *layers, size_t num_layers,
-                sp<GraphicBuffer> framebuffer);
-  int CompositeAndFinish(hwc_layer_1 *layers, size_t num_layers,
-                         sp<GraphicBuffer> framebuffer);
+                const sp<GraphicBuffer> &framebuffer);
+  void Finish();
 
  private:
+  struct CachedFramebuffer {
+    // If the strong_framebuffer is non-NULL, we are holding a strong reference
+    // until we are sure rendering is done. The weak reference will be equal in
+    // that case.
+    sp<GraphicBuffer> strong_framebuffer;
+    wp<GraphicBuffer> weak_framebuffer;
+    AutoEGLDisplayImage egl_fb_image;
+    AutoGLTexture gl_fb_tex;
+    AutoGLFramebuffer gl_fb;
+
+    CachedFramebuffer(const sp<GraphicBuffer> &gb, AutoEGLDisplayImage &&image,
+                      AutoGLTexture &&tex, AutoGLFramebuffer &&fb);
+
+    bool Promote();
+  };
+
+  CachedFramebuffer *FindCachedFramebuffer(
+      const sp<GraphicBuffer> &framebuffer);
+  CachedFramebuffer *PrepareAndCacheFramebuffer(
+      const sp<GraphicBuffer> &framebuffer);
+
   EGLDisplay egl_display_;
   EGLContext egl_ctx_;
 
   std::vector<AutoGLProgram> blend_programs_;
   AutoGLBuffer vertex_buffer_;
+
+  std::vector<CachedFramebuffer> cached_framebuffers_;
 };
 }