hwc: Extend partial frame update for split displays
Extends partial frame update support for split displays by calculating
changing region(ROI) independently for each half of the screen. In case of
only one half updating, display driver shuts down the DSI/MDP mixer control
of the non updating half.
Maintains two ROI's for each display. In case of Non-split
displays, only left ROI is updated.
Change-Id: I8a67fe40aac665a48b6f8a46beffb9f8027851b2
diff --git a/libhwcomposer/hwc_mdpcomp.cpp b/libhwcomposer/hwc_mdpcomp.cpp
index 8a717c0..fab9a8b 100644
--- a/libhwcomposer/hwc_mdpcomp.cpp
+++ b/libhwcomposer/hwc_mdpcomp.cpp
@@ -42,7 +42,6 @@
bool MDPComp::sDebugLogs = false;
bool MDPComp::sEnabled = false;
bool MDPComp::sEnableMixedMode = true;
-bool MDPComp::sEnablePartialFrameUpdate = false;
int MDPComp::sMaxPipesPerMixer = MAX_PIPES_PER_MIXER;
bool MDPComp::sEnable4k2kYUVSplit = false;
bool MDPComp::sSrcSplitEnabled = false;
@@ -116,18 +115,6 @@
sDebugLogs = true;
}
- // We read from drivers if panel supports partial updating
- // and we enable partial update computations if supported.
- // Keeping this property to disable partial update for
- // debugging by setting below property to 0 & only 0.
- property_get("persist.hwc.partialupdate", property, "-1");
- if((atoi(property) != 0) &&
- qdutils::MDPVersion::getInstance().isPartialUpdateEnabled()) {
- sEnablePartialFrameUpdate = true;
- }
- ALOGE_IF(isDebug(), "%s: Partial Update applicable?: %d",__FUNCTION__,
- sEnablePartialFrameUpdate);
-
sMaxPipesPerMixer = MAX_PIPES_PER_MIXER;
if(property_get("debug.mdpcomp.maxpermixer", property, "-1") > 0) {
int val = atoi(property);
@@ -421,23 +408,21 @@
return ret;
}
-/*
- * 1) Identify layers that are not visible in the updating ROI and drop them
- * from composition.
- * 2) If we have a scaling layers which needs cropping against generated ROI.
- * Reset ROI to full resolution.
- */
-bool MDPComp::validateAndApplyROI(hwc_context_t *ctx,
- hwc_display_contents_1_t* list, hwc_rect_t roi) {
+void MDPCompNonSplit::trimAgainstROI(hwc_context_t *ctx, hwc_rect_t& fbRect) {
+ hwc_rect_t roi = ctx->listStats[mDpy].lRoi;
+ fbRect = getIntersection(fbRect, roi);
+}
+
+/* 1) Identify layers that are not visible or lying outside the updating ROI and
+ * drop them from composition.
+ * 2) If we have a scaling layer which needs cropping against generated
+ * ROI, reset ROI to full resolution. */
+bool MDPCompNonSplit::validateAndApplyROI(hwc_context_t *ctx,
+ hwc_display_contents_1_t* list) {
int numAppLayers = ctx->listStats[mDpy].numAppLayers;
-
- if(!isValidRect(roi))
- return false;
-
- hwc_rect_t visibleRect = roi;
+ hwc_rect_t visibleRect = ctx->listStats[mDpy].lRoi;
for(int i = numAppLayers - 1; i >= 0; i--){
-
if(!isValidRect(visibleRect)) {
mCurrentFrame.drop[i] = true;
mCurrentFrame.dropCount++;
@@ -445,24 +430,16 @@
}
const hwc_layer_1_t* layer = &list->hwLayers[i];
-
hwc_rect_t dstRect = layer->displayFrame;
- hwc_rect_t srcRect = integerizeSourceCrop(layer->sourceCropf);
-
hwc_rect_t res = getIntersection(visibleRect, dstRect);
- int res_w = res.right - res.left;
- int res_h = res.bottom - res.top;
- int dst_w = dstRect.right - dstRect.left;
- int dst_h = dstRect.bottom - dstRect.top;
-
if(!isValidRect(res)) {
mCurrentFrame.drop[i] = true;
mCurrentFrame.dropCount++;
- }else {
+ } else {
/* Reset frame ROI when any layer which needs scaling also needs ROI
* cropping */
- if((res_w != dst_w || res_h != dst_h) && needsScaling (layer)) {
+ if(!isSameRect(res, dstRect) && needsScaling (layer)) {
ALOGI("%s: Resetting ROI due to scaling", __FUNCTION__);
memset(&mCurrentFrame.drop, 0, sizeof(mCurrentFrame.drop));
mCurrentFrame.dropCount = 0;
@@ -477,52 +454,167 @@
return true;
}
-bool MDPComp::canDoPartialUpdate(hwc_context_t *ctx,
- hwc_display_contents_1_t* list){
- if(!qdutils::MDPVersion::getInstance().isPartialUpdateEnabled() || mDpy ||
- isSkipPresent(ctx, mDpy) || (list->flags & HWC_GEOMETRY_CHANGED)||
- isDisplaySplit(ctx, mDpy)) {
- return false;
- }
- return true;
-}
-
-void MDPComp::generateROI(hwc_context_t *ctx, hwc_display_contents_1_t* list) {
+/* Calculate ROI for the frame by accounting all the layer's dispalyFrame which
+ * are updating. If DirtyRegion is applicable, calculate it by accounting all
+ * the changing layer's dirtyRegion. */
+void MDPCompNonSplit::generateROI(hwc_context_t *ctx,
+ hwc_display_contents_1_t* list) {
int numAppLayers = ctx->listStats[mDpy].numAppLayers;
-
- if(!canDoPartialUpdate(ctx, list))
+ if(!canPartialUpdate(ctx, list))
return;
struct hwc_rect roi = (struct hwc_rect){0, 0, 0, 0};
+ hwc_rect fullFrame = (struct hwc_rect) {0, 0,(int)ctx->dpyAttr[mDpy].xres,
+ (int)ctx->dpyAttr[mDpy].yres};
+
for(int index = 0; index < numAppLayers; index++ ) {
hwc_layer_1_t* layer = &list->hwLayers[index];
if ((mCachedFrame.hnd[index] != layer->handle) ||
- isYuvBuffer((private_handle_t *)layer->handle)) {
+ isYuvBuffer((private_handle_t *)layer->handle)) {
hwc_rect_t updatingRect = layer->displayFrame;
+
#ifdef QCOM_BSP
- if(!needsScaling(layer))
+ if(!needsScaling(layer) && !layer->transform)
updatingRect = layer->dirtyRect;
#endif
+
roi = getUnion(roi, updatingRect);
}
}
- hwc_rect fullFrame = (struct hwc_rect) {0, 0,(int)ctx->dpyAttr[mDpy].xres,
- (int)ctx->dpyAttr[mDpy].yres};
+ /* No layer is updating. Still SF wants a refresh.*/
+ if(!isValidRect(roi))
+ return;
// Align ROI coordinates to panel restrictions
- roi = sanitizeROI(roi, fullFrame);
+ roi = getSanitizeROI(roi, fullFrame);
- if(!validateAndApplyROI(ctx, list, roi))
- roi = fullFrame;
-
- ctx->listStats[mDpy].roi.x = roi.left;
- ctx->listStats[mDpy].roi.y = roi.top;
- ctx->listStats[mDpy].roi.w = roi.right - roi.left;
- ctx->listStats[mDpy].roi.h = roi.bottom - roi.top;
+ ctx->listStats[mDpy].lRoi = roi;
+ if(!validateAndApplyROI(ctx, list))
+ resetROI(ctx, mDpy);
ALOGD_IF(isDebug(),"%s: generated ROI: [%d, %d, %d, %d]", __FUNCTION__,
- roi.left, roi.top, roi.right, roi.bottom);
+ ctx->listStats[mDpy].lRoi.left, ctx->listStats[mDpy].lRoi.top,
+ ctx->listStats[mDpy].lRoi.right, ctx->listStats[mDpy].lRoi.bottom);
+}
+
+void MDPCompSplit::trimAgainstROI(hwc_context_t *ctx, hwc_rect_t& fbRect) {
+ hwc_rect l_roi = ctx->listStats[mDpy].lRoi;
+ hwc_rect r_roi = ctx->listStats[mDpy].rRoi;
+
+ hwc_rect_t l_fbRect = getIntersection(fbRect, l_roi);
+ hwc_rect_t r_fbRect = getIntersection(fbRect, r_roi);
+ fbRect = getUnion(l_fbRect, r_fbRect);
+}
+/* 1) Identify layers that are not visible or lying outside BOTH the updating
+ * ROI's and drop them from composition. If a layer is spanning across both
+ * the halves of the screen but needed by only ROI, the non-contributing
+ * half will not be programmed for MDP.
+ * 2) If we have a scaling layer which needs cropping against generated
+ * ROI, reset ROI to full resolution. */
+bool MDPCompSplit::validateAndApplyROI(hwc_context_t *ctx,
+ hwc_display_contents_1_t* list) {
+
+ int numAppLayers = ctx->listStats[mDpy].numAppLayers;
+
+ hwc_rect_t visibleRectL = ctx->listStats[mDpy].lRoi;
+ hwc_rect_t visibleRectR = ctx->listStats[mDpy].rRoi;
+
+ for(int i = numAppLayers - 1; i >= 0; i--){
+ if(!isValidRect(visibleRectL) && !isValidRect(visibleRectR))
+ {
+ mCurrentFrame.drop[i] = true;
+ mCurrentFrame.dropCount++;
+ continue;
+ }
+
+ const hwc_layer_1_t* layer = &list->hwLayers[i];
+ hwc_rect_t dstRect = layer->displayFrame;
+
+ hwc_rect_t l_res = getIntersection(visibleRectL, dstRect);
+ hwc_rect_t r_res = getIntersection(visibleRectR, dstRect);
+ hwc_rect_t res = getUnion(l_res, r_res);
+
+ if(!isValidRect(l_res) && !isValidRect(r_res)) {
+ mCurrentFrame.drop[i] = true;
+ mCurrentFrame.dropCount++;
+ } else {
+ /* Reset frame ROI when any layer which needs scaling also needs ROI
+ * cropping */
+ if(!isSameRect(res, dstRect) && needsScaling (layer)) {
+ memset(&mCurrentFrame.drop, 0, sizeof(mCurrentFrame.drop));
+ mCurrentFrame.dropCount = 0;
+ return false;
+ }
+
+ if (layer->blending == HWC_BLENDING_NONE) {
+ visibleRectL = deductRect(visibleRectL, l_res);
+ visibleRectR = deductRect(visibleRectR, r_res);
+ }
+ }
+ }
+ return true;
+}
+/* Calculate ROI for the frame by accounting all the layer's dispalyFrame which
+ * are updating. If DirtyRegion is applicable, calculate it by accounting all
+ * the changing layer's dirtyRegion. */
+void MDPCompSplit::generateROI(hwc_context_t *ctx,
+ hwc_display_contents_1_t* list) {
+ if(!canPartialUpdate(ctx, list))
+ return;
+
+ int numAppLayers = ctx->listStats[mDpy].numAppLayers;
+ int lSplit = getLeftSplit(ctx, mDpy);
+
+ int hw_h = (int)ctx->dpyAttr[mDpy].yres;
+ int hw_w = (int)ctx->dpyAttr[mDpy].xres;
+
+ struct hwc_rect l_frame = (struct hwc_rect){0, 0, lSplit, hw_h};
+ struct hwc_rect r_frame = (struct hwc_rect){lSplit, 0, hw_w, hw_h};
+
+ struct hwc_rect l_roi = (struct hwc_rect){0, 0, 0, 0};
+ struct hwc_rect r_roi = (struct hwc_rect){0, 0, 0, 0};
+
+ for(int index = 0; index < numAppLayers; index++ ) {
+ hwc_layer_1_t* layer = &list->hwLayers[index];
+ if ((mCachedFrame.hnd[index] != layer->handle) ||
+ isYuvBuffer((private_handle_t *)layer->handle)) {
+ hwc_rect_t updatingRect = layer->displayFrame;
+
+#ifdef QCOM_BSP
+ if(!needsScaling(layer) && !layer->transform)
+ updatingRect = layer->dirtyRect;
+#endif
+
+ hwc_rect_t l_dst = getIntersection(l_frame, updatingRect);
+ if(isValidRect(l_dst))
+ l_roi = getUnion(l_roi, l_dst);
+
+ hwc_rect_t r_dst = getIntersection(r_frame, updatingRect);
+ if(isValidRect(r_dst))
+ r_roi = getUnion(r_roi, r_dst);
+ }
+ }
+
+ /* No layer is updating. Still SF wants a refresh. */
+ if(!isValidRect(l_roi) && !isValidRect(r_roi))
+ return;
+
+ l_roi = getSanitizeROI(l_roi, l_frame);
+ r_roi = getSanitizeROI(r_roi, r_frame);
+
+ ctx->listStats[mDpy].lRoi = l_roi;
+ ctx->listStats[mDpy].rRoi = r_roi;
+
+ if(!validateAndApplyROI(ctx, list))
+ resetROI(ctx, mDpy);
+
+ ALOGD_IF(isDebug(),"%s: generated L_ROI: [%d, %d, %d, %d]"
+ "R_ROI: [%d, %d, %d, %d]", __FUNCTION__,
+ ctx->listStats[mDpy].lRoi.left, ctx->listStats[mDpy].lRoi.top,
+ ctx->listStats[mDpy].lRoi.right, ctx->listStats[mDpy].lRoi.bottom,
+ ctx->listStats[mDpy].rRoi.left, ctx->listStats[mDpy].rRoi.top,
+ ctx->listStats[mDpy].rRoi.right, ctx->listStats[mDpy].rRoi.bottom);
}
/* Checks for conditions where all the layers marked for MDP comp cannot be
@@ -799,6 +891,16 @@
return true;
}
+bool MDPComp::canPartialUpdate(hwc_context_t *ctx,
+ hwc_display_contents_1_t* list){
+ if(!qdutils::MDPVersion::getInstance().isPartialUpdateEnabled() ||
+ isSkipPresent(ctx, mDpy) || (list->flags & HWC_GEOMETRY_CHANGED) ||
+ mDpy ) {
+ return false;
+ }
+ return true;
+}
+
bool MDPComp::tryVideoOnly(hwc_context_t *ctx,
hwc_display_contents_1_t* list) {
const bool secureOnly = true;
@@ -1093,7 +1195,8 @@
mCurrentFrame.fbCount);
}
-hwc_rect_t MDPComp::getUpdatingFBRect(hwc_display_contents_1_t* list){
+hwc_rect_t MDPComp::getUpdatingFBRect(hwc_context_t *ctx,
+ hwc_display_contents_1_t* list){
hwc_rect_t fbRect = (struct hwc_rect){0, 0, 0, 0};
/* Update only the region of FB needed for composition */
@@ -1104,6 +1207,7 @@
fbRect = getUnion(fbRect, dst);
}
}
+ trimAgainstROI(ctx, fbRect);
return fbRect;
}
@@ -1124,7 +1228,7 @@
//Configure framebuffer first if applicable
if(mCurrentFrame.fbZ >= 0) {
- hwc_rect_t fbRect = getUpdatingFBRect(list);
+ hwc_rect_t fbRect = getUpdatingFBRect(ctx, list);
if(!ctx->mFBUpdate[mDpy]->prepare(ctx, list, fbRect, mCurrentFrame.fbZ))
{
ALOGD_IF(isDebug(), "%s configure framebuffer failed",
@@ -1276,6 +1380,7 @@
if(tryFullFrame(ctx, list) || tryVideoOnly(ctx, list)) {
setMDPCompLayerFlags(ctx, list);
} else {
+ resetROI(ctx, mDpy);
reset(ctx);
memset(&mCurrentFrame.drop, 0, sizeof(mCurrentFrame.drop));
mCurrentFrame.dropCount = 0;
@@ -1587,13 +1692,17 @@
pipeSpecs.mixer = Overlay::MIXER_LEFT;
pipeSpecs.fb = false;
- if (dst.left < lSplit) {
+ // Acquire pipe only for the updating half
+ hwc_rect_t l_roi = ctx->listStats[mDpy].lRoi;
+ hwc_rect_t r_roi = ctx->listStats[mDpy].rRoi;
+
+ if (dst.left < lSplit && isValidRect(getIntersection(dst, l_roi))) {
pipe_info.lIndex = ctx->mOverlay->getPipe(pipeSpecs);
if(pipe_info.lIndex == ovutils::OV_INVALID)
return false;
}
- if(dst.right > lSplit) {
+ if(dst.right > lSplit && isValidRect(getIntersection(dst, r_roi))) {
pipeSpecs.mixer = Overlay::MIXER_RIGHT;
pipe_info.rIndex = ctx->mOverlay->getPipe(pipeSpecs);
if(pipe_info.rIndex == ovutils::OV_INVALID)