libhwcomposer: Add support for 3 layer bypass

This change allows 3 or less layer use cases to get bypassed completely via MDP pipes.
If more than 3 layers, all of them gets composited on FB.

Intended FB fallback cases:
  1) 4 or more layers
  2) Any MDP pipe is in use
  3) Any layer needs rotation
  4) Any layer is allocated from non-pmem

Change-Id: Ieb2b30b8ab9893fd68c7fc61ec889ac9a01cb7dd
(cherry picked from commit 001ffcfef1d63948926b1be67c4977d13ae1b159)

Conflicts:

	libhwcomposer/hwcomposer.cpp
This commit is contained in:
Zhoulu Luo 2012-03-04 15:43:08 -08:00 committed by Andrew Sutherland
parent ab890c7518
commit 84961f81a5
2 changed files with 459 additions and 297 deletions

View File

@ -46,31 +46,30 @@
#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false ))
enum HWCLayerType{
HWC_SINGLE_VIDEO = 0x1,
HWC_ORIG_RESOLUTION = 0x2,
HWC_S3D_LAYER = 0x4,
HWC_STOP_UI_MIRRORING_MASK = 0xF
};
#ifdef COMPOSITION_BYPASS
#define MAX_BYPASS_LAYERS 3
#define BYPASS_DEBUG 0
#define BYPASS_INDEX_OFFSET 4
enum BypassState {
BYPASS_ON,
BYPASS_OFF,
BYPASS_OFF_PENDING,
};
enum {
MAX_BYPASS_LAYERS = 2,
ANIM_FRAME_COUNT = 30,
};
enum BypassBufferLockState {
BYPASS_BUFFER_UNLOCKED,
BYPASS_BUFFER_LOCKED,
};
#endif
enum HWCLayerType{
HWC_SINGLE_VIDEO = 0x1,
HWC_ORIG_RESOLUTION = 0x2,
HWC_S3D_LAYER = 0x4,
HWC_STOP_UI_MIRRORING_MASK = 0xF
};
enum eHWCOverlayStatus {
HWC_OVERLAY_OPEN,
HWC_OVERLAY_PREPARE_TO_CLOSE,
@ -86,7 +85,8 @@ struct hwc_context_t {
overlay::OverlayUI* mOvUI[MAX_BYPASS_LAYERS];
native_handle_t* previousBypassHandle[MAX_BYPASS_LAYERS];
BypassBufferLockState bypassBufferLockState[MAX_BYPASS_LAYERS];
int animCount;
int layerindex[MAX_BYPASS_LAYERS];
int nPipesUsed;
BypassState bypassState;
#endif
#if defined HDMI_DUAL_DISPLAY
@ -153,6 +153,374 @@ static inline int min(const int& a, const int& b) {
static inline int max(const int& a, const int& b) {
return (a > b) ? a : b;
}
#ifdef COMPOSITION_BYPASS
void setLayerbypassIndex(hwc_layer_t* layer, const int bypass_index)
{
layer->flags &= ~HWC_BYPASS_INDEX_MASK;
layer->flags |= bypass_index << BYPASS_INDEX_OFFSET;
}
int getLayerbypassIndex(hwc_layer_t* layer)
{
int byp_index = -1;
if(layer->flags & HWC_COMP_BYPASS) {
byp_index = ((layer->flags & HWC_BYPASS_INDEX_MASK) >> BYPASS_INDEX_OFFSET);
byp_index = (byp_index < MAX_BYPASS_LAYERS ? byp_index : -1 );
}
return byp_index;
}
void unlockPreviousBypassBuffers(hwc_context_t* ctx) {
// Unlock the previous bypass buffers. We can blindly unlock the buffers here,
// because buffers will be in this list only if the lock was successfully acquired.
for(int i = 0; i < MAX_BYPASS_LAYERS && ctx->previousBypassHandle[i]; i++) {
private_handle_t *hnd = (private_handle_t*) ctx->previousBypassHandle[i];
// Validate the handle to make sure it hasn't been deallocated.
if (private_handle_t::validate(ctx->previousBypassHandle[i])) {
continue;
}
// Check if the handle was locked previously
if (private_handle_t::PRIV_FLAGS_HWC_LOCK & hnd->flags) {
if (GENLOCK_FAILURE == genlock_unlock_buffer(ctx->previousBypassHandle[i])) {
LOGE("%s: genlock_unlock_buffer failed", __FUNCTION__);
} else {
ctx->previousBypassHandle[i] = NULL;
// Reset the lock flag
hnd->flags &= ~private_handle_t::PRIV_FLAGS_HWC_LOCK;
}
}
}
}
void 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;
LOGE_IF(BYPASS_DEBUG, "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));
}
//Crops source buffer against destination and FB boundaries
void calculate_crop_rects(hwc_rect_t& crop, hwc_rect_t& dst, int hw_w, int hw_h) {
int& crop_x = crop.left;
int& crop_y = crop.top;
int& crop_r = crop.right;
int& crop_b = crop.bottom;
int crop_w = crop.right - crop.left;
int crop_h = crop.bottom - crop.top;
int& dst_x = dst.left;
int& dst_y = dst.top;
int& dst_r = dst.right;
int& dst_b = dst.bottom;
int dst_w = dst.right - dst.left;
int dst_h = dst.bottom - dst.top;
if(dst_x < 0) {
float scale_x = crop_w * 1.0f / dst_w;
float diff_factor = (scale_x * abs(dst_x));
crop_x = crop_x + (int)diff_factor;
crop_w = crop_r - crop_x;
dst_x = 0;
dst_w = dst_r - dst_x;;
}
if(dst_r > hw_w) {
float scale_x = crop_w * 1.0f / dst_w;
float diff_factor = scale_x * (dst_r - hw_w);
crop_r = crop_r - diff_factor;
crop_w = crop_r - crop_x;
dst_r = hw_w;
dst_w = dst_r - dst_x;
}
if(dst_y < 0) {
float scale_y = crop_h * 1.0f / dst_h;
float diff_factor = scale_y * abs(dst_y);
crop_y = crop_y + diff_factor;
crop_h = crop_b - crop_y;
dst_y = 0;
dst_h = dst_b - dst_y;
}
if(dst_b > hw_h) {
float scale_y = crop_h * 1.0f / dst_h;
float diff_factor = scale_y * (dst_b - hw_h);
crop_b = crop_b - diff_factor;
crop_h = crop_b - crop_y;
dst_b = hw_h;
dst_h = dst_b - dst_y;
}
LOGE_IF(BYPASS_DEBUG,"crop: [%d,%d,%d,%d] dst:[%d,%d,%d,%d]",
crop_x, crop_y, crop_w, crop_h,dst_x, dst_y, dst_w, dst_h);
}
/*
* Configures pipe(s) for composition bypass
*/
static int prepareBypass(hwc_context_t *ctx, hwc_layer_t *layer,
int nPipeIndex, int vsync_wait, int isFG) {
if (ctx && ctx->mOvUI[nPipeIndex]) {
overlay::OverlayUI *ovUI = ctx->mOvUI[nPipeIndex];
private_hwc_module_t* hwcModule = reinterpret_cast<
private_hwc_module_t*>(ctx->device.common.module);
if (!hwcModule) {
LOGE("%s: NULL Module", __FUNCTION__);
return -1;
}
private_handle_t *hnd = (private_handle_t *)layer->handle;
if(!hnd) {
LOGE("%s: layer handle is NULL", __FUNCTION__);
return -1;
}
int hw_w = hwcModule->fbDevice->width;
int hw_h = hwcModule->fbDevice->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;
if(hnd != NULL && (hnd->flags & private_handle_t::PRIV_FLAGS_NONCONTIGUOUS_MEM )) {
LOGE("%s: Unable to setup bypass due to non-pmem memory",__FUNCTION__);
return -1;
}
if(dst.left < 0 || dst.right < 0 || dst.right > hw_w || dst.bottom > hw_h) {
LOGE_IF(BYPASS_DEBUG,"%s: Destination has negative coordinates", __FUNCTION__);
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)) {
LOGE_IF(BYPASS_DEBUG,"%s: Destination rectangle exceeds FB resolution", __FUNCTION__);
print_info(layer);
dst_w = hw_w;
dst_h = hw_h;
}
overlay_buffer_info info;
info.width = src_w;
info.height = src_h;
info.format = hnd->format;
info.size = hnd->size;
int fbnum = 0;
int orientation = layer->transform;
const bool useVGPipe = (nPipeIndex != (MAX_BYPASS_LAYERS-1));
//only last layer should wait for vsync
const bool waitForVsync = vsync_wait;
const bool isFg = isFG;
//Just to differentiate zorders for different layers
const int zorder = nPipeIndex;
ovUI->setSource(info, orientation);
ovUI->setCrop(crop.left, crop.top, crop_w, crop_h);
ovUI->setDisplayParams(fbnum, waitForVsync, isFg, zorder, useVGPipe);
ovUI->setPosition(dst.left, dst.top, dst_w, dst_h);
if(ovUI->commit() != overlay::NO_ERROR) {
LOGE("%s: Overlay Commit failed", __FUNCTION__);
return -1;
}
LOGE_IF(BYPASS_DEBUG,"%s: Bypass set: crop[%d,%d,%d,%d] dst[%d,%d,%d,%d] waitforVsync: %d \
isFg: %d zorder: %d VG = %d nPipe: %d",__FUNCTION__,
crop.left, crop.top, crop_w, crop_h,
dst.left, dst.top, dst_w, dst_h,
waitForVsync, isFg, zorder, useVGPipe, nPipeIndex );
}
return 0;
}
/*
* Checks if doing comp. bypass is possible.
* It is possible if
* 1. No MDP pipe is used
* 2. Rotation is not needed
* 3. We have atmost MAX_BYPASS_LAYERS
*/
inline static bool isBypassDoable(hwc_composer_device_t *dev, const int yuvCount,
const hwc_layer_list_t* list) {
hwc_context_t* ctx = (hwc_context_t*)(dev);
private_hwc_module_t* hwcModule = reinterpret_cast<private_hwc_module_t*>(
dev->common.module);
//Check if enabled in build.prop
if(hwcModule->isBypassEnabled == false) {
return false;
}
if(list->numHwLayers < 1) {
return false;
}
#if defined HDMI_DUAL_DISPLAY
//Disable bypass when HDMI is enabled
if(ctx->mHDMIEnabled || ctx->pendingHDMI) {
return false;
}
#endif
//Bypass is not efficient if rotation is needed.
for(int i = 0; i < list->numHwLayers; ++i) {
if(list->hwLayers[i].transform) {
return false;
}
}
return (yuvCount == 0) && (ctx->hwcOverlayStatus == HWC_OVERLAY_CLOSED)
&& (list->numHwLayers <= MAX_BYPASS_LAYERS);
}
void setBypassLayerFlags(hwc_context_t* ctx, hwc_layer_list_t* list)
{
for(int index = 0 ; index < MAX_BYPASS_LAYERS; index++ )
{
int layer_index = ctx->layerindex[index];
if(layer_index >= 0) {
hwc_layer_t* layer = &(list->hwLayers[layer_index]);
layer->flags |= HWC_COMP_BYPASS;
layer->compositionType = HWC_USE_OVERLAY;
layer->hints |= HWC_HINT_CLEAR_FB;
}
}
if( list->numHwLayers > ctx->nPipesUsed ) {
list->flags &= ~HWC_SKIP_COMPOSITION; //Compose to FB
} else {
list->flags |= HWC_SKIP_COMPOSITION; // Dont
}
}
bool setupBypass(hwc_context_t* ctx, hwc_layer_list_t* list) {
int nPipeIndex, vsync_wait, isFG;
int numHwLayers = list->numHwLayers;
int nPipeAvailable = MAX_BYPASS_LAYERS;
for (int index = 0 ; (index < numHwLayers) && nPipeAvailable; index++) {
hwc_layer_t* layer = &(list->hwLayers[index]);
nPipeIndex = MAX_BYPASS_LAYERS - nPipeAvailable;
//Set VSYNC wait is needed only for the last pipe queued
vsync_wait = (nPipeIndex == (numHwLayers-1));
//Set isFG to true for layer with z-order zero
isFG = !index;
//Clear Bypass flags for the layer
layer->flags &= ~HWC_COMP_BYPASS;
layer->flags |= HWC_BYPASS_INDEX_MASK;
if( prepareBypass(ctx, &(list->hwLayers[index]), nPipeIndex, vsync_wait, isFG) != 0 ) {
LOGE_IF(BYPASS_DEBUG, "%s: layer %d failed to configure bypass for pipe index: %d",
__FUNCTION__, index, nPipeIndex);
return false;
} else {
ctx->layerindex[nPipeIndex] = index;
setLayerbypassIndex(layer, nPipeIndex);
nPipeAvailable--;
}
}
ctx->nPipesUsed = MAX_BYPASS_LAYERS - nPipeAvailable;
return true;
}
void unsetBypassLayerFlags(hwc_layer_list_t* list) {
for (int index = 0 ; index < list->numHwLayers; index++) {
if(list->hwLayers[index].flags & HWC_COMP_BYPASS) {
list->hwLayers[index].flags &= ~HWC_COMP_BYPASS;
}
}
}
void unsetBypassBufferLockState(hwc_context_t* ctx) {
for (int i= 0; i< MAX_BYPASS_LAYERS; i++) {
ctx->bypassBufferLockState[i] = BYPASS_BUFFER_UNLOCKED;
}
}
void storeLockedBypassHandle(hwc_layer_list_t* list, hwc_context_t* ctx) {
for(int index = 0; index < MAX_BYPASS_LAYERS; index++ ) {
hwc_layer_t layer = list->hwLayers[ctx->layerindex[index]];
if (layer.flags & HWC_COMP_BYPASS) {
private_handle_t *hnd = (private_handle_t*)layer.handle;
if (ctx->bypassBufferLockState[index] == BYPASS_BUFFER_LOCKED) {
ctx->previousBypassHandle[index] = (native_handle_t*)layer.handle;
hnd->flags |= private_handle_t::PRIV_FLAGS_HWC_LOCK;
} else {
ctx->previousBypassHandle[index] = NULL;
}
}
}
}
void closeExtraPipes(hwc_context_t* ctx) {
int pipes_used = ctx->nPipesUsed;
//Unused pipes must be of higher z-order
for (int i = pipes_used ; i < MAX_BYPASS_LAYERS; i++) {
if (ctx->previousBypassHandle[i]) {
private_handle_t *hnd = (private_handle_t*) ctx->previousBypassHandle[i];
if (private_handle_t::validate(ctx->previousBypassHandle[i])) {
continue;
}
if (GENLOCK_FAILURE == genlock_unlock_buffer(ctx->previousBypassHandle[i])) {
LOGE("%s: genlock_unlock_buffer failed", __FUNCTION__);
} else {
ctx->previousBypassHandle[i] = NULL;
ctx->bypassBufferLockState[i] = BYPASS_BUFFER_UNLOCKED;
hnd->flags &= ~private_handle_t::PRIV_FLAGS_HWC_LOCK;
}
}
ctx->mOvUI[i]->closeChannel();
ctx->layerindex[i] = -1;
}
}
#endif //COMPOSITION_BYPASS
static int setVideoOverlayStatusInGralloc(hwc_context_t* ctx, const bool enable) {
#if defined HDMI_DUAL_DISPLAY
@ -217,43 +585,6 @@ static int hwc_closeOverlayChannels(hwc_context_t* ctx) {
return 0;
}
#ifdef COMPOSITION_BYPASS
// To-do: Merge this with other blocks & move them to a separate file.
void unlockPreviousBypassBuffers(hwc_context_t* ctx) {
// Unlock the previous bypass buffers. We can blindly unlock the buffers here,
// because buffers will be in this list only if the lock was successfully acquired.
for(int i = 0; i < MAX_BYPASS_LAYERS; i++) {
if (ctx->previousBypassHandle[i]) {
private_handle_t *hnd = (private_handle_t*) ctx->previousBypassHandle[i];
// Validate the handle to make sure it hasn't been deallocated.
if (private_handle_t::validate(ctx->previousBypassHandle[i])) {
continue;
}
// Check if the handle was locked previously
if (private_handle_t::PRIV_FLAGS_HWC_LOCK & hnd->flags) {
if (GENLOCK_FAILURE == genlock_unlock_buffer(ctx->previousBypassHandle[i])) {
LOGE("%s: genlock_unlock_buffer failed", __FUNCTION__);
} else {
ctx->previousBypassHandle[i] = NULL;
// Reset the lock flag
hnd->flags &= ~private_handle_t::PRIV_FLAGS_HWC_LOCK;
}
}
}
}
}
void closeBypass(hwc_context_t* ctx) {
unlockPreviousBypassBuffers(ctx);
for (int index = 0 ; index < MAX_BYPASS_LAYERS; index++) {
ctx->mOvUI[index]->closeChannel();
}
#ifdef DEBUG
LOGE("%s", __FUNCTION__);
#endif
}
#endif
/*
* Configures mdp pipes
*/
@ -262,7 +593,8 @@ static int prepareOverlay(hwc_context_t *ctx, hwc_layer_t *layer, const bool wai
#ifdef COMPOSITION_BYPASS
if(ctx && (ctx->bypassState != BYPASS_OFF)) {
closeBypass(ctx);
ctx->nPipesUsed = 0;
closeExtraPipes(ctx);
ctx->bypassState = BYPASS_OFF;
}
#endif
@ -439,234 +771,6 @@ static bool canUseCopybit(const framebuffer_device_t* fbDev, const hwc_layer_lis
return use_copybit;
}
#ifdef COMPOSITION_BYPASS
/*
* Configures pipe(s) for composition bypass
*/
static int prepareBypass(hwc_context_t *ctx, hwc_layer_t *layer, int index,
int lastLayerIndex) {
if (ctx && ctx->mOvUI[index]) {
private_hwc_module_t* hwcModule = reinterpret_cast<
private_hwc_module_t*>(ctx->device.common.module);
if (!hwcModule) {
LOGE("prepareBypass null module ");
return -1;
}
private_handle_t *hnd = (private_handle_t *)layer->handle;
if(!hnd) {
LOGE("prepareBypass handle null");
return -1;
}
hwc_rect_t sourceCrop = layer->sourceCrop;
if((sourceCrop.right - sourceCrop.left) > hwcModule->fbDevice->width ||
(sourceCrop.bottom - sourceCrop.top) > hwcModule->fbDevice->height) {
ctx->animCount = ANIM_FRAME_COUNT;
return -1;
}
overlay::OverlayUI *ovUI = ctx->mOvUI[index];
int ret = 0;
int orientation = layer->transform;
overlay_buffer_info info;
info.width = sourceCrop.right - sourceCrop.left;
info.height = sourceCrop.bottom - sourceCrop.top;
info.format = hnd->format;
info.size = hnd->size;
info.secure = (hnd->flags &
private_handle_t::PRIV_FLAGS_SECURE_BUFFER)? true:false;
const bool useVGPipe = true;
//only last layer should wait for vsync
const bool waitForVsync = (index == lastLayerIndex);
const int fbnum = 0;
const bool isFg = (index == 0);
//Just to differentiate zorders for different layers
const int zorder = index;
const bool isVGPipe = true;
ovUI->setSource(info, orientation);
ovUI->setDisplayParams(fbnum, waitForVsync, isFg, zorder, isVGPipe);
hwc_rect_t displayFrame = layer->displayFrame;
ovUI->setPosition(displayFrame.left, displayFrame.top,
(displayFrame.right - displayFrame.left),
(displayFrame.bottom - displayFrame.top));
if(ovUI->commit() != overlay::NO_ERROR) {
LOGE("%s: Bypass Overlay Commit failed", __FUNCTION__);
return -1;
}
}
return 0;
}
static int drawLayerUsingBypass(hwc_context_t *ctx, hwc_layer_t *layer,
int index) {
if (ctx && ctx->mOvUI[index]) {
overlay::OverlayUI *ovUI = ctx->mOvUI[index];
int ret = 0;
private_handle_t *hnd = (private_handle_t *)layer->handle;
ctx->bypassBufferLockState[index] = BYPASS_BUFFER_UNLOCKED;
if (GENLOCK_FAILURE == genlock_lock_buffer(hnd, GENLOCK_READ_LOCK,
GENLOCK_MAX_TIMEOUT)) {
LOGE("%s: genlock_lock_buffer(READ) failed", __FUNCTION__);
return -1;
}
ctx->bypassBufferLockState[index] = BYPASS_BUFFER_LOCKED;
ret = ovUI->queueBuffer(hnd);
if (ret) {
LOGE("drawLayerUsingBypass queueBuffer failed");
// Unlock the locked buffer
if (GENLOCK_FAILURE == genlock_unlock_buffer(hnd)) {
LOGE("%s: genlock_unlock_buffer failed", __FUNCTION__);
}
ctx->bypassBufferLockState[index] = BYPASS_BUFFER_UNLOCKED;
return -1;
}
}
return 0;
}
/* Checks if 2 layers intersect */
static bool isIntersect(const hwc_rect_t& one, const hwc_rect_t& two) {
hwc_rect_t result;
result.left = max(one.left, two.left);
result.top = max(one.top, two.top);
result.right = min(one.right, two.right);
result.bottom = min(one.bottom, two.bottom);
const int width = result.right - result.left;
const int height = result.bottom - result.top;
const bool isEmpty = width <= 0 || height <= 0;
return !isEmpty;
}
/* Check if layers are disjoint */
static bool isDisjoint(const hwc_layer_list_t* list) {
//Validate supported layer range
if(list->numHwLayers <= 0 || list->numHwLayers > MAX_BYPASS_LAYERS) {
return false;
}
for(int i = 0; i < (list->numHwLayers) - 1; i++) {
for(int j = i + 1; j < list->numHwLayers; j++) {
if(isIntersect(list->hwLayers[i].displayFrame,
list->hwLayers[j].displayFrame)) {
return false;
}
}
}
return true;
}
static bool usesContiguousMemory(const hwc_layer_list_t* list) {
for(int i = 0; i < list->numHwLayers; i++) {
const private_handle_t *hnd =
reinterpret_cast<const private_handle_t *>(list->hwLayers[i].handle);
if(hnd != NULL && (hnd->flags &
private_handle_t::PRIV_FLAGS_NONCONTIGUOUS_MEM
)) {
// Bypass cannot work for non contiguous buffers
return false;
}
}
return true;
}
/*
* Checks if doing comp. bypass is possible.
* It is possible if
* 1. If video is not on
* 2. There are 2 layers
* 3. The memory type is contiguous
*/
inline static bool isBypassDoable(hwc_composer_device_t *dev, const int yuvCount,
const hwc_layer_list_t* list) {
hwc_context_t* ctx = (hwc_context_t*)(dev);
private_hwc_module_t* hwcModule = reinterpret_cast<private_hwc_module_t*>(
dev->common.module);
//Check if enabled in build.prop
if(hwcModule->isBypassEnabled == false) {
return false;
}
// Check if memory type is contiguous
if(!usesContiguousMemory(list))
return false;
//Disable bypass during animation
if(UNLIKELY(ctx->animCount)) {
--(ctx->animCount);
return false;
}
#if defined HDMI_DUAL_DISPLAY
//Disable bypass when HDMI is enabled
if(ctx->mHDMIEnabled || ctx->pendingHDMI) {
return false;
}
#endif
return (yuvCount == 0) && (ctx->hwcOverlayStatus == HWC_OVERLAY_CLOSED) && isDisjoint(list);
}
/*
* Bypass is not efficient if area is greater than 1280x720
* AND rotation is necessary, since the rotator consumes
* time greater than 1 Vsync and is sequential.
*/
inline static bool isBypassEfficient(const framebuffer_device_t* fbDev,
const hwc_layer_list_t* list, hwc_context_t* ctx) {
bool rotationNeeded = false;
for(int i = 0; i < list->numHwLayers; ++i) {
if(list->hwLayers[i].transform) {
rotationNeeded = true;
break;
}
}
return !(rotationNeeded);
}
bool setupBypass(hwc_context_t* ctx, hwc_layer_list_t* list) {
for (int index = 0 ; index < list->numHwLayers; index++) {
if(prepareBypass(ctx, &(list->hwLayers[index]), index,
list->numHwLayers - 1) != 0) {
return false;
}
}
return true;
}
void setBypassLayerFlags(hwc_context_t* ctx, hwc_layer_list_t* list) {
for (int index = 0 ; index < list->numHwLayers; index++) {
list->hwLayers[index].flags |= HWC_COMP_BYPASS;
list->hwLayers[index].compositionType = HWC_USE_OVERLAY;
#ifdef DEBUG
LOGE("%s: layer = %d", __FUNCTION__, index);
#endif
}
}
void unsetBypassLayerFlags(hwc_layer_list_t* list) {
for (int index = 0 ; index < list->numHwLayers; index++) {
if(list->hwLayers[index].flags & HWC_COMP_BYPASS) {
list->hwLayers[index].flags = 0;
}
}
}
void unsetBypassBufferLockState(hwc_context_t* ctx) {
for (int i=0; i< MAX_BYPASS_LAYERS; i++) {
ctx->bypassBufferLockState[i] = BYPASS_BUFFER_UNLOCKED;
}
}
void storeLockedBypassHandle(hwc_layer_list_t* list, hwc_context_t* ctx) {
for (int index = 0 ; index < list->numHwLayers; index++) {
// Store the current bypass handle.
if (list->hwLayers[index].flags & HWC_COMP_BYPASS) {
private_handle_t *hnd = (private_handle_t*)list->hwLayers[index].handle;
if (ctx->bypassBufferLockState[index] == BYPASS_BUFFER_LOCKED) {
ctx->previousBypassHandle[index] = (native_handle_t*)list->hwLayers[index].handle;
hnd->flags |= private_handle_t::PRIV_FLAGS_HWC_LOCK;
} else
ctx->previousBypassHandle[index] = NULL;
}
}
}
#endif //COMPOSITION_BYPASS
static void handleHDMIStateChange(hwc_composer_device_t *dev, int externaltype) {
#if defined HDMI_DUAL_DISPLAY
hwc_context_t* ctx = (hwc_context_t*)(dev);
@ -687,7 +791,6 @@ static void handleHDMIStateChange(hwc_composer_device_t *dev, int externaltype)
#endif
}
/*
* function to set the status of external display in hwc
* Just mark flags and do stuff after eglSwapBuffers
@ -860,6 +963,7 @@ static int hwc_prepare(hwc_composer_device_t *dev, hwc_layer_list_t* list) {
int s3dVideoFormat = 0;
int numLayersNotUpdating = 0;
bool useCopybit = false;
bool isSkipLayerPresent = false;
if (list) {
useCopybit = canUseCopybit(hwcModule->fbDevice, list);
@ -889,11 +993,14 @@ static int hwc_prepare(hwc_composer_device_t *dev, hwc_layer_list_t* list) {
for (size_t i=0 ; i<list->numHwLayers ; i++) {
private_handle_t *hnd = (private_handle_t *)list->hwLayers[i].handle;
// If there is a single Fullscreen layer, we can bypass it - TBD
// If there is only one video/camera buffer, we can bypass itn
if (list->hwLayers[i].flags & HWC_SKIP_LAYER) {
// During the animaton UI layers are marked as SKIP
// need to still mark the layer for S3D composition
isSkipLayerPresent = true;
if (isS3DCompositionNeeded)
markUILayerForS3DComposition(list->hwLayers[i], s3dVideoFormat);
@ -978,27 +1085,32 @@ static int hwc_prepare(hwc_composer_device_t *dev, hwc_layer_list_t* list) {
}
#ifdef COMPOSITION_BYPASS
bool isBypassUsed = true;
//Check if bypass is feasible
if(isBypassDoable(dev, yuvBufferCount, list) &&
isBypassEfficient(hwcModule->fbDevice, list, ctx)) {
//Setup bypass
if(isBypassDoable(dev, yuvBufferCount, list) && !isSkipLayerPresent) {
if(setupBypass(ctx, list)) {
//Overwrite layer flags only if setup succeeds.
setBypassLayerFlags(ctx, list);
list->flags |= HWC_SKIP_COMPOSITION;
ctx->bypassState = BYPASS_ON;
} else {
LOGE_IF(BYPASS_DEBUG,"%s: Bypass setup Failed",__FUNCTION__);
isBypassUsed = false;
}
} else {
unlockPreviousBypassBuffers(ctx);
LOGE_IF(BYPASS_DEBUG,"%s: Bypass not possible[%d,%d]",__FUNCTION__,
isBypassDoable(dev, yuvBufferCount, list) && !isSkipLayerPresent );
isBypassUsed = false;
}
//Reset bypass states
if(!isBypassUsed) {
ctx->nPipesUsed = 0;
unsetBypassLayerFlags(list);
unsetBypassBufferLockState(ctx);
if(ctx->bypassState == BYPASS_ON) {
ctx->bypassState = BYPASS_OFF_PENDING;
}
}
#endif
}
return 0;
}
// ---------------------------------------------------------------------------
@ -1277,6 +1389,53 @@ static int drawLayerUsingOverlay(hwc_context_t *ctx, hwc_layer_t *layer)
return -1;
}
#ifdef COMPOSITION_BYPASS
static int drawLayerUsingBypass(hwc_context_t *ctx, hwc_layer_t *layer, int layer_index) {
int index = getLayerbypassIndex(layer);
if(index < 0) {
LOGE("%s: Invalid bypass index (%d)", __FUNCTION__, index);
return -1;
}
if (ctx && ctx->mOvUI[index]) {
overlay::OverlayUI *ovUI = ctx->mOvUI[index];
int ret = 0;
private_handle_t *hnd = (private_handle_t *)layer->handle;
if(!hnd) {
LOGE("%s handle null", __FUNCTION__);
return -1;
}
ctx->bypassBufferLockState[index] = BYPASS_BUFFER_UNLOCKED;
if (GENLOCK_FAILURE == genlock_lock_buffer(hnd, GENLOCK_READ_LOCK,
GENLOCK_MAX_TIMEOUT)) {
LOGE("%s: genlock_lock_buffer(READ) failed", __FUNCTION__);
return -1;
}
ctx->bypassBufferLockState[index] = BYPASS_BUFFER_LOCKED;
LOGE_IF(BYPASS_DEBUG,"%s: Bypassing layer: %p using pipe: %d",__FUNCTION__, layer, index );
ret = ovUI->queueBuffer(hnd);
if (ret) {
// Unlock the locked buffer
if (GENLOCK_FAILURE == genlock_unlock_buffer(hnd)) {
LOGE("%s: genlock_unlock_buffer failed", __FUNCTION__);
}
ctx->bypassBufferLockState[index] = BYPASS_BUFFER_UNLOCKED;
return -1;
}
}
return 0;
}
#endif
static int hwc_set(hwc_composer_device_t *dev,
hwc_display_t dpy,
hwc_surface_t sur,
@ -1323,6 +1482,11 @@ static int hwc_set(hwc_composer_device_t *dev,
storeLockedBypassHandle(list, ctx);
// We have stored the handles, unset the current lock states in the context.
unsetBypassBufferLockState(ctx);
closeExtraPipes(ctx);
#if BYPASS_DEBUG
if(list->flags & HWC_SKIP_COMPOSITION)
LOGE("%s: skipping eglSwapBuffer call", __FUNCTION__);
#endif
#endif
// Do not call eglSwapBuffers if we the skip composition flag is set on the list.
if (!(list->flags & HWC_SKIP_COMPOSITION)) {
@ -1330,16 +1494,10 @@ static int hwc_set(hwc_composer_device_t *dev,
if (!sucess) {
ret = HWC_EGL_ERROR;
}
}
else {
} else {
CALC_FPS();
}
#ifdef COMPOSITION_BYPASS
if(ctx->bypassState == BYPASS_OFF_PENDING) {
closeBypass(ctx);
ctx->bypassState = BYPASS_OFF;
}
#endif
#if defined HDMI_DUAL_DISPLAY
if(ctx->pendingHDMI) {
handleHDMIStateChange(dev, ctx->mHDMIEnabled);
@ -1449,6 +1607,7 @@ static int hwc_device_open(const struct hw_module_t* module, const char* name,
struct hw_device_t** device)
{
int status = -EINVAL;
if (!strcmp(name, HWC_HARDWARE_COMPOSER)) {
private_hwc_module_t* hwcModule = reinterpret_cast<private_hwc_module_t*>
(const_cast<hw_module_t*>(module));
@ -1471,7 +1630,6 @@ static int hwc_device_open(const struct hw_module_t* module, const char* name,
dev->previousBypassHandle[i] = NULL;
}
unsetBypassBufferLockState(dev);
dev->animCount = 0;
dev->bypassState = BYPASS_OFF;
#endif

View File

@ -41,6 +41,8 @@ using namespace android;
using android::sp;
using android::GraphicBuffer;
#define HWC_BYPASS_INDEX_MASK 0x00000030
/*
* Qcom specific Native Window perform operations
*/
@ -81,6 +83,8 @@ enum {
HWC_USE_ORIGINAL_RESOLUTION = 0x10000000,
HWC_DO_NOT_USE_OVERLAY = 0x20000000,
HWC_COMP_BYPASS = 0x40000000,
HWC_BYPASS_RESERVE_0 = 0x00000010,
HWC_BYPASS_RESERVE_1 = 0x00000020,
};
enum HWCCompositionType {