drm_hwcomposer: implement squashing

Change-Id: Ifd4feaa0de303ddfd519d4415ab31d2a72f26022
diff --git a/drmcomposition.cpp b/drmcomposition.cpp
index 09bdba7..e7b02b6 100644
--- a/drmcomposition.cpp
+++ b/drmcomposition.cpp
@@ -80,8 +80,8 @@
       continue;
     }
 
-    ret = composition_map_[display]->SetLayers(map.layers.data(),
-                                               map.layers.size());
+    ret = composition_map_[display]->SetLayers(
+        map.layers.data(), map.layers.size(), map.geometry_changed);
     if (ret)
       return ret;
   }
diff --git a/drmcomposition.h b/drmcomposition.h
index ed176f1..80a7eea 100644
--- a/drmcomposition.h
+++ b/drmcomposition.h
@@ -34,6 +34,7 @@
 
 struct DrmCompositionDisplayLayersMap {
   int display;
+  bool geometry_changed = true;
   std::vector<DrmHwcLayer> layers;
 
   DrmCompositionDisplayLayersMap() = default;
diff --git a/drmdisplaycomposition.cpp b/drmdisplaycomposition.cpp
index 49bacad..34d879e 100644
--- a/drmdisplaycomposition.cpp
+++ b/drmdisplaycomposition.cpp
@@ -85,11 +85,13 @@
   return ret;
 }
 
-int DrmDisplayComposition::SetLayers(DrmHwcLayer *layers, size_t num_layers) {
-  int ret = 0;
+int DrmDisplayComposition::SetLayers(DrmHwcLayer *layers, size_t num_layers,
+                                     bool geometry_changed) {
   if (!validate_composition_type(DRM_COMPOSITION_TYPE_FRAME))
     return -EINVAL;
 
+  geometry_changed_ = geometry_changed;
+
   for (size_t layer_index = 0; layer_index < num_layers; layer_index++) {
     layers_.emplace_back(std::move(layers[layer_index]));
   }
@@ -162,7 +164,7 @@
   return out;
 }
 
-static void SeperateLayers(DrmHwcLayer *layers, size_t *used_layers,
+static void SeparateLayers(DrmHwcLayer *layers, size_t *used_layers,
                            size_t num_used_layers,
                            DrmHwcRect<int> *exclude_rects,
                            size_t num_exclude_rects,
@@ -203,82 +205,7 @@
   }
 }
 
-int DrmDisplayComposition::Plan(SquashState *squash,
-                                std::vector<DrmPlane *> *primary_planes,
-                                std::vector<DrmPlane *> *overlay_planes) {
-  size_t planes_can_use =
-      CountUsablePlanes(crtc_, primary_planes, overlay_planes);
-  if (planes_can_use == 0) {
-    ALOGE("Display %d has no usable planes", crtc_->display());
-    return -ENODEV;
-  }
-
-  std::vector<int> layer_squash_area(layers_.size());
-  if (squash != NULL && planes_can_use >= 3) {
-    std::vector<bool> changed_regions;
-    squash->GenerateHistory(layers_.data(), changed_regions);
-
-    std::vector<bool> stable_regions;
-    squash->StableRegionsWithMarginalHistory(changed_regions, stable_regions);
-
-    squash->RecordHistory(layers_.data(), changed_regions);
-
-    squash->RecordSquashed(stable_regions);
-
-    for (size_t region_index = 0; region_index < stable_regions.size();
-         region_index++) {
-      const SquashState::Region &region = squash->regions()[region_index];
-      if (stable_regions[region_index]) {
-        squash_regions_.emplace_back();
-        DrmCompositionRegion &squash_region = squash_regions_.back();
-        squash_region.frame = region.rect;
-        for (size_t layer_index = 0; layer_index < SquashState::kMaxLayers;
-             layer_index++) {
-          if (region.layer_refs[layer_index]) {
-            squash_region.source_layers.push_back(layer_index);
-            layer_squash_area[layer_index] += squash_region.frame.area();
-          }
-        }
-      }
-    }
-  }
-
-  std::vector<size_t> layers_remaining;
-  for (size_t layer_index = 0; layer_index < layers_.size(); layer_index++) {
-    // Skip layers that were completely squashed
-    if (layer_squash_area[layer_index] >=
-        layers_[layer_index].display_frame.area()) {
-      continue;
-    }
-
-    layers_remaining.push_back(layer_index);
-  }
-
-  size_t layer_to_composite = layers_remaining.size();
-  size_t num_layers_to_pre_composite = 0;
-  if (squash_regions_.size() > 0) {
-    layers_remaining.push_back(DrmCompositionPlane::kSourceSquash);
-  }
-
-  if (layers_remaining.size() > planes_can_use) {
-    layers_remaining.insert(layers_remaining.begin() + layer_to_composite,
-                            DrmCompositionPlane::kSourcePreComp);
-    size_t num_layers_to_pre_composite =
-        layer_to_composite - planes_can_use + 1;
-    size_t first_layer_to_pre_composite = planes_can_use - 1;
-    SeperateLayers(layers_.data(),
-                   &layers_remaining[first_layer_to_pre_composite],
-                   num_layers_to_pre_composite, NULL, 0, pre_comp_regions_);
-    layers_remaining.erase(
-        layers_remaining.begin() + first_layer_to_pre_composite,
-        layers_remaining.begin() + layer_to_composite);
-  }
-
-  for (size_t i : layers_remaining) {
-    composition_planes_.emplace_back(DrmCompositionPlane{
-        TakePlane(crtc_, primary_planes, overlay_planes), crtc_, i});
-  }
-
+int DrmDisplayComposition::CreateAndAssignReleaseFences() {
   std::unordered_set<DrmHwcLayer *> squash_layers;
   std::unordered_set<DrmHwcLayer *> pre_comp_layers;
   std::unordered_set<DrmHwcLayer *> comp_layers;
@@ -329,6 +256,122 @@
   return 0;
 }
 
+int DrmDisplayComposition::Plan(SquashState *squash,
+                                std::vector<DrmPlane *> *primary_planes,
+                                std::vector<DrmPlane *> *overlay_planes) {
+  if (type_ != DRM_COMPOSITION_TYPE_FRAME)
+    return 0;
+
+  size_t planes_can_use =
+      CountUsablePlanes(crtc_, primary_planes, overlay_planes);
+  if (planes_can_use == 0) {
+    ALOGE("Display %d has no usable planes", crtc_->display());
+    return -ENODEV;
+  }
+
+  bool use_squash_framebuffer = false;
+  // Used to determine which layers were entirely squashed
+  std::vector<int> layer_squash_area(layers_.size(), 0);
+  // Used to avoid rerendering regions that were squashed
+  std::vector<DrmHwcRect<int>> exclude_rects;
+  if (squash != NULL && planes_can_use >= 3) {
+    if (geometry_changed_) {
+      squash->Init(layers_.data(), layers_.size());
+    } else {
+      std::vector<bool> changed_regions;
+      squash->GenerateHistory(layers_.data(), layers_.size(), changed_regions);
+
+      std::vector<bool> stable_regions;
+      squash->StableRegionsWithMarginalHistory(changed_regions, stable_regions);
+
+      // Only if SOME region is stable
+      use_squash_framebuffer =
+          std::find(stable_regions.begin(), stable_regions.end(), true) !=
+          stable_regions.end();
+
+      squash->RecordHistory(layers_.data(), layers_.size(), changed_regions);
+
+      // Changes in which regions are squashed triggers a rerender via
+      // squash_regions.
+      bool render_squash = squash->RecordAndCompareSquashed(stable_regions);
+
+      for (size_t region_index = 0; region_index < stable_regions.size();
+           region_index++) {
+        const SquashState::Region &region = squash->regions()[region_index];
+        if (!stable_regions[region_index])
+          continue;
+
+        exclude_rects.emplace_back(region.rect);
+
+        if (render_squash) {
+          squash_regions_.emplace_back();
+          squash_regions_.back().frame = region.rect;
+        }
+
+        int frame_area = region.rect.area();
+        // Source layers are sorted front to back i.e. top layer has lowest
+        // index.
+        for (size_t layer_index = layers_.size();
+             layer_index-- > 0;  // Yes, I double checked this
+             /* See condition */) {
+          if (!region.layer_refs[layer_index])
+            continue;
+          layer_squash_area[layer_index] += frame_area;
+          if (render_squash)
+            squash_regions_.back().source_layers.push_back(layer_index);
+        }
+      }
+    }
+  }
+
+  std::vector<size_t> layers_remaining;
+  for (size_t layer_index = 0; layer_index < layers_.size(); layer_index++) {
+    // Skip layers that were completely squashed
+    if (layer_squash_area[layer_index] >=
+        layers_[layer_index].display_frame.area()) {
+      continue;
+    }
+
+    layers_remaining.push_back(layer_index);
+  }
+
+  if (use_squash_framebuffer)
+    planes_can_use--;
+
+  if (layers_remaining.size() > planes_can_use)
+    planes_can_use--;
+
+  size_t last_composition_layer = 0;
+  for (last_composition_layer = 0;
+       last_composition_layer < layers_remaining.size() && planes_can_use > 0;
+       last_composition_layer++, planes_can_use--) {
+    composition_planes_.emplace_back(
+        DrmCompositionPlane{TakePlane(crtc_, primary_planes, overlay_planes),
+                            crtc_, layers_remaining[last_composition_layer]});
+  }
+
+  layers_remaining.erase(layers_remaining.begin(),
+                         layers_remaining.begin() + last_composition_layer);
+
+  if (layers_remaining.size() > 0) {
+    composition_planes_.emplace_back(
+        DrmCompositionPlane{TakePlane(crtc_, primary_planes, overlay_planes),
+                            crtc_, DrmCompositionPlane::kSourcePreComp});
+
+    SeparateLayers(layers_.data(), layers_remaining.data(),
+                   layers_remaining.size(), exclude_rects.data(),
+                   exclude_rects.size(), pre_comp_regions_);
+  }
+
+  if (use_squash_framebuffer) {
+    composition_planes_.emplace_back(
+        DrmCompositionPlane{TakePlane(crtc_, primary_planes, overlay_planes),
+                            crtc_, DrmCompositionPlane::kSourceSquash});
+  }
+
+  return CreateAndAssignReleaseFences();
+}
+
 static const char *DrmCompositionTypeToString(DrmCompositionType type) {
   switch (type) {
     case DRM_COMPOSITION_TYPE_EMPTY:
diff --git a/drmdisplaycomposition.h b/drmdisplaycomposition.h
index 2ac93e0..69324fd 100644
--- a/drmdisplaycomposition.h
+++ b/drmdisplaycomposition.h
@@ -65,7 +65,7 @@
   int Init(DrmResources *drm, DrmCrtc *crtc, Importer *importer,
            uint64_t frame_no);
 
-  int SetLayers(DrmHwcLayer *layers, size_t num_layers);
+  int SetLayers(DrmHwcLayer *layers, size_t num_layers, bool geometry_changed);
   int AddPlaneDisable(DrmPlane *plane);
   int SetDpmsMode(uint32_t dpms_mode);
   int SetDisplayMode(const DrmMode &display_mode);
@@ -131,6 +131,8 @@
 
   int IncreaseTimelineToPoint(int point);
 
+  int CreateAndAssignReleaseFences();
+
   DrmResources *drm_ = NULL;
   DrmCrtc *crtc_ = NULL;
   Importer *importer_ = NULL;
@@ -145,6 +147,7 @@
   int timeline_squash_done_ = 0;
   int timeline_pre_comp_done_ = 0;
 
+  bool geometry_changed_;
   std::vector<DrmHwcLayer> layers_;
   std::vector<DrmCompositionRegion> squash_regions_;
   std::vector<DrmCompositionRegion> pre_comp_regions_;
diff --git a/drmdisplaycompositor.cpp b/drmdisplaycompositor.cpp
index f238f43..c3042f5 100644
--- a/drmdisplaycompositor.cpp
+++ b/drmdisplaycompositor.cpp
@@ -63,8 +63,14 @@
   }
 }
 
-void SquashState::GenerateHistory(DrmHwcLayer *layers,
+void SquashState::GenerateHistory(DrmHwcLayer *layers, size_t num_layers,
                                   std::vector<bool> &changed_regions) const {
+  changed_regions.resize(regions_.size());
+  if (num_layers != last_handles_.size()) {
+    ALOGE("SquashState::GenerateHistory expected %zu layers but got %zu layers",
+          last_handles_.size(), num_layers);
+    return;
+  }
   std::bitset<kMaxLayers> changed_layers;
   for (size_t i = 0; i < last_handles_.size(); i++) {
     DrmHwcLayer *layer = &layers[i];
@@ -73,7 +79,6 @@
     }
   }
 
-  changed_regions.resize(regions_.size());
   for (size_t i = 0; i < regions_.size(); i++) {
     changed_regions[i] = (regions_[i].layer_refs & changed_layers).any();
   }
@@ -88,8 +93,19 @@
   }
 }
 
-void SquashState::RecordHistory(DrmHwcLayer *layers,
+void SquashState::RecordHistory(DrmHwcLayer *layers, size_t num_layers,
                                 const std::vector<bool> &changed_regions) {
+  if (num_layers != last_handles_.size()) {
+    ALOGE("SquashState::RecordHistory expected %zu layers but got %zu layers",
+          last_handles_.size(), num_layers);
+    return;
+  }
+  if (changed_regions.size() != regions_.size()) {
+    ALOGE("SquashState::RecordHistory expected %zu regions but got %zu regions",
+          regions_.size(), changed_regions.size());
+    return;
+  }
+
   for (size_t i = 0; i < last_handles_.size(); i++) {
     DrmHwcLayer *layer = &layers[i];
     last_handles_[i] = layer->sf_handle;
@@ -103,10 +119,23 @@
   valid_history_++;
 }
 
-void SquashState::RecordSquashed(const std::vector<bool> &squashed_regions) {
-  for (size_t i = 0; i < regions_.size(); i++) {
-    regions_[i].squashed = squashed_regions[i];
+bool SquashState::RecordAndCompareSquashed(
+    const std::vector<bool> &squashed_regions) {
+  if (squashed_regions.size() != regions_.size()) {
+    ALOGE(
+        "SquashState::RecordAndCompareSquashed expected %zu regions but got "
+        "%zu regions",
+        regions_.size(), squashed_regions.size());
+    return false;
   }
+  bool changed = false;
+  for (size_t i = 0; i < regions_.size(); i++) {
+    if (regions_[i].squashed != squashed_regions[i]) {
+      regions_[i].squashed = squashed_regions[i];
+      changed = true;
+    }
+  }
+  return changed;
 }
 
 void SquashState::Dump(std::ostringstream *out) const {
@@ -137,6 +166,14 @@
   }
 }
 
+static bool UsesSquash(const std::vector<DrmCompositionPlane> &comp_planes) {
+  return std::any_of(comp_planes.begin(), comp_planes.end(),
+                     [](const DrmCompositionPlane &plane) {
+                       return plane.source_layer ==
+                              DrmCompositionPlane::kSourceSquash;
+                     });
+}
+
 DrmDisplayCompositor::DrmDisplayCompositor()
     : drm_(NULL),
       display_(-1),
@@ -145,6 +182,7 @@
       active_(false),
       needs_modeset_(false),
       framebuffer_index_(0),
+      squash_framebuffer_index_(0),
       dump_frames_composited_(0),
       dump_last_timestamp_ns_(0) {
   struct timespec ts;
@@ -301,6 +339,38 @@
   return ret;
 }
 
+int DrmDisplayCompositor::ApplySquash(DrmDisplayComposition *display_comp) {
+  int ret = 0;
+
+  DrmFramebuffer &fb = squash_framebuffers_[squash_framebuffer_index_];
+  ret = PrepareFramebuffer(fb, display_comp);
+  if (ret) {
+    ALOGE("Failed to prepare framebuffer for squash %d", ret);
+    return ret;
+  }
+
+  std::vector<DrmCompositionRegion> &regions = display_comp->squash_regions();
+  ret = pre_compositor_->Composite(display_comp->layers().data(),
+                                   regions.data(), regions.size(), fb.buffer());
+  pre_compositor_->Finish();
+
+  if (ret) {
+    ALOGE("Failed to squash layers");
+    return ret;
+  }
+
+  ret = display_comp->CreateNextTimelineFence();
+  if (ret <= 0) {
+    ALOGE("Failed to create squash framebuffer release fence %d", ret);
+    return ret;
+  }
+
+  fb.set_release_fence_fd(ret);
+  display_comp->SignalSquashDone();
+
+  return 0;
+}
+
 int DrmDisplayCompositor::ApplyPreComposite(
     DrmDisplayComposition *display_comp) {
   int ret = 0;
@@ -308,7 +378,7 @@
   DrmFramebuffer &fb = framebuffers_[framebuffer_index_];
   ret = PrepareFramebuffer(fb, display_comp);
   if (ret) {
-    ALOGE("Failed to prepare framebuffer for precomposite %d", ret);
+    ALOGE("Failed to prepare framebuffer for pre-composite %d", ret);
     return ret;
   }
 
@@ -318,13 +388,13 @@
   pre_compositor_->Finish();
 
   if (ret) {
-    ALOGE("Failed to composite layers");
+    ALOGE("Failed to pre-composite layers");
     return ret;
   }
 
   ret = display_comp->CreateNextTimelineFence();
   if (ret <= 0) {
-    ALOGE("Failed to create pre comp framebuffer release fence %d", ret);
+    ALOGE("Failed to create pre-composite framebuffer release fence %d", ret);
     return ret;
   }
 
@@ -374,13 +444,50 @@
   std::vector<DrmHwcLayer> &layers = display_comp->layers();
   std::vector<DrmCompositionPlane> &comp_planes =
       display_comp->composition_planes();
+  std::vector<DrmCompositionRegion> &squash_regions =
+      display_comp->squash_regions();
   std::vector<DrmCompositionRegion> &pre_comp_regions =
       display_comp->pre_comp_regions();
 
-  bool do_pre_comp = pre_comp_regions.size() > 0;
-  DrmFramebuffer *pre_comp_fb;
-  int pre_comp_layer_index = -1;
+  int squash_layer_index = -1;
+  if (squash_regions.size() > 0) {
+    squash_framebuffer_index_ = (squash_framebuffer_index_ + 1) % 2;
+    ret = ApplySquash(display_comp);
+    if (ret)
+      return ret;
 
+    squash_layer_index = layers.size() - 1;
+  } else {
+    if (UsesSquash(comp_planes)) {
+      DrmFramebuffer &fb = squash_framebuffers_[squash_framebuffer_index_];
+      layers.emplace_back();
+      squash_layer_index = layers.size() - 1;
+      DrmHwcLayer &squash_layer = layers.back();
+      ret = squash_layer.buffer.ImportBuffer(fb.buffer()->handle,
+                                             display_comp->importer());
+      if (ret) {
+        ALOGE("Failed to import old squashed framebuffer %d", ret);
+        return ret;
+      }
+      squash_layer.sf_handle = fb.buffer()->handle;
+      squash_layer.source_crop = DrmHwcRect<float>(
+          0, 0, squash_layer.buffer->width, squash_layer.buffer->height);
+      squash_layer.display_frame = DrmHwcRect<int>(
+          0, 0, squash_layer.buffer->width, squash_layer.buffer->height);
+      ret = display_comp->CreateNextTimelineFence();
+
+      if (ret <= 0) {
+        ALOGE("Failed to create squash framebuffer release fence %d", ret);
+        return ret;
+      }
+
+      fb.set_release_fence_fd(ret);
+      ret = 0;
+    }
+  }
+
+  bool do_pre_comp = pre_comp_regions.size() > 0;
+  int pre_comp_layer_index = -1;
   if (do_pre_comp) {
     ret = ApplyPreComposite(display_comp);
     if (ret)
@@ -461,11 +568,18 @@
     switch (comp_plane.source_layer) {
       case DrmCompositionPlane::kSourceNone:
         break;
+      case DrmCompositionPlane::kSourceSquash: {
+        DrmHwcLayer &layer = layers[squash_layer_index];
+        fb_id = layer.buffer->fb_id;
+        display_frame = layer.display_frame;
+        source_crop = layer.source_crop;
+        break;
+      }
       case DrmCompositionPlane::kSourcePreComp: {
         if (!do_pre_comp) {
           ALOGE(
               "Can not use pre composite framebuffer with no pre composite "
-              "layers");
+              "regions");
           ret = -EINVAL;
           goto out;
         }
@@ -475,8 +589,6 @@
         source_crop = layer.source_crop;
         break;
       }
-      case DrmCompositionPlane::kSourceSquash:
-        break;
       default: {
         if (comp_plane.source_layer >= layers.size()) {
           ALOGE("Source layer index %zu out of bounds %zu",
@@ -789,6 +901,8 @@
   if (active_composition_)
     active_composition_->Dump(out);
 
+  squash_state_.Dump(out);
+
   pthread_mutex_unlock(&lock_);
 }
 }
diff --git a/drmdisplaycompositor.h b/drmdisplaycompositor.h
index cacaa66..e9b529b 100644
--- a/drmdisplaycompositor.h
+++ b/drmdisplaycompositor.h
@@ -40,7 +40,7 @@
 
 class SquashState {
  public:
-  static const unsigned kHistoryLength = 6;
+  static const unsigned kHistoryLength = 6; // TODO: make this number not magic
   static const unsigned kMaxLayers = 64;
 
   struct Region {
@@ -60,14 +60,14 @@
   }
 
   void Init(DrmHwcLayer *layers, size_t num_layers);
-  void GenerateHistory(DrmHwcLayer *layers,
+  void GenerateHistory(DrmHwcLayer *layers, size_t num_layers,
                        std::vector<bool> &changed_regions) const;
   void StableRegionsWithMarginalHistory(
       const std::vector<bool> &changed_regions,
       std::vector<bool> &stable_regions) const;
-  void RecordHistory(DrmHwcLayer *layers,
+  void RecordHistory(DrmHwcLayer *layers, size_t num_layers,
                      const std::vector<bool> &changed_regions);
-  void RecordSquashed(const std::vector<bool> &squashed_regions);
+  bool RecordAndCompareSquashed(const std::vector<bool> &squashed_regions);
 
   void Dump(std::ostringstream *out) const;
 
@@ -96,7 +96,7 @@
   bool HaveQueuedComposites() const;
 
   SquashState *squash_state() {
-    return NULL;
+    return &squash_state_;
   }
 
  private:
@@ -109,6 +109,7 @@
 
   int PrepareFramebuffer(DrmFramebuffer &fb,
                          DrmDisplayComposition *display_comp);
+  int ApplySquash(DrmDisplayComposition *display_comp);
   int ApplyPreComposite(DrmDisplayComposition *display_comp);
   int ApplyFrame(DrmDisplayComposition *display_comp);
   int ApplyDpms(DrmDisplayComposition *display_comp);
@@ -132,6 +133,10 @@
   DrmFramebuffer framebuffers_[DRM_DISPLAY_BUFFERS];
   std::unique_ptr<GLWorkerCompositor> pre_compositor_;
 
+  SquashState squash_state_;
+  int squash_framebuffer_index_;
+  DrmFramebuffer squash_framebuffers_[2];
+
   // mutable since we need to acquire in HaveQueuedComposites
   mutable pthread_mutex_t lock_;
 
diff --git a/hwcomposer.cpp b/hwcomposer.cpp
index f1d6fbb..7fec244 100644
--- a/hwcomposer.cpp
+++ b/hwcomposer.cpp
@@ -482,6 +482,8 @@
     layers_map.emplace_back();
     DrmCompositionDisplayLayersMap &map = layers_map.back();
     map.display = i;
+    map.geometry_changed =
+        (dc->flags & HWC_GEOMETRY_CHANGED) == HWC_GEOMETRY_CHANGED;
     std::vector<size_t> &indices_to_composite = layers_indices[i];
     for (size_t j : indices_to_composite) {
       hwc_layer_1_t *sf_layer = &dc->hwLayers[j];