libgui: Pass surface damage through BufferQueue

This change adds support for passing surface damage all of the way
down from the EGL interface through the consumer side of the
BufferQueue. Depends on system/core change
Ie645e6a52b37b5c1b3be19481e8348570d1aa62c

Bug: 11239309
Change-Id: I4457ea826e9ade4ec187f973851d855b7b93a31b
diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp
index 312fb3b..239da20 100644
--- a/libs/gui/BufferItem.cpp
+++ b/libs/gui/BufferItem.cpp
@@ -64,6 +64,8 @@
         c += mFence->getFlattenedSize();
         FlattenableUtils::align<4>(c);
     }
+    c += mSurfaceDamage.getFlattenedSize();
+    FlattenableUtils::align<4>(c);
     return sizeof(int32_t) + c + getPodSize();
 }
 
@@ -105,6 +107,9 @@
         size -= FlattenableUtils::align<4>(buffer);
         flags |= 2;
     }
+    status_t err = mSurfaceDamage.flatten(buffer, size);
+    if (err) return err;
+    size -= FlattenableUtils::align<4>(buffer);
 
     // check we have enough space (in case flattening the fence/graphicbuffer lied to us)
     if (size < getPodSize()) {
@@ -148,6 +153,9 @@
         if (err) return err;
         size -= FlattenableUtils::align<4>(buffer);
     }
+    status_t err = mSurfaceDamage.unflatten(buffer, size);
+    if (err) return err;
+    size -= FlattenableUtils::align<4>(buffer);
 
     // check we have enough space
     if (size < getPodSize()) {
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 4c22ba3..6452cdd 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -525,6 +525,7 @@
     sp<Fence> fence;
     input.deflate(&timestamp, &isAutoTimestamp, &dataSpace, &crop, &scalingMode,
             &transform, &async, &fence, &stickyTransform);
+    Region surfaceDamage = input.getSurfaceDamage();
 
     if (fence == NULL) {
         BQ_LOGE("queueBuffer: fence is NULL");
@@ -621,6 +622,7 @@
         item.mSlot = slot;
         item.mFence = fence;
         item.mIsDroppable = mCore->mDequeueBufferCannotBlock || async;
+        item.mSurfaceDamage = surfaceDamage;
 
         mStickyTransform = stickyTransform;
 
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index a3e6fb2..b7982a9 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -446,7 +446,8 @@
          + sizeof(transform)
          + sizeof(stickyTransform)
          + sizeof(async)
-         + fence->getFlattenedSize();
+         + fence->getFlattenedSize()
+         + surfaceDamage.getFlattenedSize();
 }
 
 size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const {
@@ -467,7 +468,11 @@
     FlattenableUtils::write(buffer, size, transform);
     FlattenableUtils::write(buffer, size, stickyTransform);
     FlattenableUtils::write(buffer, size, async);
-    return fence->flatten(buffer, size, fds, count);
+    status_t result = fence->flatten(buffer, size, fds, count);
+    if (result != NO_ERROR) {
+        return result;
+    }
+    return surfaceDamage.flatten(buffer, size);
 }
 
 status_t IGraphicBufferProducer::QueueBufferInput::unflatten(
@@ -497,7 +502,11 @@
     FlattenableUtils::read(buffer, size, async);
 
     fence = new Fence();
-    return fence->unflatten(buffer, size, fds, count);
+    status_t result = fence->unflatten(buffer, size, fds, count);
+    if (result != NO_ERROR) {
+        return result;
+    }
+    return surfaceDamage.unflatten(buffer, size);
 }
 
 }; // namespace android
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index b80890f..245f7a3 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -27,6 +27,7 @@
 #include <utils/NativeHandle.h>
 
 #include <ui/Fence.h>
+#include <ui/Region.h>
 
 #include <gui/IProducerListener.h>
 #include <gui/ISurfaceComposer.h>
@@ -320,6 +321,25 @@
     IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp,
             mDataSpace, crop, mScalingMode, mTransform ^ mStickyTransform,
             mSwapIntervalZero, fence, mStickyTransform);
+
+    if (mDirtyRegion.bounds() == Rect::INVALID_RECT) {
+        input.setSurfaceDamage(Region::INVALID_REGION);
+    } else {
+        // The surface damage was specified using the OpenGL ES convention of
+        // the origin being in the bottom-left corner. Here we flip to the
+        // convention that the rest of the system uses (top-left corner) by
+        // subtracting all top/bottom coordinates from the buffer height.
+        Region flippedRegion;
+        for (auto rect : mDirtyRegion) {
+            auto top = buffer->height - rect.bottom;
+            auto bottom = buffer->height - rect.top;
+            Rect flippedRect{rect.left, top, rect.right, bottom};
+            flippedRegion.orSelf(flippedRect);
+        }
+
+        input.setSurfaceDamage(flippedRegion);
+    }
+
     status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);
     if (err != OK)  {
         ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
@@ -336,6 +356,9 @@
 
     mConsumerRunningBehind = (numPendingBuffers >= 2);
 
+    // Clear surface damage back to full-buffer
+    mDirtyRegion = Region::INVALID_REGION;
+
     return err;
 }
 
@@ -453,6 +476,9 @@
     case NATIVE_WINDOW_SET_BUFFERS_DATASPACE:
         res = dispatchSetBuffersDataSpace(args);
         break;
+    case NATIVE_WINDOW_SET_SURFACE_DAMAGE:
+        res = dispatchSetSurfaceDamage(args);
+        break;
     default:
         res = NAME_NOT_FOUND;
         break;
@@ -556,6 +582,13 @@
     return setBuffersDataSpace(dataspace);
 }
 
+int Surface::dispatchSetSurfaceDamage(va_list args) {
+    android_native_rect_t* rects = va_arg(args, android_native_rect_t*);
+    size_t numRects = va_arg(args, size_t);
+    setSurfaceDamage(rects, numRects);
+    return NO_ERROR;
+}
+
 int Surface::connect(int api) {
     static sp<IProducerListener> listener = new DummyProducerListener();
     return connect(api, listener);
@@ -582,7 +615,13 @@
     }
     if (!err && api == NATIVE_WINDOW_API_CPU) {
         mConnectedToCpu = true;
+        // Clear the dirty region in case we're switching from a non-CPU API
+        mDirtyRegion.clear();
+    } else if (!err) {
+        // Initialize the dirty region for tracking surface damage
+        mDirtyRegion = Region::INVALID_REGION;
     }
+
     return err;
 }
 
@@ -800,6 +839,27 @@
     }
 }
 
+void Surface::setSurfaceDamage(android_native_rect_t* rects, size_t numRects) {
+    ATRACE_CALL();
+    ALOGV("Surface::setSurfaceDamage");
+    Mutex::Autolock lock(mMutex);
+
+    if (numRects == 0) {
+        mDirtyRegion = Region::INVALID_REGION;
+        return;
+    }
+
+    mDirtyRegion.clear();
+    for (size_t r = 0; r < numRects; ++r) {
+        // We intentionally flip top and bottom here, since because they're
+        // specified with a bottom-left origin, top > bottom, which fails
+        // validation in the Region class. We will fix this up when we flip to a
+        // top-left origin in queueBuffer.
+        Rect rect(rects[r].left, rects[r].bottom, rects[r].right, rects[r].top);
+        mDirtyRegion.orSelf(rect);
+    }
+}
+
 // ----------------------------------------------------------------------
 // the lock/unlock APIs must be used from the same thread
 
diff --git a/libs/ui/Rect.cpp b/libs/ui/Rect.cpp
index b480f3a..dcce21f 100644
--- a/libs/ui/Rect.cpp
+++ b/libs/ui/Rect.cpp
@@ -19,6 +19,8 @@
 
 namespace android {
 
+const Rect Rect::INVALID_RECT{0, 0, -1, -1};
+
 static inline int32_t min(int32_t a, int32_t b) {
     return (a < b) ? a : b;
 }
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index 62ec35c..3810da4 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -53,6 +53,8 @@
     direction_RTL
 };
 
+const Region Region::INVALID_REGION(Rect::INVALID_RECT);
+
 // ----------------------------------------------------------------------------
 
 Region::Region() {
@@ -517,8 +519,12 @@
     Rect b(*prev);
     while (cur != tail) {
         if (cur->isValid() == false) {
-            ALOGE_IF(!silent, "%s: region contains an invalid Rect", name);
-            result = false;
+            // We allow this particular flavor of invalid Rect, since it is used
+            // as a signal value in various parts of the system
+            if (*cur != Rect::INVALID_RECT) {
+                ALOGE_IF(!silent, "%s: region contains an invalid Rect", name);
+                result = false;
+            }
         }
         if (cur->right > region_operator<Rect>::max_value) {
             ALOGE_IF(!silent, "%s: rect->right > max_value", name);
@@ -690,7 +696,9 @@
         const Region& lhs,
         const Rect& rhs, int dx, int dy)
 {
-    if (!rhs.isValid()) {
+    // We allow this particular flavor of invalid Rect, since it is used as a
+    // signal value in various parts of the system
+    if (!rhs.isValid() && rhs != Rect::INVALID_RECT) {
         ALOGE("Region::boolean_operation(op=%d) invalid Rect={%d,%d,%d,%d}",
                 op, rhs.left, rhs.top, rhs.right, rhs.bottom);
         return;