hwc: cache layers on framebuffer

In the cases where all the layers that are marked as HWC_FRAMEBUFFER did not
change, we can mark them as HWC_OVERLAY since their contents are already on the
framebuffer and do not need any additional processing.

This optimization shows power improvement for video playback.

Bug: 7623741
Change-Id: Ia178a926e6f56a3ec9291250a22a66f212c30b14
Signed-off-by: Iliyan Malchev <malchev@google.com>
diff --git a/libhwcomposer/hwc.cpp b/libhwcomposer/hwc.cpp
index a2ea3c9..a63aed7 100644
--- a/libhwcomposer/hwc.cpp
+++ b/libhwcomposer/hwc.cpp
@@ -77,11 +77,23 @@
 }
 
 //Helper
-static void reset(hwc_context_t *ctx, int numDisplays) {
+static void reset(hwc_context_t *ctx, int numDisplays,
+                  hwc_display_contents_1_t** displays) {
     memset(ctx->listStats, 0, sizeof(ctx->listStats));
     for(int i = 0; i < HWC_NUM_DISPLAY_TYPES; i++){
         ctx->overlayInUse[i] = false;
         ctx->listStats[i].yuvIndex = -1;
+        hwc_display_contents_1_t *list = displays[i];
+        // XXX:SurfaceFlinger no longer guarantees that this
+        // value is reset on every prepare. However, for the layer
+        // cache we need to reset it.
+        // We can probably rethink that later on
+        if (LIKELY(list && list->numHwLayers > 1)) {
+            for(uint32_t j = 0; j < list->numHwLayers; j++) {
+                if(list->hwLayers[j].compositionType != HWC_FRAMEBUFFER_TARGET)
+                    list->hwLayers[j].compositionType = HWC_FRAMEBUFFER;
+            }
+        }
     }
 }
 
@@ -103,6 +115,7 @@
             } else {
                 ctx->overlayInUse[HWC_DISPLAY_PRIMARY] = false;
             }
+            ctx->mLayerCache->updateLayerCache(list);
         }
     }
     return 0;
@@ -141,7 +154,7 @@
 {
     int ret = 0;
     hwc_context_t* ctx = (hwc_context_t*)(dev);
-    reset(ctx, numDisplays);
+    reset(ctx, numDisplays, displays);
 
     //If securing of h/w in progress skip comp using overlay.
     if(ctx->mSecuring == true) return 0;
diff --git a/libhwcomposer/hwc_utils.cpp b/libhwcomposer/hwc_utils.cpp
index 41a9c5a..a6a6f3e 100644
--- a/libhwcomposer/hwc_utils.cpp
+++ b/libhwcomposer/hwc_utils.cpp
@@ -60,6 +60,7 @@
     ctx->mMDP.hasOverlay = qdutils::MDPVersion::getInstance().hasOverlay();
     ctx->mMDP.panel = qdutils::MDPVersion::getInstance().getPanelType();
     ctx->mExtDisplay = new ExternalDisplay(ctx);
+    ctx->mLayerCache = new LayerCache();
     MDPComp::init(ctx);
 
     pthread_mutex_init(&(ctx->vstate.lock), NULL);
@@ -208,7 +209,7 @@
     int ret = 0;
 #ifdef USE_FENCE_SYNC
     struct mdp_buf_sync data;
-    int acquireFd[4];
+    int acquireFd[MAX_NUM_LAYERS];
     int count = 0;
     int releaseFd = -1;
     int fbFd = -1;
@@ -219,7 +220,8 @@
     for(uint32_t i = 0; i < list->numHwLayers; i++) {
         if((list->hwLayers[i].compositionType == HWC_OVERLAY ||
             list->hwLayers[i].compositionType == HWC_FRAMEBUFFER_TARGET) &&
-            list->hwLayers[i].acquireFenceFd != -1) {
+            list->hwLayers[i].acquireFenceFd != -1 &&
+            (list->hwLayers[i].flags & HWC_MDPCOMP)) {
             acquireFd[count++] = list->hwLayers[i].acquireFenceFd;
         }
     }
@@ -251,4 +253,71 @@
     return ret;
 }
 
+void LayerCache::resetLayerCache(int num) {
+    for(uint32_t i = 0; i < MAX_NUM_LAYERS; i++) {
+        hnd[i] = NULL;
+    }
+    numHwLayers = num;
+}
+
+void LayerCache::updateLayerCache(hwc_display_contents_1_t* list) {
+
+    int numFbLayers = 0;
+    int numCacheableLayers = 0;
+
+    canUseLayerCache = false;
+    //Bail if geometry changed or num of layers changed
+    if(list->flags & HWC_GEOMETRY_CHANGED ||
+       list->numHwLayers != numHwLayers ) {
+        resetLayerCache(list->numHwLayers);
+        return;
+    }
+
+    for(uint32_t i = 0; i < list->numHwLayers; i++) {
+        //Bail on skip layers
+        if(list->hwLayers[i].flags & HWC_SKIP_LAYER) {
+            resetLayerCache(list->numHwLayers);
+            return;
+        }
+
+        if(list->hwLayers[i].compositionType == HWC_FRAMEBUFFER) {
+            numFbLayers++;
+            if(hnd[i] == NULL) {
+                hnd[i] = list->hwLayers[i].handle;
+            } else if (hnd[i] ==
+                       list->hwLayers[i].handle) {
+                numCacheableLayers++;
+            } else {
+                hnd[i] = NULL;
+                return;
+            }
+        } else {
+            hnd[i] = NULL;
+        }
+    }
+    if(numFbLayers == numCacheableLayers)
+        canUseLayerCache = true;
+
+    //XXX: The marking part is separate, if MDP comp wants
+    // to use it in the future. Right now getting MDP comp
+    // to use this is more trouble than it is worth.
+    markCachedLayersAsOverlay(list);
+}
+
+void LayerCache::markCachedLayersAsOverlay(hwc_display_contents_1_t* list) {
+    //This optimization only works if ALL the layer handles
+    //that were on the framebuffer didn't change.
+    if(canUseLayerCache){
+        for(uint32_t i = 0; i < list->numHwLayers; i++) {
+            if (list->hwLayers[i].handle &&
+                list->hwLayers[i].handle == hnd[i] &&
+                list->hwLayers[i].compositionType != HWC_FRAMEBUFFER_TARGET)
+            {
+                list->hwLayers[i].compositionType = HWC_OVERLAY;
+            }
+        }
+    }
+
+}
+
 };//namespace
diff --git a/libhwcomposer/hwc_utils.h b/libhwcomposer/hwc_utils.h
index ced542e..0f93dd9 100644
--- a/libhwcomposer/hwc_utils.h
+++ b/libhwcomposer/hwc_utils.h
@@ -29,6 +29,7 @@
 #define UNLIKELY( exp )     (__builtin_expect( (exp) != 0, false ))
 #define FINAL_TRANSFORM_MASK 0x000F
 #define MAX_NUM_DISPLAYS 4 //Yes, this is ambitious
+#define MAX_NUM_LAYERS 32
 
 //Fwrd decls
 struct hwc_context_t;
@@ -81,6 +82,26 @@
     HWC_LAYER_RESERVED_1 = 0x00000008
 };
 
+class LayerCache {
+    public:
+    LayerCache() {
+        canUseLayerCache = false;
+        numHwLayers = 0;
+        for(uint32_t i = 0; i < MAX_NUM_LAYERS; i++) {
+            hnd[i] = NULL;
+        }
+    }
+    //LayerCache optimization
+    void updateLayerCache(hwc_display_contents_1_t* list);
+    void resetLayerCache(int num);
+    void markCachedLayersAsOverlay(hwc_display_contents_1_t* list);
+    private:
+    uint32_t numHwLayers;
+    bool canUseLayerCache;
+    buffer_handle_t hnd[MAX_NUM_LAYERS];
+
+};
+
 
 // -----------------------------------------------------------------------------
 // Utility functions - implemented in hwc_utils.cpp
@@ -196,6 +217,8 @@
 
     qhwc::ListStats listStats[HWC_NUM_DISPLAY_TYPES];
 
+    qhwc::LayerCache *mLayerCache;
+
     //Securing in progress indicator
     bool mSecuring;