hwc: Full MDP Composition mode with overlap region removal
MDP bandwidth limitations can be avoided, if the overlapping region
covered by the smallest layer at a higher z-order, can be composed
on a render buffer using Copybit, and then it can be queued to MDP
along with other layers. It helps to improve performace by avoiding
GPU Composition in use cases like Home Screen pan. To enable this
feature, set system prop "persist.hwc.ptor.enable" to "true" or '1'
and enable Copybit HAL compilation.
Change-Id: Ib719d3dd42477837519518cc427f1b95356c28f7
diff --git a/libhwcomposer/hwc.cpp b/libhwcomposer/hwc.cpp
index 902f882..f04bd76 100644
--- a/libhwcomposer/hwc.cpp
+++ b/libhwcomposer/hwc.cpp
@@ -580,8 +580,14 @@
hwc_layer_1_t *fbLayer = &list->hwLayers[last];
int fd = -1; //FenceFD from the Copybit(valid in async mode)
bool copybitDone = false;
- if(ctx->mCopyBit[dpy])
- copybitDone = ctx->mCopyBit[dpy]->draw(ctx, list, dpy, &fd);
+
+ if (ctx->mCopyBit[dpy]) {
+ if (ctx->mMDP.version < qdutils::MDP_V4_0)
+ copybitDone = ctx->mCopyBit[dpy]->draw(ctx, list, dpy, &fd);
+ else
+ fd = ctx->mMDPComp[dpy]->drawOverlap(ctx, list);
+ }
+
if(list->numHwLayers > 1)
hwc_sync(ctx, list, dpy, fd);
diff --git a/libhwcomposer/hwc_copybit.cpp b/libhwcomposer/hwc_copybit.cpp
index 1df8635..f703fa0 100644
--- a/libhwcomposer/hwc_copybit.cpp
+++ b/libhwcomposer/hwc_copybit.cpp
@@ -208,6 +208,46 @@
return -1;
}
+bool CopyBit::prepareOverlap(hwc_context_t *ctx, hwc_display_contents_1_t *list,
+ int overlapIndex) {
+
+ if (ctx->mMDP.version < qdutils::MDP_V4_0) {
+ ALOGE("%s: Invalid request", __FUNCTION__);
+ return false;
+ }
+
+ if (mEngine == NULL || !(validateParams(ctx, list))) {
+ ALOGE("%s: Invalid Params", __FUNCTION__);
+ return false;
+ }
+
+ //Allocate render buffers if they're not allocated
+ hwc_rect_t overlap = list->hwLayers[overlapIndex].displayFrame;
+ int width = overlap.right - overlap.left;
+ int height = overlap.bottom - overlap.top;
+ int alignW, alignH;
+
+ getBufferSizeAndDimensions(width, height, HAL_PIXEL_FORMAT_RGBA_8888,
+ alignW, alignH);
+
+ if ((mAlignedWidth != alignW) || (mAlignedHeight != alignH)) {
+ // Overlap rect has changed, so free render buffers
+ freeRenderBuffers();
+ }
+
+ int ret = allocRenderBuffers(alignW, alignH, HAL_PIXEL_FORMAT_RGBA_8888);
+
+ if (ret < 0) {
+ ALOGE("%s: Render buffer allocation failed", __FUNCTION__);
+ return false;
+ }
+
+ mAlignedWidth = alignW;
+ mAlignedHeight = alignH;
+ mCurRenderBufferIndex = (mCurRenderBufferIndex + 1) % NUM_RENDER_BUFFERS;
+ return true;
+}
+
bool CopyBit::prepare(hwc_context_t *ctx, hwc_display_contents_1_t *list,
int dpy) {
@@ -286,8 +326,8 @@
//Allocate render buffers if they're not allocated
if (ctx->mMDP.version != qdutils::MDP_V3_0_4 &&
(useCopybitForYUV || useCopybitForRGB)) {
- int ret = allocRenderBuffers(mAlignedFBWidth,
- mAlignedFBHeight,
+ int ret = allocRenderBuffers(mAlignedWidth,
+ mAlignedHeight,
HAL_PIXEL_FORMAT_RGBA_8888);
if (ret < 0) {
return false;
@@ -502,6 +542,132 @@
return true;
}
+int CopyBit::drawOverlap(hwc_context_t *ctx, hwc_display_contents_1_t *list,
+ int overlapIndex) {
+ int fd = -1;
+
+ if (ctx->mMDP.version < qdutils::MDP_V4_0) {
+ ALOGE("%s: Invalid request", __FUNCTION__);
+ return fd;
+ }
+
+ private_handle_t *renderBuffer = getCurrentRenderBuffer();
+
+ if (!renderBuffer) {
+ ALOGE("%s: Render buffer layer handle is NULL", __FUNCTION__);
+ return fd;
+ }
+
+ int copybitLayerCount = 0;
+ hwc_rect_t overlap = list->hwLayers[overlapIndex].displayFrame;
+
+ // Draw overlapped content of layers on render buffer
+ for (int i = 0; i <= overlapIndex; i++) {
+
+ int ret = -1;
+ if ((list->hwLayers[i].acquireFenceFd != -1)) {
+ // Wait for acquire fence on the App buffers.
+ ret = sync_wait(list->hwLayers[i].acquireFenceFd, 1000);
+ if (ret < 0) {
+ ALOGE("%s: sync_wait error!! error no = %d err str = %s",
+ __FUNCTION__, errno, strerror(errno));
+ }
+ close(list->hwLayers[i].acquireFenceFd);
+ list->hwLayers[i].acquireFenceFd = -1;
+ }
+
+ hwc_layer_1_t *layer = &list->hwLayers[i];
+ int retVal = drawRectUsingCopybit(ctx, layer, renderBuffer, overlap);
+ copybitLayerCount++;
+
+ if(retVal < 0) {
+ ALOGE("%s: drawRectUsingCopybit failed", __FUNCTION__);
+ copybitLayerCount = 0;
+ }
+ }
+
+ if (copybitLayerCount) {
+ copybit_device_t *copybit = getCopyBitDevice();
+ copybit->flush_get_fence(copybit, &fd);
+ }
+
+ ALOGD_IF(DEBUG_COPYBIT, "%s: done!", __FUNCTION__);
+ return fd;
+}
+
+int CopyBit::drawRectUsingCopybit(hwc_context_t *dev, hwc_layer_1_t *layer,
+ private_handle_t *renderBuffer, hwc_rect_t rect)
+{
+ hwc_context_t* ctx = (hwc_context_t*)(dev);
+ if (!ctx) {
+ ALOGE("%s: null context ", __FUNCTION__);
+ return -1;
+ }
+
+ private_handle_t *hnd = (private_handle_t *)layer->handle;
+ if (!hnd) {
+ ALOGE("%s: invalid handle", __FUNCTION__);
+ return -1;
+ }
+
+ private_handle_t *dstHandle = (private_handle_t *)renderBuffer;
+ if (!dstHandle) {
+ ALOGE("%s: RenderBuffer handle is NULL", __FUNCTION__);
+ return -1;
+ }
+
+ // Set the Copybit Source
+ copybit_image_t src;
+ src.handle = (native_handle_t *)layer->handle;
+ src.w = hnd->width;
+ src.h = hnd->height;
+ src.base = (void *)hnd->base;
+ src.format = hnd->format;
+ src.horiz_padding = 0;
+ src.vert_padding = 0;
+
+ hwc_rect_t dispFrame = layer->displayFrame;
+ hwc_rect_t iRect = getIntersection(dispFrame, rect);
+ hwc_rect_t crop = integerizeSourceCrop(layer->sourceCropf);
+ qhwc::calculate_crop_rects(crop, dispFrame, iRect, layer->transform);
+
+ // Copybit source rect
+ copybit_rect_t srcRect = {crop.left, crop.top, crop.right, crop.bottom};
+
+ // Copybit destination rect
+ copybit_rect_t dstRect = {rect.left, rect.top, rect.right, rect.bottom};
+
+ // Copybit dst
+ copybit_image_t dst;
+ dst.handle = (native_handle_t *)dstHandle;
+ dst.w = ALIGN(dstHandle->width, 32);
+ dst.h = dstHandle->height;
+ dst.base = (void *)dstHandle->base;
+ dst.format = dstHandle->format;
+
+ copybit_device_t *copybit = mEngine;
+
+ // Copybit region
+ hwc_region_t region = layer->visibleRegionScreen;
+ region_iterator copybitRegion(region);
+ int acquireFd = layer->acquireFenceFd;
+
+ copybit->set_parameter(copybit, COPYBIT_FRAMEBUFFER_WIDTH, renderBuffer->width);
+ copybit->set_parameter(copybit, COPYBIT_FRAMEBUFFER_HEIGHT, renderBuffer->height);
+ copybit->set_parameter(copybit, COPYBIT_TRANSFORM, layer->transform);
+ copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, layer->planeAlpha);
+ copybit->set_parameter(copybit, COPYBIT_BLEND_MODE, layer->blending);
+ copybit->set_parameter(copybit, COPYBIT_DITHER,
+ (dst.format == HAL_PIXEL_FORMAT_RGB_565) ? COPYBIT_ENABLE : COPYBIT_DISABLE);
+ copybit->set_sync(copybit, acquireFd);
+ int err = copybit->stretch(copybit, &dst, &src, &dstRect, &srcRect, ©bitRegion);
+
+ if (err < 0)
+ ALOGE("%s: copybit stretch failed",__FUNCTION__);
+
+ return err;
+}
+
int CopyBit::drawLayerUsingCopybit(hwc_context_t *dev, hwc_layer_1_t *layer,
private_handle_t *renderBuffer, bool isFG)
{
@@ -872,6 +1038,18 @@
mRelFd[mCurRenderBufferIndex] = dup(fd);
}
+void CopyBit::setReleaseFdSync(int fd) {
+ if (mRelFd[mCurRenderBufferIndex] >=0) {
+ int ret = -1;
+ ret = sync_wait(mRelFd[mCurRenderBufferIndex], 1000);
+ if (ret < 0)
+ ALOGE("%s: sync_wait error! errno = %d, err str = %s",
+ __FUNCTION__, errno, strerror(errno));
+ close(mRelFd[mCurRenderBufferIndex]);
+ }
+ mRelFd[mCurRenderBufferIndex] = dup(fd);
+}
+
struct copybit_device_t* CopyBit::getCopyBitDevice() {
return mEngine;
}
@@ -882,8 +1060,8 @@
getBufferSizeAndDimensions(ctx->dpyAttr[dpy].xres,
ctx->dpyAttr[dpy].yres,
HAL_PIXEL_FORMAT_RGBA_8888,
- mAlignedFBWidth,
- mAlignedFBHeight);
+ mAlignedWidth,
+ mAlignedHeight);
hw_module_t const *module;
for (int i = 0; i < NUM_RENDER_BUFFERS; i++) {
diff --git a/libhwcomposer/hwc_copybit.h b/libhwcomposer/hwc_copybit.h
index 3e9dff0..3d3d302 100644
--- a/libhwcomposer/hwc_copybit.h
+++ b/libhwcomposer/hwc_copybit.h
@@ -49,6 +49,14 @@
void setReleaseFd(int fd);
+ void setReleaseFdSync(int fd);
+
+ bool prepareOverlap(hwc_context_t *ctx, hwc_display_contents_1_t *list,
+ int overlapIndex);
+
+ int drawOverlap(hwc_context_t *ctx, hwc_display_contents_1_t *list,
+ int overlapIndex);
+
private:
/* cached data */
struct LayerCache {
@@ -79,6 +87,8 @@
// Helper functions for copybit composition
int drawLayerUsingCopybit(hwc_context_t *dev, hwc_layer_1_t *layer,
private_handle_t *renderBuffer, bool isFG);
+ int drawRectUsingCopybit(hwc_context_t *dev, hwc_layer_1_t *layer,
+ private_handle_t *renderBuffer, hwc_rect_t rect);
int fillColorUsingCopybit(hwc_layer_1_t *layer,
private_handle_t *renderBuffer);
bool canUseCopybitForYUV (hwc_context_t *ctx);
@@ -114,8 +124,8 @@
//Dynamic composition threshold for deciding copybit usage.
double mDynThreshold;
bool mSwapRectEnable;
- int mAlignedFBWidth;
- int mAlignedFBHeight;
+ int mAlignedWidth;
+ int mAlignedHeight;
int mDirtyLayerIndex;
LayerCache mLayerCache;
FbCache mFbCache;
diff --git a/libhwcomposer/hwc_mdpcomp.cpp b/libhwcomposer/hwc_mdpcomp.cpp
index 06e0b12..204d275 100644
--- a/libhwcomposer/hwc_mdpcomp.cpp
+++ b/libhwcomposer/hwc_mdpcomp.cpp
@@ -26,6 +26,7 @@
#include "hwc_fbupdate.h"
#include "hwc_ad.h"
#include <overlayRotator.h>
+#include "hwc_copybit.h"
using namespace overlay;
using namespace qdutils;
@@ -149,6 +150,14 @@
(!strncasecmp(property,"true", PROPERTY_VALUE_MAX )))) {
sEnable4k2kYUVSplit = true;
}
+
+ if ((property_get("persist.hwc.ptor.enable", property, NULL) > 0) &&
+ ((!strncasecmp(property, "true", PROPERTY_VALUE_MAX )) ||
+ (!strncmp(property, "1", PROPERTY_VALUE_MAX )))) {
+ ctx->mCopyBit[HWC_DISPLAY_PRIMARY] = new CopyBit(ctx,
+ HWC_DISPLAY_PRIMARY);
+ }
+
return true;
}
@@ -703,6 +712,8 @@
bool ret = false;
if(fullMDPComp(ctx, list)) {
ret = true;
+ } else if(fullMDPCompWithPTOR(ctx, list)) {
+ ret = true;
} else if(partialMDPComp(ctx, list)) {
ret = true;
}
@@ -752,6 +763,136 @@
return true;
}
+/* Full MDP Composition with Peripheral Tiny Overlap Removal.
+ * MDP bandwidth limitations can be avoided, if the overlap region
+ * covered by the smallest layer at a higher z-order, gets composed
+ * by Copybit on a render buffer, which can be queued to MDP.
+ */
+bool MDPComp::fullMDPCompWithPTOR(hwc_context_t *ctx,
+ hwc_display_contents_1_t* list) {
+
+ const int numAppLayers = ctx->listStats[mDpy].numAppLayers;
+ const int stagesForMDP = min(sMaxPipesPerMixer,
+ ctx->mOverlay->availablePipes(mDpy, Overlay::MIXER_DEFAULT));
+
+ // Hard checks where we cannot use this mode
+ if (mDpy || !ctx->mCopyBit[mDpy] || isDisplaySplit(ctx, mDpy)) {
+ ALOGD_IF(isDebug(), "%s: Feature not supported!", __FUNCTION__);
+ return false;
+ }
+
+ // Frame level checks
+ if ((numAppLayers > stagesForMDP) || isSkipPresent(ctx, mDpy) ||
+ isYuvPresent(ctx, mDpy) || mCurrentFrame.dropCount ||
+ isSecurePresent(ctx, mDpy)) {
+ ALOGD_IF(isDebug(), "%s: Frame not supported!", __FUNCTION__);
+ return false;
+ }
+
+ // Find overlap index
+ int overlapIdx = numAppLayers - 1;
+ uint32_t layerPixelCount, minPixelCount = 0;
+ for (int i = numAppLayers - 1; i >= 0; i--) {
+ hwc_layer_1_t* layer = &list->hwLayers[i];
+ hwc_rect_t crop = integerizeSourceCrop(layer->sourceCropf);
+ layerPixelCount = (crop.right - crop.left) * (crop.bottom - crop.top);
+ if (!minPixelCount || (layerPixelCount < minPixelCount)) {
+ minPixelCount = layerPixelCount;
+ overlapIdx = i;
+ }
+ }
+
+ // No overlap
+ if (!overlapIdx)
+ return false;
+
+ /* We cannot use this composition mode, if:
+ 1. A below layer needs scaling.
+ 2. Overlap is not peripheral to display.
+ 3. Overlap or a below layer has 90 degree transform.
+ 4. Intersection of Overlap layer with a below layer is not valid.
+ 5. Overlap area > (1/3 * FrameBuffer) area, based on Perf inputs.
+ */
+
+ hwc_rect_t overlap = list->hwLayers[overlapIdx].displayFrame;
+ if (!isPeripheral(overlap, ctx->mViewFrame[mDpy]))
+ return false;
+
+ if ((3 * (overlap.right - overlap.left) * (overlap.bottom - overlap.top)) >
+ ((int)ctx->dpyAttr[mDpy].xres * (int)ctx->dpyAttr[mDpy].yres))
+ return false;
+
+ for (int i = overlapIdx; i >= 0; i--) {
+ hwc_layer_1_t* layer = &list->hwLayers[i];
+ hwc_rect_t dispFrame = layer->displayFrame;
+
+ if (has90Transform(layer))
+ return false;
+
+ if (i < overlapIdx) {
+ if (needsScaling(layer) ||
+ !isValidRect(getIntersection(dispFrame, overlap)))
+ return false;
+ }
+ }
+
+ mOverlapIndex = overlapIdx;
+ if (!ctx->mCopyBit[mDpy]->prepareOverlap(ctx, list, overlapIdx)) {
+ ALOGD_IF(isDebug(), "%s: Overlap prepare failed!",__FUNCTION__);
+ mOverlapIndex = -1;
+ return false;
+ }
+
+ hwc_rect_t sourceCrop[overlapIdx];
+ hwc_rect_t displayFrame[overlapIdx];
+
+ // Remove overlap from crop & displayFrame of below layers
+ for (int i = 0; i < overlapIdx; i++) {
+ hwc_layer_1_t* layer = &list->hwLayers[i];
+ displayFrame[i] = layer->displayFrame;
+ sourceCrop[i] = integerizeSourceCrop(layer->sourceCropf);
+
+ // Update layer attributes
+ hwc_rect_t srcCrop = integerizeSourceCrop(layer->sourceCropf);
+ hwc_rect_t destRect = deductRect(layer->displayFrame, overlap);
+ qhwc::calculate_crop_rects(srcCrop, layer->displayFrame, destRect,
+ layer->transform);
+
+ layer->sourceCropf.left = (float)srcCrop.left;
+ layer->sourceCropf.top = (float)srcCrop.top;
+ layer->sourceCropf.right = (float)srcCrop.right;
+ layer->sourceCropf.bottom = (float)srcCrop.bottom;
+ }
+
+ mCurrentFrame.mdpCount = numAppLayers;
+ mCurrentFrame.fbCount = 0;
+ mCurrentFrame.fbZ = -1;
+
+ for (int j = 0; j < numAppLayers; j++)
+ mCurrentFrame.isFBComposed[j] = false;
+
+ bool result = postHeuristicsHandling(ctx, list);
+
+ // Restore layer attributes
+ for (int i = 0; i < overlapIdx; i++) {
+ hwc_layer_1_t* layer = &list->hwLayers[i];
+ layer->displayFrame = displayFrame[i];
+ layer->sourceCropf.left = (float)sourceCrop[i].left;
+ layer->sourceCropf.top = (float)sourceCrop[i].top;
+ layer->sourceCropf.right = (float)sourceCrop[i].right;
+ layer->sourceCropf.bottom = (float)sourceCrop[i].bottom;
+ }
+
+ if (!result) {
+ mOverlapIndex = -1;
+ reset(ctx);
+ }
+
+ ALOGD_IF(isDebug(), "%s: Postheuristics %s!, Overlap index = %d",
+ __FUNCTION__, (result ? "successful" : "failed"), mOverlapIndex);
+ return result;
+}
+
bool MDPComp::partialMDPComp(hwc_context_t *ctx, hwc_display_contents_1_t* list)
{
if(!sEnableMixedMode) {
@@ -1390,6 +1531,7 @@
sSimulationFlags, sSimulationFlags);
}
}
+ mOverlapIndex = -1;
//Do not cache the information for next draw cycle.
if(numLayers > MAX_NUM_APP_LAYERS or (!numLayers)) {
@@ -1484,6 +1626,18 @@
}
return bRet;
}
+
+int MDPComp::drawOverlap(hwc_context_t *ctx, hwc_display_contents_1_t* list) {
+ int fd = -1;
+ if (mOverlapIndex != -1) {
+ fd = ctx->mCopyBit[mDpy]->drawOverlap(ctx, list, mOverlapIndex);
+ if (fd < 0) {
+ ALOGD_IF(isDebug(),"%s: failed", __FUNCTION__);
+ mOverlapIndex = -1;
+ }
+ }
+ return fd;
+}
//=============MDPCompNonSplit==================================================
void MDPCompNonSplit::adjustForSourceSplit(hwc_context_t *ctx,
@@ -1674,6 +1828,10 @@
continue;
}
+ if (!mDpy && (i == mOverlapIndex)) {
+ hnd = ctx->mCopyBit[mDpy]->getCurrentRenderBuffer();
+ }
+
ALOGD_IF(isDebug(),"%s: MDP Comp: Drawing layer: %p hnd: %p \
using pipe: %d", __FUNCTION__, layer,
hnd, dest );
@@ -1919,6 +2077,10 @@
ovutils::eDest indexL = pipe_info.lIndex;
ovutils::eDest indexR = pipe_info.rIndex;
+ if (!mDpy && (i == mOverlapIndex)) {
+ hnd = ctx->mCopyBit[mDpy]->getCurrentRenderBuffer();
+ }
+
int fd = hnd->fd;
int offset = (uint32_t)hnd->offset;
diff --git a/libhwcomposer/hwc_mdpcomp.h b/libhwcomposer/hwc_mdpcomp.h
index 4d9d232..66d0b0b 100644
--- a/libhwcomposer/hwc_mdpcomp.h
+++ b/libhwcomposer/hwc_mdpcomp.h
@@ -46,6 +46,8 @@
/* dumpsys */
void dump(android::String8& buf);
bool isGLESOnlyComp() { return (mCurrentFrame.mdpCount == 0); }
+ bool isPTORActive() { return (mOverlapIndex != -1); }
+ int drawOverlap(hwc_context_t *ctx, hwc_display_contents_1_t* list);
static MDPComp* getObject(hwc_context_t *ctx, const int& dpy);
/* Handler to invoke frame redraw on Idle Timer expiry */
static void timeout_handler(void *udata);
@@ -170,6 +172,8 @@
bool tryFullFrame(hwc_context_t *ctx, hwc_display_contents_1_t* list);
/* checks if full MDP comp can be done */
bool fullMDPComp(hwc_context_t *ctx, hwc_display_contents_1_t* list);
+ /* Full MDP Composition with Peripheral Tiny Overlap Removal */
+ bool fullMDPCompWithPTOR(hwc_context_t *ctx,hwc_display_contents_1_t* list);
/* check if we can use layer cache to do at least partial MDP comp */
bool partialMDPComp(hwc_context_t *ctx, hwc_display_contents_1_t* list);
/* Partial MDP comp that uses caching to save power as primary goal */
@@ -244,6 +248,8 @@
struct LayerCache mCachedFrame;
//Enable 4kx2k yuv layer split
static bool sEnable4k2kYUVSplit;
+ /* Overlap layer index */
+ int mOverlapIndex;
bool allocSplitVGPipesfor4k2k(hwc_context_t *ctx, int index);
};
diff --git a/libhwcomposer/hwc_utils.cpp b/libhwcomposer/hwc_utils.cpp
index 18ae28f..09600e2 100644
--- a/libhwcomposer/hwc_utils.cpp
+++ b/libhwcomposer/hwc_utils.cpp
@@ -1358,6 +1358,11 @@
}
}
+ if ((fd >= 0) && !dpy && ctx->mMDPComp[dpy]->isPTORActive()) {
+ // Acquire c2d fence of Overlap render buffer
+ acquireFd[count++] = fd;
+ }
+
data.acq_fen_fd_cnt = count;
fbFd = ctx->dpyAttr[dpy].fd;
@@ -1418,8 +1423,12 @@
fd = -1;
}
- if (ctx->mCopyBit[dpy])
- ctx->mCopyBit[dpy]->setReleaseFd(releaseFd);
+ if (!dpy && ctx->mCopyBit[dpy]) {
+ if (ctx->mMDPComp[dpy]->isPTORActive())
+ ctx->mCopyBit[dpy]->setReleaseFdSync(releaseFd);
+ else
+ ctx->mCopyBit[dpy]->setReleaseFd(releaseFd);
+ }
//Signals when MDP finishes reading rotator buffers.
ctx->mLayerRotMap[dpy]->setReleaseFd(releaseFd);
@@ -2143,6 +2152,20 @@
#endif
}
+bool isPeripheral(const hwc_rect_t& rect1, const hwc_rect_t& rect2) {
+ // To be peripheral, 3 boundaries should match.
+ uint8_t eqBounds = 0;
+ if (rect1.left == rect2.left)
+ eqBounds++;
+ if (rect1.top == rect2.top)
+ eqBounds++;
+ if (rect1.right == rect2.right)
+ eqBounds++;
+ if (rect1.bottom == rect2.bottom)
+ eqBounds++;
+ return (eqBounds == 3);
+}
+
void BwcPM::setBwc(const hwc_rect_t& crop,
const hwc_rect_t& dst, const int& transform,
ovutils::eMdpFlags& mdpFlags) {
diff --git a/libhwcomposer/hwc_utils.h b/libhwcomposer/hwc_utils.h
index d7fa73d..c7b7ded 100644
--- a/libhwcomposer/hwc_utils.h
+++ b/libhwcomposer/hwc_utils.h
@@ -376,6 +376,9 @@
// due to idle fallback or MDP composition.
void setGPUHint(hwc_context_t* ctx, hwc_display_contents_1_t* list);
+// Returns true if rect1 is peripheral to rect2, false otherwise.
+bool isPeripheral(const hwc_rect_t& rect1, const hwc_rect_t& rect2);
+
// Inline utility functions
static inline bool isSkipLayer(const hwc_layer_1_t* l) {
return (UNLIKELY(l && (l->flags & HWC_SKIP_LAYER)));