drm_hwcomposer: implement squashing

Change-Id: Ifd4feaa0de303ddfd519d4415ab31d2a72f26022
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_);
 }
 }