diff --git a/include/gui/BufferItem.h b/include/gui/BufferItem.h index cc41bae46..000ef0e87 100644 --- a/include/gui/BufferItem.h +++ b/include/gui/BufferItem.h @@ -21,6 +21,7 @@ #include #include +#include #include @@ -106,6 +107,10 @@ class BufferItem : public Flattenable { // Indicates this buffer must be transformed by the inverse transform of the screen // it is displayed onto. This is applied after mTransform. bool mTransformToDisplayInverse; + + // Describes the portion of the surface that has been modified since the + // previous frame + Region mSurfaceDamage; }; } // namespace android diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h index 374245a7e..2d99f2452 100644 --- a/include/gui/IGraphicBufferProducer.h +++ b/include/gui/IGraphicBufferProducer.h @@ -28,6 +28,7 @@ #include #include #include +#include namespace android { // ---------------------------------------------------------------------------- @@ -281,7 +282,7 @@ public: : timestamp(timestamp), isAutoTimestamp(isAutoTimestamp), dataSpace(dataSpace), crop(crop), scalingMode(scalingMode), transform(transform), stickyTransform(sticky), - async(async), fence(fence) { } + async(async), fence(fence), surfaceDamage() { } inline void deflate(int64_t* outTimestamp, bool* outIsAutoTimestamp, android_dataspace* outDataSpace, Rect* outCrop, int* outScalingMode, @@ -306,6 +307,9 @@ public: status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const; status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count); + const Region& getSurfaceDamage() const { return surfaceDamage; } + void setSurfaceDamage(const Region& damage) { surfaceDamage = damage; } + private: int64_t timestamp; int isAutoTimestamp; @@ -316,6 +320,7 @@ public: uint32_t stickyTransform; int async; sp fence; + Region surfaceDamage; }; // QueueBufferOutput must be a POD structure diff --git a/include/gui/Surface.h b/include/gui/Surface.h index e97348396..8217652c3 100644 --- a/include/gui/Surface.h +++ b/include/gui/Surface.h @@ -147,6 +147,7 @@ private: int dispatchUnlockAndPost(va_list args); int dispatchSetSidebandStream(va_list args); int dispatchSetBuffersDataSpace(va_list args); + int dispatchSetSurfaceDamage(va_list args); protected: virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd); @@ -171,6 +172,7 @@ protected: virtual int setBuffersDataSpace(android_dataspace dataSpace); virtual int setCrop(Rect const* rect); virtual int setUsage(uint32_t reqUsage); + virtual void setSurfaceDamage(android_native_rect_t* rects, size_t numRects); public: virtual int lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds); @@ -296,7 +298,12 @@ private: sp mPostedBuffer; bool mConnectedToCpu; - // must be accessed from lock/unlock thread only + // In the lock/unlock context, this reflects the region that the producer + // wished to update and whether the Surface was able to copy the previous + // buffer back to allow a partial update. + // + // In the dequeue/queue context, this reflects the surface damage (the + // damage since the last frame) passed in by the producer. Region mDirtyRegion; }; diff --git a/include/ui/Rect.h b/include/ui/Rect.h index 40d11665d..3886f9314 100644 --- a/include/ui/Rect.h +++ b/include/ui/Rect.h @@ -31,6 +31,8 @@ class Rect : public ARect, public LightFlattenablePod public: typedef ARect::value_type value_type; + static const Rect INVALID_RECT; + // we don't provide copy-ctor and operator= on purpose // because we want the compiler generated versions diff --git a/include/ui/Region.h b/include/ui/Region.h index 49740f7ed..2a1491837 100644 --- a/include/ui/Region.h +++ b/include/ui/Region.h @@ -35,6 +35,8 @@ class String8; class Region : public LightFlattenable { public: + static const Region INVALID_REGION; + Region(); Region(const Region& rhs); explicit Region(const Rect& rhs); diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp index 312fb3b0d..239da20a0 100644 --- a/libs/gui/BufferItem.cpp +++ b/libs/gui/BufferItem.cpp @@ -64,6 +64,8 @@ size_t BufferItem::getFlattenedSize() const { c += mFence->getFlattenedSize(); FlattenableUtils::align<4>(c); } + c += mSurfaceDamage.getFlattenedSize(); + FlattenableUtils::align<4>(c); return sizeof(int32_t) + c + getPodSize(); } @@ -105,6 +107,9 @@ status_t BufferItem::flatten( 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 @@ status_t BufferItem::unflatten( 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 4c22ba381..6452cddcf 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -525,6 +525,7 @@ status_t BufferQueueProducer::queueBuffer(int slot, sp fence; input.deflate(×tamp, &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 @@ status_t BufferQueueProducer::queueBuffer(int slot, 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 a3e6fb2a2..b7982a9f5 100644 --- a/libs/gui/IGraphicBufferProducer.cpp +++ b/libs/gui/IGraphicBufferProducer.cpp @@ -446,7 +446,8 @@ size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const { + sizeof(transform) + sizeof(stickyTransform) + sizeof(async) - + fence->getFlattenedSize(); + + fence->getFlattenedSize() + + surfaceDamage.getFlattenedSize(); } size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const { @@ -467,7 +468,11 @@ status_t IGraphicBufferProducer::QueueBufferInput::flatten( 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 @@ status_t IGraphicBufferProducer::QueueBufferInput::unflatten( 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 b80890fc5..245f7a374 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -320,6 +321,25 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { 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 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { mConsumerRunningBehind = (numPendingBuffers >= 2); + // Clear surface damage back to full-buffer + mDirtyRegion = Region::INVALID_REGION; + return err; } @@ -453,6 +476,9 @@ int Surface::perform(int operation, va_list args) 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 @@ int Surface::dispatchSetBuffersDataSpace(va_list args) { 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 listener = new DummyProducerListener(); return connect(api, listener); @@ -582,7 +615,13 @@ int Surface::connect(int api, const sp& listener) { } 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::freeAllBuffers() { } } +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 b480f3a62..dcce21f15 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 62ec35cfc..3810da404 100644 --- a/libs/ui/Region.cpp +++ b/libs/ui/Region.cpp @@ -53,6 +53,8 @@ enum { direction_RTL }; +const Region Region::INVALID_REGION(Rect::INVALID_RECT); + // ---------------------------------------------------------------------------- Region::Region() { @@ -517,8 +519,12 @@ bool Region::validate(const Region& reg, const char* name, bool silent) 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::max_value) { ALOGE_IF(!silent, "%s: rect->right > max_value", name); @@ -690,7 +696,9 @@ void Region::boolean_operation(int op, Region& dst, 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;