hwc/overlay: Implement rotator caching

* A non-updating layer requiring rotation, can make use of
the older rotator buffer written when the layer got first
updated, rather than invoking a new rotator cycle.

* A rotator play is avoided in cases where incoming layer
buffer fd, offset, whf, src-rect, dst-rect, etc are similar
to that of the previous input layer to the rotator.

* For ex: In a usecase where video layer updates happen at
30fps and all other asynchrous UI updates happen at 60fps,
instead of the traditional 60 calls of rotator play per sec,
we now do only 30 thereby saving rotator bandwidth.

* Property "debug.rotcache.disable" can be used to disable
this feature.

Change-Id: I1d1c352c63007b7e0b4fee40882086ccd2f5a4aa
diff --git a/libhwcomposer/hwc_utils.cpp b/libhwcomposer/hwc_utils.cpp
index f3243fc..944f152 100644
--- a/libhwcomposer/hwc_utils.cpp
+++ b/libhwcomposer/hwc_utils.cpp
@@ -1324,7 +1324,9 @@
             rotData.acq_fen_fd_cnt = 1; //1 ioctl call per rot session
         }
         int ret = 0;
-        ret = ioctl(rotFd, MSMFB_BUFFER_SYNC, &rotData);
+        if(not ctx->mLayerRotMap[dpy]->isRotCached(i))
+            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));
@@ -2260,9 +2262,27 @@
     reset();
 }
 
+bool LayerRotMap::isRotCached(uint32_t index) const {
+    overlay::Rotator* rot = getRot(index);
+    hwc_layer_1_t* layer =  getLayer(index);
+
+    if(rot and layer and layer->handle) {
+        private_handle_t *hnd = (private_handle_t *)(layer->handle);
+        return (rot->isRotCached(hnd->fd,(uint32_t)(hnd->offset)));
+    }
+    return false;
+}
+
 void LayerRotMap::setReleaseFd(const int& fence) {
     for(uint32_t i = 0; i < mCount; i++) {
-        mRot[i]->setReleaseFd(dup(fence));
+        if(mRot[i] and mLayer[i] and mLayer[i]->handle) {
+            /* Ensure that none of the above (Rotator-instance,
+             * layer and layer-handle) are NULL*/
+            if(isRotCached(i))
+                mRot[i]->setPrevBufReleaseFd(dup(fence));
+            else
+                mRot[i]->setCurrBufReleaseFd(dup(fence));
+        }
     }
 }
 
diff --git a/libhwcomposer/hwc_utils.h b/libhwcomposer/hwc_utils.h
index ad71919..3648ba7 100644
--- a/libhwcomposer/hwc_utils.h
+++ b/libhwcomposer/hwc_utils.h
@@ -201,6 +201,7 @@
     uint32_t getCount() const;
     hwc_layer_1_t* getLayer(uint32_t index) const;
     overlay::Rotator* getRot(uint32_t index) const;
+    bool isRotCached(uint32_t index) const;
     void setReleaseFd(const int& fence);
 private:
     hwc_layer_1_t* mLayer[overlay::RotMgr::MAX_ROT_SESS];
diff --git a/liboverlay/overlayMdpRot.cpp b/liboverlay/overlayMdpRot.cpp
index 6105736..d322897 100755
--- a/liboverlay/overlayMdpRot.cpp
+++ b/liboverlay/overlayMdpRot.cpp
@@ -37,10 +37,18 @@
 
 void MdpRot::setRotations(uint32_t r) { mRotImgInfo.rotations = (uint8_t)r; }
 
+int MdpRot::getSrcMemId() const {
+    return mRotDataInfo.src.memory_id;
+}
+
 int MdpRot::getDstMemId() const {
     return mRotDataInfo.dst.memory_id;
 }
 
+uint32_t MdpRot::getSrcOffset() const {
+    return mRotDataInfo.src.offset;
+}
+
 uint32_t MdpRot::getDstOffset() const {
     return mRotDataInfo.dst.offset;
 }
@@ -147,7 +155,6 @@
             mRotImgInfo.enable = 0;
             return false;
         }
-        save();
         mRotDataInfo.session_id = mRotImgInfo.session_id;
     }
     return true;
@@ -238,7 +245,10 @@
 }
 
 bool MdpRot::queueBuffer(int fd, uint32_t offset) {
-    if(enabled()) {
+    if(enabled() and (not isRotCached(fd,offset))) {
+        int prev_fd = getSrcMemId();
+        uint32_t prev_offset = getSrcOffset();
+
         mRotDataInfo.src.memory_id = fd;
         mRotDataInfo.src.offset = offset;
 
@@ -249,14 +259,17 @@
 
         mRotDataInfo.dst.offset =
                 mMem.mRotOffset[mMem.mCurrIndex];
-        mMem.mCurrIndex =
-                (mMem.mCurrIndex + 1) % mMem.mem.numBufs();
 
         if(!overlay::mdp_wrapper::rotate(mFd.getFD(), mRotDataInfo)) {
             ALOGE("MdpRot failed rotate");
             dump();
+            mRotDataInfo.src.memory_id = prev_fd;
+            mRotDataInfo.src.offset = prev_offset;
             return false;
         }
+        save();
+        mMem.mCurrIndex =
+                (mMem.mCurrIndex + 1) % mMem.mem.numBufs();
     }
     return true;
 }
diff --git a/liboverlay/overlayMdssRot.cpp b/liboverlay/overlayMdssRot.cpp
index 8e55362..87e134a 100644
--- a/liboverlay/overlayMdssRot.cpp
+++ b/liboverlay/overlayMdssRot.cpp
@@ -51,10 +51,18 @@
 
 void MdssRot::setRotations(uint32_t flags) { mRotInfo.flags |= flags; }
 
+int MdssRot::getSrcMemId() const {
+    return mRotData.data.memory_id;
+}
+
 int MdssRot::getDstMemId() const {
     return mRotData.dst_data.memory_id;
 }
 
+uint32_t MdssRot::getSrcOffset() const {
+    return mRotData.data.offset;
+}
+
 uint32_t MdssRot::getDstOffset() const {
     return mRotData.dst_data.offset;
 }
@@ -81,6 +89,19 @@
 
 uint32_t MdssRot::getSessId() const { return mRotInfo.id; }
 
+void MdssRot::save() {
+    mLSRotInfo = mRotInfo;
+}
+
+bool MdssRot::rotConfChanged() const {
+    // 0 means same
+    if(0 == ::memcmp(&mRotInfo, &mLSRotInfo,
+                     sizeof (mdp_overlay))) {
+        return false;
+    }
+    return true;
+}
+
 bool MdssRot::init() {
     if(!utils::openDev(mFd, 0, Res::fbPath, O_RDWR)) {
         ALOGE("MdssRot failed to init fb0");
@@ -164,7 +185,10 @@
 }
 
 bool MdssRot::queueBuffer(int fd, uint32_t offset) {
-    if(enabled()) {
+    if(enabled() and (not isRotCached(fd,offset))) {
+        int prev_fd = getSrcMemId();
+        uint32_t prev_offset = getSrcOffset();
+
         mRotData.data.memory_id = fd;
         mRotData.data.offset = offset;
 
@@ -175,14 +199,17 @@
 
         mRotData.dst_data.offset =
                 mMem.mRotOffset[mMem.mCurrIndex];
-        mMem.mCurrIndex =
-                (mMem.mCurrIndex + 1) % mMem.mem.numBufs();
 
         if(!overlay::mdp_wrapper::play(mFd.getFD(), mRotData)) {
             ALOGE("MdssRot play failed!");
             dump();
+            mRotData.data.memory_id = prev_fd;
+            mRotData.data.offset = prev_offset;
             return false;
         }
+        save();
+        mMem.mCurrIndex =
+                (mMem.mCurrIndex + 1) % mMem.mem.numBufs();
     }
     return true;
 }
@@ -260,6 +287,7 @@
 
 void MdssRot::reset() {
     ovutils::memset0(mRotInfo);
+    ovutils::memset0(mLSRotInfo);
     ovutils::memset0(mRotData);
     mRotData.data.memory_id = -1;
     mRotInfo.id = MSMFB_NEW_REQUEST;
diff --git a/liboverlay/overlayRotator.cpp b/liboverlay/overlayRotator.cpp
index 06e9074..b55f06a 100644
--- a/liboverlay/overlayRotator.cpp
+++ b/liboverlay/overlayRotator.cpp
@@ -29,6 +29,17 @@
 
 //============Rotator=========================
 
+Rotator::Rotator() {
+    char property[PROPERTY_VALUE_MAX];
+    mRotCacheDisabled = false;
+    if((property_get("debug.rotcache.disable", property, NULL) > 0) &&
+       (!strncmp(property, "1", PROPERTY_VALUE_MAX ) ||
+        (!strncasecmp(property,"true", PROPERTY_VALUE_MAX )))) {
+        /* Used in debugging to turnoff rotator caching */
+        mRotCacheDisabled = true;
+    }
+}
+
 Rotator::~Rotator() {}
 
 Rotator* Rotator::getRotator() {
@@ -70,6 +81,20 @@
     return TYPE_MDP;
 }
 
+bool Rotator::isRotCached(int fd, uint32_t offset) const {
+    if(mRotCacheDisabled or rotConfChanged() or rotDataChanged(fd,offset))
+        return false;
+    return true;
+}
+
+bool Rotator::rotDataChanged(int fd, uint32_t offset) const {
+    /* fd and offset are the attributes of the current rotator input buffer.
+     * At this instance, getSrcMemId() and getSrcOffset() return the
+     * attributes of the previous rotator input buffer */
+    if( (fd == getSrcMemId()) and (offset == getSrcOffset()) )
+        return false;
+    return true;
+}
 
 //============RotMem=========================
 
@@ -98,7 +123,7 @@
     }
 }
 
-void RotMem::setReleaseFd(const int& fence) {
+void RotMem::setCurrBufReleaseFd(const int& fence) {
     int ret = 0;
 
     if(mRelFence[mCurrIndex] >= 0) {
@@ -116,6 +141,20 @@
     mRelFence[mCurrIndex] = fence;
 }
 
+void RotMem::setPrevBufReleaseFd(const int& fence) {
+    uint32_t numRotBufs = mem.numBufs();
+    uint32_t prevIndex = (mCurrIndex + numRotBufs - 1) % (numRotBufs);
+
+    if(mRelFence[prevIndex] >= 0) {
+        /* No need of any wait as nothing will be written into this
+         * buffer by the rotator (this func is called when rotator is
+         * in cache mode) */
+        ::close(mRelFence[prevIndex]);
+    }
+
+    mRelFence[prevIndex] = fence;
+}
+
 //============RotMgr=========================
 RotMgr * RotMgr::sRotMgr = NULL;
 
diff --git a/liboverlay/overlayRotator.h b/liboverlay/overlayRotator.h
index 3f09a23..e045b44 100644
--- a/liboverlay/overlayRotator.h
+++ b/liboverlay/overlayRotator.h
@@ -52,7 +52,8 @@
     bool close();
     bool valid() { return mem.valid(); }
     uint32_t size() const { return mem.bufSz(); }
-    void setReleaseFd(const int& fence);
+    void setCurrBufReleaseFd(const int& fence);
+    void setPrevBufReleaseFd(const int& fence);
 
     // rotator data info dst offset
     uint32_t mRotOffset[ROT_NUM_BUFS];
@@ -72,9 +73,20 @@
     virtual void setFlags(const utils::eMdpFlags& flags) = 0;
     virtual void setTransform(const utils::eTransform& rot) = 0;
     virtual bool commit() = 0;
+    /* return true if the current rotator state is cached */
+    virtual bool isRotCached(int fd, uint32_t offset) const;
+    /* return true if current rotator config is same as the last round*/
+    virtual bool rotConfChanged() const = 0;
+    /* return true if the current rotator input buffer fd and offset
+     * are same as the last round */
+    virtual bool rotDataChanged(int fd, uint32_t offset) const;
     virtual void setDownscale(int ds) = 0;
+    /* returns the src buffer of the rotator for the previous/current round,
+     * depending on when it is called(before/after the queuebuffer)*/
+    virtual int getSrcMemId() const = 0;
     //Mem id and offset should be retrieved only after rotator kickoff
     virtual int getDstMemId() const = 0;
+    virtual uint32_t getSrcOffset() const = 0;
     virtual uint32_t getDstOffset() const = 0;
     //Destination width, height, format, position should be retrieved only after
     //rotator configuration is committed via commit API
@@ -85,7 +97,12 @@
     virtual bool queueBuffer(int fd, uint32_t offset) = 0;
     virtual void dump() const = 0;
     virtual void getDump(char *buf, size_t len) const = 0;
-    void setReleaseFd(const int& fence) { mMem.setReleaseFd(fence); }
+    inline void setCurrBufReleaseFd(const int& fence) {
+        mMem.setCurrBufReleaseFd(fence);
+    }
+    inline void setPrevBufReleaseFd(const int& fence) {
+        mMem.setPrevBufReleaseFd(fence);
+    }
     static Rotator *getRotator();
     /* Returns downscale by successfully applying constraints
      * Returns 0 if target doesnt support rotator downscaling
@@ -98,10 +115,11 @@
 protected:
     /* Rotator memory manager */
     RotMem mMem;
-    explicit Rotator() {}
+    Rotator();
     static uint32_t calcOutputBufSize(const utils::Whf& destWhf);
 
 private:
+    bool mRotCacheDisabled;
     /*Returns rotator h/w type */
     static int getRotatorHwType();
     friend class RotMgr;
@@ -119,8 +137,11 @@
     virtual void setFlags(const utils::eMdpFlags& flags);
     virtual void setTransform(const utils::eTransform& rot);
     virtual bool commit();
+    virtual bool rotConfChanged() const;
     virtual void setDownscale(int ds);
+    virtual int getSrcMemId() const;
     virtual int getDstMemId() const;
+    virtual uint32_t getSrcOffset() const;
     virtual uint32_t getDstOffset() const;
     virtual uint32_t getDstFormat() const;
     virtual utils::Whf getDstWhf() const;
@@ -143,9 +164,6 @@
     void doTransform();
     /* reset underlying data, basically memset 0 */
     void reset();
-    /* return true if current rotator config is different
-     * than last known config */
-    bool rotConfChanged() const;
     /* save mRotImgInfo to be last known good config*/
     void save();
     /* Calculates the rotator's o/p buffer size post the transform calcs and
@@ -191,8 +209,11 @@
     virtual void setFlags(const utils::eMdpFlags& flags);
     virtual void setTransform(const utils::eTransform& rot);
     virtual bool commit();
+    virtual bool rotConfChanged() const;
     virtual void setDownscale(int ds);
+    virtual int getSrcMemId() const;
     virtual int getDstMemId() const;
+    virtual uint32_t getSrcOffset() const;
     virtual uint32_t getDstOffset() const;
     virtual uint32_t getDstFormat() const;
     virtual utils::Whf getDstWhf() const;
@@ -215,6 +236,8 @@
     void doTransform();
     /* reset underlying data, basically memset 0 */
     void reset();
+    /* save mRotInfo to be last known good config*/
+    void save();
     /* Calculates the rotator's o/p buffer size post the transform calcs and
      * knowing the o/p format depending on whether fastYuv is enabled or not */
     uint32_t calcOutputBufSize();
@@ -241,7 +264,9 @@
             const uint32_t& downscale);
 
     /* MdssRot info structure */
-    mdp_overlay   mRotInfo;
+    mdp_overlay mRotInfo;
+    /* Last saved MdssRot info structure*/
+    mdp_overlay mLSRotInfo;
     /* MdssRot data structure */
     msmfb_overlay_data mRotData;
     /* Orientation */