Merge "hwc: Full MDP Composition mode with overlap region removal"
diff --git a/libhwcomposer/hwc.cpp b/libhwcomposer/hwc.cpp
index dd56ba3..3d88b3b 100644
--- a/libhwcomposer/hwc.cpp
+++ b/libhwcomposer/hwc.cpp
@@ -577,8 +577,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 4520230..8b64193 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;
@@ -164,6 +165,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;
}
@@ -718,6 +727,8 @@
bool ret = false;
if(fullMDPComp(ctx, list)) {
ret = true;
+ } else if(fullMDPCompWithPTOR(ctx, list)) {
+ ret = true;
} else if(partialMDPComp(ctx, list)) {
ret = true;
}
@@ -767,6 +778,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) {
@@ -1405,6 +1546,7 @@
sSimulationFlags, sSimulationFlags);
}
}
+ mOverlapIndex = -1;
//Do not cache the information for next draw cycle.
if(numLayers > MAX_NUM_APP_LAYERS or (!numLayers)) {
@@ -1499,6 +1641,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,
@@ -1689,6 +1843,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 );
@@ -1934,6 +2092,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 38bf919..520c176 100644
--- a/libhwcomposer/hwc_mdpcomp.h
+++ b/libhwcomposer/hwc_mdpcomp.h
@@ -46,6 +46,8 @@
/* dumpsys */
void dump(android::String8& buf, hwc_context_t *ctx);
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)));