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;