display: External display refactor
- cleans up external library
- add separate library for virtual display
- process virtual updates in its separate path
in hwc.
- Acquire blank mutex lock for one complete drawing
cycle
Change-Id: Ib984c578464a131ecdb27ee48960f58d68b7a5a7
diff --git a/libhwcomposer/hwc.cpp b/libhwcomposer/hwc.cpp
index 09fef80..558cc29 100644
--- a/libhwcomposer/hwc.cpp
+++ b/libhwcomposer/hwc.cpp
@@ -67,6 +67,17 @@
}
};
+/* In case of proprietary WFD session, we are fooling SF by piggybacking on
+ * HDMI display ID for virtual. This helper is needed to differentiate their
+ * paths in HAL.
+ * TODO: Not needed once we have WFD client working on top of Google API's */
+
+static int getHWCDpy(hwc_context_t *ctx, int dpy) {
+ if(dpy == HWC_DISPLAY_EXTERNAL && ctx->mVirtualonExtActive)
+ return HWC_DISPLAY_VIRTUAL;
+ return dpy;
+}
+
/*
* Save callback functions registered to HWC
*/
@@ -125,9 +136,10 @@
struct mdp_display_commit commit_info;
memset(&commit_info, 0, sizeof(struct mdp_display_commit));
commit_info.flags = MDP_DISPLAY_COMMIT_OVERLAY;
- if(ioctl(ctx->dpyAttr[dpy].fd, MSMFB_DISPLAY_COMMIT, &commit_info) == -1) {
- ALOGE("%s: MSMFB_DISPLAY_COMMIT for dpy %d failed", __FUNCTION__,dpy);
- return -errno;
+ if(ioctl(ctx->dpyAttr[dpy].fd, MSMFB_DISPLAY_COMMIT, &commit_info) < 0){
+ ALOGE("%s: MSMFB_DISPLAY_COMMIT for dpy %d failed", __FUNCTION__,
+ dpy);
+ return -errno;
}
return 0;
}
@@ -157,9 +169,9 @@
}
static int hwc_prepare_external(hwc_composer_device_1 *dev,
- hwc_display_contents_1_t *list, int dpy) {
-
+ hwc_display_contents_1_t *list) {
hwc_context_t* ctx = (hwc_context_t*)(dev);
+ const int dpy = HWC_DISPLAY_EXTERNAL;
if (LIKELY(list && list->numHwLayers > 1) &&
ctx->dpyAttr[dpy].isActive &&
@@ -169,19 +181,13 @@
hwc_layer_1_t *fbLayer = &list->hwLayers[last];
if(!ctx->dpyAttr[dpy].isPause) {
if(fbLayer->handle) {
- ctx->mExtDispConfiguring = false;
+ ctx->dpyAttr[dpy].isConfiguring = false;
setListStats(ctx, list, dpy);
if(ctx->mMDPComp[dpy]->prepare(ctx, list) < 0) {
const int fbZ = 0;
ctx->mFBUpdate[dpy]->prepare(ctx, list, fbZ);
}
- /* Temporarily commenting out C2D until we support partial
- copybit composition for mixed mode MDP
-
- if((fbZOrder >= 0) && ctx->mCopyBit[dpy])
- ctx->mCopyBit[dpy]->prepare(ctx, list, dpy);
- */
if(ctx->listStats[dpy].isDisplayAnimating) {
// Mark all app layers as HWC_OVERLAY for external during
// animation, so that SF doesnt draw it on FB
@@ -202,12 +208,54 @@
return 0;
}
+static int hwc_prepare_virtual(hwc_composer_device_1 *dev,
+ hwc_display_contents_1_t *list) {
+
+ hwc_context_t* ctx = (hwc_context_t*)(dev);
+ const int dpy = HWC_DISPLAY_VIRTUAL;
+
+ if (LIKELY(list && list->numHwLayers > 1) &&
+ ctx->dpyAttr[dpy].isActive &&
+ ctx->dpyAttr[dpy].connected) {
+ reset_layer_prop(ctx, dpy, list->numHwLayers - 1);
+ uint32_t last = list->numHwLayers - 1;
+ hwc_layer_1_t *fbLayer = &list->hwLayers[last];
+ if(!ctx->dpyAttr[dpy].isPause) {
+ if(fbLayer->handle) {
+ ctx->dpyAttr[dpy].isConfiguring = false;
+ setListStats(ctx, list, dpy);
+ if(ctx->mMDPComp[dpy]->prepare(ctx, list) < 0) {
+ const int fbZ = 0;
+ ctx->mFBUpdate[dpy]->prepare(ctx, list, fbZ);
+ }
+
+ if(ctx->listStats[dpy].isDisplayAnimating) {
+ // Mark all app layers as HWC_OVERLAY for virtual during
+ // animation, so that SF doesnt draw it on FB
+ for(int i = 0 ;i < ctx->listStats[dpy].numAppLayers; i++) {
+ hwc_layer_1_t *layer = &list->hwLayers[i];
+ layer->compositionType = HWC_OVERLAY;
+ }
+ }
+ }
+ } else {
+ // Virtual Display is in Pause state.
+ // ToDo:
+ // Mark all application layers as OVERLAY so that
+ // GPU will not compose. This is done for power
+ // optimization
+ }
+ }
+ return 0;
+}
+
+
static int hwc_prepare(hwc_composer_device_1 *dev, size_t numDisplays,
hwc_display_contents_1_t** displays)
{
int ret = 0;
hwc_context_t* ctx = (hwc_context_t*)(dev);
- Locker::Autolock _bl(ctx->mBlankLock);
+ ctx->mBlankLock.lock();
//Will be unlocked at the end of set
ctx->mExtLock.lock();
reset(ctx, numDisplays, displays);
@@ -220,13 +268,16 @@
for (int32_t i = numDisplays; i >= 0; i--) {
hwc_display_contents_1_t *list = displays[i];
- switch(i) {
+ int dpy = getHWCDpy(ctx, i);
+ switch(dpy) {
case HWC_DISPLAY_PRIMARY:
ret = hwc_prepare_primary(dev, list);
break;
case HWC_DISPLAY_EXTERNAL:
+ ret = hwc_prepare_external(dev, list);
+ break;
case HWC_DISPLAY_VIRTUAL:
- ret = hwc_prepare_external(dev, list, i);
+ ret = hwc_prepare_virtual(dev, list);
break;
default:
ret = -EINVAL;
@@ -278,7 +329,7 @@
hwc_context_t* ctx = (hwc_context_t*)(dev);
Locker::Autolock _l(ctx->mBlankLock);
- int ret = 0;
+ int ret = 0, value = 0;
ALOGD_IF(BLANK_DEBUG, "%s: %s display: %d", __FUNCTION__,
blank==1 ? "Blanking":"Unblanking", dpy);
if(blank) {
@@ -291,57 +342,44 @@
overlay::Writeback::clear();
}
switch(dpy) {
- case HWC_DISPLAY_PRIMARY:
- if(blank) {
- ret = ioctl(ctx->dpyAttr[dpy].fd, FBIOBLANK,FB_BLANK_POWERDOWN);
+ case HWC_DISPLAY_PRIMARY:
+ value = blank ? FB_BLANK_POWERDOWN : FB_BLANK_UNBLANK;
+ if(ioctl(ctx->dpyAttr[dpy].fd, FBIOBLANK, value) < 0 ) {
+ ALOGE("%s: Failed to handle blank event(%d) for Primary!!",
+ __FUNCTION__, blank );
+ return -1;
+ }
- if(ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].connected == true) {
- // Surfaceflinger does not send Blank/unblank event to hwc
- // for virtual display, handle it explicitly when blank for
- // primary is invoked, so that any pipes unset get committed
- if (display_commit(ctx, HWC_DISPLAY_VIRTUAL) < 0) {
- ret = -1;
- ALOGE("%s:post failed for virtual display !!",
- __FUNCTION__);
- } else {
- ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].isActive = !blank;
- }
- }
- } else {
- ret = ioctl(ctx->dpyAttr[dpy].fd, FBIOBLANK, FB_BLANK_UNBLANK);
- if(ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].connected == true) {
- ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].isActive = !blank;
- }
- }
- break;
- case HWC_DISPLAY_EXTERNAL:
- if(blank) {
- // call external framebuffer commit on blank,
- // so that any pipe unsets gets committed
- if (display_commit(ctx, dpy) < 0) {
- ret = -1;
- ALOGE("%s:post failed for external display !! ",
- __FUNCTION__);
- }
- } else {
- }
- break;
- default:
- return -EINVAL;
+ if(!blank) {
+ // Enable HPD here, as during bootup unblank is called
+ // when SF is completely initialized
+ ctx->mExtDisplay->setHPD(1);
+ }
+
+ /* Since SF is not aware of VIRTUAL DISPLAY being handle by HWC,
+ * it wont send blank / unblank events for it. We piggyback on
+ * PRIMARY DISPLAY events to release mdp pips and
+ * activate/deactive VIRTUAL DISPLAY */
+
+ if(ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].connected) {
+ if(blank)
+ display_commit(ctx, HWC_DISPLAY_VIRTUAL);
+ ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].isActive = !blank;
+ }
+ break;
+ case HWC_DISPLAY_EXTERNAL:
+ if(blank) {
+ display_commit(ctx, HWC_DISPLAY_EXTERNAL);
+ }
+ break;
+ default:
+ return -EINVAL;
}
- // Enable HPD here, as during bootup unblank is called
- // when SF is completely initialized
- ctx->mExtDisplay->setHPD(1);
- if(ret == 0){
- ctx->dpyAttr[dpy].isActive = !blank;
- } else {
- ALOGE("%s: Failed in %s display: %d error:%s", __FUNCTION__,
- blank==1 ? "blanking":"unblanking", dpy, strerror(errno));
- return ret;
- }
+
+ ctx->dpyAttr[dpy].isActive = !blank;
ALOGD_IF(BLANK_DEBUG, "%s: Done %s display: %d", __FUNCTION__,
- blank==1 ? "blanking":"unblanking", dpy);
+ blank ? "blanking":"unblanking", dpy);
return 0;
}
@@ -417,11 +455,14 @@
}
static int hwc_set_external(hwc_context_t *ctx,
- hwc_display_contents_1_t* list, int dpy)
+ hwc_display_contents_1_t* list)
{
ATRACE_CALL();
int ret = 0;
+ const int dpy = HWC_DISPLAY_EXTERNAL;
+
+
if (LIKELY(list) && ctx->dpyAttr[dpy].isActive &&
ctx->dpyAttr[dpy].connected) {
@@ -474,26 +515,92 @@
return ret;
}
+static int hwc_set_virtual(hwc_context_t *ctx,
+ hwc_display_contents_1_t* list)
+{
+ ATRACE_CALL();
+ int ret = 0;
+ const int dpy = HWC_DISPLAY_VIRTUAL;
+
+ if (LIKELY(list) && ctx->dpyAttr[dpy].isActive &&
+ ctx->dpyAttr[dpy].connected) {
+ if(!ctx->dpyAttr[dpy].isPause) {
+ uint32_t last = list->numHwLayers - 1;
+ hwc_layer_1_t *fbLayer = &list->hwLayers[last];
+ int fd = -1; //FenceFD from the Copybit(valid in async mode)
+ bool copybitDone = false;
+ if(ctx->mCopyBit[dpy])
+ copybitDone = ctx->mCopyBit[dpy]->draw(ctx, list, dpy, &fd);
+
+ if(list->numHwLayers > 1)
+ hwc_sync(ctx, list, dpy, fd);
+
+ // Dump the layers for virtual
+ if(ctx->mHwcDebug[dpy])
+ ctx->mHwcDebug[dpy]->dumpLayers(list);
+
+ if (!ctx->mMDPComp[dpy]->draw(ctx, list)) {
+ ALOGE("%s: MDPComp draw failed", __FUNCTION__);
+ ret = -1;
+ }
+
+ int extOnlyLayerIndex =
+ ctx->listStats[dpy].extOnlyLayerIndex;
+
+ private_handle_t *hnd = (private_handle_t *)fbLayer->handle;
+ if(extOnlyLayerIndex!= -1) {
+ hwc_layer_1_t *extLayer = &list->hwLayers[extOnlyLayerIndex];
+ hnd = (private_handle_t *)extLayer->handle;
+ } else if(copybitDone) {
+ hnd = ctx->mCopyBit[dpy]->getCurrentRenderBuffer();
+ }
+
+ if(hnd && !isYuvBuffer(hnd)) {
+ if (!ctx->mFBUpdate[dpy]->draw(ctx, hnd)) {
+ ALOGE("%s: FBUpdate::draw fail!", __FUNCTION__);
+ ret = -1;
+ }
+ }
+ }
+
+ if (display_commit(ctx, dpy) < 0) {
+ ALOGE("%s: display commit fail!", __FUNCTION__);
+ ret = -1;
+ }
+ }
+
+ closeAcquireFds(list);
+
+ if (list && !ctx->mVirtualonExtActive) {
+ // SF assumes HWC waits for the acquire fence and returns a new fence
+ // that signals when we're done. Since we don't wait, and also don't
+ // touch the buffer, we can just handle the acquire fence back to SF
+ // as the retire fence.
+ list->retireFenceFd = list->outbufAcquireFenceFd;
+ }
+
+ return ret;
+}
+
+
static int hwc_set(hwc_composer_device_1 *dev,
size_t numDisplays,
hwc_display_contents_1_t** displays)
{
int ret = 0;
hwc_context_t* ctx = (hwc_context_t*)(dev);
- Locker::Autolock _bl(ctx->mBlankLock);
for (uint32_t i = 0; i <= numDisplays; i++) {
hwc_display_contents_1_t* list = displays[i];
- switch(i) {
+ int dpy = getHWCDpy(ctx, i);
+ switch(dpy) {
case HWC_DISPLAY_PRIMARY:
ret = hwc_set_primary(ctx, list);
break;
case HWC_DISPLAY_EXTERNAL:
+ ret = hwc_set_external(ctx, list);
+ break;
case HWC_DISPLAY_VIRTUAL:
- /* ToDo: We are using hwc_set_external path for both External and
- Virtual displays on HWC1.1. Eventually, we will have
- separate functions when we move to HWC1.2
- */
- ret = hwc_set_external(ctx, list, i);
+ ret = hwc_set_virtual(ctx, list);
break;
default:
ret = -EINVAL;
@@ -506,6 +613,7 @@
ctx->mVideoTransFlag = false;
//Was locked at the beginning of prepare
ctx->mExtLock.unlock();
+ ctx->mBlankLock.unlock();
return ret;
}
@@ -513,6 +621,7 @@
uint32_t* configs, size_t* numConfigs) {
int ret = 0;
hwc_context_t* ctx = (hwc_context_t*)(dev);
+ disp = getHWCDpy(ctx, disp);
//in 1.1 there is no way to choose a config, report as config id # 0
//This config is passed to getDisplayAttributes. Ignore for now.
switch(disp) {
@@ -542,8 +651,9 @@
uint32_t config, const uint32_t* attributes, int32_t* values) {
hwc_context_t* ctx = (hwc_context_t*)(dev);
+ disp = getHWCDpy(ctx, disp);
//If hotpluggable displays(i.e, HDMI, WFD) are inactive return error
- if( (disp >= HWC_DISPLAY_EXTERNAL) && !ctx->dpyAttr[disp].connected) {
+ if( (disp != HWC_DISPLAY_PRIMARY) && !ctx->dpyAttr[disp].connected) {
return -1;
}