drm_hwcomposer: enhance stability using various wrapper classes

This commit contains a lot of churn because it changes code to use automatic
resource lifetimes as much as possible. As more things get changed, this is
essential to maintaining stability.

In addition, this change changes how layers are passed through the compositor
API. Before each layer was passed down one at a time. Now they are passed in
all at once. This is simpler for the implementation because it makes errors
more atomic and makes decisions easier for the compositors.

Change-Id: Ic3e6b5d0089fb1631ea256adcce9910ed8f38366
diff --git a/autogl.h b/autogl.h
new file mode 100644
index 0000000..5d25e44
--- /dev/null
+++ b/autogl.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_AUTO_GL_H_
+#define ANDROID_AUTO_GL_H_
+
+#include <memory>
+#define EGL_EGLEXT_PROTOTYPES
+#define GL_GLEXT_PROTOTYPES
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+// TODO(zachr): use hwc_drm_bo to turn buffer handles into textures
+#ifndef EGL_NATIVE_HANDLE_ANDROID_NVX
+#define EGL_NATIVE_HANDLE_ANDROID_NVX 0x322A
+#endif
+
+namespace android {
+
+#define AUTO_GL_TYPE(name, type, zero, deleter) \
+  struct name##Deleter {                        \
+    typedef type pointer;                       \
+                                                \
+    void operator()(pointer p) const {          \
+      if (p != zero) {                          \
+        deleter;                                \
+      }                                         \
+    }                                           \
+  };                                            \
+  typedef std::unique_ptr<type, name##Deleter> name;
+
+AUTO_GL_TYPE(AutoGLFramebuffer, GLuint, 0, glDeleteFramebuffers(1, &p))
+AUTO_GL_TYPE(AutoGLBuffer, GLuint, 0, glDeleteBuffers(1, &p))
+AUTO_GL_TYPE(AutoGLTexture, GLuint, 0, glDeleteTextures(1, &p))
+AUTO_GL_TYPE(AutoGLShader, GLint, 0, glDeleteShader(p))
+AUTO_GL_TYPE(AutoGLProgram, GLint, 0, glDeleteProgram(p))
+
+struct AutoEGLDisplayImage {
+  AutoEGLDisplayImage() : display_(EGL_NO_DISPLAY), image_(EGL_NO_IMAGE_KHR) {
+  }
+
+  AutoEGLDisplayImage(EGLDisplay display, EGLImageKHR image)
+      : display_(display), image_(image) {
+  }
+
+  AutoEGLDisplayImage(const AutoEGLDisplayImage& rhs) = delete;
+  AutoEGLDisplayImage(AutoEGLDisplayImage&& rhs) {
+    clear();
+    std::swap(display_, rhs.display_);
+    std::swap(image_, rhs.image_);
+  }
+
+  ~AutoEGLDisplayImage() {
+    clear();
+  }
+
+  AutoEGLDisplayImage& operator=(const AutoEGLDisplayImage& rhs) = delete;
+  AutoEGLDisplayImage& operator=(AutoEGLDisplayImage&& rhs) {
+    clear();
+    std::swap(display_, rhs.display_);
+    std::swap(image_, rhs.image_);
+    return *this;
+  }
+
+  void reset(EGLDisplay display, EGLImageKHR image) {
+    clear();
+    display_ = display;
+    image_ = image;
+  }
+
+  void clear() {
+    if (image_ != EGL_NO_IMAGE_KHR) {
+      eglDestroyImageKHR(display_, image_);
+      display_ = EGL_NO_DISPLAY;
+      image_ = EGL_NO_IMAGE_KHR;
+    }
+  }
+
+  EGLImageKHR image() const {
+    return image_;
+  }
+
+ private:
+  EGLDisplay display_;
+  EGLImageKHR image_;
+};
+
+struct AutoEGLImageAndGLTexture {
+  AutoEGLDisplayImage image;
+  AutoGLTexture texture;
+};
+}
+
+#endif
diff --git a/drmcomposition.cpp b/drmcomposition.cpp
index f4e8d9d..0c0f394 100644
--- a/drmcomposition.cpp
+++ b/drmcomposition.cpp
@@ -42,9 +42,6 @@
   }
 }
 
-DrmComposition::~DrmComposition() {
-}
-
 int DrmComposition::Init() {
   for (DrmResources::ConnectorIter iter = drm_->begin_connectors();
        iter != drm_->end_connectors(); ++iter) {
@@ -54,7 +51,12 @@
       ALOGE("Failed to allocate new display composition\n");
       return -ENOMEM;
     }
-    int ret = composition_map_[(*iter)->display()]->Init(drm_, importer_);
+    DrmCrtc *crtc = drm_->GetCrtcForDisplay(display);
+    if (!crtc) {
+      ALOGE("Failed to find crtc for display %d", display);
+      return -ENODEV;
+    }
+    int ret = composition_map_[(*iter)->display()]->Init(drm_, crtc, importer_);
     if (ret) {
       ALOGE("Failed to init display composition for %d", (*iter)->display());
       return ret;
@@ -63,42 +65,26 @@
   return 0;
 }
 
-unsigned DrmComposition::GetRemainingLayers(int /*display*/,
-                                            unsigned num_needed) const {
-  return num_needed;
+int DrmComposition::SetLayers(size_t num_displays,
+                              const DrmCompositionDisplayLayersMap *maps) {
+  int ret = 0;
+  for (size_t display_index = 0; display_index < num_displays;
+       display_index++) {
+    const DrmCompositionDisplayLayersMap &map = maps[display_index];
+    int display = map.display;
+
+    ret = composition_map_[display]->SetLayers(
+        map.layers, map.num_layers, map.layer_indices, &primary_planes_,
+        &overlay_planes_);
+    if (ret)
+      return ret;
+  }
+
+  return DisableUnusedPlanes();
 }
 
-int DrmComposition::AddLayer(int display, hwc_layer_1_t *layer,
-                             hwc_drm_bo *bo) {
-  DrmCrtc *crtc = drm_->GetCrtcForDisplay(display);
-  if (!crtc) {
-    ALOGE("Failed to find crtc for display %d", display);
-    return -ENODEV;
-  }
-
-  // Find a plane for the layer
-  DrmPlane *plane = NULL;
-  for (std::vector<DrmPlane *>::iterator iter = primary_planes_.begin();
-       iter != primary_planes_.end(); ++iter) {
-    if ((*iter)->GetCrtcSupported(*crtc)) {
-      plane = *iter;
-      primary_planes_.erase(iter);
-      break;
-    }
-  }
-  for (std::vector<DrmPlane *>::iterator iter = overlay_planes_.begin();
-       !plane && iter != overlay_planes_.end(); ++iter) {
-    if ((*iter)->GetCrtcSupported(*crtc)) {
-      plane = *iter;
-      overlay_planes_.erase(iter);
-      break;
-    }
-  }
-  return composition_map_[display]->AddLayer(layer, bo, crtc, plane);
-}
-
-int DrmComposition::AddDpmsMode(int display, uint32_t dpms_mode) {
-  return composition_map_[display]->AddDpmsMode(dpms_mode);
+int DrmComposition::SetDpmsMode(int display, uint32_t dpms_mode) {
+  return composition_map_[display]->SetDpmsMode(dpms_mode);
 }
 
 std::unique_ptr<DrmDisplayComposition> DrmComposition::TakeDisplayComposition(
diff --git a/drmcomposition.h b/drmcomposition.h
index 69bf6d9..ee2e258 100644
--- a/drmcomposition.h
+++ b/drmcomposition.h
@@ -31,21 +31,26 @@
 
 namespace android {
 
+struct DrmCompositionDisplayLayersMap {
+  int display;
+  size_t num_layers;
+  hwc_layer_1_t *layers;
+  size_t *layer_indices;
+};
+
 class DrmComposition {
  public:
   DrmComposition(DrmResources *drm, Importer *importer);
-  ~DrmComposition();
 
   int Init();
 
-  unsigned GetRemainingLayers(int display, unsigned num_needed) const;
-  int AddLayer(int display, hwc_layer_1_t *layer, hwc_drm_bo_t *bo);
-  int AddDpmsMode(int display, uint32_t dpms_mode);
-
-  int DisableUnusedPlanes();
+  int SetLayers(size_t num_displays,
+                const DrmCompositionDisplayLayersMap *maps);
+  int SetDpmsMode(int display, uint32_t dpms_mode);
 
   std::unique_ptr<DrmDisplayComposition> TakeDisplayComposition(int display);
   DrmDisplayComposition *GetDisplayComposition(int display);
+  int DisableUnusedPlanes();
 
  private:
   DrmComposition(const DrmComposition &) = delete;
@@ -62,7 +67,6 @@
    */
   std::map<int, std::unique_ptr<DrmDisplayComposition>> composition_map_;
 };
-
 }
 
 #endif  // ANDROID_DRM_COMPOSITION_H_
diff --git a/drmcompositor.cpp b/drmcompositor.cpp
index 082e75d..c61e60c 100644
--- a/drmcompositor.cpp
+++ b/drmcompositor.cpp
@@ -62,7 +62,8 @@
   return composition;
 }
 
-int DrmCompositor::QueueComposition(DrmComposition *composition) {
+int DrmCompositor::QueueComposition(
+    std::unique_ptr<DrmComposition> composition) {
   int ret = composition->DisableUnusedPlanes();
   if (ret) {
     ALOGE("Failed to disable unused planes %d", ret);
@@ -76,7 +77,6 @@
         composition->TakeDisplayComposition(display));
     if (ret) {
       ALOGE("Failed to queue composition for display %d", display);
-      delete composition;
       return ret;
     }
   }
diff --git a/drmcompositor.h b/drmcompositor.h
index aa4a876..3fc2bb1 100644
--- a/drmcompositor.h
+++ b/drmcompositor.h
@@ -22,6 +22,7 @@
 #include "importer.h"
 
 #include <map>
+#include <memory>
 #include <sstream>
 
 namespace android {
@@ -35,7 +36,7 @@
 
   DrmComposition *CreateComposition(Importer *importer);
 
-  int QueueComposition(DrmComposition *composition);
+  int QueueComposition(std::unique_ptr<DrmComposition> composition);
   int Composite();
   void Dump(std::ostringstream *out) const;
 
diff --git a/drmdisplaycomposition.cpp b/drmdisplaycomposition.cpp
index d29b957..c7168c0 100644
--- a/drmdisplaycomposition.cpp
+++ b/drmdisplaycomposition.cpp
@@ -60,13 +60,11 @@
 DrmCompositionLayer::DrmCompositionLayer()
     : crtc(NULL), plane(NULL), handle(NULL) {
   memset(&layer, 0, sizeof(layer));
+  layer.releaseFenceFd = -1;
   layer.acquireFenceFd = -1;
   memset(&bo, 0, sizeof(bo));
 }
 
-DrmCompositionLayer::~DrmCompositionLayer() {
-}
-
 DrmDisplayComposition::DrmDisplayComposition()
     : drm_(NULL),
       importer_(NULL),
@@ -74,6 +72,8 @@
       timeline_fd_(-1),
       timeline_(0),
       timeline_current_(0),
+      timeline_pre_comp_done_(0),
+      pre_composition_layer_index_(-1),
       dpms_mode_(DRM_MODE_DPMS_ON) {
 }
 
@@ -99,8 +99,10 @@
   }
 }
 
-int DrmDisplayComposition::Init(DrmResources *drm, Importer *importer) {
+int DrmDisplayComposition::Init(DrmResources *drm, DrmCrtc *crtc,
+                                Importer *importer) {
   drm_ = drm;
+  crtc_ = crtc;
   importer_ = importer;
 
   int ret = hw_get_module(GRALLOC_HARDWARE_MODULE_ID,
@@ -127,69 +129,190 @@
   return type_ == DRM_COMPOSITION_TYPE_EMPTY || type_ == des;
 }
 
-int DrmDisplayComposition::AddLayer(hwc_layer_1_t *layer, hwc_drm_bo_t *bo,
-                                    DrmCrtc *crtc, DrmPlane *plane) {
-  if (!validate_composition_type(DRM_COMPOSITION_TYPE_FRAME))
-    return -EINVAL;
-
-  native_handle_t *handle_copy = dup_buffer_handle(layer->handle);
-  if (handle_copy == NULL) {
-    ALOGE("Failed to duplicate handle");
-    return -ENOMEM;
+static DrmPlane *TakePlane(DrmCrtc *crtc, std::vector<DrmPlane *> *planes) {
+  for (auto iter = planes->begin(); iter != planes->end(); ++iter) {
+    if ((*iter)->GetCrtcSupported(*crtc)) {
+      DrmPlane *plane = *iter;
+      planes->erase(iter);
+      return plane;
+    }
   }
-
-  int ret = gralloc_->registerBuffer(gralloc_, handle_copy);
-  if (ret) {
-    ALOGE("Failed to register buffer handle %d", ret);
-    free_buffer_handle(handle_copy);
-    return ret;
-  }
-
-  ++timeline_;
-  layer->releaseFenceFd =
-      sw_sync_fence_create(timeline_fd_, "drm_fence", timeline_);
-  if (layer->releaseFenceFd < 0) {
-    free_buffer_handle(handle_copy);
-    ALOGE("Could not create release fence %d", layer->releaseFenceFd);
-    return layer->releaseFenceFd;
-  }
-
-  DrmCompositionLayer_t c_layer;
-  c_layer.layer = *layer;
-  c_layer.bo = *bo;
-  c_layer.crtc = crtc;
-  c_layer.plane = plane;
-  c_layer.handle = handle_copy;
-
-  layer->acquireFenceFd = -1;  // We own this now
-  layers_.push_back(c_layer);
-  type_ = DRM_COMPOSITION_TYPE_FRAME;
-  return 0;
+  return NULL;
 }
 
-int DrmDisplayComposition::AddLayer(hwc_layer_1_t *layer, DrmCrtc *crtc,
-                                    DrmPlane *plane) {
-  if (layer->transform != 0)
-    return -EINVAL;
+static DrmPlane *TakePlane(DrmCrtc *crtc,
+                           std::vector<DrmPlane *> *primary_planes,
+                           std::vector<DrmPlane *> *overlay_planes) {
+  DrmPlane *plane = TakePlane(crtc, primary_planes);
+  if (plane)
+    return plane;
+  return TakePlane(crtc, overlay_planes);
+}
 
-  if (!validate_composition_type(DRM_COMPOSITION_TYPE_FRAME))
-    return -EINVAL;
+int DrmDisplayComposition::CreateNextTimelineFence() {
+  ++timeline_;
+  return sw_sync_fence_create(timeline_fd_, "drm_fence", timeline_);
+}
 
-  hwc_drm_bo_t bo;
-  int ret = importer_->ImportBuffer(layer->handle, &bo);
-  if (ret) {
-    ALOGE("Failed to import handle of layer %d", ret);
-    return ret;
-  }
+int DrmDisplayComposition::IncreaseTimelineToPoint(int point) {
+  int timeline_increase = point - timeline_current_;
+  if (timeline_increase <= 0)
+    return 0;
 
-  ret = AddLayer(layer, &bo, crtc, plane);
+  int ret = sw_sync_timeline_inc(timeline_fd_, timeline_increase);
   if (ret)
-    importer_->ReleaseBuffer(&bo);
+    ALOGE("Failed to increment sync timeline %d", ret);
+  else
+    timeline_current_ = point;
 
   return ret;
 }
 
-int DrmDisplayComposition::AddDpmsMode(uint32_t dpms_mode) {
+int DrmDisplayComposition::SetLayers(hwc_layer_1_t *layers, size_t num_layers,
+                                     size_t *layer_indices,
+                                     std::vector<DrmPlane *> *primary_planes,
+                                     std::vector<DrmPlane *> *overlay_planes) {
+  int ret = 0;
+  if (!validate_composition_type(DRM_COMPOSITION_TYPE_FRAME))
+    return -EINVAL;
+
+  for (size_t layer_index = 0; layer_index < num_layers; layer_index++) {
+    hwc_layer_1_t *layer = &layers[layer_indices[layer_index]];
+    if (layer->transform != 0)
+      return -EINVAL;
+
+    native_handle_t *handle_copy = dup_buffer_handle(layer->handle);
+    if (handle_copy == NULL) {
+      ALOGE("Failed to duplicate handle");
+      return -ENOMEM;
+    }
+
+    int ret = gralloc_->registerBuffer(gralloc_, handle_copy);
+    if (ret) {
+      ALOGE("Failed to register buffer handle %d", ret);
+      free_buffer_handle(handle_copy);
+      return ret;
+    }
+
+    layers_.emplace_back();
+    DrmCompositionLayer_t *c_layer = &layers_.back();
+    c_layer->layer = *layer;
+    c_layer->handle = handle_copy;
+    c_layer->crtc = crtc_;
+
+    ret = importer_->ImportBuffer(layer->handle, &c_layer->bo);
+    if (ret) {
+      ALOGE("Failed to import handle of layer %d", ret);
+      goto fail;
+    }
+
+    if (pre_composition_layer_index_ == -1) {
+      c_layer->plane = TakePlane(crtc_, primary_planes, overlay_planes);
+      if (c_layer->plane == NULL) {
+        if (layers_.size() <= 1) {
+          ALOGE("Failed to match any planes to the crtc of this display");
+          ret = -ENODEV;
+          goto fail;
+        }
+
+        layers_.emplace_back();
+        // c_layer's address might have changed when we resized the vector
+        c_layer = &layers_[layers_.size() - 2];
+        DrmCompositionLayer_t &pre_comp_layer = layers_.back();
+        pre_comp_layer.crtc = crtc_;
+        hwc_layer_1_t &pre_comp_output_layer = pre_comp_layer.layer;
+        memset(&pre_comp_output_layer, 0, sizeof(pre_comp_output_layer));
+        pre_comp_output_layer.compositionType = HWC_OVERLAY;
+        pre_comp_output_layer.acquireFenceFd = -1;
+        pre_comp_output_layer.releaseFenceFd = -1;
+        pre_comp_output_layer.planeAlpha = 0xff;
+        pre_comp_output_layer.visibleRegionScreen.numRects = 1;
+        pre_comp_output_layer.visibleRegionScreen.rects =
+            &pre_comp_output_layer.displayFrame;
+
+        pre_composition_layer_index_ = layers_.size() - 1;
+
+        // This is all to fix up the previous layer, which has now become part
+        // of the set of pre-composition layers because we are stealing its
+        // plane.
+        DrmCompositionLayer_t &last_c_layer = layers_[layers_.size() - 3];
+        std::swap(pre_comp_layer.plane, last_c_layer.plane);
+        hwc_layer_1_t *last_layer = &layers[layer_indices[layer_index - 1]];
+        ret = last_layer->releaseFenceFd = CreateNextTimelineFence();
+        if (ret < 0) {
+          ALOGE("Could not create release fence %d", ret);
+          goto fail;
+        }
+      }
+    }
+
+    if (c_layer->plane == NULL) {
+      // Layers to be pre composited all get the earliest release fences as they
+      // will get released soonest.
+      ret = layer->releaseFenceFd = CreateNextTimelineFence();
+      if (ret < 0) {
+        ALOGE("Could not create release fence %d", ret);
+        goto fail;
+      }
+    }
+  }
+
+  timeline_pre_comp_done_ = timeline_;
+
+  for (size_t layer_index = 0; layer_index < num_layers; layer_index++) {
+    hwc_layer_1_t *layer = &layers[layer_indices[layer_index]];
+    if (layer->releaseFenceFd >= 0)
+      continue;
+
+    ret = layer->releaseFenceFd = CreateNextTimelineFence();
+    if (ret < 0) {
+      ALOGE("Could not create release fence %d", ret);
+      goto fail;
+    }
+  }
+
+  for (size_t layer_index = 0; layer_index < num_layers; layer_index++) {
+    hwc_layer_1_t *layer = &layers[layer_indices[layer_index]];
+    layer->acquireFenceFd = -1;  // We own this now
+  }
+
+  type_ = DRM_COMPOSITION_TYPE_FRAME;
+  return 0;
+
+fail:
+
+  for (size_t c_layer_index = 0; c_layer_index < layers_.size();
+       c_layer_index++) {
+    DrmCompositionLayer_t &c_layer = layers_[c_layer_index];
+    if (c_layer.handle) {
+      gralloc_->unregisterBuffer(gralloc_, c_layer.handle);
+      free_buffer_handle(c_layer.handle);
+    }
+    if (c_layer.bo.fb_id)
+      importer_->ReleaseBuffer(&c_layer.bo);
+    if (c_layer.plane != NULL) {
+      std::vector<DrmPlane *> *return_to =
+          (c_layer.plane->type() == DRM_PLANE_TYPE_PRIMARY) ? primary_planes
+                                                            : overlay_planes;
+      return_to->insert(return_to->begin() + c_layer_index, c_layer.plane);
+    }
+  }
+  layers_.clear();
+
+  for (size_t layer_index = 0; layer_index < num_layers; layer_index++) {
+    hwc_layer_1_t *layer = &layers[layer_indices[layer_index]];
+    if (layer->releaseFenceFd >= 0) {
+      close(layer->releaseFenceFd);
+      layer->releaseFenceFd = -1;
+    }
+  }
+  sw_sync_timeline_inc(timeline_fd_, timeline_ - timeline_current_);
+
+  timeline_ = timeline_current_;
+  return ret;
+}
+
+int DrmDisplayComposition::SetDpmsMode(uint32_t dpms_mode) {
   if (!validate_composition_type(DRM_COMPOSITION_TYPE_DPMS))
     return -EINVAL;
   dpms_mode_ = dpms_mode;
@@ -198,10 +321,10 @@
 }
 
 int DrmDisplayComposition::AddPlaneDisable(DrmPlane *plane) {
-  DrmCompositionLayer_t c_layer;
+  layers_.emplace_back();
+  DrmCompositionLayer_t &c_layer = layers_.back();
   c_layer.crtc = NULL;
   c_layer.plane = plane;
-  layers_.push_back(c_layer);
   return 0;
 }
 
@@ -231,24 +354,22 @@
       layers_.end());
 }
 
+int DrmDisplayComposition::SignalPreCompositionDone() {
+  return IncreaseTimelineToPoint(timeline_pre_comp_done_);
+}
+
 int DrmDisplayComposition::FinishComposition() {
-  int timeline_increase = timeline_ - timeline_current_;
-  if (timeline_increase <= 0)
-    return 0;
-
-  int ret = sw_sync_timeline_inc(timeline_fd_, timeline_increase);
-  if (ret)
-    ALOGE("Failed to increment sync timeline %d", ret);
-  else
-    timeline_current_ = timeline_;
-
-  return ret;
+  return IncreaseTimelineToPoint(timeline_);
 }
 
 DrmCompositionLayerVector_t *DrmDisplayComposition::GetCompositionLayers() {
   return &layers_;
 }
 
+int DrmDisplayComposition::pre_composition_layer_index() const {
+  return pre_composition_layer_index_;
+}
+
 uint32_t DrmDisplayComposition::dpms_mode() const {
   return dpms_mode_;
 }
diff --git a/drmdisplaycomposition.h b/drmdisplaycomposition.h
index 76da993..4d4f2df 100644
--- a/drmdisplaycomposition.h
+++ b/drmdisplaycomposition.h
@@ -18,7 +18,9 @@
 #define ANDROID_DRM_DISPLAY_COMPOSITION_H_
 
 #include "drm_hwcomposer.h"
+#include "drmcrtc.h"
 #include "drmplane.h"
+#include "glworker.h"
 #include "importer.h"
 
 #include <vector>
@@ -37,7 +39,6 @@
 
 typedef struct DrmCompositionLayer {
   DrmCompositionLayer();
-  ~DrmCompositionLayer();
 
   hwc_layer_1_t layer;
   hwc_drm_bo_t bo;
@@ -52,22 +53,22 @@
   DrmDisplayComposition();
   ~DrmDisplayComposition();
 
-  int Init(DrmResources *drm, Importer *importer);
+  int Init(DrmResources *drm, DrmCrtc *crtc, Importer *importer);
 
   DrmCompositionType type() const;
 
-  int AddLayer(hwc_layer_1_t *layer, hwc_drm_bo_t *bo, DrmCrtc *crtc,
-               DrmPlane *plane);
-  // Like the AddLayer that accepts a hwc_drm_bo_t, but uses Importer to import
-  // the layer->handle itself.
-  int AddLayer(hwc_layer_1_t *layer, DrmCrtc *crtc, DrmPlane *plane);
+  int SetLayers(hwc_layer_1_t *layers, size_t num_layers, size_t *layer_indices,
+                std::vector<DrmPlane *> *primary_planes,
+                std::vector<DrmPlane *> *overlay_planes);
   int AddPlaneDisable(DrmPlane *plane);
-  int AddDpmsMode(uint32_t dpms_mode);
+  int SetDpmsMode(uint32_t dpms_mode);
 
   void RemoveNoPlaneLayers();
+  int SignalPreCompositionDone();
   int FinishComposition();
 
   DrmCompositionLayerVector_t *GetCompositionLayers();
+  int pre_composition_layer_index() const;
   uint32_t dpms_mode() const;
 
   Importer *importer() const;
@@ -77,17 +78,24 @@
 
   bool validate_composition_type(DrmCompositionType desired);
 
+  int CreateNextTimelineFence();
+  int IncreaseTimelineToPoint(int point);
+
   DrmResources *drm_;
+  DrmCrtc *crtc_;
   Importer *importer_;
   const gralloc_module_t *gralloc_;
+  EGLDisplay egl_display_;
 
   DrmCompositionType type_;
 
   int timeline_fd_;
   int timeline_;
   int timeline_current_;
+  int timeline_pre_comp_done_;
 
   DrmCompositionLayerVector_t layers_;
+  int pre_composition_layer_index_;
   uint32_t dpms_mode_;
 };
 }
diff --git a/drmdisplaycompositor.cpp b/drmdisplaycompositor.cpp
index 1e02e73..7259d07 100644
--- a/drmdisplaycompositor.cpp
+++ b/drmdisplaycompositor.cpp
@@ -154,17 +154,6 @@
   int ret = 0;
   DrmCompositionLayerVector_t *layers = display_comp->GetCompositionLayers();
 
-  auto last_layer = find_if(layers->rbegin(), layers->rend(),
-                            drm_composition_layer_has_plane);
-  if (last_layer == layers->rend()) {
-    ALOGE("Frame has no overlays");
-    return -EINVAL;
-  }
-
-  DrmCompositionLayer_t &comp_layer = *last_layer;
-  DrmPlane *stolen_plane = NULL;
-  std::swap(stolen_plane, comp_layer.plane);
-
   DrmConnector *connector = drm_->GetConnectorForDisplay(display_);
   if (connector == NULL) {
     ALOGE("Failed to determine display mode: no connector for display %d",
@@ -186,15 +175,6 @@
     return -ENOMEM;
   }
 
-  if (!pre_compositor_) {
-    pre_compositor_.reset(new GLWorkerCompositor());
-    ret = pre_compositor_->Init();
-    if (ret) {
-      ALOGE("Failed to initialize OpenGL compositor %d", ret);
-      return ret;
-    }
-  }
-
   std::vector<hwc_layer_1_t> pre_comp_layers;
   for (auto &comp_layer : *layers) {
     if (comp_layer.plane == NULL) {
@@ -219,48 +199,35 @@
     return ret;
   }
 
-  display_comp->RemoveNoPlaneLayers();
-
-  hwc_layer_1_t pre_comp_output_layer;
-  memset(&pre_comp_output_layer, 0, sizeof(pre_comp_output_layer));
-  pre_comp_output_layer.compositionType = HWC_OVERLAY;
+  DrmCompositionLayer_t &pre_comp_layer =
+      layers->at(display_comp->pre_composition_layer_index());
+  ret = display_comp->importer()->ImportBuffer(fb.buffer()->handle,
+                                               &pre_comp_layer.bo);
+  if (ret) {
+    ALOGE("Failed to import handle of layer %d", ret);
+    return ret;
+  }
+  hwc_layer_1_t &pre_comp_output_layer = pre_comp_layer.layer;
   pre_comp_output_layer.handle = fb.buffer()->handle;
-  pre_comp_output_layer.acquireFenceFd = -1;
-  pre_comp_output_layer.releaseFenceFd = -1;
-  pre_comp_output_layer.planeAlpha = 0xff;
-  pre_comp_output_layer.visibleRegionScreen.numRects = 1;
   pre_comp_output_layer.visibleRegionScreen.rects =
       &pre_comp_output_layer.displayFrame;
-  pre_comp_output_layer.sourceCropf.top =
-      pre_comp_output_layer.displayFrame.top = 0;
-  pre_comp_output_layer.sourceCropf.left =
-      pre_comp_output_layer.displayFrame.left = 0;
   pre_comp_output_layer.sourceCropf.right =
       pre_comp_output_layer.displayFrame.right = fb.buffer()->getWidth();
   pre_comp_output_layer.sourceCropf.bottom =
       pre_comp_output_layer.displayFrame.bottom = fb.buffer()->getHeight();
 
-  ret = display_comp->AddLayer(&pre_comp_output_layer,
-                               drm_->GetCrtcForDisplay(display_), stolen_plane);
-  if (ret) {
-    ALOGE("Failed to add composited layer %d", ret);
-    return ret;
-  }
-
   fb.set_release_fence_fd(pre_comp_output_layer.releaseFenceFd);
   framebuffer_index_ = (framebuffer_index_ + 1) % DRM_DISPLAY_BUFFERS;
 
+  display_comp->RemoveNoPlaneLayers();
+  display_comp->SignalPreCompositionDone();
   return ret;
 }
 
 int DrmDisplayCompositor::ApplyFrame(DrmDisplayComposition *display_comp) {
   int ret = 0;
 
-  DrmCompositionLayerVector_t *layers = display_comp->GetCompositionLayers();
-  bool use_pre_comp = std::any_of(layers->begin(), layers->end(),
-                                  drm_composition_layer_has_no_plane);
-
-  if (use_pre_comp) {
+  if (display_comp->pre_composition_layer_index() >= 0) {
     ret = ApplyPreComposite(display_comp);
     if (ret)
       return ret;
@@ -272,6 +239,7 @@
     return -ENOMEM;
   }
 
+  DrmCompositionLayerVector_t *layers = display_comp->GetCompositionLayers();
   for (DrmCompositionLayerVector_t::iterator iter = layers->begin();
        iter != layers->end(); ++iter) {
     hwc_layer_1_t *layer = &iter->layer;
@@ -409,6 +377,16 @@
 
 int DrmDisplayCompositor::Composite() {
   ATRACE_CALL();
+
+  if (!pre_compositor_) {
+    pre_compositor_.reset(new GLWorkerCompositor());
+    int ret = pre_compositor_->Init();
+    if (ret) {
+      ALOGE("Failed to initialize OpenGL compositor %d", ret);
+      return ret;
+    }
+  }
+
   int ret = pthread_mutex_lock(&lock_);
   if (ret) {
     ALOGE("Failed to acquire compositor lock %d", ret);
diff --git a/drmresources.cpp b/drmresources.cpp
index feb5187..32fe5cc 100644
--- a/drmresources.cpp
+++ b/drmresources.cpp
@@ -467,18 +467,17 @@
     return -EINVAL;
   }
 
-  DrmComposition *comp = compositor_.CreateComposition(NULL);
+  std::unique_ptr<DrmComposition> comp(compositor_.CreateComposition(NULL));
   if (!comp) {
     ALOGE("Failed to create composition for dpms on %d", display);
     return -ENOMEM;
   }
-  int ret = comp->AddDpmsMode(display, mode);
+  int ret = comp->SetDpmsMode(display, mode);
   if (ret) {
     ALOGE("Failed to add dpms %ld to composition on %d %d", mode, display, ret);
-    delete comp;
     return ret;
   }
-  ret = compositor_.QueueComposition(comp);
+  ret = compositor_.QueueComposition(std::move(comp));
   if (ret) {
     ALOGE("Failed to queue dpms composition on %d %d", display, ret);
     return ret;
diff --git a/glworker.cpp b/glworker.cpp
index fe2fd66..10e895f 100644
--- a/glworker.cpp
+++ b/glworker.cpp
@@ -444,7 +444,7 @@
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
   glBindTexture(GL_TEXTURE_2D, 0);
 
-  out->image.reset(image);
+  out->image.reset(egl_display, image);
   out->texture.reset(texture);
 
   return 0;
@@ -567,13 +567,13 @@
   GLint frame_height = framebuffer->getHeight();
   EGLSyncKHR finished_sync;
 
-  AutoEGLImageKHR egl_fb_image(
+  AutoEGLDisplayImage egl_fb_image(
+      egl_display_,
       eglCreateImageKHR(egl_display_, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
                         (EGLClientBuffer)framebuffer->getNativeBuffer(),
-                        NULL /* no attribs */),
-      EGLImageDeleter(egl_display_));
+                        NULL /* no attribs */));
 
-  if (egl_fb_image.get() == EGL_NO_IMAGE_KHR) {
+  if (egl_fb_image.image() == EGL_NO_IMAGE_KHR) {
     ALOGE("Failed to make image from target buffer: %s", GetEGLError());
     return -EINVAL;
   }
@@ -583,7 +583,7 @@
   AutoGLTexture gl_fb_tex_auto(gl_fb_tex);
   glBindTexture(GL_TEXTURE_2D, gl_fb_tex);
   glEGLImageTargetTexture2DOES(GL_TEXTURE_2D,
-                               (GLeglImageOES)egl_fb_image.get());
+                               (GLeglImageOES)egl_fb_image.image());
   glBindTexture(GL_TEXTURE_2D, 0);
 
   GLuint gl_fb;
@@ -610,9 +610,10 @@
       continue;
     }
 
-    layer_textures.emplace_back(egl_display_);
+    layer_textures.emplace_back();
     ret = CreateTextureFromHandle(egl_display_, layer->handle,
                                   &layer_textures.back());
+
     if (!ret) {
       ret = EGLFenceWait(egl_display_, layer->acquireFenceFd);
       layer->acquireFenceFd = -1;
diff --git a/glworker.h b/glworker.h
index 8252b62..11f4bd9 100644
--- a/glworker.h
+++ b/glworker.h
@@ -17,7 +17,6 @@
 #ifndef ANDROID_GL_WORKER_H_
 #define ANDROID_GL_WORKER_H_
 
-#include <memory>
 #include <vector>
 
 #define EGL_EGLEXT_PROTOTYPES
@@ -30,53 +29,10 @@
 
 #include <ui/GraphicBuffer.h>
 
-struct hwc_layer_1;
+#include "autogl.h"
 
 namespace android {
 
-#define AUTO_GL_TYPE(name, type, zero, deleter) \
-  struct name##Deleter {                        \
-    typedef type pointer;                       \
-                                                \
-    void operator()(pointer p) const {          \
-      if (p != zero) {                          \
-        deleter;                                \
-      }                                         \
-    }                                           \
-  };                                            \
-  typedef std::unique_ptr<type, name##Deleter> name;
-
-AUTO_GL_TYPE(AutoGLFramebuffer, GLuint, 0, glDeleteFramebuffers(1, &p))
-AUTO_GL_TYPE(AutoGLBuffer, GLuint, 0, glDeleteBuffers(1, &p))
-AUTO_GL_TYPE(AutoGLTexture, GLuint, 0, glDeleteTextures(1, &p))
-AUTO_GL_TYPE(AutoGLShader, GLint, 0, glDeleteShader(p))
-AUTO_GL_TYPE(AutoGLProgram, GLint, 0, glDeleteProgram(p))
-
-struct EGLImageDeleter {
-  typedef EGLImageKHR pointer;
-
-  EGLDisplay egl_display_;
-
-  EGLImageDeleter(EGLDisplay egl_display) : egl_display_(egl_display) {
-  }
-
-  void operator()(EGLImageKHR p) const {
-    if (p != EGL_NO_IMAGE_KHR) {
-      eglDestroyImageKHR(egl_display_, p);
-    }
-  }
-};
-typedef std::unique_ptr<EGLImageKHR, EGLImageDeleter> AutoEGLImageKHR;
-
-struct AutoEGLImageAndGLTexture {
-  AutoEGLImageKHR image;
-  AutoGLTexture texture;
-
-  AutoEGLImageAndGLTexture(EGLDisplay egl_display)
-      : image(EGL_NO_IMAGE_KHR, EGLImageDeleter(egl_display)) {
-  }
-};
-
 class GLWorkerCompositor {
  public:
   GLWorkerCompositor();
diff --git a/hwcomposer.cpp b/hwcomposer.cpp
index 787be72..a00c95f 100644
--- a/hwcomposer.cpp
+++ b/hwcomposer.cpp
@@ -22,12 +22,14 @@
 #include "importer.h"
 #include "vsyncworker.h"
 
+#include <stdlib.h>
+
+#include <map>
+#include <vector>
+
 #include <errno.h>
 #include <fcntl.h>
-#include <list>
-#include <map>
 #include <pthread.h>
-#include <stdlib.h>
 #include <sys/param.h>
 #include <sys/resource.h>
 #include <xf86drm.h>
@@ -109,8 +111,7 @@
 }
 
 static void hwc_set_cleanup(size_t num_displays,
-                            hwc_display_contents_1_t **display_contents,
-                            DrmComposition *composition) {
+                            hwc_display_contents_1_t **display_contents) {
   for (int i = 0; i < (int)num_displays; ++i) {
     if (!display_contents[i])
       continue;
@@ -128,32 +129,10 @@
       dc->outbufAcquireFenceFd = -1;
     }
   }
-
-  delete composition;
 }
 
-static int hwc_add_layer(int display, hwc_context_t *ctx, hwc_layer_1_t *layer,
-                         DrmComposition *composition) {
-  hwc_drm_bo_t bo;
-  int ret = ctx->importer->ImportBuffer(layer->handle, &bo);
-  if (ret) {
-    ALOGE("Failed to import handle to bo %d", ret);
-    return ret;
-  }
-
-  ret = composition->AddLayer(display, layer, &bo);
-  if (!ret)
-    return 0;
-
-  int destroy_ret = ctx->importer->ReleaseBuffer(&bo);
-  if (destroy_ret)
-    ALOGE("Failed to destroy buffer %d", destroy_ret);
-
-  return ret;
-}
-
-static void hwc_add_layer_to_retire_fence(hwc_layer_1_t *layer,
-    hwc_display_contents_1_t *display_contents) {
+static void hwc_add_layer_to_retire_fence(
+    hwc_layer_1_t *layer, hwc_display_contents_1_t *display_contents) {
   if (layer->releaseFenceFd < 0)
     return;
 
@@ -171,64 +150,77 @@
                    hwc_display_contents_1_t **display_contents) {
   ATRACE_CALL();
   struct hwc_context_t *ctx = (struct hwc_context_t *)&dev->common;
-  DrmComposition *composition =
-      ctx->drm.compositor()->CreateComposition(ctx->importer);
+  int ret;
+  std::unique_ptr<DrmComposition> composition(
+      ctx->drm.compositor()->CreateComposition(ctx->importer));
   if (!composition) {
     ALOGE("Drm composition init failed");
-    hwc_set_cleanup(num_displays, display_contents, NULL);
+    hwc_set_cleanup(num_displays, display_contents);
     return -EINVAL;
   }
 
-  int ret;
+  std::vector<DrmCompositionDisplayLayersMap> layers_map;
+  std::vector<std::vector<size_t>> layers_indices;
+  layers_map.reserve(num_displays);
+  layers_indices.reserve(num_displays);
+
   for (int i = 0; i < (int)num_displays; ++i) {
     if (!display_contents[i])
       continue;
-
     hwc_display_contents_1_t *dc = display_contents[i];
-    int j;
-    unsigned num_layers = 0;
+
+    layers_map.emplace_back();
+    DrmCompositionDisplayLayersMap &map = layers_map[i];
+    map.display = i;
+    map.layers = dc->hwLayers;
+
+    std::vector<size_t> indices_to_composite;
     unsigned num_dc_layers = dc->numHwLayers;
-    for (j = 0; j < (int)num_dc_layers; ++j) {
+    for (int j = 0; j < (int)num_dc_layers; ++j) {
       hwc_layer_1_t *layer = &dc->hwLayers[j];
       if (layer->flags & HWC_SKIP_LAYER)
         continue;
       if (layer->compositionType == HWC_OVERLAY)
-        num_layers++;
+        indices_to_composite.push_back(j);
     }
 
-    unsigned num_planes = composition->GetRemainingLayers(i, num_layers);
+    map.num_layers = indices_to_composite.size();
+    layers_indices.emplace_back(std::move(indices_to_composite));
+    map.layer_indices = layers_indices.back().data();
+  }
 
-    if (num_layers > num_planes) {
-      ALOGE("Can not composite %u with only %u planes", num_layers, num_planes);
-    }
+  ret = composition->SetLayers(layers_map.size(), layers_map.data());
+  if (ret) {
+    hwc_set_cleanup(num_displays, display_contents);
+    return -EINVAL;
+  }
 
-    for (j = 0; num_planes && j < (int)num_dc_layers; ++j) {
+  ret = ctx->drm.compositor()->QueueComposition(std::move(composition));
+  if (ret) {
+    hwc_set_cleanup(num_displays, display_contents);
+    return -EINVAL;
+  }
+
+  composition.reset(NULL);
+
+  for (int i = 0; i < (int)num_displays; ++i) {
+    if (!display_contents[i])
+      continue;
+    hwc_display_contents_1_t *dc = display_contents[i];
+    unsigned num_dc_layers = dc->numHwLayers;
+    for (int j = 0; j < (int)num_dc_layers; ++j) {
       hwc_layer_1_t *layer = &dc->hwLayers[j];
       if (layer->flags & HWC_SKIP_LAYER)
         continue;
-      if (layer->compositionType != HWC_OVERLAY)
-        continue;
-
-      ret = hwc_add_layer(i, ctx, layer, composition);
-      if (ret) {
-        ALOGE("Add layer failed %d", ret);
-        hwc_set_cleanup(num_displays, display_contents, composition);
-        return ret;
-      }
-      hwc_add_layer_to_retire_fence(layer, dc);
-
-      --num_planes;
+      if (layer->compositionType == HWC_OVERLAY)
+        hwc_add_layer_to_retire_fence(layer, dc);
     }
   }
 
-  ret = ctx->drm.compositor()->QueueComposition(composition);
-  composition = NULL;
   if (ret) {
     ALOGE("Failed to queue the composition");
-    hwc_set_cleanup(num_displays, display_contents, NULL);
-    return ret;
   }
-  hwc_set_cleanup(num_displays, display_contents, NULL);
+  hwc_set_cleanup(num_displays, display_contents);
   return ret;
 }