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
This commit is contained in:
Dan Stoza 2015-03-17 16:23:42 -07:00
parent 4d769d8bdc
commit 5065a55291
11 changed files with 118 additions and 8 deletions

View File

@ -21,6 +21,7 @@
#include <EGL/eglext.h>
#include <ui/Rect.h>
#include <ui/Region.h>
#include <system/graphics.h>
@ -106,6 +107,10 @@ class BufferItem : public Flattenable<BufferItem> {
// 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

View File

@ -28,6 +28,7 @@
#include <ui/Fence.h>
#include <ui/GraphicBuffer.h>
#include <ui/Rect.h>
#include <ui/Region.h>
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> fence;
Region surfaceDamage;
};
// QueueBufferOutput must be a POD structure

View File

@ -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<GraphicBuffer> 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;
};

View File

@ -31,6 +31,8 @@ class Rect : public ARect, public LightFlattenablePod<Rect>
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

View File

@ -35,6 +35,8 @@ class String8;
class Region : public LightFlattenable<Region>
{
public:
static const Region INVALID_REGION;
Region();
Region(const Region& rhs);
explicit Region(const Rect& rhs);

View File

@ -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()) {

View File

@ -525,6 +525,7 @@ status_t BufferQueueProducer::queueBuffer(int slot,
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 @@ status_t BufferQueueProducer::queueBuffer(int slot,
item.mSlot = slot;
item.mFence = fence;
item.mIsDroppable = mCore->mDequeueBufferCannotBlock || async;
item.mSurfaceDamage = surfaceDamage;
mStickyTransform = stickyTransform;

View File

@ -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

View File

@ -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 @@ 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<IProducerListener> listener = new DummyProducerListener();
return connect(api, listener);
@ -582,7 +615,13 @@ int Surface::connect(int api, const sp<IProducerListener>& 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

View File

@ -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;
}

View File

@ -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<Rect>::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;