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:
parent
ab890c7518
commit
84961f81a5
@ -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
|
||||
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user