Earlier, on configuration errors, we simply drained the fences held by current rotator objects and went to GPU comp. The rotator objects were deleted in the subsequent rounds, if they remained unused. If we don't use GPU comp on failure but use other strategies, then we need to mark the rotator objects for a display for reuse, so that other strategies can use them. Also any failure resets on overlay or rotator need to happen from functions that call configuration helpers rather than the helper themselves. Leaving cleanup to black-box helpers is not desirable. Change-Id: I59b5b1bb774b82ee85a037e934c9d2d023db30af
1985 lines
68 KiB
C++
1985 lines
68 KiB
C++
/*
|
|
* Copyright (C) 2010 The Android Open Source Project
|
|
* Copyright (C) 2012-2013, The Linux Foundation 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.
|
|
*/
|
|
#define ATRACE_TAG (ATRACE_TAG_GRAPHICS | ATRACE_TAG_HAL)
|
|
#define HWC_UTILS_DEBUG 0
|
|
#include <math.h>
|
|
#include <sys/ioctl.h>
|
|
#include <linux/fb.h>
|
|
#include <binder/IServiceManager.h>
|
|
#include <EGL/egl.h>
|
|
#include <cutils/properties.h>
|
|
#include <utils/Trace.h>
|
|
#include <gralloc_priv.h>
|
|
#include <overlay.h>
|
|
#include <overlayRotator.h>
|
|
#include <overlayWriteback.h>
|
|
#include "hwc_utils.h"
|
|
#include "hwc_mdpcomp.h"
|
|
#include "hwc_fbupdate.h"
|
|
#include "hwc_ad.h"
|
|
#include "mdp_version.h"
|
|
#include "hwc_copybit.h"
|
|
#include "hwc_dump_layers.h"
|
|
#include "hwc_vpuclient.h"
|
|
#include "external.h"
|
|
#include "virtual.h"
|
|
#include "hwc_qclient.h"
|
|
#include "QService.h"
|
|
#include "comptype.h"
|
|
|
|
using namespace qClient;
|
|
using namespace qService;
|
|
using namespace android;
|
|
using namespace overlay;
|
|
using namespace overlay::utils;
|
|
namespace ovutils = overlay::utils;
|
|
|
|
namespace qhwc {
|
|
|
|
static int openFramebufferDevice(hwc_context_t *ctx)
|
|
{
|
|
struct fb_fix_screeninfo finfo;
|
|
struct fb_var_screeninfo info;
|
|
|
|
int fb_fd = openFb(HWC_DISPLAY_PRIMARY);
|
|
if(fb_fd < 0) {
|
|
ALOGE("%s: Error Opening FB : %s", __FUNCTION__, strerror(errno));
|
|
return -errno;
|
|
}
|
|
|
|
if (ioctl(fb_fd, FBIOGET_VSCREENINFO, &info) == -1) {
|
|
ALOGE("%s:Error in ioctl FBIOGET_VSCREENINFO: %s", __FUNCTION__,
|
|
strerror(errno));
|
|
close(fb_fd);
|
|
return -errno;
|
|
}
|
|
|
|
if (int(info.width) <= 0 || int(info.height) <= 0) {
|
|
// the driver doesn't return that information
|
|
// default to 160 dpi
|
|
info.width = ((info.xres * 25.4f)/160.0f + 0.5f);
|
|
info.height = ((info.yres * 25.4f)/160.0f + 0.5f);
|
|
}
|
|
|
|
float xdpi = (info.xres * 25.4f) / info.width;
|
|
float ydpi = (info.yres * 25.4f) / info.height;
|
|
|
|
#ifdef MSMFB_METADATA_GET
|
|
struct msmfb_metadata metadata;
|
|
memset(&metadata, 0 , sizeof(metadata));
|
|
metadata.op = metadata_op_frame_rate;
|
|
|
|
if (ioctl(fb_fd, MSMFB_METADATA_GET, &metadata) == -1) {
|
|
ALOGE("%s:Error retrieving panel frame rate: %s", __FUNCTION__,
|
|
strerror(errno));
|
|
close(fb_fd);
|
|
return -errno;
|
|
}
|
|
|
|
float fps = metadata.data.panel_frame_rate;
|
|
#else
|
|
//XXX: Remove reserved field usage on all baselines
|
|
//The reserved[3] field is used to store FPS by the driver.
|
|
float fps = info.reserved[3] & 0xFF;
|
|
#endif
|
|
|
|
if (ioctl(fb_fd, FBIOGET_FSCREENINFO, &finfo) == -1) {
|
|
ALOGE("%s:Error in ioctl FBIOGET_FSCREENINFO: %s", __FUNCTION__,
|
|
strerror(errno));
|
|
close(fb_fd);
|
|
return -errno;
|
|
}
|
|
|
|
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].fd = fb_fd;
|
|
//xres, yres may not be 32 aligned
|
|
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].stride = finfo.line_length /(info.xres/8);
|
|
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].xres = info.xres;
|
|
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].yres = info.yres;
|
|
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].xdpi = xdpi;
|
|
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].ydpi = ydpi;
|
|
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].vsync_period = 1000000000l / fps;
|
|
|
|
//Unblank primary on first boot
|
|
if(ioctl(fb_fd, FBIOBLANK,FB_BLANK_UNBLANK) < 0) {
|
|
ALOGE("%s: Failed to unblank display", __FUNCTION__);
|
|
return -errno;
|
|
}
|
|
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].isActive = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void initContext(hwc_context_t *ctx)
|
|
{
|
|
openFramebufferDevice(ctx);
|
|
ctx->mMDP.version = qdutils::MDPVersion::getInstance().getMDPVersion();
|
|
ctx->mMDP.hasOverlay = qdutils::MDPVersion::getInstance().hasOverlay();
|
|
ctx->mMDP.panel = qdutils::MDPVersion::getInstance().getPanelType();
|
|
overlay::Overlay::initOverlay();
|
|
ctx->mOverlay = overlay::Overlay::getInstance();
|
|
ctx->mRotMgr = RotMgr::getInstance();
|
|
|
|
//Is created and destroyed only once for primary
|
|
//For external it could get created and destroyed multiple times depending
|
|
//on what external we connect to.
|
|
ctx->mFBUpdate[HWC_DISPLAY_PRIMARY] =
|
|
IFBUpdate::getObject(ctx, HWC_DISPLAY_PRIMARY);
|
|
|
|
// Check if the target supports copybit compostion (dyn/mdp/c2d) to
|
|
// decide if we need to open the copybit module.
|
|
int compositionType =
|
|
qdutils::QCCompositionType::getInstance().getCompositionType();
|
|
|
|
if (compositionType & (qdutils::COMPOSITION_TYPE_DYN |
|
|
qdutils::COMPOSITION_TYPE_MDP |
|
|
qdutils::COMPOSITION_TYPE_C2D)) {
|
|
ctx->mCopyBit[HWC_DISPLAY_PRIMARY] = new CopyBit(ctx,
|
|
HWC_DISPLAY_PRIMARY);
|
|
}
|
|
|
|
ctx->mExtDisplay = new ExternalDisplay(ctx);
|
|
ctx->mVirtualDisplay = new VirtualDisplay(ctx);
|
|
ctx->mVirtualonExtActive = false;
|
|
ctx->dpyAttr[HWC_DISPLAY_EXTERNAL].isActive = false;
|
|
ctx->dpyAttr[HWC_DISPLAY_EXTERNAL].connected = false;
|
|
ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].isActive = false;
|
|
ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].connected = false;
|
|
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].mDownScaleMode= false;
|
|
ctx->dpyAttr[HWC_DISPLAY_EXTERNAL].mDownScaleMode = false;
|
|
ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].mDownScaleMode = false;
|
|
|
|
ctx->mMDPComp[HWC_DISPLAY_PRIMARY] =
|
|
MDPComp::getObject(ctx, HWC_DISPLAY_PRIMARY);
|
|
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].connected = true;
|
|
|
|
for (uint32_t i = 0; i < HWC_NUM_DISPLAY_TYPES; i++) {
|
|
ctx->mHwcDebug[i] = new HwcDebug(i);
|
|
ctx->mLayerRotMap[i] = new LayerRotMap();
|
|
}
|
|
|
|
MDPComp::init(ctx);
|
|
ctx->mAD = new AssertiveDisplay(ctx);
|
|
|
|
ctx->vstate.enable = false;
|
|
ctx->vstate.fakevsync = false;
|
|
ctx->mExtOrientation = 0;
|
|
|
|
//Right now hwc starts the service but anybody could do it, or it could be
|
|
//independent process as well.
|
|
QService::init();
|
|
sp<IQClient> client = new QClient(ctx);
|
|
interface_cast<IQService>(
|
|
defaultServiceManager()->getService(
|
|
String16("display.qservice")))->connect(client);
|
|
|
|
// Initialize "No animation on external display" related parameters.
|
|
ctx->deviceOrientation = 0;
|
|
ctx->mPrevCropVideo.left = ctx->mPrevCropVideo.top =
|
|
ctx->mPrevCropVideo.right = ctx->mPrevCropVideo.bottom = 0;
|
|
ctx->mPrevDestVideo.left = ctx->mPrevDestVideo.top =
|
|
ctx->mPrevDestVideo.right = ctx->mPrevDestVideo.bottom = 0;
|
|
ctx->mPrevTransformVideo = 0;
|
|
ctx->mBufferMirrorMode = false;
|
|
#ifdef VPU_TARGET
|
|
ctx->mVPUClient = new VPUClient();
|
|
#endif
|
|
|
|
ALOGI("Initializing Qualcomm Hardware Composer");
|
|
ALOGI("MDP version: %d", ctx->mMDP.version);
|
|
}
|
|
|
|
void closeContext(hwc_context_t *ctx)
|
|
{
|
|
if(ctx->mOverlay) {
|
|
delete ctx->mOverlay;
|
|
ctx->mOverlay = NULL;
|
|
}
|
|
|
|
if(ctx->mRotMgr) {
|
|
delete ctx->mRotMgr;
|
|
ctx->mRotMgr = NULL;
|
|
}
|
|
|
|
for(int i = 0; i < HWC_NUM_DISPLAY_TYPES; i++) {
|
|
if(ctx->mCopyBit[i]) {
|
|
delete ctx->mCopyBit[i];
|
|
ctx->mCopyBit[i] = NULL;
|
|
}
|
|
}
|
|
|
|
if(ctx->dpyAttr[HWC_DISPLAY_PRIMARY].fd) {
|
|
close(ctx->dpyAttr[HWC_DISPLAY_PRIMARY].fd);
|
|
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].fd = -1;
|
|
}
|
|
|
|
if(ctx->mExtDisplay) {
|
|
delete ctx->mExtDisplay;
|
|
ctx->mExtDisplay = NULL;
|
|
}
|
|
|
|
#ifdef VPU_TARGET
|
|
if(ctx->mVPUClient) {
|
|
delete ctx->mVPUClient;
|
|
}
|
|
#endif
|
|
|
|
for(int i = 0; i < HWC_NUM_DISPLAY_TYPES; i++) {
|
|
if(ctx->mFBUpdate[i]) {
|
|
delete ctx->mFBUpdate[i];
|
|
ctx->mFBUpdate[i] = NULL;
|
|
}
|
|
if(ctx->mMDPComp[i]) {
|
|
delete ctx->mMDPComp[i];
|
|
ctx->mMDPComp[i] = NULL;
|
|
}
|
|
if(ctx->mHwcDebug[i]) {
|
|
delete ctx->mHwcDebug[i];
|
|
ctx->mHwcDebug[i] = NULL;
|
|
}
|
|
if(ctx->mLayerRotMap[i]) {
|
|
delete ctx->mLayerRotMap[i];
|
|
ctx->mLayerRotMap[i] = NULL;
|
|
}
|
|
}
|
|
if(ctx->mAD) {
|
|
delete ctx->mAD;
|
|
ctx->mAD = NULL;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
void dumpsys_log(android::String8& buf, const char* fmt, ...)
|
|
{
|
|
va_list varargs;
|
|
va_start(varargs, fmt);
|
|
buf.appendFormatV(fmt, varargs);
|
|
va_end(varargs);
|
|
}
|
|
|
|
int getExtOrientation(hwc_context_t* ctx) {
|
|
int extOrient = ctx->mExtOrientation;
|
|
if(ctx->mBufferMirrorMode)
|
|
extOrient = getMirrorModeOrientation(ctx);
|
|
return extOrient;
|
|
}
|
|
|
|
/* Calculates the destination position based on the action safe rectangle */
|
|
void getActionSafePosition(hwc_context_t *ctx, int dpy, hwc_rect_t& rect) {
|
|
// Position
|
|
int x = rect.left, y = rect.top;
|
|
int w = rect.right - rect.left;
|
|
int h = rect.bottom - rect.top;
|
|
|
|
// if external supports underscan, do nothing
|
|
// it will be taken care in the driver
|
|
if(ctx->mExtDisplay->isCEUnderscanSupported())
|
|
return;
|
|
|
|
char value[PROPERTY_VALUE_MAX];
|
|
// Read action safe properties
|
|
property_get("persist.sys.actionsafe.width", value, "0");
|
|
int asWidthRatio = atoi(value);
|
|
property_get("persist.sys.actionsafe.height", value, "0");
|
|
int asHeightRatio = atoi(value);
|
|
|
|
if(!asWidthRatio && !asHeightRatio) {
|
|
//No action safe ratio set, return
|
|
return;
|
|
}
|
|
|
|
float wRatio = 1.0;
|
|
float hRatio = 1.0;
|
|
float xRatio = 1.0;
|
|
float yRatio = 1.0;
|
|
|
|
int fbWidth = ctx->dpyAttr[dpy].xres;
|
|
int fbHeight = ctx->dpyAttr[dpy].yres;
|
|
if(ctx->dpyAttr[dpy].mDownScaleMode) {
|
|
// if downscale Mode is enabled for external, need to query
|
|
// the actual width and height, as that is the physical w & h
|
|
ctx->mExtDisplay->getAttributes(fbWidth, fbHeight);
|
|
}
|
|
|
|
|
|
// Since external is rotated 90, need to swap width/height
|
|
int extOrient = getExtOrientation(ctx);
|
|
|
|
if(extOrient & HWC_TRANSFORM_ROT_90)
|
|
swap(fbWidth, fbHeight);
|
|
|
|
float asX = 0;
|
|
float asY = 0;
|
|
float asW = fbWidth;
|
|
float asH = fbHeight;
|
|
|
|
// based on the action safe ratio, get the Action safe rectangle
|
|
asW = fbWidth * (1.0f - asWidthRatio / 100.0f);
|
|
asH = fbHeight * (1.0f - asHeightRatio / 100.0f);
|
|
asX = (fbWidth - asW) / 2;
|
|
asY = (fbHeight - asH) / 2;
|
|
|
|
// calculate the position ratio
|
|
xRatio = (float)x/fbWidth;
|
|
yRatio = (float)y/fbHeight;
|
|
wRatio = (float)w/fbWidth;
|
|
hRatio = (float)h/fbHeight;
|
|
|
|
//Calculate the position...
|
|
x = (xRatio * asW) + asX;
|
|
y = (yRatio * asH) + asY;
|
|
w = (wRatio * asW);
|
|
h = (hRatio * asH);
|
|
|
|
// Convert it back to hwc_rect_t
|
|
rect.left = x;
|
|
rect.top = y;
|
|
rect.right = w + rect.left;
|
|
rect.bottom = h + rect.top;
|
|
|
|
return;
|
|
}
|
|
|
|
/* Calculates the aspect ratio for based on src & dest */
|
|
void getAspectRatioPosition(int destWidth, int destHeight, int srcWidth,
|
|
int srcHeight, hwc_rect_t& rect) {
|
|
int x =0, y =0;
|
|
|
|
if (srcWidth * destHeight > destWidth * srcHeight) {
|
|
srcHeight = destWidth * srcHeight / srcWidth;
|
|
srcWidth = destWidth;
|
|
} else if (srcWidth * destHeight < destWidth * srcHeight) {
|
|
srcWidth = destHeight * srcWidth / srcHeight;
|
|
srcHeight = destHeight;
|
|
} else {
|
|
srcWidth = destWidth;
|
|
srcHeight = destHeight;
|
|
}
|
|
if (srcWidth > destWidth) srcWidth = destWidth;
|
|
if (srcHeight > destHeight) srcHeight = destHeight;
|
|
x = (destWidth - srcWidth) / 2;
|
|
y = (destHeight - srcHeight) / 2;
|
|
ALOGD_IF(HWC_UTILS_DEBUG, "%s: AS Position: x = %d, y = %d w = %d h = %d",
|
|
__FUNCTION__, x, y, srcWidth , srcHeight);
|
|
// Convert it back to hwc_rect_t
|
|
rect.left = x;
|
|
rect.top = y;
|
|
rect.right = srcWidth + rect.left;
|
|
rect.bottom = srcHeight + rect.top;
|
|
}
|
|
|
|
// This function gets the destination position for Seconday display
|
|
// based on the position and aspect ratio with orientation
|
|
void getAspectRatioPosition(hwc_context_t* ctx, int dpy, int extOrientation,
|
|
hwc_rect_t& inRect, hwc_rect_t& outRect) {
|
|
hwc_rect_t viewFrame = ctx->mViewFrame[dpy];
|
|
// Physical display resolution
|
|
float fbWidth = ctx->dpyAttr[dpy].xres;
|
|
float fbHeight = ctx->dpyAttr[dpy].yres;
|
|
//display position(x,y,w,h) in correct aspectratio after rotation
|
|
int xPos = 0;
|
|
int yPos = 0;
|
|
float width = fbWidth;
|
|
float height = fbHeight;
|
|
// Width/Height used for calculation, after rotation
|
|
float actualWidth = fbWidth;
|
|
float actualHeight = fbHeight;
|
|
|
|
float wRatio = 1.0;
|
|
float hRatio = 1.0;
|
|
float xRatio = 1.0;
|
|
float yRatio = 1.0;
|
|
hwc_rect_t rect = {0, 0, (int)fbWidth, (int)fbHeight};
|
|
|
|
Dim inPos(inRect.left, inRect.top, inRect.right - inRect.left,
|
|
inRect.bottom - inRect.top);
|
|
Dim outPos(outRect.left, outRect.top, outRect.right - outRect.left,
|
|
outRect.bottom - outRect.top);
|
|
|
|
Whf whf(fbWidth, fbHeight, 0);
|
|
eTransform extorient = static_cast<eTransform>(extOrientation);
|
|
// To calculate the destination co-ordinates in the new orientation
|
|
preRotateSource(extorient, whf, inPos);
|
|
|
|
if(extOrientation & HAL_TRANSFORM_ROT_90) {
|
|
// Swap width/height for input position
|
|
swapWidthHeight(actualWidth, actualHeight);
|
|
getAspectRatioPosition(fbWidth, fbHeight, (int)actualWidth,
|
|
(int)actualHeight, rect);
|
|
xPos = rect.left;
|
|
yPos = rect.top;
|
|
width = rect.right - rect.left;
|
|
height = rect.bottom - rect.top;
|
|
// swap viewframe coordinates for 90 degree rotation.
|
|
swap(viewFrame.left, viewFrame.top);
|
|
swap(viewFrame.right, viewFrame.bottom);
|
|
}
|
|
// if viewframe left and top coordinates are non zero value then exclude it
|
|
// during the computation of xRatio and yRatio
|
|
xRatio = (inPos.x - viewFrame.left)/actualWidth;
|
|
yRatio = (inPos.y - viewFrame.top)/actualHeight;
|
|
// Use viewframe width and height to compute wRatio and hRatio.
|
|
wRatio = inPos.w/(viewFrame.right - viewFrame.left);
|
|
hRatio = inPos.h/(viewFrame.bottom - viewFrame.top);
|
|
|
|
|
|
//Calculate the position...
|
|
outPos.x = (xRatio * width) + xPos;
|
|
outPos.y = (yRatio * height) + yPos;
|
|
outPos.w = wRatio * width;
|
|
outPos.h = hRatio * height;
|
|
ALOGD_IF(HWC_UTILS_DEBUG, "%s: Calculated AspectRatio Position: x = %d,"
|
|
"y = %d w = %d h = %d", __FUNCTION__, outPos.x, outPos.y,
|
|
outPos.w, outPos.h);
|
|
|
|
// For sidesync, the dest fb will be in portrait orientation, and the crop
|
|
// will be updated to avoid the black side bands, and it will be upscaled
|
|
// to fit the dest RB, so recalculate
|
|
// the position based on the new width and height
|
|
if ((extOrientation & HWC_TRANSFORM_ROT_90) &&
|
|
isOrientationPortrait(ctx)) {
|
|
hwc_rect_t r;
|
|
//Calculate the position
|
|
xRatio = (outPos.x - xPos)/width;
|
|
// GetaspectRatio -- tricky to get the correct aspect ratio
|
|
// But we need to do this.
|
|
getAspectRatioPosition(width, height, width, height, r);
|
|
xPos = r.left;
|
|
yPos = r.top;
|
|
float tempWidth = r.right - r.left;
|
|
float tempHeight = r.bottom - r.top;
|
|
yRatio = yPos/height;
|
|
wRatio = outPos.w/width;
|
|
hRatio = tempHeight/height;
|
|
|
|
//Map the coordinates back to Framebuffer domain
|
|
outPos.x = (xRatio * fbWidth);
|
|
outPos.y = (yRatio * fbHeight);
|
|
outPos.w = wRatio * fbWidth;
|
|
outPos.h = hRatio * fbHeight;
|
|
|
|
ALOGD_IF(HWC_UTILS_DEBUG, "%s: Calculated AspectRatio for device in"
|
|
"portrait: x = %d,y = %d w = %d h = %d", __FUNCTION__,
|
|
outPos.x, outPos.y,
|
|
outPos.w, outPos.h);
|
|
}
|
|
if(ctx->dpyAttr[dpy].mDownScaleMode) {
|
|
int extW, extH;
|
|
if(dpy == HWC_DISPLAY_EXTERNAL)
|
|
ctx->mExtDisplay->getAttributes(extW, extH);
|
|
else
|
|
ctx->mVirtualDisplay->getAttributes(extW, extH);
|
|
fbWidth = ctx->dpyAttr[dpy].xres;
|
|
fbHeight = ctx->dpyAttr[dpy].yres;
|
|
//Calculate the position...
|
|
xRatio = outPos.x/fbWidth;
|
|
yRatio = outPos.y/fbHeight;
|
|
wRatio = outPos.w/fbWidth;
|
|
hRatio = outPos.h/fbHeight;
|
|
|
|
outPos.x = xRatio * extW;
|
|
outPos.y = yRatio * extH;
|
|
outPos.w = wRatio * extW;
|
|
outPos.h = hRatio * extH;
|
|
}
|
|
// Convert Dim to hwc_rect_t
|
|
outRect.left = outPos.x;
|
|
outRect.top = outPos.y;
|
|
outRect.right = outPos.x + outPos.w;
|
|
outRect.bottom = outPos.y + outPos.h;
|
|
|
|
return;
|
|
}
|
|
|
|
bool isPrimaryPortrait(hwc_context_t *ctx) {
|
|
int fbWidth = ctx->dpyAttr[HWC_DISPLAY_PRIMARY].xres;
|
|
int fbHeight = ctx->dpyAttr[HWC_DISPLAY_PRIMARY].yres;
|
|
if(fbWidth < fbHeight) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool isOrientationPortrait(hwc_context_t *ctx) {
|
|
if(isPrimaryPortrait(ctx)) {
|
|
return !(ctx->deviceOrientation & 0x1);
|
|
}
|
|
return (ctx->deviceOrientation & 0x1);
|
|
}
|
|
|
|
void calcExtDisplayPosition(hwc_context_t *ctx,
|
|
private_handle_t *hnd,
|
|
int dpy,
|
|
hwc_rect_t& sourceCrop,
|
|
hwc_rect_t& displayFrame,
|
|
int& transform,
|
|
ovutils::eTransform& orient) {
|
|
// Swap width and height when there is a 90deg transform
|
|
int extOrient = getExtOrientation(ctx);
|
|
if(dpy && !qdutils::MDPVersion::getInstance().is8x26()) {
|
|
if(!isYuvBuffer(hnd)) {
|
|
if(extOrient & HWC_TRANSFORM_ROT_90) {
|
|
int dstWidth = ctx->dpyAttr[dpy].xres;
|
|
int dstHeight = ctx->dpyAttr[dpy].yres;;
|
|
int srcWidth = ctx->dpyAttr[HWC_DISPLAY_PRIMARY].xres;
|
|
int srcHeight = ctx->dpyAttr[HWC_DISPLAY_PRIMARY].yres;
|
|
if(!isPrimaryPortrait(ctx)) {
|
|
swap(srcWidth, srcHeight);
|
|
} // Get Aspect Ratio for external
|
|
getAspectRatioPosition(dstWidth, dstHeight, srcWidth,
|
|
srcHeight, displayFrame);
|
|
// Crop - this is needed, because for sidesync, the dest fb will
|
|
// be in portrait orientation, so update the crop to not show the
|
|
// black side bands.
|
|
if (isOrientationPortrait(ctx)) {
|
|
sourceCrop = displayFrame;
|
|
displayFrame.left = 0;
|
|
displayFrame.top = 0;
|
|
displayFrame.right = dstWidth;
|
|
displayFrame.bottom = dstHeight;
|
|
}
|
|
}
|
|
if(ctx->dpyAttr[dpy].mDownScaleMode) {
|
|
int extW, extH;
|
|
// if downscale is enabled, map the co-ordinates to new
|
|
// domain(downscaled)
|
|
float fbWidth = ctx->dpyAttr[dpy].xres;
|
|
float fbHeight = ctx->dpyAttr[dpy].yres;
|
|
// query MDP configured attributes
|
|
if(dpy == HWC_DISPLAY_EXTERNAL)
|
|
ctx->mExtDisplay->getAttributes(extW, extH);
|
|
else
|
|
ctx->mVirtualDisplay->getAttributes(extW, extH);
|
|
//Calculate the ratio...
|
|
float wRatio = ((float)extW)/fbWidth;
|
|
float hRatio = ((float)extH)/fbHeight;
|
|
|
|
//convert Dim to hwc_rect_t
|
|
displayFrame.left *= wRatio;
|
|
displayFrame.top *= hRatio;
|
|
displayFrame.right *= wRatio;
|
|
displayFrame.bottom *= hRatio;
|
|
}
|
|
}else {
|
|
if(extOrient || ctx->dpyAttr[dpy].mDownScaleMode) {
|
|
getAspectRatioPosition(ctx, dpy, extOrient,
|
|
displayFrame, displayFrame);
|
|
}
|
|
}
|
|
// If there is a external orientation set, use that
|
|
if(extOrient) {
|
|
transform = extOrient;
|
|
orient = static_cast<ovutils::eTransform >(extOrient);
|
|
}
|
|
// Calculate the actionsafe dimensions for External(dpy = 1 or 2)
|
|
getActionSafePosition(ctx, dpy, displayFrame);
|
|
}
|
|
}
|
|
|
|
/* Returns the orientation which needs to be set on External for
|
|
* SideSync/Buffer Mirrormode
|
|
*/
|
|
int getMirrorModeOrientation(hwc_context_t *ctx) {
|
|
int extOrientation = 0;
|
|
int deviceOrientation = ctx->deviceOrientation;
|
|
if(!isPrimaryPortrait(ctx))
|
|
deviceOrientation = (deviceOrientation + 1) % 4;
|
|
if (deviceOrientation == 0)
|
|
extOrientation = HWC_TRANSFORM_ROT_270;
|
|
else if (deviceOrientation == 1)//90
|
|
extOrientation = 0;
|
|
else if (deviceOrientation == 2)//180
|
|
extOrientation = HWC_TRANSFORM_ROT_90;
|
|
else if (deviceOrientation == 3)//270
|
|
extOrientation = HWC_TRANSFORM_FLIP_V | HWC_TRANSFORM_FLIP_H;
|
|
|
|
return extOrientation;
|
|
}
|
|
|
|
bool isDownscaleRequired(hwc_layer_1_t const* layer) {
|
|
hwc_rect_t displayFrame = layer->displayFrame;
|
|
hwc_rect_t sourceCrop = integerizeSourceCrop(layer->sourceCropf);
|
|
int dst_w, dst_h, src_w, src_h;
|
|
dst_w = displayFrame.right - displayFrame.left;
|
|
dst_h = displayFrame.bottom - displayFrame.top;
|
|
src_w = sourceCrop.right - sourceCrop.left;
|
|
src_h = sourceCrop.bottom - sourceCrop.top;
|
|
|
|
if(((src_w > dst_w) || (src_h > dst_h)))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
bool needsScaling(hwc_layer_1_t const* layer) {
|
|
int dst_w, dst_h, src_w, src_h;
|
|
|
|
hwc_rect_t displayFrame = layer->displayFrame;
|
|
hwc_rect_t sourceCrop = integerizeSourceCrop(layer->sourceCropf);
|
|
|
|
dst_w = displayFrame.right - displayFrame.left;
|
|
dst_h = displayFrame.bottom - displayFrame.top;
|
|
src_w = sourceCrop.right - sourceCrop.left;
|
|
src_h = sourceCrop.bottom - sourceCrop.top;
|
|
|
|
if(((src_w != dst_w) || (src_h != dst_h)))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
// Checks if layer needs scaling with split
|
|
bool needsScalingWithSplit(hwc_context_t* ctx, hwc_layer_1_t const* layer,
|
|
const int& dpy) {
|
|
|
|
int src_width_l, src_height_l;
|
|
int src_width_r, src_height_r;
|
|
int dst_width_l, dst_height_l;
|
|
int dst_width_r, dst_height_r;
|
|
int hw_w = ctx->dpyAttr[dpy].xres;
|
|
int hw_h = ctx->dpyAttr[dpy].yres;
|
|
hwc_rect_t cropL, dstL, cropR, dstR;
|
|
const int lSplit = getLeftSplit(ctx, dpy);
|
|
hwc_rect_t sourceCrop = integerizeSourceCrop(layer->sourceCropf);
|
|
hwc_rect_t displayFrame = layer->displayFrame;
|
|
private_handle_t *hnd = (private_handle_t *)layer->handle;
|
|
|
|
cropL = sourceCrop;
|
|
dstL = displayFrame;
|
|
hwc_rect_t scissorL = { 0, 0, lSplit, hw_h };
|
|
scissorL = getIntersection(ctx->mViewFrame[dpy], scissorL);
|
|
qhwc::calculate_crop_rects(cropL, dstL, scissorL, 0);
|
|
|
|
cropR = sourceCrop;
|
|
dstR = displayFrame;
|
|
hwc_rect_t scissorR = { lSplit, 0, hw_w, hw_h };
|
|
scissorR = getIntersection(ctx->mViewFrame[dpy], scissorR);
|
|
qhwc::calculate_crop_rects(cropR, dstR, scissorR, 0);
|
|
|
|
// Sanitize Crop to stitch
|
|
sanitizeSourceCrop(cropL, cropR, hnd);
|
|
|
|
// Calculate the left dst
|
|
dst_width_l = dstL.right - dstL.left;
|
|
dst_height_l = dstL.bottom - dstL.top;
|
|
src_width_l = cropL.right - cropL.left;
|
|
src_height_l = cropL.bottom - cropL.top;
|
|
|
|
// check if there is any scaling on the left
|
|
if(((src_width_l != dst_width_l) || (src_height_l != dst_height_l)))
|
|
return true;
|
|
|
|
// Calculate the right dst
|
|
dst_width_r = dstR.right - dstR.left;
|
|
dst_height_r = dstR.bottom - dstR.top;
|
|
src_width_r = cropR.right - cropR.left;
|
|
src_height_r = cropR.bottom - cropR.top;
|
|
|
|
// check if there is any scaling on the right
|
|
if(((src_width_r != dst_width_r) || (src_height_r != dst_height_r)))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool isAlphaScaled(hwc_layer_1_t const* layer) {
|
|
if(needsScaling(layer) && isAlphaPresent(layer)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool isAlphaPresent(hwc_layer_1_t const* layer) {
|
|
private_handle_t *hnd = (private_handle_t *)layer->handle;
|
|
if(hnd) {
|
|
int format = hnd->format;
|
|
switch(format) {
|
|
case HAL_PIXEL_FORMAT_RGBA_8888:
|
|
case HAL_PIXEL_FORMAT_BGRA_8888:
|
|
// In any more formats with Alpha go here..
|
|
return true;
|
|
default : return false;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void trimLayer(hwc_context_t *ctx, const int& dpy, const int& transform,
|
|
hwc_rect_t& crop, hwc_rect_t& dst) {
|
|
int hw_w = ctx->dpyAttr[dpy].xres;
|
|
int hw_h = ctx->dpyAttr[dpy].yres;
|
|
if(dst.left < 0 || dst.top < 0 ||
|
|
dst.right > hw_w || dst.bottom > hw_h) {
|
|
hwc_rect_t scissor = {0, 0, hw_w, hw_h };
|
|
scissor = getIntersection(ctx->mViewFrame[dpy], scissor);
|
|
qhwc::calculate_crop_rects(crop, dst, scissor, transform);
|
|
}
|
|
}
|
|
|
|
static void trimList(hwc_context_t *ctx, hwc_display_contents_1_t *list,
|
|
const int& dpy) {
|
|
for(uint32_t i = 0; i < list->numHwLayers - 1; i++) {
|
|
hwc_layer_1_t *layer = &list->hwLayers[i];
|
|
hwc_rect_t crop = integerizeSourceCrop(layer->sourceCropf);
|
|
trimLayer(ctx, dpy,
|
|
list->hwLayers[i].transform,
|
|
(hwc_rect_t&)crop,
|
|
(hwc_rect_t&)list->hwLayers[i].displayFrame);
|
|
layer->sourceCropf.left = crop.left;
|
|
layer->sourceCropf.right = crop.right;
|
|
layer->sourceCropf.top = crop.top;
|
|
layer->sourceCropf.bottom = crop.bottom;
|
|
}
|
|
}
|
|
|
|
void setListStats(hwc_context_t *ctx,
|
|
hwc_display_contents_1_t *list, int dpy) {
|
|
const int prevYuvCount = ctx->listStats[dpy].yuvCount;
|
|
memset(&ctx->listStats[dpy], 0, sizeof(ListStats));
|
|
ctx->listStats[dpy].numAppLayers = list->numHwLayers - 1;
|
|
ctx->listStats[dpy].fbLayerIndex = list->numHwLayers - 1;
|
|
ctx->listStats[dpy].skipCount = 0;
|
|
ctx->listStats[dpy].preMultipliedAlpha = false;
|
|
ctx->listStats[dpy].isSecurePresent = false;
|
|
ctx->listStats[dpy].yuvCount = 0;
|
|
char property[PROPERTY_VALUE_MAX];
|
|
ctx->listStats[dpy].extOnlyLayerIndex = -1;
|
|
ctx->listStats[dpy].isDisplayAnimating = false;
|
|
ctx->listStats[dpy].roi = ovutils::Dim(0, 0,
|
|
(int)ctx->dpyAttr[dpy].xres, (int)ctx->dpyAttr[dpy].yres);
|
|
ctx->listStats[dpy].secureUI = false;
|
|
ctx->listStats[dpy].yuv4k2kCount = 0;
|
|
ctx->mViewFrame[dpy] = (hwc_rect_t){0, 0, 0, 0};
|
|
|
|
trimList(ctx, list, dpy);
|
|
optimizeLayerRects(ctx, list, dpy);
|
|
|
|
for (size_t i = 0; i < (size_t)ctx->listStats[dpy].numAppLayers; i++) {
|
|
hwc_layer_1_t const* layer = &list->hwLayers[i];
|
|
private_handle_t *hnd = (private_handle_t *)layer->handle;
|
|
|
|
// Calculate view frame of each display from the layer displayframe
|
|
ctx->mViewFrame[dpy] = getUnion(ctx->mViewFrame[dpy],
|
|
layer->displayFrame);
|
|
#ifdef QCOM_BSP
|
|
if (layer->flags & HWC_SCREENSHOT_ANIMATOR_LAYER) {
|
|
ctx->listStats[dpy].isDisplayAnimating = true;
|
|
}
|
|
if(isSecureDisplayBuffer(hnd)) {
|
|
ctx->listStats[dpy].secureUI = true;
|
|
}
|
|
#endif
|
|
// continue if number of app layers exceeds MAX_NUM_APP_LAYERS
|
|
if(ctx->listStats[dpy].numAppLayers > MAX_NUM_APP_LAYERS)
|
|
continue;
|
|
|
|
//reset yuv indices
|
|
ctx->listStats[dpy].yuvIndices[i] = -1;
|
|
ctx->listStats[dpy].yuv4k2kIndices[i] = -1;
|
|
|
|
if (isSecureBuffer(hnd)) {
|
|
ctx->listStats[dpy].isSecurePresent = true;
|
|
}
|
|
|
|
if (isSkipLayer(&list->hwLayers[i])) {
|
|
ctx->listStats[dpy].skipCount++;
|
|
}
|
|
|
|
if (UNLIKELY(isYuvBuffer(hnd))) {
|
|
int& yuvCount = ctx->listStats[dpy].yuvCount;
|
|
ctx->listStats[dpy].yuvIndices[yuvCount] = i;
|
|
yuvCount++;
|
|
|
|
if(UNLIKELY(is4kx2kYuvBuffer(hnd))){
|
|
int& yuv4k2kCount = ctx->listStats[dpy].yuv4k2kCount;
|
|
ctx->listStats[dpy].yuv4k2kIndices[yuv4k2kCount] = i;
|
|
yuv4k2kCount++;
|
|
}
|
|
|
|
if((layer->transform & HWC_TRANSFORM_ROT_90) &&
|
|
canUseRotator(ctx, dpy)) {
|
|
if( (dpy == HWC_DISPLAY_PRIMARY) &&
|
|
ctx->mOverlay->isPipeTypeAttached(OV_MDP_PIPE_DMA)) {
|
|
ctx->isPaddingRound = true;
|
|
}
|
|
Overlay::setDMAMode(Overlay::DMA_BLOCK_MODE);
|
|
}
|
|
}
|
|
if(layer->blending == HWC_BLENDING_PREMULT)
|
|
ctx->listStats[dpy].preMultipliedAlpha = true;
|
|
|
|
|
|
if(UNLIKELY(isExtOnly(hnd))){
|
|
ctx->listStats[dpy].extOnlyLayerIndex = i;
|
|
}
|
|
}
|
|
if(ctx->listStats[dpy].yuvCount > 0) {
|
|
if (property_get("hw.cabl.yuv", property, NULL) > 0) {
|
|
if (atoi(property) != 1) {
|
|
property_set("hw.cabl.yuv", "1");
|
|
}
|
|
}
|
|
} else {
|
|
if (property_get("hw.cabl.yuv", property, NULL) > 0) {
|
|
if (atoi(property) != 0) {
|
|
property_set("hw.cabl.yuv", "0");
|
|
}
|
|
}
|
|
}
|
|
if(dpy) {
|
|
//uncomment the below code for testing purpose.
|
|
/* char value[PROPERTY_VALUE_MAX];
|
|
property_get("sys.ext_orientation", value, "0");
|
|
// Assuming the orientation value is in terms of HAL_TRANSFORM,
|
|
// This needs mapping to HAL, if its in different convention
|
|
ctx->mExtOrientation = atoi(value); */
|
|
// Assuming the orientation value is in terms of HAL_TRANSFORM,
|
|
// This needs mapping to HAL, if its in different convention
|
|
if(ctx->mExtOrientation || ctx->mBufferMirrorMode) {
|
|
ALOGD_IF(HWC_UTILS_DEBUG, "%s: ext orientation = %d"
|
|
"BufferMirrorMode = %d", __FUNCTION__,
|
|
ctx->mExtOrientation, ctx->mBufferMirrorMode);
|
|
if(ctx->mOverlay->isPipeTypeAttached(OV_MDP_PIPE_DMA)) {
|
|
ctx->isPaddingRound = true;
|
|
}
|
|
Overlay::setDMAMode(Overlay::DMA_BLOCK_MODE);
|
|
}
|
|
}
|
|
|
|
//The marking of video begin/end is useful on some targets where we need
|
|
//to have a padding round to be able to shift pipes across mixers.
|
|
if(prevYuvCount != ctx->listStats[dpy].yuvCount) {
|
|
ctx->mVideoTransFlag = true;
|
|
}
|
|
if(dpy == HWC_DISPLAY_PRIMARY) {
|
|
ctx->mAD->markDoable(ctx, list);
|
|
}
|
|
}
|
|
|
|
|
|
static void calc_cut(double& leftCutRatio, double& topCutRatio,
|
|
double& rightCutRatio, double& bottomCutRatio, int orient) {
|
|
if(orient & HAL_TRANSFORM_FLIP_H) {
|
|
swap(leftCutRatio, rightCutRatio);
|
|
}
|
|
if(orient & HAL_TRANSFORM_FLIP_V) {
|
|
swap(topCutRatio, bottomCutRatio);
|
|
}
|
|
if(orient & HAL_TRANSFORM_ROT_90) {
|
|
//Anti clock swapping
|
|
double tmpCutRatio = leftCutRatio;
|
|
leftCutRatio = topCutRatio;
|
|
topCutRatio = rightCutRatio;
|
|
rightCutRatio = bottomCutRatio;
|
|
bottomCutRatio = tmpCutRatio;
|
|
}
|
|
}
|
|
|
|
bool isSecuring(hwc_context_t* ctx, hwc_layer_1_t const* layer) {
|
|
if((ctx->mMDP.version < qdutils::MDSS_V5) &&
|
|
(ctx->mMDP.version > qdutils::MDP_V3_0) &&
|
|
ctx->mSecuring) {
|
|
return true;
|
|
}
|
|
if (isSecureModePolicy(ctx->mMDP.version)) {
|
|
private_handle_t *hnd = (private_handle_t *)layer->handle;
|
|
if(ctx->mSecureMode) {
|
|
if (! isSecureBuffer(hnd)) {
|
|
ALOGD_IF(HWC_UTILS_DEBUG,"%s:Securing Turning ON ...",
|
|
__FUNCTION__);
|
|
return true;
|
|
}
|
|
} else {
|
|
if (isSecureBuffer(hnd)) {
|
|
ALOGD_IF(HWC_UTILS_DEBUG,"%s:Securing Turning OFF ...",
|
|
__FUNCTION__);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool isSecureModePolicy(int mdpVersion) {
|
|
if (mdpVersion < qdutils::MDSS_V5)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
int getBlending(int blending) {
|
|
switch(blending) {
|
|
case HWC_BLENDING_NONE:
|
|
return overlay::utils::OVERLAY_BLENDING_OPAQUE;
|
|
case HWC_BLENDING_PREMULT:
|
|
return overlay::utils::OVERLAY_BLENDING_PREMULT;
|
|
case HWC_BLENDING_COVERAGE :
|
|
default:
|
|
return overlay::utils::OVERLAY_BLENDING_COVERAGE;
|
|
}
|
|
}
|
|
|
|
//Crops source buffer against destination and FB boundaries
|
|
void calculate_crop_rects(hwc_rect_t& crop, hwc_rect_t& dst,
|
|
const hwc_rect_t& scissor, int orient) {
|
|
|
|
int& crop_l = crop.left;
|
|
int& crop_t = 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_l = dst.left;
|
|
int& dst_t = dst.top;
|
|
int& dst_r = dst.right;
|
|
int& dst_b = dst.bottom;
|
|
int dst_w = abs(dst.right - dst.left);
|
|
int dst_h = abs(dst.bottom - dst.top);
|
|
|
|
const int& sci_l = scissor.left;
|
|
const int& sci_t = scissor.top;
|
|
const int& sci_r = scissor.right;
|
|
const int& sci_b = scissor.bottom;
|
|
int sci_w = abs(sci_r - sci_l);
|
|
int sci_h = abs(sci_b - sci_t);
|
|
|
|
double leftCutRatio = 0.0, rightCutRatio = 0.0, topCutRatio = 0.0,
|
|
bottomCutRatio = 0.0;
|
|
|
|
if(dst_l < sci_l) {
|
|
leftCutRatio = (double)(sci_l - dst_l) / (double)dst_w;
|
|
dst_l = sci_l;
|
|
}
|
|
|
|
if(dst_r > sci_r) {
|
|
rightCutRatio = (double)(dst_r - sci_r) / (double)dst_w;
|
|
dst_r = sci_r;
|
|
}
|
|
|
|
if(dst_t < sci_t) {
|
|
topCutRatio = (double)(sci_t - dst_t) / (double)dst_h;
|
|
dst_t = sci_t;
|
|
}
|
|
|
|
if(dst_b > sci_b) {
|
|
bottomCutRatio = (double)(dst_b - sci_b) / (double)dst_h;
|
|
dst_b = sci_b;
|
|
}
|
|
|
|
calc_cut(leftCutRatio, topCutRatio, rightCutRatio, bottomCutRatio, orient);
|
|
crop_l += crop_w * leftCutRatio;
|
|
crop_t += crop_h * topCutRatio;
|
|
crop_r -= crop_w * rightCutRatio;
|
|
crop_b -= crop_h * bottomCutRatio;
|
|
}
|
|
|
|
bool areLayersIntersecting(const hwc_layer_1_t* layer1,
|
|
const hwc_layer_1_t* layer2) {
|
|
hwc_rect_t irect = getIntersection(layer1->displayFrame,
|
|
layer2->displayFrame);
|
|
return isValidRect(irect);
|
|
}
|
|
|
|
bool isValidRect(const hwc_rect& rect)
|
|
{
|
|
return ((rect.bottom > rect.top) && (rect.right > rect.left)) ;
|
|
}
|
|
|
|
/* computes the intersection of two rects */
|
|
hwc_rect_t getIntersection(const hwc_rect_t& rect1, const hwc_rect_t& rect2)
|
|
{
|
|
hwc_rect_t res;
|
|
|
|
if(!isValidRect(rect1) || !isValidRect(rect2)){
|
|
return (hwc_rect_t){0, 0, 0, 0};
|
|
}
|
|
|
|
|
|
res.left = max(rect1.left, rect2.left);
|
|
res.top = max(rect1.top, rect2.top);
|
|
res.right = min(rect1.right, rect2.right);
|
|
res.bottom = min(rect1.bottom, rect2.bottom);
|
|
|
|
if(!isValidRect(res))
|
|
return (hwc_rect_t){0, 0, 0, 0};
|
|
|
|
return res;
|
|
}
|
|
|
|
/* computes the union of two rects */
|
|
hwc_rect_t getUnion(const hwc_rect &rect1, const hwc_rect &rect2)
|
|
{
|
|
hwc_rect_t res;
|
|
|
|
if(!isValidRect(rect1)){
|
|
return rect2;
|
|
}
|
|
|
|
if(!isValidRect(rect2)){
|
|
return rect1;
|
|
}
|
|
|
|
res.left = min(rect1.left, rect2.left);
|
|
res.top = min(rect1.top, rect2.top);
|
|
res.right = max(rect1.right, rect2.right);
|
|
res.bottom = max(rect1.bottom, rect2.bottom);
|
|
|
|
return res;
|
|
}
|
|
|
|
/* Not a geometrical rect deduction. Deducts rect2 from rect1 only if it results
|
|
* a single rect */
|
|
hwc_rect_t deductRect(const hwc_rect_t& rect1, const hwc_rect_t& rect2) {
|
|
|
|
hwc_rect_t res = rect1;
|
|
|
|
if((rect1.left == rect2.left) && (rect1.right == rect2.right)) {
|
|
if((rect1.top == rect2.top) && (rect2.bottom <= rect1.bottom))
|
|
res.top = rect2.bottom;
|
|
else if((rect1.bottom == rect2.bottom)&& (rect2.top >= rect1.top))
|
|
res.bottom = rect2.top;
|
|
}
|
|
else if((rect1.top == rect2.top) && (rect1.bottom == rect2.bottom)) {
|
|
if((rect1.left == rect2.left) && (rect2.right <= rect1.right))
|
|
res.left = rect2.right;
|
|
else if((rect1.right == rect2.right)&& (rect2.left >= rect1.left))
|
|
res.right = rect2.left;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
void optimizeLayerRects(hwc_context_t *ctx,
|
|
const hwc_display_contents_1_t *list, const int& dpy) {
|
|
int i=list->numHwLayers-2;
|
|
hwc_rect_t irect;
|
|
while(i > 0) {
|
|
|
|
//see if there is no blending required.
|
|
//If it is opaque see if we can substract this region from below layers.
|
|
if(list->hwLayers[i].blending == HWC_BLENDING_NONE) {
|
|
int j= i-1;
|
|
hwc_rect_t& topframe =
|
|
(hwc_rect_t&)list->hwLayers[i].displayFrame;
|
|
while(j >= 0) {
|
|
if(!needsScaling(&list->hwLayers[j])) {
|
|
hwc_layer_1_t* layer = (hwc_layer_1_t*)&list->hwLayers[j];
|
|
hwc_rect_t& bottomframe = layer->displayFrame;
|
|
hwc_rect_t& bottomCrop = layer->sourceCrop;
|
|
int transform =layer->transform;
|
|
|
|
hwc_rect_t irect = getIntersection(bottomframe, topframe);
|
|
if(isValidRect(irect)) {
|
|
hwc_rect_t dest_rect;
|
|
//if intersection is valid rect, deduct it
|
|
dest_rect = deductRect(bottomframe, irect);
|
|
qhwc::calculate_crop_rects(bottomCrop, bottomframe,
|
|
dest_rect, transform);
|
|
|
|
}
|
|
}
|
|
j--;
|
|
}
|
|
}
|
|
i--;
|
|
}
|
|
}
|
|
|
|
void getNonWormholeRegion(hwc_display_contents_1_t* list,
|
|
hwc_rect_t& nwr)
|
|
{
|
|
uint32_t last = list->numHwLayers - 1;
|
|
hwc_rect_t fbDisplayFrame = list->hwLayers[last].displayFrame;
|
|
//Initiliaze nwr to first frame
|
|
nwr.left = list->hwLayers[0].displayFrame.left;
|
|
nwr.top = list->hwLayers[0].displayFrame.top;
|
|
nwr.right = list->hwLayers[0].displayFrame.right;
|
|
nwr.bottom = list->hwLayers[0].displayFrame.bottom;
|
|
|
|
for (uint32_t i = 1; i < last; i++) {
|
|
hwc_rect_t displayFrame = list->hwLayers[i].displayFrame;
|
|
nwr = getUnion(nwr, displayFrame);
|
|
}
|
|
|
|
//Intersect with the framebuffer
|
|
nwr = getIntersection(nwr, fbDisplayFrame);
|
|
}
|
|
|
|
bool isExternalActive(hwc_context_t* ctx) {
|
|
return ctx->dpyAttr[HWC_DISPLAY_EXTERNAL].isActive;
|
|
}
|
|
|
|
void closeAcquireFds(hwc_display_contents_1_t* list) {
|
|
for(uint32_t i = 0; list && i < list->numHwLayers; i++) {
|
|
//Close the acquireFenceFds
|
|
//HWC_FRAMEBUFFER are -1 already by SF, rest we close.
|
|
if(list->hwLayers[i].acquireFenceFd >= 0) {
|
|
close(list->hwLayers[i].acquireFenceFd);
|
|
list->hwLayers[i].acquireFenceFd = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
int hwc_sync(hwc_context_t *ctx, hwc_display_contents_1_t* list, int dpy,
|
|
int fd) {
|
|
ATRACE_CALL();
|
|
int ret = 0;
|
|
int acquireFd[MAX_NUM_APP_LAYERS];
|
|
int count = 0;
|
|
int releaseFd = -1;
|
|
int fbFd = -1;
|
|
bool swapzero = false;
|
|
int mdpVersion = qdutils::MDPVersion::getInstance().getMDPVersion();
|
|
|
|
struct mdp_buf_sync data;
|
|
memset(&data, 0, sizeof(data));
|
|
data.acq_fen_fd = acquireFd;
|
|
data.rel_fen_fd = &releaseFd;
|
|
|
|
char property[PROPERTY_VALUE_MAX];
|
|
if(property_get("debug.egl.swapinterval", property, "1") > 0) {
|
|
if(atoi(property) == 0)
|
|
swapzero = true;
|
|
}
|
|
|
|
bool isExtAnimating = false;
|
|
if(dpy)
|
|
isExtAnimating = ctx->listStats[dpy].isDisplayAnimating;
|
|
|
|
//Send acquireFenceFds to rotator
|
|
for(uint32_t i = 0; i < ctx->mLayerRotMap[dpy]->getCount(); i++) {
|
|
int rotFd = ctx->mRotMgr->getRotDevFd();
|
|
int rotReleaseFd = -1;
|
|
struct mdp_buf_sync rotData;
|
|
memset(&rotData, 0, sizeof(rotData));
|
|
rotData.acq_fen_fd =
|
|
&ctx->mLayerRotMap[dpy]->getLayer(i)->acquireFenceFd;
|
|
rotData.rel_fen_fd = &rotReleaseFd; //driver to populate this
|
|
rotData.session_id = ctx->mLayerRotMap[dpy]->getRot(i)->getSessId();
|
|
int ret = 0;
|
|
ret = ioctl(rotFd, MSMFB_BUFFER_SYNC, &rotData);
|
|
if(ret < 0) {
|
|
ALOGE("%s: ioctl MSMFB_BUFFER_SYNC failed for rot sync, err=%s",
|
|
__FUNCTION__, strerror(errno));
|
|
} else {
|
|
close(ctx->mLayerRotMap[dpy]->getLayer(i)->acquireFenceFd);
|
|
//For MDP to wait on.
|
|
ctx->mLayerRotMap[dpy]->getLayer(i)->acquireFenceFd =
|
|
dup(rotReleaseFd);
|
|
//A buffer is free to be used by producer as soon as its copied to
|
|
//rotator
|
|
ctx->mLayerRotMap[dpy]->getLayer(i)->releaseFenceFd =
|
|
rotReleaseFd;
|
|
}
|
|
}
|
|
|
|
//Accumulate acquireFenceFds for MDP
|
|
for(uint32_t i = 0; i < list->numHwLayers; i++) {
|
|
if(list->hwLayers[i].compositionType == HWC_OVERLAY &&
|
|
list->hwLayers[i].acquireFenceFd >= 0) {
|
|
if(UNLIKELY(swapzero))
|
|
acquireFd[count++] = -1;
|
|
else
|
|
acquireFd[count++] = list->hwLayers[i].acquireFenceFd;
|
|
}
|
|
if(list->hwLayers[i].compositionType == HWC_FRAMEBUFFER_TARGET) {
|
|
if(UNLIKELY(swapzero))
|
|
acquireFd[count++] = -1;
|
|
else if(fd >= 0) {
|
|
//set the acquireFD from fd - which is coming from c2d
|
|
acquireFd[count++] = fd;
|
|
// Buffer sync IOCTL should be async when using c2d fence is
|
|
// used
|
|
data.flags &= ~MDP_BUF_SYNC_FLAG_WAIT;
|
|
} else if(list->hwLayers[i].acquireFenceFd >= 0)
|
|
acquireFd[count++] = list->hwLayers[i].acquireFenceFd;
|
|
}
|
|
}
|
|
|
|
data.acq_fen_fd_cnt = count;
|
|
fbFd = ctx->dpyAttr[dpy].fd;
|
|
|
|
//Waits for acquire fences, returns a release fence
|
|
if(LIKELY(!swapzero)) {
|
|
uint64_t start = systemTime();
|
|
ret = ioctl(fbFd, MSMFB_BUFFER_SYNC, &data);
|
|
ALOGD_IF(HWC_UTILS_DEBUG, "%s: time taken for MSMFB_BUFFER_SYNC IOCTL = %d",
|
|
__FUNCTION__, (size_t) ns2ms(systemTime() - start));
|
|
}
|
|
|
|
if(ret < 0) {
|
|
ALOGE("%s: ioctl MSMFB_BUFFER_SYNC failed, err=%s",
|
|
__FUNCTION__, strerror(errno));
|
|
ALOGE("%s: acq_fen_fd_cnt=%d flags=%d fd=%d dpy=%d numHwLayers=%d",
|
|
__FUNCTION__, data.acq_fen_fd_cnt, data.flags, fbFd,
|
|
dpy, list->numHwLayers);
|
|
}
|
|
|
|
for(uint32_t i = 0; i < list->numHwLayers; i++) {
|
|
if(list->hwLayers[i].compositionType == HWC_OVERLAY ||
|
|
list->hwLayers[i].compositionType == HWC_FRAMEBUFFER_TARGET) {
|
|
//Populate releaseFenceFds.
|
|
if(UNLIKELY(swapzero)) {
|
|
list->hwLayers[i].releaseFenceFd = -1;
|
|
} else if(isExtAnimating) {
|
|
// Release all the app layer fds immediately,
|
|
// if animation is in progress.
|
|
hwc_layer_1_t const* layer = &list->hwLayers[i];
|
|
private_handle_t *hnd = (private_handle_t *)layer->handle;
|
|
if(isYuvBuffer(hnd)) {
|
|
list->hwLayers[i].releaseFenceFd = dup(releaseFd);
|
|
} else
|
|
list->hwLayers[i].releaseFenceFd = -1;
|
|
} else if(list->hwLayers[i].releaseFenceFd < 0) {
|
|
//If rotator has not already populated this field.
|
|
list->hwLayers[i].releaseFenceFd = dup(releaseFd);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(fd >= 0) {
|
|
close(fd);
|
|
fd = -1;
|
|
}
|
|
|
|
if (ctx->mCopyBit[dpy])
|
|
ctx->mCopyBit[dpy]->setReleaseFd(releaseFd);
|
|
|
|
//Signals when MDP finishes reading rotator buffers.
|
|
ctx->mLayerRotMap[dpy]->setReleaseFd(releaseFd);
|
|
|
|
// if external is animating, close the relaseFd
|
|
if(isExtAnimating) {
|
|
close(releaseFd);
|
|
releaseFd = -1;
|
|
}
|
|
|
|
if(UNLIKELY(swapzero)){
|
|
list->retireFenceFd = -1;
|
|
close(releaseFd);
|
|
} else {
|
|
list->retireFenceFd = releaseFd;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void setMdpFlags(hwc_layer_1_t *layer,
|
|
ovutils::eMdpFlags &mdpFlags,
|
|
int rotDownscale, int transform) {
|
|
private_handle_t *hnd = (private_handle_t *)layer->handle;
|
|
MetaData_t *metadata = hnd ? (MetaData_t *)hnd->base_metadata : NULL;
|
|
|
|
if(layer->blending == HWC_BLENDING_PREMULT) {
|
|
ovutils::setMdpFlags(mdpFlags,
|
|
ovutils::OV_MDP_BLEND_FG_PREMULT);
|
|
}
|
|
|
|
if(isYuvBuffer(hnd)) {
|
|
if(isSecureBuffer(hnd)) {
|
|
ovutils::setMdpFlags(mdpFlags,
|
|
ovutils::OV_MDP_SECURE_OVERLAY_SESSION);
|
|
}
|
|
if(metadata && (metadata->operation & PP_PARAM_INTERLACED) &&
|
|
metadata->interlaced) {
|
|
ovutils::setMdpFlags(mdpFlags,
|
|
ovutils::OV_MDP_DEINTERLACE);
|
|
}
|
|
//Pre-rotation will be used using rotator.
|
|
if(transform & HWC_TRANSFORM_ROT_90) {
|
|
ovutils::setMdpFlags(mdpFlags,
|
|
ovutils::OV_MDP_SOURCE_ROTATED_90);
|
|
}
|
|
}
|
|
|
|
if(isSecureDisplayBuffer(hnd)) {
|
|
// Secure display needs both SECURE_OVERLAY and SECURE_DISPLAY_OV
|
|
ovutils::setMdpFlags(mdpFlags,
|
|
ovutils::OV_MDP_SECURE_OVERLAY_SESSION);
|
|
ovutils::setMdpFlags(mdpFlags,
|
|
ovutils::OV_MDP_SECURE_DISPLAY_OVERLAY_SESSION);
|
|
}
|
|
//No 90 component and no rot-downscale then flips done by MDP
|
|
//If we use rot then it might as well do flips
|
|
if(!(transform & HWC_TRANSFORM_ROT_90) && !rotDownscale) {
|
|
if(transform & HWC_TRANSFORM_FLIP_H) {
|
|
ovutils::setMdpFlags(mdpFlags, ovutils::OV_MDP_FLIP_H);
|
|
}
|
|
|
|
if(transform & HWC_TRANSFORM_FLIP_V) {
|
|
ovutils::setMdpFlags(mdpFlags, ovutils::OV_MDP_FLIP_V);
|
|
}
|
|
}
|
|
|
|
if(metadata &&
|
|
((metadata->operation & PP_PARAM_HSIC)
|
|
|| (metadata->operation & PP_PARAM_IGC)
|
|
|| (metadata->operation & PP_PARAM_SHARP2))) {
|
|
ovutils::setMdpFlags(mdpFlags, ovutils::OV_MDP_PP_EN);
|
|
}
|
|
}
|
|
|
|
int configRotator(Rotator *rot, Whf& whf,
|
|
hwc_rect_t& crop, const eMdpFlags& mdpFlags,
|
|
const eTransform& orient, const int& downscale) {
|
|
|
|
// Fix alignments for TILED format
|
|
if(whf.format == MDP_Y_CRCB_H2V2_TILE ||
|
|
whf.format == MDP_Y_CBCR_H2V2_TILE) {
|
|
whf.w = utils::alignup(whf.w, 64);
|
|
whf.h = utils::alignup(whf.h, 32);
|
|
}
|
|
rot->setSource(whf);
|
|
|
|
if (qdutils::MDPVersion::getInstance().getMDPVersion() >=
|
|
qdutils::MDSS_V5) {
|
|
uint32_t crop_w = (crop.right - crop.left);
|
|
uint32_t crop_h = (crop.bottom - crop.top);
|
|
if (ovutils::isYuv(whf.format)) {
|
|
ovutils::normalizeCrop((uint32_t&)crop.left, crop_w);
|
|
ovutils::normalizeCrop((uint32_t&)crop.top, crop_h);
|
|
// For interlaced, crop.h should be 4-aligned
|
|
if ((mdpFlags & ovutils::OV_MDP_DEINTERLACE) && (crop_h % 4))
|
|
crop_h = ovutils::aligndown(crop_h, 4);
|
|
crop.right = crop.left + crop_w;
|
|
crop.bottom = crop.top + crop_h;
|
|
}
|
|
Dim rotCrop(crop.left, crop.top, crop_w, crop_h);
|
|
rot->setCrop(rotCrop);
|
|
}
|
|
|
|
rot->setFlags(mdpFlags);
|
|
rot->setTransform(orient);
|
|
rot->setDownscale(downscale);
|
|
if(!rot->commit()) return -1;
|
|
return 0;
|
|
}
|
|
|
|
int configMdp(Overlay *ov, const PipeArgs& parg,
|
|
const eTransform& orient, const hwc_rect_t& crop,
|
|
const hwc_rect_t& pos, const MetaData_t *metadata,
|
|
const eDest& dest) {
|
|
ov->setSource(parg, dest);
|
|
ov->setTransform(orient, dest);
|
|
|
|
int crop_w = crop.right - crop.left;
|
|
int crop_h = crop.bottom - crop.top;
|
|
Dim dcrop(crop.left, crop.top, crop_w, crop_h);
|
|
ov->setCrop(dcrop, dest);
|
|
|
|
int posW = pos.right - pos.left;
|
|
int posH = pos.bottom - pos.top;
|
|
Dim position(pos.left, pos.top, posW, posH);
|
|
ov->setPosition(position, dest);
|
|
|
|
if (metadata)
|
|
ov->setVisualParams(*metadata, dest);
|
|
|
|
if (!ov->commit(dest)) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int configColorLayer(hwc_context_t *ctx, hwc_layer_1_t *layer,
|
|
const int& dpy, eMdpFlags& mdpFlags, eZorder& z,
|
|
eIsFg& isFg, const eDest& dest) {
|
|
|
|
hwc_rect_t dst = layer->displayFrame;
|
|
trimLayer(ctx, dpy, 0, dst, dst);
|
|
|
|
int w = ctx->dpyAttr[dpy].xres;
|
|
int h = ctx->dpyAttr[dpy].yres;
|
|
int dst_w = dst.right - dst.left;
|
|
int dst_h = dst.bottom - dst.top;
|
|
uint32_t color = layer->transform;
|
|
Whf whf(w, h, getMdpFormat(HAL_PIXEL_FORMAT_RGBA_8888), 0);
|
|
|
|
if (layer->blending == HWC_BLENDING_PREMULT)
|
|
ovutils::setMdpFlags(mdpFlags, ovutils::OV_MDP_BLEND_FG_PREMULT);
|
|
|
|
PipeArgs parg(mdpFlags, whf, z, isFg, static_cast<eRotFlags>(0),
|
|
layer->planeAlpha,
|
|
(ovutils::eBlending) getBlending(layer->blending));
|
|
|
|
// Configure MDP pipe for Color layer
|
|
Dim pos(dst.left, dst.top, dst_w, dst_h);
|
|
ctx->mOverlay->setSource(parg, dest);
|
|
ctx->mOverlay->setColor(color, dest);
|
|
ctx->mOverlay->setTransform(0, dest);
|
|
ctx->mOverlay->setCrop(pos, dest);
|
|
ctx->mOverlay->setPosition(pos, dest);
|
|
|
|
if (!ctx->mOverlay->commit(dest)) {
|
|
ALOGE("%s: Configure color layer failed!", __FUNCTION__);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void updateSource(eTransform& orient, Whf& whf,
|
|
hwc_rect_t& crop) {
|
|
Dim srcCrop(crop.left, crop.top,
|
|
crop.right - crop.left,
|
|
crop.bottom - crop.top);
|
|
orient = static_cast<eTransform>(ovutils::getMdpOrient(orient));
|
|
preRotateSource(orient, whf, srcCrop);
|
|
if (qdutils::MDPVersion::getInstance().getMDPVersion() >=
|
|
qdutils::MDSS_V5) {
|
|
// Source for overlay will be the cropped (and rotated)
|
|
crop.left = 0;
|
|
crop.top = 0;
|
|
crop.right = srcCrop.w;
|
|
crop.bottom = srcCrop.h;
|
|
// Set width & height equal to sourceCrop w & h
|
|
whf.w = srcCrop.w;
|
|
whf.h = srcCrop.h;
|
|
} else {
|
|
crop.left = srcCrop.x;
|
|
crop.top = srcCrop.y;
|
|
crop.right = srcCrop.x + srcCrop.w;
|
|
crop.bottom = srcCrop.y + srcCrop.h;
|
|
}
|
|
}
|
|
|
|
int configureNonSplit(hwc_context_t *ctx, hwc_layer_1_t *layer,
|
|
const int& dpy, eMdpFlags& mdpFlags, eZorder& z,
|
|
eIsFg& isFg, const eDest& dest, Rotator **rot) {
|
|
|
|
private_handle_t *hnd = (private_handle_t *)layer->handle;
|
|
|
|
if(!hnd) {
|
|
if (layer->flags & HWC_COLOR_FILL) {
|
|
// Configure Color layer
|
|
return configColorLayer(ctx, layer, dpy, mdpFlags, z, isFg, dest);
|
|
}
|
|
ALOGE("%s: layer handle is NULL", __FUNCTION__);
|
|
return -1;
|
|
}
|
|
|
|
MetaData_t *metadata = (MetaData_t *)hnd->base_metadata;
|
|
|
|
hwc_rect_t crop = integerizeSourceCrop(layer->sourceCropf);
|
|
hwc_rect_t dst = layer->displayFrame;
|
|
int transform = layer->transform;
|
|
eTransform orient = static_cast<eTransform>(transform);
|
|
int downscale = 0;
|
|
int rotFlags = ovutils::ROT_FLAGS_NONE;
|
|
Whf whf(getWidth(hnd), getHeight(hnd),
|
|
getMdpFormat(hnd->format), hnd->size);
|
|
|
|
// Handle R/B swap
|
|
if (layer->flags & HWC_FORMAT_RB_SWAP) {
|
|
if (hnd->format == HAL_PIXEL_FORMAT_RGBA_8888)
|
|
whf.format = getMdpFormat(HAL_PIXEL_FORMAT_BGRA_8888);
|
|
else if (hnd->format == HAL_PIXEL_FORMAT_RGBX_8888)
|
|
whf.format = getMdpFormat(HAL_PIXEL_FORMAT_BGRX_8888);
|
|
}
|
|
|
|
if(dpy && isYuvBuffer(hnd)) {
|
|
if(!ctx->listStats[dpy].isDisplayAnimating) {
|
|
ctx->mPrevCropVideo = crop;
|
|
ctx->mPrevDestVideo = dst;
|
|
ctx->mPrevTransformVideo = transform;
|
|
} else {
|
|
// Restore the previous crop, dest rect and transform values, during
|
|
// animation to avoid displaying videos at random coordinates.
|
|
crop = ctx->mPrevCropVideo;
|
|
dst = ctx->mPrevDestVideo;
|
|
transform = ctx->mPrevTransformVideo;
|
|
orient = static_cast<eTransform>(transform);
|
|
//In you tube use case when a device rotated from landscape to
|
|
// portrait, set the isFg flag and zOrder to avoid displaying UI on
|
|
// hdmi during animation
|
|
if(ctx->deviceOrientation) {
|
|
isFg = ovutils::IS_FG_SET;
|
|
z = ZORDER_1;
|
|
}
|
|
}
|
|
calcExtDisplayPosition(ctx, hnd, dpy, crop, dst,
|
|
transform, orient);
|
|
}
|
|
|
|
if(isYuvBuffer(hnd) && ctx->mMDP.version >= qdutils::MDP_V4_2 &&
|
|
ctx->mMDP.version < qdutils::MDSS_V5) {
|
|
downscale = getDownscaleFactor(
|
|
crop.right - crop.left,
|
|
crop.bottom - crop.top,
|
|
dst.right - dst.left,
|
|
dst.bottom - dst.top);
|
|
if(downscale) {
|
|
rotFlags = ROT_DOWNSCALE_ENABLED;
|
|
}
|
|
}
|
|
|
|
setMdpFlags(layer, mdpFlags, downscale, transform);
|
|
|
|
if(isYuvBuffer(hnd) && //if 90 component or downscale, use rot
|
|
((transform & HWC_TRANSFORM_ROT_90) || downscale)) {
|
|
*rot = ctx->mRotMgr->getNext();
|
|
if(*rot == NULL) return -1;
|
|
if(!dpy)
|
|
BwcPM::setBwc(ctx, crop, dst, transform, mdpFlags);
|
|
//Configure rotator for pre-rotation
|
|
if(configRotator(*rot, whf, crop, mdpFlags, orient, downscale) < 0) {
|
|
ALOGE("%s: configRotator failed!", __FUNCTION__);
|
|
return -1;
|
|
}
|
|
ctx->mLayerRotMap[dpy]->add(layer, *rot);
|
|
whf.format = (*rot)->getDstFormat();
|
|
updateSource(orient, whf, crop);
|
|
rotFlags |= ovutils::ROT_PREROTATED;
|
|
}
|
|
|
|
//For the mdp, since either we are pre-rotating or MDP does flips
|
|
orient = OVERLAY_TRANSFORM_0;
|
|
transform = 0;
|
|
PipeArgs parg(mdpFlags, whf, z, isFg,
|
|
static_cast<eRotFlags>(rotFlags), layer->planeAlpha,
|
|
(ovutils::eBlending) getBlending(layer->blending));
|
|
|
|
if(configMdp(ctx->mOverlay, parg, orient, crop, dst, metadata, dest) < 0) {
|
|
ALOGE("%s: commit failed for low res panel", __FUNCTION__);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//Helper to 1) Ensure crops dont have gaps 2) Ensure L and W are even
|
|
void sanitizeSourceCrop(hwc_rect_t& cropL, hwc_rect_t& cropR,
|
|
private_handle_t *hnd) {
|
|
if(cropL.right - cropL.left) {
|
|
if(isYuvBuffer(hnd)) {
|
|
//Always safe to even down left
|
|
ovutils::even_floor(cropL.left);
|
|
//If right is even, automatically width is even, since left is
|
|
//already even
|
|
ovutils::even_floor(cropL.right);
|
|
}
|
|
//Make sure there are no gaps between left and right splits if the layer
|
|
//is spread across BOTH halves
|
|
if(cropR.right - cropR.left) {
|
|
cropR.left = cropL.right;
|
|
}
|
|
}
|
|
|
|
if(cropR.right - cropR.left) {
|
|
if(isYuvBuffer(hnd)) {
|
|
//Always safe to even down left
|
|
ovutils::even_floor(cropR.left);
|
|
//If right is even, automatically width is even, since left is
|
|
//already even
|
|
ovutils::even_floor(cropR.right);
|
|
}
|
|
}
|
|
}
|
|
|
|
int configureSplit(hwc_context_t *ctx, hwc_layer_1_t *layer,
|
|
const int& dpy, eMdpFlags& mdpFlagsL, eZorder& z,
|
|
eIsFg& isFg, const eDest& lDest, const eDest& rDest,
|
|
Rotator **rot) {
|
|
private_handle_t *hnd = (private_handle_t *)layer->handle;
|
|
if(!hnd) {
|
|
ALOGE("%s: layer handle is NULL", __FUNCTION__);
|
|
return -1;
|
|
}
|
|
|
|
MetaData_t *metadata = (MetaData_t *)hnd->base_metadata;
|
|
|
|
int hw_w = ctx->dpyAttr[dpy].xres;
|
|
int hw_h = ctx->dpyAttr[dpy].yres;
|
|
hwc_rect_t crop = integerizeSourceCrop(layer->sourceCropf);
|
|
hwc_rect_t dst = layer->displayFrame;
|
|
int transform = layer->transform;
|
|
eTransform orient = static_cast<eTransform>(transform);
|
|
const int downscale = 0;
|
|
int rotFlags = ROT_FLAGS_NONE;
|
|
|
|
Whf whf(getWidth(hnd), getHeight(hnd),
|
|
getMdpFormat(hnd->format), hnd->size);
|
|
|
|
// Handle R/B swap
|
|
if (layer->flags & HWC_FORMAT_RB_SWAP) {
|
|
if (hnd->format == HAL_PIXEL_FORMAT_RGBA_8888)
|
|
whf.format = getMdpFormat(HAL_PIXEL_FORMAT_BGRA_8888);
|
|
else if (hnd->format == HAL_PIXEL_FORMAT_RGBX_8888)
|
|
whf.format = getMdpFormat(HAL_PIXEL_FORMAT_BGRX_8888);
|
|
}
|
|
|
|
if(dpy && isYuvBuffer(hnd)) {
|
|
if(!ctx->listStats[dpy].isDisplayAnimating) {
|
|
ctx->mPrevCropVideo = crop;
|
|
ctx->mPrevDestVideo = dst;
|
|
ctx->mPrevTransformVideo = transform;
|
|
} else {
|
|
// Restore the previous crop, dest rect and transform values, during
|
|
// animation to avoid displaying videos at random coordinates.
|
|
crop = ctx->mPrevCropVideo;
|
|
dst = ctx->mPrevDestVideo;
|
|
transform = ctx->mPrevTransformVideo;
|
|
orient = static_cast<eTransform>(transform);
|
|
//In you tube use case when a device rotated from landscape to
|
|
// portrait, set the isFg flag and zOrder to avoid displaying UI on
|
|
// hdmi during animation
|
|
if(ctx->deviceOrientation) {
|
|
isFg = ovutils::IS_FG_SET;
|
|
z = ZORDER_1;
|
|
}
|
|
}
|
|
}
|
|
|
|
setMdpFlags(layer, mdpFlagsL, 0, transform);
|
|
|
|
if(lDest != OV_INVALID && rDest != OV_INVALID) {
|
|
//Enable overfetch
|
|
setMdpFlags(mdpFlagsL, OV_MDSS_MDP_DUAL_PIPE);
|
|
}
|
|
|
|
//Will do something only if feature enabled and conditions suitable
|
|
//hollow call otherwise
|
|
if(ctx->mAD->prepare(ctx, crop, whf, hnd)) {
|
|
overlay::Writeback *wb = overlay::Writeback::getInstance();
|
|
whf.format = wb->getOutputFormat();
|
|
}
|
|
|
|
if(isYuvBuffer(hnd) && (transform & HWC_TRANSFORM_ROT_90)) {
|
|
(*rot) = ctx->mRotMgr->getNext();
|
|
if((*rot) == NULL) return -1;
|
|
//Configure rotator for pre-rotation
|
|
if(configRotator(*rot, whf, crop, mdpFlagsL, orient, downscale) < 0) {
|
|
ALOGE("%s: configRotator failed!", __FUNCTION__);
|
|
return -1;
|
|
}
|
|
ctx->mLayerRotMap[dpy]->add(layer, *rot);
|
|
whf.format = (*rot)->getDstFormat();
|
|
updateSource(orient, whf, crop);
|
|
rotFlags |= ROT_PREROTATED;
|
|
}
|
|
|
|
eMdpFlags mdpFlagsR = mdpFlagsL;
|
|
setMdpFlags(mdpFlagsR, OV_MDSS_MDP_RIGHT_MIXER);
|
|
|
|
hwc_rect_t tmp_cropL = {0}, tmp_dstL = {0};
|
|
hwc_rect_t tmp_cropR = {0}, tmp_dstR = {0};
|
|
|
|
const int lSplit = getLeftSplit(ctx, dpy);
|
|
|
|
if(lDest != OV_INVALID) {
|
|
tmp_cropL = crop;
|
|
tmp_dstL = dst;
|
|
hwc_rect_t scissor = {0, 0, lSplit, hw_h };
|
|
scissor = getIntersection(ctx->mViewFrame[dpy], scissor);
|
|
qhwc::calculate_crop_rects(tmp_cropL, tmp_dstL, scissor, 0);
|
|
}
|
|
if(rDest != OV_INVALID) {
|
|
tmp_cropR = crop;
|
|
tmp_dstR = dst;
|
|
hwc_rect_t scissor = {lSplit, 0, hw_w, hw_h };
|
|
scissor = getIntersection(ctx->mViewFrame[dpy], scissor);
|
|
qhwc::calculate_crop_rects(tmp_cropR, tmp_dstR, scissor, 0);
|
|
}
|
|
|
|
sanitizeSourceCrop(tmp_cropL, tmp_cropR, hnd);
|
|
|
|
//When buffer is H-flipped, contents of mixer config also needs to swapped
|
|
//Not needed if the layer is confined to one half of the screen.
|
|
//If rotator has been used then it has also done the flips, so ignore them.
|
|
if((orient & OVERLAY_TRANSFORM_FLIP_H) && lDest != OV_INVALID
|
|
&& rDest != OV_INVALID && (*rot) == NULL) {
|
|
hwc_rect_t new_cropR;
|
|
new_cropR.left = tmp_cropL.left;
|
|
new_cropR.right = new_cropR.left + (tmp_cropR.right - tmp_cropR.left);
|
|
|
|
hwc_rect_t new_cropL;
|
|
new_cropL.left = new_cropR.right;
|
|
new_cropL.right = tmp_cropR.right;
|
|
|
|
tmp_cropL.left = new_cropL.left;
|
|
tmp_cropL.right = new_cropL.right;
|
|
|
|
tmp_cropR.left = new_cropR.left;
|
|
tmp_cropR.right = new_cropR.right;
|
|
|
|
}
|
|
|
|
//For the mdp, since either we are pre-rotating or MDP does flips
|
|
orient = OVERLAY_TRANSFORM_0;
|
|
transform = 0;
|
|
|
|
//configure left mixer
|
|
if(lDest != OV_INVALID) {
|
|
PipeArgs pargL(mdpFlagsL, whf, z, isFg,
|
|
static_cast<eRotFlags>(rotFlags), layer->planeAlpha,
|
|
(ovutils::eBlending) getBlending(layer->blending));
|
|
|
|
if(configMdp(ctx->mOverlay, pargL, orient,
|
|
tmp_cropL, tmp_dstL, metadata, lDest) < 0) {
|
|
ALOGE("%s: commit failed for left mixer config", __FUNCTION__);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
//configure right mixer
|
|
if(rDest != OV_INVALID) {
|
|
PipeArgs pargR(mdpFlagsR, whf, z, isFg,
|
|
static_cast<eRotFlags>(rotFlags),
|
|
layer->planeAlpha,
|
|
(ovutils::eBlending) getBlending(layer->blending));
|
|
tmp_dstR.right = tmp_dstR.right - lSplit;
|
|
tmp_dstR.left = tmp_dstR.left - lSplit;
|
|
if(configMdp(ctx->mOverlay, pargR, orient,
|
|
tmp_cropR, tmp_dstR, metadata, rDest) < 0) {
|
|
ALOGE("%s: commit failed for right mixer config", __FUNCTION__);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int configureSourceSplit(hwc_context_t *ctx, hwc_layer_1_t *layer,
|
|
const int& dpy, eMdpFlags& mdpFlagsL, eZorder& z,
|
|
eIsFg& isFg, const eDest& lDest, const eDest& rDest,
|
|
Rotator **rot) {
|
|
private_handle_t *hnd = (private_handle_t *)layer->handle;
|
|
if(!hnd) {
|
|
ALOGE("%s: layer handle is NULL", __FUNCTION__);
|
|
return -1;
|
|
}
|
|
|
|
MetaData_t *metadata = (MetaData_t *)hnd->base_metadata;
|
|
|
|
int hw_w = ctx->dpyAttr[dpy].xres;
|
|
int hw_h = ctx->dpyAttr[dpy].yres;
|
|
hwc_rect_t crop = integerizeSourceCrop(layer->sourceCropf);;
|
|
hwc_rect_t dst = layer->displayFrame;
|
|
int transform = layer->transform;
|
|
eTransform orient = static_cast<eTransform>(transform);
|
|
const int downscale = 0;
|
|
int rotFlags = ROT_FLAGS_NONE;
|
|
//Splitting only YUV layer on primary panel needs different zorders
|
|
//for both layers as both the layers are configured to single mixer
|
|
eZorder lz = z;
|
|
eZorder rz = (eZorder)(z + 1);
|
|
|
|
Whf whf(getWidth(hnd), getHeight(hnd),
|
|
getMdpFormat(hnd->format), hnd->size);
|
|
|
|
setMdpFlags(layer, mdpFlagsL, 0, transform);
|
|
trimLayer(ctx, dpy, transform, crop, dst);
|
|
|
|
if(isYuvBuffer(hnd) && (transform & HWC_TRANSFORM_ROT_90)) {
|
|
(*rot) = ctx->mRotMgr->getNext();
|
|
if((*rot) == NULL) return -1;
|
|
if(!dpy)
|
|
BwcPM::setBwc(ctx, crop, dst, transform, mdpFlagsL);
|
|
//Configure rotator for pre-rotation
|
|
if(configRotator(*rot, whf, crop, mdpFlagsL, orient, downscale) < 0) {
|
|
ALOGE("%s: configRotator failed!", __FUNCTION__);
|
|
return -1;
|
|
}
|
|
ctx->mLayerRotMap[dpy]->add(layer, *rot);
|
|
whf.format = (*rot)->getDstFormat();
|
|
updateSource(orient, whf, crop);
|
|
rotFlags |= ROT_PREROTATED;
|
|
}
|
|
|
|
eMdpFlags mdpFlagsR = mdpFlagsL;
|
|
int lSplit = dst.left + (dst.right - dst.left)/2;
|
|
|
|
hwc_rect_t tmp_cropL = {0}, tmp_dstL = {0};
|
|
hwc_rect_t tmp_cropR = {0}, tmp_dstR = {0};
|
|
|
|
if(lDest != OV_INVALID) {
|
|
tmp_cropL = crop;
|
|
tmp_dstL = dst;
|
|
hwc_rect_t scissor = {dst.left, dst.top, lSplit, dst.bottom };
|
|
qhwc::calculate_crop_rects(tmp_cropL, tmp_dstL, scissor, 0);
|
|
}
|
|
if(rDest != OV_INVALID) {
|
|
tmp_cropR = crop;
|
|
tmp_dstR = dst;
|
|
hwc_rect_t scissor = {lSplit, dst.top, dst.right, dst.bottom };
|
|
qhwc::calculate_crop_rects(tmp_cropR, tmp_dstR, scissor, 0);
|
|
}
|
|
|
|
sanitizeSourceCrop(tmp_cropL, tmp_cropR, hnd);
|
|
|
|
//When buffer is H-flipped, contents of mixer config also needs to swapped
|
|
//Not needed if the layer is confined to one half of the screen.
|
|
//If rotator has been used then it has also done the flips, so ignore them.
|
|
if((orient & OVERLAY_TRANSFORM_FLIP_H) && lDest != OV_INVALID
|
|
&& rDest != OV_INVALID && (*rot) == NULL) {
|
|
hwc_rect_t new_cropR;
|
|
new_cropR.left = tmp_cropL.left;
|
|
new_cropR.right = new_cropR.left + (tmp_cropR.right - tmp_cropR.left);
|
|
|
|
hwc_rect_t new_cropL;
|
|
new_cropL.left = new_cropR.right;
|
|
new_cropL.right = tmp_cropR.right;
|
|
|
|
tmp_cropL.left = new_cropL.left;
|
|
tmp_cropL.right = new_cropL.right;
|
|
|
|
tmp_cropR.left = new_cropR.left;
|
|
tmp_cropR.right = new_cropR.right;
|
|
|
|
}
|
|
|
|
//For the mdp, since either we are pre-rotating or MDP does flips
|
|
orient = OVERLAY_TRANSFORM_0;
|
|
transform = 0;
|
|
|
|
//configure left half
|
|
if(lDest != OV_INVALID) {
|
|
PipeArgs pargL(mdpFlagsL, whf, lz, isFg,
|
|
static_cast<eRotFlags>(rotFlags), layer->planeAlpha,
|
|
(ovutils::eBlending) getBlending(layer->blending));
|
|
|
|
if(configMdp(ctx->mOverlay, pargL, orient,
|
|
tmp_cropL, tmp_dstL, metadata, lDest) < 0) {
|
|
ALOGE("%s: commit failed for left half config", __FUNCTION__);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
//configure right half
|
|
if(rDest != OV_INVALID) {
|
|
PipeArgs pargR(mdpFlagsR, whf, rz, isFg,
|
|
static_cast<eRotFlags>(rotFlags),
|
|
layer->planeAlpha,
|
|
(ovutils::eBlending) getBlending(layer->blending));
|
|
if(configMdp(ctx->mOverlay, pargR, orient,
|
|
tmp_cropR, tmp_dstR, metadata, rDest) < 0) {
|
|
ALOGE("%s: commit failed for right half config", __FUNCTION__);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool canUseRotator(hwc_context_t *ctx, int dpy) {
|
|
if(qdutils::MDPVersion::getInstance().is8x26() &&
|
|
ctx->mVirtualDisplay->isConnected() &&
|
|
!ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].isPause) {
|
|
/* 8x26 mdss driver supports multiplexing of DMA pipe
|
|
* in LINE and BLOCK modes for writeback panels.
|
|
*/
|
|
if(dpy == HWC_DISPLAY_PRIMARY)
|
|
return false;
|
|
}
|
|
if(ctx->mMDP.version == qdutils::MDP_V3_0_4)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
int getLeftSplit(hwc_context_t *ctx, const int& dpy) {
|
|
//Default even split for all displays with high res
|
|
int lSplit = ctx->dpyAttr[dpy].xres / 2;
|
|
if(dpy == HWC_DISPLAY_PRIMARY &&
|
|
qdutils::MDPVersion::getInstance().getLeftSplit()) {
|
|
//Override if split published by driver for primary
|
|
lSplit = qdutils::MDPVersion::getInstance().getLeftSplit();
|
|
}
|
|
return lSplit;
|
|
}
|
|
|
|
bool isDisplaySplit(hwc_context_t* ctx, int dpy) {
|
|
if(ctx->dpyAttr[dpy].xres > qdutils::MAX_DISPLAY_DIM) {
|
|
return true;
|
|
}
|
|
//For testing we could split primary via device tree values
|
|
if(dpy == HWC_DISPLAY_PRIMARY &&
|
|
qdutils::MDPVersion::getInstance().getRightSplit()) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void BwcPM::setBwc(hwc_context_t *ctx, const hwc_rect_t& crop,
|
|
const hwc_rect_t& dst, const int& transform,
|
|
ovutils::eMdpFlags& mdpFlags) {
|
|
//Target doesnt support Bwc
|
|
if(!qdutils::MDPVersion::getInstance().supportsBWC()) {
|
|
return;
|
|
}
|
|
//src width > MAX mixer supported dim
|
|
if((crop.right - crop.left) > qdutils::MAX_DISPLAY_DIM) {
|
|
return;
|
|
}
|
|
//Decimation necessary, cannot use BWC. H/W requirement.
|
|
if(qdutils::MDPVersion::getInstance().supportsDecimation()) {
|
|
int src_w = crop.right - crop.left;
|
|
int src_h = crop.bottom - crop.top;
|
|
int dst_w = dst.right - dst.left;
|
|
int dst_h = dst.bottom - dst.top;
|
|
if(transform & HAL_TRANSFORM_ROT_90) {
|
|
swap(src_w, src_h);
|
|
}
|
|
float horDscale = 0.0f;
|
|
float verDscale = 0.0f;
|
|
int horzDeci = 0;
|
|
int vertDeci = 0;
|
|
ovutils::getDecimationFactor(src_w, src_h, dst_w, dst_h, horDscale,
|
|
verDscale);
|
|
//TODO Use log2f once math.h has it
|
|
if((int)horDscale)
|
|
horzDeci = (int)(log(horDscale) / log(2));
|
|
if((int)verDscale)
|
|
vertDeci = (int)(log(verDscale) / log(2));
|
|
if(horzDeci || vertDeci) return;
|
|
}
|
|
//Property
|
|
char value[PROPERTY_VALUE_MAX];
|
|
property_get("debug.disable.bwc", value, "0");
|
|
if(atoi(value)) return;
|
|
|
|
ovutils::setMdpFlags(mdpFlags, ovutils::OV_MDSS_MDP_BWC_EN);
|
|
}
|
|
|
|
void LayerRotMap::add(hwc_layer_1_t* layer, Rotator *rot) {
|
|
if(mCount >= MAX_SESS) return;
|
|
mLayer[mCount] = layer;
|
|
mRot[mCount] = rot;
|
|
mCount++;
|
|
}
|
|
|
|
void LayerRotMap::reset() {
|
|
for (int i = 0; i < MAX_SESS; i++) {
|
|
mLayer[i] = 0;
|
|
mRot[i] = 0;
|
|
}
|
|
mCount = 0;
|
|
}
|
|
|
|
void LayerRotMap::clear() {
|
|
RotMgr::getInstance()->markUnusedTop(mCount);
|
|
reset();
|
|
}
|
|
|
|
void LayerRotMap::setReleaseFd(const int& fence) {
|
|
for(uint32_t i = 0; i < mCount; i++) {
|
|
mRot[i]->setReleaseFd(dup(fence));
|
|
}
|
|
}
|
|
|
|
};//namespace qhwc
|