display: Add support for MDP Composition

This change
   1) implements MDP Composition upto 3 layers
   2) adds invalidator support to fall back to FB
      composition during idle screen condition.

Change-Id: I8d724f905c943f986b68596e2548a7f5a8ce6588
This commit is contained in:
Jeykumar Sankaran 2012-07-23 12:22:21 -07:00 committed by Naseer Ahmed
parent a72904b1dc
commit bd86fcfff6
8 changed files with 1084 additions and 11 deletions

View File

@ -11,5 +11,6 @@ LOCAL_CFLAGS := $(common_flags) -DLOG_TAG=\"hwcomposer\"
LOCAL_ADDITIONAL_DEPENDENCIES := $(common_deps)
LOCAL_SRC_FILES := hwc.cpp hwc_video.cpp hwc_utils.cpp \
hwc_uimirror.cpp hwc_external.cpp \
hwc_uevents.cpp hwc_copybit.cpp
hwc_uevents.cpp hwc_copybit.cpp \
hwc_mdpcomp.cpp
include $(BUILD_SHARED_LIBRARY)

View File

@ -30,6 +30,7 @@
#include "hwc_uimirror.h"
#include "hwc_copybit.h"
#include "hwc_external.h"
#include "hwc_mdpcomp.h"
using namespace qhwc;
@ -84,6 +85,8 @@ static int hwc_prepare(hwc_composer_device_t *dev, hwc_layer_list_t* list)
//Nothing here
} else if(UIMirrorOverlay::prepare(ctx, list)) {
ctx->overlayInUse = true;
} else if(MDPComp::configure(dev, list)) {
ctx->overlayInUse = true;
} else if (0) {
//Other features
ctx->overlayInUse = true;
@ -152,6 +155,7 @@ static int hwc_set(hwc_composer_device_t *dev,
if (LIKELY(list)) {
VideoOverlay::draw(ctx, list);
CopyBit::draw(ctx, list, (EGLDisplay)dpy, (EGLSurface)sur);
MDPComp::draw(ctx, list);
EGLBoolean sucess = eglSwapBuffers((EGLDisplay)dpy, (EGLSurface)sur);
UIMirrorOverlay::draw(ctx);
if(ctx->mExtDisplay->getExternalDisplay())

829
libhwcomposer/hwc_mdpcomp.cpp Executable file
View File

@ -0,0 +1,829 @@
/*
* Copyright (C) 2012, Code Aurora Forum. All rights reserved.
* Not a Contribution, Apache license notifications and license are retained
* for attribution purposes only.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "hwc_mdpcomp.h"
#include "hwc_qbuf.h"
#include "hwc_external.h"
#define SUPPORT_4LAYER 0
namespace qhwc {
/****** Class PipeMgr ***********/
void inline PipeMgr::reset() {
mVGPipes = MAX_VG;
mVGUsed = 0;
mVGIndex = 0;
mRGBPipes = MAX_RGB;
mRGBUsed = 0;
mRGBIndex = MAX_VG;
mTotalAvail = mVGPipes + mRGBPipes;
memset(&mStatus, 0x0 , sizeof(int)*mTotalAvail);
}
int PipeMgr::req_for_pipe(int pipe_req) {
switch(pipe_req) {
case PIPE_REQ_VG: //VG
if(mVGPipes){
mVGPipes--;
mVGUsed++;
mTotalAvail--;
return PIPE_REQ_VG;
}
case PIPE_REQ_RGB: // RGB
if(mRGBPipes) {
mRGBPipes--;
mRGBUsed++;
mTotalAvail--;
return PIPE_REQ_RGB;
}
return PIPE_NONE;
case PIPE_REQ_FB: //FB
if(mRGBPipes) {
mRGBPipes--;
mRGBUsed++;
mTotalAvail--;
mStatus[VAR_INDEX] = PIPE_IN_FB_MODE;
return PIPE_REQ_FB;
}
default:
break;
};
return PIPE_NONE;
}
int PipeMgr::assign_pipe(int pipe_pref) {
switch(pipe_pref) {
case PIPE_REQ_VG: //VG
if(mVGUsed) {
mVGUsed--;
mStatus[mVGIndex] = PIPE_IN_COMP_MODE;
return mVGIndex++;
}
case PIPE_REQ_RGB: //RGB
if(mRGBUsed) {
mRGBUsed--;
mStatus[mRGBIndex] = PIPE_IN_COMP_MODE;
return mRGBIndex++;
}
default:
ALOGE("%s: PipeMgr:invalid case in pipe_mgr_assign",
__FUNCTION__);
return -1;
};
}
/****** Class MDPComp ***********/
MDPComp::State MDPComp::sMDPCompState = MDPCOMP_OFF;
struct MDPComp::frame_info MDPComp::sCurrentFrame;
PipeMgr MDPComp::sPipeMgr;
IdleInvalidator *MDPComp::idleInvalidator = NULL;
bool MDPComp::sIdleFallBack = false;
bool MDPComp::sDebugLogs = false;
int MDPComp::sSkipCount = 0;
int MDPComp::sMaxLayers = 0;
bool MDPComp::deinit() {
//XXX: Tear down MDP comp state
return true;
}
void MDPComp::timeout_handler(void *udata) {
struct hwc_context_t* ctx = (struct hwc_context_t*)(udata);
if(!ctx) {
ALOGE("%s: received empty data in timer callback", __FUNCTION__);
return;
}
hwc_procs* proc = (hwc_procs*)ctx->device.reserved_proc[0];
if(!proc) {
ALOGE("%s: HWC proc not registered", __FUNCTION__);
return;
}
sIdleFallBack = true;
/* Trigger SF to redraw the current frame */
proc->invalidate(proc);
}
void MDPComp::reset( hwc_context_t *ctx, hwc_layer_list_t* list ) {
sCurrentFrame.count = 0;
free(sCurrentFrame.pipe_layer);
sCurrentFrame.pipe_layer = NULL;
//Reset MDP pipes
sPipeMgr.reset();
sPipeMgr.setStatus(VAR_INDEX, PIPE_IN_FB_MODE);
#if SUPPORT_4LAYER
configure_var_pipe(ctx);
#endif
//Reset flags and states
unsetMDPCompLayerFlags(ctx, list);
if(sMDPCompState == MDPCOMP_ON) {
sMDPCompState = MDPCOMP_OFF_PENDING;
}
}
void MDPComp::setLayerIndex(hwc_layer_t* layer, const int pipe_index)
{
layer->flags &= ~HWC_MDPCOMP_INDEX_MASK;
layer->flags |= pipe_index << MDPCOMP_INDEX_OFFSET;
}
int MDPComp::getLayerIndex(hwc_layer_t* layer)
{
int byp_index = -1;
if(layer->flags & HWC_MDPCOMP) {
byp_index = ((layer->flags & HWC_MDPCOMP_INDEX_MASK) >>
MDPCOMP_INDEX_OFFSET);
byp_index = (byp_index < sMaxLayers ? byp_index : -1 );
}
return byp_index;
}
void MDPComp::print_info(hwc_layer_t* layer)
{
hwc_rect_t sourceCrop = layer->sourceCrop;
hwc_rect_t displayFrame = layer->displayFrame;
int s_l = sourceCrop.left;
int s_t = sourceCrop.top;
int s_r = sourceCrop.right;
int s_b = sourceCrop.bottom;
int d_l = displayFrame.left;
int d_t = displayFrame.top;
int d_r = displayFrame.right;
int d_b = displayFrame.bottom;
ALOGD_IF(isDebug(), "src:[%d,%d,%d,%d] (%d x %d) \
dst:[%d,%d,%d,%d] (%d x %d)",
s_l, s_t, s_r, s_b, (s_r - s_l), (s_b - s_t),
d_l, d_t, d_r, d_b, (d_r - d_l), (d_b - d_t));
}
/*
* Configures pipe(s) for MDP composition
*/
int MDPComp::prepare(hwc_context_t *ctx, hwc_layer_t *layer,
mdp_pipe_info& mdp_info) {
int nPipeIndex = mdp_info.index;
if (ctx) {
private_handle_t *hnd = (private_handle_t *)layer->handle;
overlay::Overlay& ov = *(ctx->mOverlay);
if(!hnd) {
ALOGE("%s: layer handle is NULL", __FUNCTION__);
return -1;
}
int hw_w = ctx->mFbDev->width;
int hw_h = ctx->mFbDev->height;
hwc_rect_t sourceCrop = layer->sourceCrop;
hwc_rect_t displayFrame = layer->displayFrame;
const int src_w = sourceCrop.right - sourceCrop.left;
const int src_h = sourceCrop.bottom - sourceCrop.top;
hwc_rect_t crop = sourceCrop;
int crop_w = crop.right - crop.left;
int crop_h = crop.bottom - crop.top;
hwc_rect_t dst = displayFrame;
int dst_w = dst.right - dst.left;
int dst_h = dst.bottom - dst.top;
//REDUNDANT ??
if(hnd != NULL &&
(hnd->flags & private_handle_t::PRIV_FLAGS_NONCONTIGUOUS_MEM )) {
ALOGE("%s: failed due to non-pmem memory",__FUNCTION__);
return -1;
}
if(dst.left < 0 || dst.top < 0 ||
dst.right > hw_w || dst.bottom > hw_h) {
ALOGD_IF(isDebug(),"%s: Destination has negative coordinates",
__FUNCTION__);
qhwc::calculate_crop_rects(crop, dst, hw_w, hw_h);
//Update calulated width and height
crop_w = crop.right - crop.left;
crop_h = crop.bottom - crop.top;
dst_w = dst.right - dst.left;
dst_h = dst.bottom - dst.top;
}
if( (dst_w > hw_w)|| (dst_h > hw_h)) {
ALOGD_IF(isDebug(),"%s: Dest rect exceeds FB", __FUNCTION__);
print_info(layer);
dst_w = hw_w;
dst_h = hw_h;
}
// Determine pipe to set based on pipe index
ovutils::eDest dest = ovutils::OV_PIPE_ALL;
if (nPipeIndex == 0) {
dest = ovutils::OV_PIPE0;
} else if (nPipeIndex == 1) {
dest = ovutils::OV_PIPE1;
} else if (nPipeIndex == 2) {
dest = ovutils::OV_PIPE2;
}
ovutils::eZorder zOrder = ovutils::ZORDER_0;
if(mdp_info.z_order == 0 ) {
zOrder = ovutils::ZORDER_0;
} else if(mdp_info.z_order == 1 ) {
zOrder = ovutils::ZORDER_1;
} else if(mdp_info.z_order == 2 ) {
zOrder = ovutils::ZORDER_2;
}
// Order order order
// setSource - just setting source
// setParameter - changes src w/h/f accordingly
// setCrop - ROI - src_rect
// setPosition - dst_rect
// commit - commit changes to mdp driver
// queueBuffer - not here, happens when draw is called
ovutils::eTransform orient =
static_cast<ovutils::eTransform>(layer->transform);
ov.setTransform(orient, dest);
ovutils::Whf info(hnd->width, hnd->height, hnd->format, hnd->size);
ovutils::eMdpFlags mdpFlags = mdp_info.isVG ? ovutils::OV_MDP_PIPE_SHARE
: ovutils::OV_MDP_FLAGS_NONE;
ovutils::eIsFg isFG = mdp_info.isFG ? ovutils::IS_FG_SET
: ovutils::IS_FG_OFF;
ovutils::PipeArgs parg(mdpFlags,
info,
zOrder,
isFG,
ovutils::ROT_FLAG_DISABLED);
ovutils::PipeArgs pargs[MAX_PIPES] = { parg, parg, parg };
if (!ov.setSource(pargs, dest)) {
ALOGE("%s: setSource failed", __FUNCTION__);
return -1;
}
ovutils::Dim dcrop(crop.left, crop.top, crop_w, crop_h);
if (!ov.setCrop(dcrop, dest)) {
ALOGE("%s: setCrop failed", __FUNCTION__);
return -1;
}
ovutils::Dim dim(dst.left, dst.top, dst_w, dst_h);
if (!ov.setPosition(dim, dest)) {
ALOGE("%s: setPosition failed", __FUNCTION__);
return -1;
}
ALOGD_IF(isDebug(),"%s: MDP set: crop[%d,%d,%d,%d] dst[%d,%d,%d,%d] \
nPipe: %d isFG: %d zorder: %d",__FUNCTION__, dcrop.x,
dcrop.y,dcrop.w, dcrop.h, dim.x, dim.y, dim.w, dim.h,
nPipeIndex,mdp_info.isFG, mdp_info.z_order);
if (!ov.commit(dest)) {
ALOGE("%s: commit failed", __FUNCTION__);
return -1;
}
}
return 0;
}
/*
* MDPComp not possible when
* 1. We have more than sMaxLayers
* 2. External display connected
* 3. Composition is triggered by
* Idle timer expiry
* 4. Rotation is needed
* 5. Overlay in use
*/
bool MDPComp::is_doable(hwc_composer_device_t *dev,
const hwc_layer_list_t* list) {
hwc_context_t* ctx = (hwc_context_t*)(dev);
if(!ctx) {
ALOGE("%s: hwc context is NULL", __FUNCTION__);
return false;
}
//Number of layers
if(list->numHwLayers < 1 || list->numHwLayers > sMaxLayers) {
ALOGD_IF(isDebug(), "%s: Unsupported number of layers",__FUNCTION__);
return false;
}
//Disable MDPComp when ext display connected
if(ctx->mExtDisplay->getExternalDisplay()) {
ALOGD_IF(isDebug(), "%s: External display connected.", __FUNCTION__);
}
//FB composition on idle timeout
if(sIdleFallBack) {
ALOGD_IF(isDebug(), "%s: idle fallback",__FUNCTION__);
return false;
}
//MDP composition is not efficient if rotation is needed.
for(unsigned int i = 0; i < list->numHwLayers; ++i) {
if(list->hwLayers[i].transform) {
ALOGD_IF(isDebug(), "%s: orientation involved",__FUNCTION__);
return false;
}
}
return true;
}
void MDPComp::setMDPCompLayerFlags(hwc_layer_list_t* list) {
for(int index = 0 ; index < sCurrentFrame.count; index++ )
{
int layer_index = sCurrentFrame.pipe_layer[index].layer_index;
if(layer_index >= 0) {
hwc_layer_t* layer = &(list->hwLayers[layer_index]);
layer->flags |= HWC_MDPCOMP;
layer->compositionType = HWC_OVERLAY;
layer->hints |= HWC_HINT_CLEAR_FB;
}
}
}
void MDPComp::get_layer_info(hwc_layer_t* layer, int& flags) {
private_handle_t* hnd = (private_handle_t*)layer->handle;
if(layer->flags & HWC_SKIP_LAYER) {
flags |= MDPCOMP_LAYER_SKIP;
} else if(hnd != NULL &&
(hnd->flags & private_handle_t::PRIV_FLAGS_NONCONTIGUOUS_MEM )) {
flags |= MDPCOMP_LAYER_UNSUPPORTED_MEM;
}
if(layer->blending != HWC_BLENDING_NONE)
flags |= MDPCOMP_LAYER_BLEND;
int dst_w, dst_h;
getLayerResolution(layer, dst_w, dst_h);
hwc_rect_t sourceCrop = layer->sourceCrop;
const int src_w = sourceCrop.right - sourceCrop.left;
const int src_h = sourceCrop.bottom - sourceCrop.top;
if(((src_w > dst_w) || (src_h > dst_h))) {
flags |= MDPCOMP_LAYER_DOWNSCALE;
}
}
int MDPComp::mark_layers(hwc_layer_list_t* list, layer_mdp_info* layer_info,
frame_info& current_frame) {
int layer_count = list->numHwLayers;
if(layer_count > sMaxLayers) {
if(!sPipeMgr.req_for_pipe(PIPE_REQ_FB)) {
ALOGE("%s: binding var pipe to FB failed!!", __FUNCTION__);
return 0;
}
}
//Parse layers from higher z-order
for(int index = layer_count - 1 ; index >= 0; index-- ) {
hwc_layer_t* layer = &list->hwLayers[index];
int layer_prop = 0;
get_layer_info(layer, layer_prop);
ALOGD_IF(isDebug(),"%s: prop for layer [%d]: %x", __FUNCTION__,
index, layer_prop);
//Both in cases of NON-CONTIGUOUS memory or SKIP layer,
//current version of mdp composition falls back completely to FB
//composition.
//TO DO: Support dual mode composition
if(layer_prop & MDPCOMP_LAYER_UNSUPPORTED_MEM) {
ALOGD_IF(isDebug(), "%s: Non contigous memory",__FUNCTION__);
return MDPCOMP_ABORT;
}
if(layer_prop & MDPCOMP_LAYER_SKIP) {
ALOGD_IF(isDebug(), "%s:skip layer",__FUNCTION__);
return MDPCOMP_ABORT;
}
//Request for MDP pipes
int pipe_pref = PIPE_REQ_VG;
if((layer_prop & MDPCOMP_LAYER_DOWNSCALE) &&
(layer_prop & MDPCOMP_LAYER_BLEND)) {
pipe_pref = PIPE_REQ_RGB;
}
int allocated_pipe = sPipeMgr.req_for_pipe( pipe_pref);
if(allocated_pipe) {
layer_info[index].can_use_mdp = true;
layer_info[index].pipe_pref = allocated_pipe;
current_frame.count++;
}else {
ALOGE("%s: pipe marking in mark layer fails for : %d",
__FUNCTION__, allocated_pipe);
return MDPCOMP_FAILURE;
}
}
return MDPCOMP_SUCCESS;
}
void MDPComp::reset_layer_mdp_info(layer_mdp_info* layer_info, int count) {
for(int i = 0 ; i < count; i++ ) {
layer_info[i].can_use_mdp = false;
layer_info[i].pipe_pref = PIPE_NONE;
}
}
bool MDPComp::alloc_layer_pipes(hwc_layer_list_t* list,
layer_mdp_info* layer_info, frame_info& current_frame) {
int layer_count = list->numHwLayers;
int mdp_count = current_frame.count;
int fallback_count = layer_count - mdp_count;
int frame_pipe_count = 0;
ALOGD_IF(isDebug(), "%s: dual mode: %d total count: %d \
mdp count: %d fallback count: %d",
__FUNCTION__, (layer_count != mdp_count),
layer_count, mdp_count, fallback_count);
for(int index = 0 ; index < layer_count ; index++ ) {
hwc_layer_t* layer = &list->hwLayers[index];
if(layer_info[index].can_use_mdp) {
pipe_layer_pair& info = current_frame.pipe_layer[frame_pipe_count];
mdp_pipe_info& pipe_info = info.pipe_index;
pipe_info.index = sPipeMgr.assign_pipe(layer_info[index].pipe_pref);
pipe_info.isVG = (layer_info[index].pipe_pref == PIPE_REQ_VG);
pipe_info.isFG = (frame_pipe_count == 0);
/* if VAR pipe is attached to FB, FB will be updated with
VSYNC WAIT flag, so no need to set VSYNC WAIT for any
bypass pipes. if not, set VSYNC WAIT to the last updating pipe*/
pipe_info.vsync_wait =
(sPipeMgr.getStatus(VAR_INDEX) == PIPE_IN_FB_MODE) ? false:
(frame_pipe_count == (mdp_count - 1));
/* All the layers composed on FB will have MDP zorder 0, so start
assigning from 1*/
pipe_info.z_order = index -
(fallback_count ? fallback_count - 1 : fallback_count);
info.layer_index = index;
frame_pipe_count++;
}
}
return 1;
}
//returns array of layers and their allocated pipes
bool MDPComp::parse_and_allocate(hwc_context_t* ctx, hwc_layer_list_t* list,
frame_info& current_frame ) {
int layer_count = list->numHwLayers;
/* clear pipe status */
sPipeMgr.reset();
layer_mdp_info* bp_layer_info = (layer_mdp_info*)
malloc(sizeof(layer_mdp_info)* layer_count);
reset_layer_mdp_info(bp_layer_info, layer_count);
/* iterate through layer list to mark candidate */
if(mark_layers(list, bp_layer_info, current_frame) == MDPCOMP_ABORT) {
free(bp_layer_info);
current_frame.count = 0;
ALOGE_IF(isDebug(), "%s:mark_layers failed!!", __FUNCTION__);
return false;
}
current_frame.pipe_layer = (pipe_layer_pair*)
malloc(sizeof(pipe_layer_pair) * current_frame.count);
/* allocate MDP pipes for marked layers */
alloc_layer_pipes( list, bp_layer_info, current_frame);
free(bp_layer_info);
return true;
}
#if SUPPORT_4LAYER
int MDPComp::configure_var_pipe(hwc_context_t* ctx) {
if(!ctx) {
ALOGE("%s: invalid context", __FUNCTION__);
return -1;
}
framebuffer_device_t *fbDev = ctx->fbDev;
if (!fbDev) {
ALOGE("%s: fbDev is NULL", __FUNCTION__);
return -1;
}
int new_mode = -1, cur_mode;
fbDev->perform(fbDev,EVENT_GET_VAR_PIPE_MODE, (void*)&cur_mode);
if(sPipeMgr.getStatus(VAR_INDEX) == PIPE_IN_FB_MODE) {
new_mode = VAR_PIPE_FB_ATTACH;
} else if(sPipeMgr.getStatus(VAR_INDEX) == PIPE_IN_BYP_MODE) {
new_mode = VAR_PIPE_FB_DETACH;
fbDev->perform(fbDev,EVENT_WAIT_POSTBUFFER,NULL);
}
ALOGD_IF(isDebug(),"%s: old_mode: %d new_mode: %d", __FUNCTION__,
cur_mode, new_mode);
if((new_mode != cur_mode) && (new_mode >= 0)) {
if(fbDev->perform(fbDev,EVENT_SET_VAR_PIPE_MODE,(void*)&new_mode) < 0) {
ALOGE("%s: Setting var pipe mode failed", __FUNCTION__);
}
}
return 0;
}
#endif
bool MDPComp::setup(hwc_context_t* ctx, hwc_layer_list_t* list) {
int nPipeIndex, vsync_wait, isFG;
int numHwLayers = list->numHwLayers;
frame_info &current_frame = sCurrentFrame;
current_frame.count = 0;
if(!ctx) {
ALOGE("%s: invalid context", __FUNCTION__);
return -1;
}
framebuffer_device_t *fbDev = ctx->mFbDev;
if (!fbDev) {
ALOGE("%s: fbDev is NULL", __FUNCTION__);
return -1;
}
if(!parse_and_allocate(ctx, list, current_frame)) {
#if SUPPORT_4LAYER
int mode = VAR_PIPE_FB_ATTACH;
if(fbDev->perform(fbDev,EVENT_SET_VAR_PIPE_MODE,(void*)&mode) < 0 ) {
ALOGE("%s: setting var pipe mode failed", __FUNCTION__);
}
#endif
ALOGD_IF(isDebug(), "%s: Falling back to FB", __FUNCTION__);
return false;
}
#if SUPPORT_4LAYER
configure_var_pipe(ctx);
#endif
overlay::Overlay& ov = *(ctx->mOverlay);
ovutils::eOverlayState state = ov.getState();
if (current_frame.count == 1) {
state = ovutils::OV_BYPASS_1_LAYER;
} else if (current_frame.count == 2) {
state = ovutils::OV_BYPASS_2_LAYER;
} else if (current_frame.count == 3) {
state = ovutils::OV_BYPASS_3_LAYER;
}
ov.setState(state);
for (int index = 0 ; index < current_frame.count; index++) {
int layer_index = current_frame.pipe_layer[index].layer_index;
hwc_layer_t* layer = &list->hwLayers[layer_index];
mdp_pipe_info& cur_pipe = current_frame.pipe_layer[index].pipe_index;
if( prepare(ctx, layer, cur_pipe) != 0 ) {
ALOGD_IF(isDebug(), "%s: MDPComp failed to configure overlay for \
layer %d with pipe index:%d",__FUNCTION__,
index, cur_pipe.index);
return false;
} else {
setLayerIndex(layer, index);
}
}
return true;
}
void MDPComp::unsetMDPCompLayerFlags(hwc_context_t* ctx, hwc_layer_list_t* list)
{
if (!list)
return;
for (int index = 0 ; index < sCurrentFrame.count; index++) {
int l_index = sCurrentFrame.pipe_layer[index].layer_index;
if(list->hwLayers[l_index].flags & HWC_MDPCOMP) {
list->hwLayers[l_index].flags &= ~HWC_MDPCOMP;
}
}
}
int MDPComp::draw(hwc_context_t *ctx, hwc_layer_list_t* list) {
if(!isEnabled()) {
ALOGD_IF(isDebug(),"%s: MDP Comp. not enabled",__FUNCTION__);
return 0;
}
if(!ctx || !list) {
ALOGE("%s: invalid contxt or list",__FUNCTION__);
return -1;
}
overlay::Overlay& ov = *(ctx->mOverlay);
for(unsigned int i = 0; i < list->numHwLayers; i++ )
{
hwc_layer_t *layer = &list->hwLayers[i];
if(!(layer->flags & HWC_MDPCOMP)) {
ALOGD_IF(isDebug(), "%s: Layer Not flagged for MDP comp",
__FUNCTION__);
continue;
}
int data_index = getLayerIndex(layer);
mdp_pipe_info& pipe_info =
sCurrentFrame.pipe_layer[data_index].pipe_index;
int index = pipe_info.index;
if(index < 0) {
ALOGE("%s: Invalid pipe index (%d)", __FUNCTION__, index);
return -1;
}
/* reset Invalidator */
if(idleInvalidator)
idleInvalidator->markForSleep();
ovutils::eDest dest;
if (index == 0) {
dest = ovutils::OV_PIPE0;
} else if (index == 1) {
dest = ovutils::OV_PIPE1;
} else if (index == 2) {
dest = ovutils::OV_PIPE2;
}
if (ctx ) {
private_handle_t *hnd = (private_handle_t *)layer->handle;
if(!hnd) {
ALOGE("%s handle null", __FUNCTION__);
return -1;
}
//lock buffer before queue
//XXX: Handle lock failure
ctx->qbuf->lockAndAdd(hnd);
ALOGD_IF(isDebug(),"%s: MDP Comp: Drawing layer: %p hnd: %p \
using pipe: %d", __FUNCTION__, layer,
hnd, index );
if (!ov.queueBuffer(hnd->fd, hnd->offset, dest)) {
ALOGE("%s: queueBuffer failed for external", __FUNCTION__);
return -1;
}
}
layer->flags &= ~HWC_MDPCOMP;
layer->flags |= HWC_MDPCOMP_INDEX_MASK;
}
return 0;
}
bool MDPComp::init(hwc_context_t *dev) {
if(!dev) {
ALOGE("%s: Invalid hwc context!!",__FUNCTION__);
return false;
}
#if SUPPORT_4LAYER
if(MAX_MDPCOMP_LAYERS > MAX_STATIC_PIPES) {
framebuffer_device_t *fbDev = dev->fbDevice;
if(fbDev == NULL) {
ALOGE("%s: FATAL: framebuffer device is NULL", __FUNCTION__);
return false;
}
//Receive VAR pipe object from framebuffer
if(fbDev->perform(fbDev,EVENT_GET_VAR_PIPE,(void*)&ov) < 0) {
ALOGE("%s: FATAL: getVariablePipe failed!!", __FUNCTION__);
return false;
}
sPipeMgr.setStatus(VAR_INDEX, PIPE_IN_FB_MODE);
}
#endif
char property[PROPERTY_VALUE_MAX];
sMaxLayers = 0;
if(property_get("debug.mdpcomp.maxlayer", property, NULL) > 0) {
if(atoi(property) != 0)
sMaxLayers = atoi(property);
}
sDebugLogs = false;
if(property_get("debug.mdpcomp.logs", property, NULL) > 0) {
if(atoi(property) != 0)
sDebugLogs = true;
}
unsigned long idle_timeout = DEFAULT_IDLE_TIME;
if(property_get("debug.mdpcomp.idletime", property, NULL) > 0) {
if(atoi(property) != 0)
idle_timeout = atoi(property);
}
//create Idle Invalidator
idleInvalidator = IdleInvalidator::getInstance();
if(idleInvalidator == NULL) {
ALOGE("%s: failed to instantiate idleInvalidator object", __FUNCTION__);
} else {
idleInvalidator->init(timeout_handler, dev, idle_timeout);
}
return true;
}
bool MDPComp::configure(hwc_composer_device_t *dev, hwc_layer_list_t* list) {
if(!isEnabled()) {
ALOGD_IF(isDebug(),"%s: MDP Comp. not enabled.", __FUNCTION__);
return false;
}
hwc_context_t* ctx = (hwc_context_t*)(dev);
bool isMDPCompUsed = true;
bool doable = is_doable(dev, list);
if(doable) {
if(setup(ctx, list)) {
setMDPCompLayerFlags(list);
sMDPCompState = MDPCOMP_ON;
} else {
ALOGD_IF(isDebug(),"%s: MDP Comp Failed",__FUNCTION__);
isMDPCompUsed = false;
}
} else {
ALOGD_IF( isDebug(),"%s: MDP Comp not possible[%d]",__FUNCTION__,
doable);
isMDPCompUsed = false;
}
//Reset states
if(!isMDPCompUsed) {
//Reset current frame
reset(ctx, list);
}
sIdleFallBack = false;
return isMDPCompUsed;
}
}; //namespace

220
libhwcomposer/hwc_mdpcomp.h Executable file
View File

@ -0,0 +1,220 @@
/*
* Copyright (C) 2012, Code Aurora Forum. All rights reserved.
* Not a Contribution, Apache license notifications and license are retained
* for attribution purposes only.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef HWC_MDP_COMP
#define HWC_MDP_COMP
#include <hwc_utils.h>
#include <idle_invalidator.h>
#include <cutils/properties.h>
#include <overlay.h>
#define MAX_STATIC_PIPES 3
#define MDPCOMP_INDEX_OFFSET 4
#define DEFAULT_IDLE_TIME 2000
#define MAX_VG 2
#define MAX_RGB 2
#define VAR_INDEX 3
#define MAX_PIPES (MAX_VG + MAX_RGB)
#define HWC_MDPCOMP_INDEX_MASK 0x00000030
//struct hwc_context_t;
namespace qhwc {
// pipe status
enum {
PIPE_UNASSIGNED = 0,
PIPE_IN_FB_MODE,
PIPE_IN_COMP_MODE,
};
// pipe request
enum {
PIPE_NONE = 0,
PIPE_REQ_VG,
PIPE_REQ_RGB,
PIPE_REQ_FB,
};
// MDP Comp Status
enum {
MDPCOMP_SUCCESS = 0,
MDPCOMP_FAILURE,
MDPCOMP_ABORT,
};
//This class manages the status of 4 MDP pipes and keeps
//track of Variable pipe mode.
class PipeMgr {
public:
PipeMgr() { reset();}
//reset pipemgr params
void reset();
//Based on the preference received, pipe mgr
//allocates the best available pipe to handle
//the case
int req_for_pipe(int pipe_req);
//Allocate requested pipe and update availablity
int assign_pipe(int pipe_pref);
// Get/Set pipe status
void setStatus(int pipe_index, int pipe_status) {
mStatus[pipe_index] = pipe_status;
}
int getStatus(int pipe_index) {
return mStatus[pipe_index];
}
private:
int mVGPipes;
int mVGUsed;
int mVGIndex;
int mRGBPipes;
int mRGBUsed;
int mRGBIndex;
int mTotalAvail;
int mStatus[MAX_PIPES];
};
class MDPComp {
enum State {
MDPCOMP_ON = 0,
MDPCOMP_OFF,
MDPCOMP_OFF_PENDING,
};
enum {
MDPCOMP_LAYER_BLEND = 1,
MDPCOMP_LAYER_DOWNSCALE = 2,
MDPCOMP_LAYER_SKIP = 4,
MDPCOMP_LAYER_UNSUPPORTED_MEM = 8,
};
struct mdp_pipe_info {
int index;
int z_order;
bool isVG;
bool isFG;
bool vsync_wait;
};
struct pipe_layer_pair {
int layer_index;
mdp_pipe_info pipe_index;
native_handle_t* handle;
};
struct frame_info {
int count;
struct pipe_layer_pair* pipe_layer;
};
struct layer_mdp_info {
bool can_use_mdp;
int pipe_pref;
};
static State sMDPCompState;
static IdleInvalidator *idleInvalidator;
static struct frame_info sCurrentFrame;
static PipeMgr sPipeMgr;
static int sSkipCount;
static int sMaxLayers;
static bool sDebugLogs;
static bool sIdleFallBack;
public:
/* Handler to invoke frame redraw on Idle Timer expiry */
static void timeout_handler(void *udata);
/* configure/tear-down MDPComp params*/
static bool init(hwc_context_t *ctx);
static bool deinit();
/*sets up mdp comp for the current frame */
static bool configure(hwc_composer_device_t *ctx, hwc_layer_list_t* list);
/* draw */
static int draw(hwc_context_t *ctx, hwc_layer_list_t *list);
/* store frame stats */
static void setStats(int skipCt) { sSkipCount = skipCt;};
private:
/* get/set pipe index associated with overlay layers */
static void setLayerIndex(hwc_layer_t* layer, const int pipe_index);
static int getLayerIndex(hwc_layer_t* layer);
/* set/reset flags for MDPComp */
static void setMDPCompLayerFlags(hwc_layer_list_t* list);
static void unsetMDPCompLayerFlags(hwc_context_t* ctx,
hwc_layer_list_t* list);
static void print_info(hwc_layer_t* layer);
/* configure's overlay pipes for the frame */
static int prepare(hwc_context_t *ctx, hwc_layer_t *layer,
mdp_pipe_info& mdp_info);
/* checks for conditions where mdpcomp is not possible */
static bool is_doable(hwc_composer_device_t *dev,
const hwc_layer_list_t* list);
static bool setup(hwc_context_t* ctx, hwc_layer_list_t* list);
/* parses layer for properties affecting mdp comp */
static void get_layer_info(hwc_layer_t* layer, int& flags);
/* iterates through layer list to choose candidate to use overlay */
static int mark_layers(hwc_layer_list_t* list, layer_mdp_info* layer_info,
frame_info& current_frame);
static bool parse_and_allocate(hwc_context_t* ctx, hwc_layer_list_t* list,
frame_info& current_frame );
/* clears layer info struct */
static void reset_layer_mdp_info(layer_mdp_info* layer_mdp_info,int count);
/* allocates pipes to selected candidates */
static bool alloc_layer_pipes(hwc_layer_list_t* list,
layer_mdp_info* layer_info,
frame_info& current_frame);
/* updates variable pipe mode for the current frame */
static int configure_var_pipe(hwc_context_t* ctx);
/* get/set states */
static State get_state() { return sMDPCompState; };
static void set_state(State state) { sMDPCompState = state; };
/* reset state */
static void reset( hwc_context_t *ctx, hwc_layer_list_t* list );
/* Is feature enabled */
static bool isEnabled() { return sMaxLayers ? true : false; };
/* Is debug enabled */
static bool isDebug() { return sDebugLogs ? true : false; };
};
}; //namespace
#endif

View File

@ -22,6 +22,7 @@
#include "hwc_qbuf.h"
#include "hwc_copybit.h"
#include "hwc_external.h"
#include "hwc_mdpcomp.h"
namespace qhwc {
@ -43,6 +44,7 @@ void initContext(hwc_context_t *ctx)
ctx->hasOverlay = qdutils::MDPVersion::getInstance().hasOverlay();
ctx->mCopybitEngine = CopybitEngine::getInstance();
ctx->mExtDisplay = new ExternalDisplay(ctx);
MDPComp::init(ctx);
init_uevent_thread(ctx);
@ -103,6 +105,7 @@ void getLayerStats(hwc_context_t *ctx, const hwc_layer_list_t *list)
int yuvCount = 0;
int yuvLayerIndex = -1;
bool isYuvLayerSkip = false;
int skipCount = 0;
for (size_t i = 0; i < list->numHwLayers; i++) {
private_handle_t *hnd =
@ -120,11 +123,13 @@ void getLayerStats(hwc_context_t *ctx, const hwc_layer_list_t *list)
if(yuvLayerIndex != -1 && yuvLayerIndex < (ssize_t)i) {
isYuvLayerSkip = true;
}
skipCount++;
}
}
VideoOverlay::setStats(yuvCount, yuvLayerIndex, isYuvLayerSkip);
CopyBit::setStats(yuvCount, yuvLayerIndex, isYuvLayerSkip);
MDPComp::setStats(skipCount);
ctx->numHwLayers = list->numHwLayers;
return;

View File

@ -50,6 +50,12 @@ enum HWCCompositionType {
HWC_USE_COPYBIT // This layer is to be handled by copybit
};
enum {
HWC_MDPCOMP = 0x00000002,
HWC_LAYER_RESERVED_0 = 0x00000004,
HWC_LAYER_RESERVED_1 = 0x00000008
};
class ExternalDisplay;
class CopybitEngine;
@ -81,6 +87,13 @@ static inline bool isBufferLocked(const private_handle_t* hnd) {
// Initialize uevent thread
void init_uevent_thread(hwc_context_t* ctx);
inline void getLayerResolution(const hwc_layer_t* layer,
int& width, int& height)
{
hwc_rect_t displayFrame = layer->displayFrame;
width = displayFrame.right - displayFrame.left;
height = displayFrame.bottom - displayFrame.top;
}
}; //qhwc namespace
// -----------------------------------------------------------------------------

View File

@ -8,5 +8,6 @@ LOCAL_SHARED_LIBRARIES := $(common_libs)
LOCAL_C_INCLUDES := $(common_includes) $(kernel_includes)
LOCAL_CFLAGS := $(common_flags)
LOCAL_ADDITIONAL_DEPENDENCIES := $(common_deps)
LOCAL_SRC_FILES := profiler.cpp mdp_version.cpp
LOCAL_SRC_FILES := profiler.cpp mdp_version.cpp \
idle_invalidator.cpp
include $(BUILD_SHARED_LIBRARY)

18
libqdutils/idle_invalidator.cpp Normal file → Executable file
View File

@ -27,10 +27,10 @@
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "IdleInvalidator.h"
#include "idle_invalidator.h"
#include <unistd.h>
#define II_DEBUG 1
#define II_DEBUG 0
static const char *threadName = "Invalidator";
InvalidatorHandler IdleInvalidator::mHandler = NULL;
@ -38,12 +38,12 @@ android::sp<IdleInvalidator> IdleInvalidator::sInstance(0);
IdleInvalidator::IdleInvalidator(): Thread(false), mHwcContext(0),
mSleepAgain(false), mSleepTime(0) {
ALOGE_IF(II_DEBUG, "shs %s", __func__);
ALOGD_IF(II_DEBUG, "%s", __func__);
}
int IdleInvalidator::init(InvalidatorHandler reg_handler, void* user_data,
unsigned int idleSleepTime) {
ALOGE_IF(II_DEBUG, "shs %s", __func__);
ALOGD_IF(II_DEBUG, "%s", __func__);
/* store registered handler */
mHandler = reg_handler;
@ -53,8 +53,8 @@ int IdleInvalidator::init(InvalidatorHandler reg_handler, void* user_data,
}
bool IdleInvalidator::threadLoop() {
ALOGE_IF(II_DEBUG, "shs %s", __func__);
usleep(mSleepTime * 1000);
ALOGD_IF(II_DEBUG, "%s", __func__);
usleep(mSleepTime * 500);
if(mSleepAgain) {
//We need to sleep again!
mSleepAgain = false;
@ -66,12 +66,12 @@ bool IdleInvalidator::threadLoop() {
}
int IdleInvalidator::readyToRun() {
ALOGE_IF(II_DEBUG, "shs %s", __func__);
ALOGD_IF(II_DEBUG, "%s", __func__);
return 0; /*NO_ERROR*/
}
void IdleInvalidator::onFirstRef() {
ALOGE_IF(II_DEBUG, "shs %s", __func__);
ALOGD_IF(II_DEBUG, "%s", __func__);
}
void IdleInvalidator::markForSleep() {
@ -81,7 +81,7 @@ void IdleInvalidator::markForSleep() {
}
IdleInvalidator *IdleInvalidator::getInstance() {
ALOGE_IF(II_DEBUG, "shs %s", __func__);
ALOGD_IF(II_DEBUG, "%s", __func__);
if(sInstance.get() == NULL)
sInstance = new IdleInvalidator();
return sInstance.get();