summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/gui/Android.bp1
-rw-r--r--libs/gui/BufferQueue.cpp9
-rw-r--r--libs/gui/BufferQueueConsumer.cpp14
-rw-r--r--libs/gui/BufferQueueCore.cpp20
-rw-r--r--libs/gui/BufferQueueProducer.cpp87
-rw-r--r--libs/gui/ConsumerBase.cpp24
-rw-r--r--libs/gui/IConsumerListener.cpp61
-rw-r--r--libs/gui/IGraphicBufferConsumer.cpp67
-rw-r--r--libs/gui/IGraphicBufferProducer.cpp151
-rw-r--r--libs/gui/IProducerListener.cpp26
-rw-r--r--libs/gui/ISurfaceComposer.cpp128
-rw-r--r--libs/gui/ISurfaceComposerClient.cpp51
-rw-r--r--libs/gui/OccupancyTracker.cpp117
-rw-r--r--libs/gui/Surface.cpp113
-rw-r--r--libs/gui/SurfaceComposerClient.cpp60
-rw-r--r--libs/gui/SurfaceControl.cpp11
-rw-r--r--libs/gui/tests/BufferQueue_test.cpp217
-rw-r--r--libs/gui/tests/IGraphicBufferProducer_test.cpp5
-rw-r--r--libs/gui/tests/SurfaceTextureGL_test.cpp14
-rw-r--r--libs/ui/Fence.cpp15
-rw-r--r--libs/ui/Region.cpp5
21 files changed, 1099 insertions, 97 deletions
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index b558ec9d87..8e8bb802b3 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -83,6 +83,7 @@ cc_library_shared {
"ISurfaceComposer.cpp",
"ISurfaceComposerClient.cpp",
"LayerState.cpp",
+ "OccupancyTracker.cpp",
"Sensor.cpp",
"SensorEventQueue.cpp",
"SensorManager.cpp",
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index ccbb5a25f3..6de98f5a25 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -61,6 +61,15 @@ void BufferQueue::ProxyConsumerListener::onSidebandStreamChanged() {
}
}
+bool BufferQueue::ProxyConsumerListener::getFrameTimestamps(
+ uint64_t frameNumber, FrameTimestamps* outTimestamps) const {
+ sp<ConsumerListener> listener(mConsumerListener.promote());
+ if (listener != NULL) {
+ return listener->getFrameTimestamps(frameNumber, outTimestamps);
+ }
+ return false;
+}
+
void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
sp<IGraphicBufferConsumer>* outConsumer,
const sp<IGraphicBufferAlloc>& allocator) {
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 856115dc74..7fbf312727 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -261,6 +261,7 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer,
ATRACE_INT(mCore->mConsumerName.string(),
static_cast<int32_t>(mCore->mQueue.size()));
+ mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());
VALIDATE_CONSISTENCY();
}
@@ -718,6 +719,19 @@ sp<NativeHandle> BufferQueueConsumer::getSidebandStream() const {
return mCore->mSidebandStream;
}
+status_t BufferQueueConsumer::getOccupancyHistory(bool forceFlush,
+ std::vector<OccupancyTracker::Segment>* outHistory) {
+ Mutex::Autolock lock(mCore->mMutex);
+ *outHistory = mCore->mOccupancyTracker.getSegmentHistory(forceFlush);
+ return NO_ERROR;
+}
+
+status_t BufferQueueConsumer::discardFreeBuffers() {
+ Mutex::Autolock lock(mCore->mMutex);
+ mCore->discardFreeBuffersLocked();
+ return NO_ERROR;
+}
+
void BufferQueueConsumer::dumpState(String8& result, const char* prefix) const {
const IPCThreadState* ipc = IPCThreadState::self();
const pid_t pid = ipc->getCallingPid();
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index 032779dc3a..d74d32c06d 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -47,6 +47,12 @@ static String8 getUniqueName() {
android_atomic_inc(&counter));
}
+static uint64_t getUniqueId() {
+ static std::atomic<uint32_t> counter{0};
+ static uint64_t id = static_cast<uint64_t>(getpid()) << 32;
+ return id | counter++;
+}
+
BufferQueueCore::BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator) :
mAllocator(allocator),
mMutex(),
@@ -56,6 +62,7 @@ BufferQueueCore::BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator) :
mConsumerListener(),
mConsumerUsageBits(0),
mConnectedApi(NO_CONNECTED_API),
+ mLinkedToDeath(),
mConnectedProducerListener(),
mSlots(),
mQueue(),
@@ -85,7 +92,8 @@ BufferQueueCore::BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator) :
mAutoRefresh(false),
mSharedBufferSlot(INVALID_BUFFER_SLOT),
mSharedBufferCache(Rect::INVALID_RECT, 0, NATIVE_WINDOW_SCALING_MODE_FREEZE,
- HAL_DATASPACE_UNKNOWN)
+ HAL_DATASPACE_UNKNOWN),
+ mUniqueId(getUniqueId())
{
if (allocator == NULL) {
@@ -256,6 +264,16 @@ void BufferQueueCore::freeAllBuffersLocked() {
VALIDATE_CONSISTENCY();
}
+void BufferQueueCore::discardFreeBuffersLocked() {
+ for (int s : mFreeBuffers) {
+ mFreeSlots.insert(s);
+ clearBufferSlotLocked(s);
+ }
+ mFreeBuffers.clear();
+
+ VALIDATE_CONSISTENCY();
+}
+
bool BufferQueueCore::adjustAvailableSlotsLocked(int delta) {
if (delta >= 0) {
// If we're going to fail, do so before modifying anything
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index e853dfb551..f8f38725b5 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -28,6 +28,7 @@
#define EGL_EGLEXT_PROTOTYPES
+#include <binder/IPCThreadState.h>
#include <gui/BufferItem.h>
#include <gui/BufferQueueCore.h>
#include <gui/BufferQueueProducer.h>
@@ -510,11 +511,15 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
mCore->mIsAllocatingCondition.broadcast();
if (graphicBuffer == NULL) {
+ mCore->mFreeSlots.insert(*outSlot);
+ mCore->clearBufferSlotLocked(*outSlot);
BQ_LOGE("dequeueBuffer: createGraphicBuffer failed");
return error;
}
if (mCore->mIsAbandoned) {
+ mCore->mFreeSlots.insert(*outSlot);
+ mCore->clearBufferSlotLocked(*outSlot);
BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned");
return NO_INIT;
}
@@ -897,10 +902,12 @@ status_t BufferQueueProducer::queueBuffer(int slot,
output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight,
mCore->mTransformHint,
- static_cast<uint32_t>(mCore->mQueue.size()));
+ static_cast<uint32_t>(mCore->mQueue.size()),
+ mCore->mFrameCounter + 1);
ATRACE_INT(mCore->mConsumerName.string(),
static_cast<int32_t>(mCore->mQueue.size()));
+ mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());
// Take a ticket for the callback functions
callbackTicket = mNextCallbackTicket++;
@@ -1104,27 +1111,32 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener,
mCore->mConnectedApi = api;
output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight,
mCore->mTransformHint,
- static_cast<uint32_t>(mCore->mQueue.size()));
-
- // Set up a death notification so that we can disconnect
- // automatically if the remote producer dies
- if (listener != NULL &&
- IInterface::asBinder(listener)->remoteBinder() != NULL) {
- status = IInterface::asBinder(listener)->linkToDeath(
- static_cast<IBinder::DeathRecipient*>(this));
- if (status != NO_ERROR) {
- BQ_LOGE("connect: linkToDeath failed: %s (%d)",
- strerror(-status), status);
+ static_cast<uint32_t>(mCore->mQueue.size()),
+ mCore->mFrameCounter + 1);
+
+ if (listener != NULL) {
+ // Set up a death notification so that we can disconnect
+ // automatically if the remote producer dies
+ if (IInterface::asBinder(listener)->remoteBinder() != NULL) {
+ status = IInterface::asBinder(listener)->linkToDeath(
+ static_cast<IBinder::DeathRecipient*>(this));
+ if (status != NO_ERROR) {
+ BQ_LOGE("connect: linkToDeath failed: %s (%d)",
+ strerror(-status), status);
+ }
+ mCore->mLinkedToDeath = listener;
+ }
+ if (listener->needsReleaseNotify()) {
+ mCore->mConnectedProducerListener = listener;
}
}
- mCore->mConnectedProducerListener = listener;
break;
default:
BQ_LOGE("connect: unknown API %d", api);
status = BAD_VALUE;
break;
}
-
+ mCore->mConnectedPid = IPCThreadState::self()->getCallingPid();
mCore->mBufferHasBeenQueued = false;
mCore->mDequeueBufferCannotBlock = false;
if (mDequeueTimeout < 0) {
@@ -1137,7 +1149,7 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener,
return status;
}
-status_t BufferQueueProducer::disconnect(int api) {
+status_t BufferQueueProducer::disconnect(int api, DisconnectMode mode) {
ATRACE_CALL();
BQ_LOGV("disconnect: api %d", api);
@@ -1145,6 +1157,14 @@ status_t BufferQueueProducer::disconnect(int api) {
sp<IConsumerListener> listener;
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
+
+ if (mode == DisconnectMode::AllLocal) {
+ if (IPCThreadState::self()->getCallingPid() != mCore->mConnectedPid) {
+ return NO_ERROR;
+ }
+ api = BufferQueueCore::CURRENTLY_CONNECTED_API;
+ }
+
mCore->waitWhileAllocatingLocked();
if (mCore->mIsAbandoned) {
@@ -1171,9 +1191,9 @@ status_t BufferQueueProducer::disconnect(int api) {
mCore->freeAllBuffersLocked();
// Remove our death notification callback if we have one
- if (mCore->mConnectedProducerListener != NULL) {
+ if (mCore->mLinkedToDeath != NULL) {
sp<IBinder> token =
- IInterface::asBinder(mCore->mConnectedProducerListener);
+ IInterface::asBinder(mCore->mLinkedToDeath);
// This can fail if we're here because of the death
// notification, but we just ignore it
token->unlinkToDeath(
@@ -1181,8 +1201,10 @@ status_t BufferQueueProducer::disconnect(int api) {
}
mCore->mSharedBufferSlot =
BufferQueueCore::INVALID_BUFFER_SLOT;
+ mCore->mLinkedToDeath = NULL;
mCore->mConnectedProducerListener = NULL;
mCore->mConnectedApi = BufferQueueCore::NO_CONNECTED_API;
+ mCore->mConnectedPid = -1;
mCore->mSidebandStream.clear();
mCore->mDequeueCondition.broadcast();
listener = mCore->mConsumerListener;
@@ -1340,14 +1362,6 @@ String8 BufferQueueProducer::getConsumerName() const {
return mConsumerName;
}
-uint64_t BufferQueueProducer::getNextFrameNumber() const {
- ATRACE_CALL();
-
- Mutex::Autolock lock(mCore->mMutex);
- uint64_t nextFrameNumber = mCore->mFrameCounter + 1;
- return nextFrameNumber;
-}
-
status_t BufferQueueProducer::setSharedBufferMode(bool sharedBufferMode) {
ATRACE_CALL();
BQ_LOGV("setSharedBufferMode: %d", sharedBufferMode);
@@ -1416,6 +1430,22 @@ status_t BufferQueueProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
return NO_ERROR;
}
+bool BufferQueueProducer::getFrameTimestamps(uint64_t frameNumber,
+ FrameTimestamps* outTimestamps) const {
+ ATRACE_CALL();
+ BQ_LOGV("getFrameTimestamps, %" PRIu64, frameNumber);
+ sp<IConsumerListener> listener;
+
+ {
+ Mutex::Autolock lock(mCore->mMutex);
+ listener = mCore->mConsumerListener;
+ }
+ if (listener != NULL) {
+ return listener->getFrameTimestamps(frameNumber, outTimestamps);
+ }
+ return false;
+}
+
void BufferQueueProducer::binderDied(const wp<android::IBinder>& /* who */) {
// If we're here, it means that a producer we were connected to died.
// We're guaranteed that we are still connected to it because we remove
@@ -1425,4 +1455,11 @@ void BufferQueueProducer::binderDied(const wp<android::IBinder>& /* who */) {
disconnect(api);
}
+status_t BufferQueueProducer::getUniqueId(uint64_t* outId) const {
+ BQ_LOGV("getUniqueId");
+
+ *outId = mCore->mUniqueId;
+ return NO_ERROR;
+}
+
} // namespace android
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index 5d2e5cffd8..5546d5416c 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -235,6 +235,25 @@ status_t ConsumerBase::setDefaultBufferDataSpace(
return mConsumer->setDefaultBufferDataSpace(defaultDataSpace);
}
+status_t ConsumerBase::getOccupancyHistory(bool forceFlush,
+ std::vector<OccupancyTracker::Segment>* outHistory) {
+ Mutex::Autolock _l(mMutex);
+ if (mAbandoned) {
+ CB_LOGE("getOccupancyHistory: ConsumerBase is abandoned!");
+ return NO_INIT;
+ }
+ return mConsumer->getOccupancyHistory(forceFlush, outHistory);
+}
+
+status_t ConsumerBase::discardFreeBuffers() {
+ Mutex::Autolock _l(mMutex);
+ if (mAbandoned) {
+ CB_LOGE("discardFreeBuffers: ConsumerBase is abandoned!");
+ return NO_INIT;
+ }
+ return mConsumer->discardFreeBuffers();
+}
+
void ConsumerBase::dumpState(String8& result) const {
dumpState(result, "");
}
@@ -296,9 +315,10 @@ status_t ConsumerBase::addReleaseFenceLocked(int slot,
if (!mSlots[slot].mFence.get()) {
mSlots[slot].mFence = fence;
} else {
+ char fenceName[32] = {};
+ snprintf(fenceName, 32, "%.28s:%d", mName.string(), slot);
sp<Fence> mergedFence = Fence::merge(
- String8::format("%.28s:%d", mName.string(), slot),
- mSlots[slot].mFence, fence);
+ fenceName, mSlots[slot].mFence, fence);
if (!mergedFence.get()) {
CB_LOGE("failed to merge release fences");
// synchronization is broken, the best we can do is hope fences
diff --git a/libs/gui/IConsumerListener.cpp b/libs/gui/IConsumerListener.cpp
index 4382ee8b26..ff7b83a7d6 100644
--- a/libs/gui/IConsumerListener.cpp
+++ b/libs/gui/IConsumerListener.cpp
@@ -31,6 +31,7 @@ enum {
ON_FRAME_AVAILABLE = IBinder::FIRST_CALL_TRANSACTION,
ON_BUFFER_RELEASED,
ON_SIDEBAND_STREAM_CHANGED,
+ GET_FRAME_TIMESTAMPS
};
class BpConsumerListener : public BpInterface<IConsumerListener>
@@ -60,6 +61,42 @@ public:
data.writeInterfaceToken(IConsumerListener::getInterfaceDescriptor());
remote()->transact(ON_SIDEBAND_STREAM_CHANGED, data, &reply, IBinder::FLAG_ONEWAY);
}
+
+ virtual bool getFrameTimestamps(uint64_t frameNumber,
+ FrameTimestamps* outTimestamps) const {
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(
+ IConsumerListener::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("getFrameTimestamps failed to write token: %d", result);
+ return false;
+ }
+ result = data.writeUint64(frameNumber);
+ if (result != NO_ERROR) {
+ ALOGE("getFrameTimestamps failed to write: %d", result);
+ return false;
+ }
+ result = remote()->transact(GET_FRAME_TIMESTAMPS, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("getFrameTimestamps failed to transact: %d", result);
+ return false;
+ }
+ bool found = false;
+ result = reply.readBool(&found);
+ if (result != NO_ERROR) {
+ ALOGE("getFrameTimestamps failed to read: %d", result);
+ return false;
+ }
+ if (found) {
+ result = reply.read(*outTimestamps);
+ if (result != NO_ERROR) {
+ ALOGE("getFrameTimestamps failed to read timestamps: %d",
+ result);
+ return false;
+ }
+ }
+ return found;
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -88,6 +125,30 @@ status_t BnConsumerListener::onTransact(
CHECK_INTERFACE(IConsumerListener, data, reply);
onSidebandStreamChanged();
return NO_ERROR; }
+ case GET_FRAME_TIMESTAMPS: {
+ CHECK_INTERFACE(IConsumerListener, data, reply);
+ uint64_t frameNumber = 0;
+ status_t result = data.readUint64(&frameNumber);
+ if (result != NO_ERROR) {
+ ALOGE("onTransact failed to read: %d", result);
+ return result;
+ }
+ FrameTimestamps timestamps;
+ bool found = getFrameTimestamps(frameNumber, &timestamps);
+ result = reply->writeBool(found);
+ if (result != NO_ERROR) {
+ ALOGE("onTransact failed to write: %d", result);
+ return result;
+ }
+ if (found) {
+ result = reply->write(timestamps);
+ if (result != NO_ERROR) {
+ ALOGE("onTransact failed to write timestamps: %d", result);
+ return result;
+ }
+ }
+ return NO_ERROR;
+ }
}
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp
index 6d33a10ab1..240146455e 100644
--- a/libs/gui/IGraphicBufferConsumer.cpp
+++ b/libs/gui/IGraphicBufferConsumer.cpp
@@ -51,6 +51,8 @@ enum {
SET_CONSUMER_USAGE_BITS,
SET_TRANSFORM_HINT,
GET_SIDEBAND_STREAM,
+ GET_OCCUPANCY_HISTORY,
+ DISCARD_FREE_BUFFERS,
DUMP,
};
@@ -260,6 +262,46 @@ public:
return stream;
}
+ virtual status_t getOccupancyHistory(bool forceFlush,
+ std::vector<OccupancyTracker::Segment>* outHistory) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
+ status_t error = data.writeBool(forceFlush);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = remote()->transact(GET_OCCUPANCY_HISTORY, data,
+ &reply);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = reply.readParcelableVector(outHistory);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ status_t result = NO_ERROR;
+ error = reply.readInt32(&result);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ return result;
+ }
+
+ virtual status_t discardFreeBuffers() {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
+ status_t error = remote()->transact(DISCARD_FREE_BUFFERS, data, &reply);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ int32_t result = NO_ERROR;
+ error = reply.readInt32(&result);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ return result;
+ }
+
virtual void dumpState(String8& result, const char* prefix) const {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
@@ -409,6 +451,31 @@ status_t BnGraphicBufferConsumer::onTransact(
}
return NO_ERROR;
}
+ case GET_OCCUPANCY_HISTORY: {
+ CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
+ bool forceFlush = false;
+ status_t error = data.readBool(&forceFlush);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ std::vector<OccupancyTracker::Segment> history;
+ status_t result = getOccupancyHistory(forceFlush, &history);
+ error = reply->writeParcelableVector(history);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = reply->writeInt32(result);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ return NO_ERROR;
+ }
+ case DISCARD_FREE_BUFFERS: {
+ CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
+ status_t result = discardFreeBuffers();
+ status_t error = reply->writeInt32(result);
+ return error;
+ }
case DUMP: {
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
String8 result = data.readString8();
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 81e9407f11..846c205c00 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -50,11 +50,12 @@ enum {
GET_CONSUMER_NAME,
SET_MAX_DEQUEUED_BUFFER_COUNT,
SET_ASYNC_MODE,
- GET_NEXT_FRAME_NUMBER,
SET_SHARED_BUFFER_MODE,
SET_AUTO_REFRESH,
SET_DEQUEUE_TIMEOUT,
GET_LAST_QUEUED_BUFFER,
+ GET_FRAME_TIMESTAMPS,
+ GET_UNIQUE_ID
};
class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@@ -132,7 +133,11 @@ public:
bool nonNull = reply.readInt32();
if (nonNull) {
*fence = new Fence();
- reply.read(**fence);
+ result = reply.read(**fence);
+ if (result != NO_ERROR) {
+ fence->clear();
+ return result;
+ }
}
result = reply.readInt32();
return result;
@@ -170,12 +175,21 @@ public:
bool nonNull = reply.readInt32();
if (nonNull) {
*outBuffer = new GraphicBuffer;
- reply.read(**outBuffer);
+ result = reply.read(**outBuffer);
+ if (result != NO_ERROR) {
+ outBuffer->clear();
+ return result;
+ }
}
nonNull = reply.readInt32();
if (nonNull) {
*outFence = new Fence;
- reply.read(**outFence);
+ result = reply.read(**outFence);
+ if (result != NO_ERROR) {
+ outBuffer->clear();
+ outFence->clear();
+ return result;
+ }
}
}
return result;
@@ -256,10 +270,11 @@ public:
return result;
}
- virtual status_t disconnect(int api) {
+ virtual status_t disconnect(int api, DisconnectMode mode) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeInt32(api);
+ data.writeInt32(static_cast<int32_t>(mode));
status_t result =remote()->transact(DISCONNECT, data, &reply);
if (result != NO_ERROR) {
return result;
@@ -332,18 +347,6 @@ public:
return reply.readString8();
}
- virtual uint64_t getNextFrameNumber() const {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
- status_t result = remote()->transact(GET_NEXT_FRAME_NUMBER, data, &reply);
- if (result != NO_ERROR) {
- ALOGE("getNextFrameNumber failed to transact: %d", result);
- return 0;
- }
- uint64_t frameNumber = reply.readUint64();
- return frameNumber;
- }
-
virtual status_t setSharedBufferMode(bool sharedBufferMode) {
Parcel data, reply;
data.writeInterfaceToken(
@@ -418,6 +421,61 @@ public:
*outFence = fence;
return result;
}
+
+ virtual bool getFrameTimestamps(uint64_t frameNumber,
+ FrameTimestamps* outTimestamps) const {
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(
+ IGraphicBufferProducer::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("getFrameTimestamps failed to write token: %d", result);
+ return false;
+ }
+ result = data.writeUint64(frameNumber);
+ if (result != NO_ERROR) {
+ ALOGE("getFrameTimestamps failed to write: %d", result);
+ return false;
+ }
+ result = remote()->transact(GET_FRAME_TIMESTAMPS, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("getFrameTimestamps failed to transact: %d", result);
+ return false;
+ }
+ bool found = false;
+ result = reply.readBool(&found);
+ if (result != NO_ERROR) {
+ ALOGE("getFrameTimestamps failed to read: %d", result);
+ return false;
+ }
+ if (found) {
+ result = reply.read(*outTimestamps);
+ if (result != NO_ERROR) {
+ ALOGE("getFrameTimestamps failed to read timestamps: %d",
+ result);
+ return false;
+ }
+ }
+ return found;
+ }
+
+ virtual status_t getUniqueId(uint64_t* outId) const {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ status_t result = remote()->transact(GET_UNIQUE_ID, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("getUniqueId failed to transact: %d", result);
+ }
+ status_t actualResult = NO_ERROR;
+ result = reply.readInt32(&actualResult);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.readUint64(outId);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ return actualResult;
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -504,9 +562,11 @@ status_t BnGraphicBufferProducer::onTransact(
case ATTACH_BUFFER: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
sp<GraphicBuffer> buffer = new GraphicBuffer();
- data.read(*buffer.get());
+ status_t result = data.read(*buffer.get());
int slot = 0;
- int result = attachBuffer(&slot, buffer);
+ if (result == NO_ERROR) {
+ result = attachBuffer(&slot, buffer);
+ }
reply->writeInt32(slot);
reply->writeInt32(result);
return NO_ERROR;
@@ -527,8 +587,10 @@ status_t BnGraphicBufferProducer::onTransact(
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int buf = data.readInt32();
sp<Fence> fence = new Fence();
- data.read(*fence.get());
- status_t result = cancelBuffer(buf, fence);
+ status_t result = data.read(*fence.get());
+ if (result == NO_ERROR) {
+ result = cancelBuffer(buf, fence);
+ }
reply->writeInt32(result);
return NO_ERROR;
}
@@ -560,7 +622,8 @@ status_t BnGraphicBufferProducer::onTransact(
case DISCONNECT: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int api = data.readInt32();
- status_t res = disconnect(api);
+ DisconnectMode mode = static_cast<DisconnectMode>(data.readInt32());
+ status_t res = disconnect(api, mode);
reply->writeInt32(res);
return NO_ERROR;
}
@@ -602,12 +665,6 @@ status_t BnGraphicBufferProducer::onTransact(
reply->writeString8(getConsumerName());
return NO_ERROR;
}
- case GET_NEXT_FRAME_NUMBER: {
- CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
- uint64_t frameNumber = getNextFrameNumber();
- reply->writeUint64(frameNumber);
- return NO_ERROR;
- }
case SET_SHARED_BUFFER_MODE: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
bool sharedBufferMode = data.readInt32();
@@ -659,6 +716,44 @@ status_t BnGraphicBufferProducer::onTransact(
}
return NO_ERROR;
}
+ case GET_FRAME_TIMESTAMPS: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ uint64_t frameNumber = 0;
+ status_t result = data.readUint64(&frameNumber);
+ if (result != NO_ERROR) {
+ ALOGE("onTransact failed to read: %d", result);
+ return result;
+ }
+ FrameTimestamps timestamps;
+ bool found = getFrameTimestamps(frameNumber, &timestamps);
+ result = reply->writeBool(found);
+ if (result != NO_ERROR) {
+ ALOGE("onTransact failed to write: %d", result);
+ return result;
+ }
+ if (found) {
+ result = reply->write(timestamps);
+ if (result != NO_ERROR) {
+ ALOGE("onTransact failed to write timestamps: %d", result);
+ return result;
+ }
+ }
+ return NO_ERROR;
+ }
+ case GET_UNIQUE_ID: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ uint64_t outId = 0;
+ status_t actualResult = getUniqueId(&outId);
+ status_t result = reply->writeInt32(actualResult);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply->writeUint64(outId);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ return NO_ERROR;
+ }
}
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/IProducerListener.cpp b/libs/gui/IProducerListener.cpp
index 855a4885cf..62abfa81c4 100644
--- a/libs/gui/IProducerListener.cpp
+++ b/libs/gui/IProducerListener.cpp
@@ -22,6 +22,7 @@ namespace android {
enum {
ON_BUFFER_RELEASED = IBinder::FIRST_CALL_TRANSACTION,
+ NEEDS_RELEASE_NOTIFY,
};
class BpProducerListener : public BpInterface<IProducerListener>
@@ -37,6 +38,23 @@ public:
data.writeInterfaceToken(IProducerListener::getInterfaceDescriptor());
remote()->transact(ON_BUFFER_RELEASED, data, &reply, IBinder::FLAG_ONEWAY);
}
+
+ virtual bool needsReleaseNotify() {
+ bool result;
+ Parcel data, reply;
+ data.writeInterfaceToken(IProducerListener::getInterfaceDescriptor());
+ status_t err = remote()->transact(NEEDS_RELEASE_NOTIFY, data, &reply);
+ if (err != NO_ERROR) {
+ ALOGE("IProducerListener: binder call \'needsReleaseNotify\' failed");
+ return true;
+ }
+ err = reply.readBool(&result);
+ if (err != NO_ERROR) {
+ ALOGE("IProducerListener: malformed binder reply");
+ return true;
+ }
+ return result;
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -52,6 +70,10 @@ status_t BnProducerListener::onTransact(uint32_t code, const Parcel& data,
CHECK_INTERFACE(IProducerListener, data, reply);
onBufferReleased();
return NO_ERROR;
+ case NEEDS_RELEASE_NOTIFY:
+ CHECK_INTERFACE(IProducerListener, data, reply);
+ reply->writeBool(needsReleaseNotify());
+ return NO_ERROR;
}
return BBinder::onTransact(code, data, reply, flags);
}
@@ -60,4 +82,8 @@ ProducerListener::~ProducerListener() = default;
DummyProducerListener::~DummyProducerListener() = default;
+bool BnProducerListener::needsReleaseNotify() {
+ return true;
+}
+
} // namespace android
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 015945a4fc..0a8e6a555e 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -32,6 +32,8 @@
#include <private/gui/LayerState.h>
+#include <system/graphics.h>
+
#include <ui/DisplayInfo.h>
#include <ui/DisplayStatInfo.h>
#include <ui/HdrCapabilities.h>
@@ -269,6 +271,82 @@ public:
return reply.readInt32();
}
+ virtual status_t getDisplayColorModes(const sp<IBinder>& display,
+ Vector<android_color_mode_t>* outColorModes) {
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("getDisplayColorModes failed to writeInterfaceToken: %d", result);
+ return result;
+ }
+ result = data.writeStrongBinder(display);
+ if (result != NO_ERROR) {
+ ALOGE("getDisplayColorModes failed to writeStrongBinder: %d", result);
+ return result;
+ }
+ result = remote()->transact(BnSurfaceComposer::GET_DISPLAY_COLOR_MODES, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("getDisplayColorModes failed to transact: %d", result);
+ return result;
+ }
+ result = static_cast<status_t>(reply.readInt32());
+ if (result == NO_ERROR) {
+ size_t numModes = reply.readUint32();
+ outColorModes->clear();
+ outColorModes->resize(numModes);
+ for (size_t i = 0; i < numModes; ++i) {
+ outColorModes->replaceAt(static_cast<android_color_mode_t>(reply.readInt32()), i);
+ }
+ }
+ return result;
+ }
+
+ virtual android_color_mode_t getActiveColorMode(const sp<IBinder>& display) {
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("getActiveColorMode failed to writeInterfaceToken: %d", result);
+ return static_cast<android_color_mode_t>(result);
+ }
+ result = data.writeStrongBinder(display);
+ if (result != NO_ERROR) {
+ ALOGE("getActiveColorMode failed to writeStrongBinder: %d", result);
+ return static_cast<android_color_mode_t>(result);
+ }
+ result = remote()->transact(BnSurfaceComposer::GET_ACTIVE_COLOR_MODE, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("getActiveColorMode failed to transact: %d", result);
+ return static_cast<android_color_mode_t>(result);
+ }
+ return static_cast<android_color_mode_t>(reply.readInt32());
+ }
+
+ virtual status_t setActiveColorMode(const sp<IBinder>& display,
+ android_color_mode_t colorMode) {
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("setActiveColorMode failed to writeInterfaceToken: %d", result);
+ return result;
+ }
+ result = data.writeStrongBinder(display);
+ if (result != NO_ERROR) {
+ ALOGE("setActiveColorMode failed to writeStrongBinder: %d", result);
+ return result;
+ }
+ result = data.writeInt32(colorMode);
+ if (result != NO_ERROR) {
+ ALOGE("setActiveColorMode failed to writeInt32: %d", result);
+ return result;
+ }
+ result = remote()->transact(BnSurfaceComposer::SET_ACTIVE_COLOR_MODE, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("setActiveColorMode failed to transact: %d", result);
+ return result;
+ }
+ return static_cast<status_t>(reply.readInt32());
+ }
+
virtual status_t clearAnimationFrameStats() {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -469,6 +547,56 @@ status_t BnSurfaceComposer::onTransact(
reply->writeInt32(result);
return NO_ERROR;
}
+ case GET_DISPLAY_COLOR_MODES: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ Vector<android_color_mode_t> colorModes;
+ sp<IBinder> display = nullptr;
+ status_t result = data.readStrongBinder(&display);
+ if (result != NO_ERROR) {
+ ALOGE("getDisplayColorModes failed to readStrongBinder: %d", result);
+ return result;
+ }
+ result = getDisplayColorModes(display, &colorModes);
+ reply->writeInt32(result);
+ if (result == NO_ERROR) {
+ reply->writeUint32(static_cast<uint32_t>(colorModes.size()));
+ for (size_t i = 0; i < colorModes.size(); ++i) {
+ reply->writeInt32(colorModes[i]);
+ }
+ }
+ return NO_ERROR;
+ }
+ case GET_ACTIVE_COLOR_MODE: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> display = nullptr;
+ status_t result = data.readStrongBinder(&display);
+ if (result != NO_ERROR) {
+ ALOGE("getActiveColorMode failed to readStrongBinder: %d", result);
+ return result;
+ }
+ android_color_mode_t colorMode = getActiveColorMode(display);
+ result = reply->writeInt32(static_cast<int32_t>(colorMode));
+ return result;
+ }
+ case SET_ACTIVE_COLOR_MODE: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> display = nullptr;
+ status_t result = data.readStrongBinder(&display);
+ if (result != NO_ERROR) {
+ ALOGE("getActiveColorMode failed to readStrongBinder: %d", result);
+ return result;
+ }
+ int32_t colorModeInt = 0;
+ result = data.readInt32(&colorModeInt);
+ if (result != NO_ERROR) {
+ ALOGE("setActiveColorMode failed to readInt32: %d", result);
+ return result;
+ }
+ result = setActiveColorMode(display,
+ static_cast<android_color_mode_t>(colorModeInt));
+ result = reply->writeInt32(result);
+ return result;
+ }
case CLEAR_ANIMATION_FRAME_STATS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
status_t result = clearAnimationFrameStats();
diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp
index decffbf9ba..47cb0473e8 100644
--- a/libs/gui/ISurfaceComposerClient.cpp
+++ b/libs/gui/ISurfaceComposerClient.cpp
@@ -41,7 +41,8 @@ enum {
CREATE_SURFACE = IBinder::FIRST_CALL_TRANSACTION,
DESTROY_SURFACE,
CLEAR_LAYER_FRAME_STATS,
- GET_LAYER_FRAME_STATS
+ GET_LAYER_FRAME_STATS,
+ GET_TRANSFORM_TO_DISPLAY_INVERSE
};
class BpSurfaceComposerClient : public BpInterface<ISurfaceComposerClient>
@@ -94,6 +95,35 @@ public:
reply.read(*outStats);
return reply.readInt32();
}
+
+ virtual status_t getTransformToDisplayInverse(const sp<IBinder>& handle,
+ bool* outTransformToDisplayInverse) const {
+ Parcel data, reply;
+ status_t result =
+ data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = data.writeStrongBinder(handle);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = remote()->transact(GET_TRANSFORM_TO_DISPLAY_INVERSE, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ int transformInverse;
+ result = reply.readInt32(&transformInverse);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ *outTransformToDisplayInverse = transformInverse != 0 ? true : false;
+ status_t result2 = reply.readInt32(&result);
+ if (result2 != NO_ERROR) {
+ return result2;
+ }
+ return result;
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -145,6 +175,25 @@ status_t BnSurfaceComposerClient::onTransact(
reply->writeInt32(result);
return NO_ERROR;
}
+ case GET_TRANSFORM_TO_DISPLAY_INVERSE: {
+ CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
+ sp<IBinder> handle;
+ status_t result = data.readStrongBinder(&handle);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ bool transformInverse = false;
+ result = getTransformToDisplayInverse(handle, &transformInverse);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply->writeInt32(transformInverse ? 1 : 0);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply->writeInt32(NO_ERROR);
+ return result;
+ }
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/OccupancyTracker.cpp b/libs/gui/OccupancyTracker.cpp
new file mode 100644
index 0000000000..9687aaf744
--- /dev/null
+++ b/libs/gui/OccupancyTracker.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "OccupancyTracker"
+
+#include <gui/OccupancyTracker.h>
+#include <binder/Parcel.h>
+#include <utils/String8.h>
+#include <utils/Trace.h>
+
+#include <inttypes.h>
+
+namespace android {
+
+status_t OccupancyTracker::Segment::writeToParcel(Parcel* parcel) const {
+ status_t result = parcel->writeInt64(totalTime);
+ if (result != OK) {
+ return result;
+ }
+ result = parcel->writeUint64(static_cast<uint64_t>(numFrames));
+ if (result != OK) {
+ return result;
+ }
+ result = parcel->writeFloat(occupancyAverage);
+ if (result != OK) {
+ return result;
+ }
+ return parcel->writeBool(usedThirdBuffer);
+}
+
+status_t OccupancyTracker::Segment::readFromParcel(const Parcel* parcel) {
+ status_t result = parcel->readInt64(&totalTime);
+ if (result != OK) {
+ return result;
+ }
+ uint64_t uintNumFrames = 0;
+ result = parcel->readUint64(&uintNumFrames);
+ if (result != OK) {
+ return result;
+ }
+ numFrames = static_cast<size_t>(uintNumFrames);
+ result = parcel->readFloat(&occupancyAverage);
+ if (result != OK) {
+ return result;
+ }
+ return parcel->readBool(&usedThirdBuffer);
+}
+
+void OccupancyTracker::registerOccupancyChange(size_t occupancy) {
+ ATRACE_CALL();
+ nsecs_t now = systemTime();
+ nsecs_t delta = now - mLastOccupancyChangeTime;
+ if (delta > NEW_SEGMENT_DELAY) {
+ recordPendingSegment();
+ } else {
+ mPendingSegment.totalTime += delta;
+ if (mPendingSegment.mOccupancyTimes.count(mLastOccupancy)) {
+ mPendingSegment.mOccupancyTimes[mLastOccupancy] += delta;
+ } else {
+ mPendingSegment.mOccupancyTimes[mLastOccupancy] = delta;
+ }
+ }
+ if (occupancy > mLastOccupancy) {
+ ++mPendingSegment.numFrames;
+ }
+ mLastOccupancyChangeTime = now;
+ mLastOccupancy = occupancy;
+}
+
+std::vector<OccupancyTracker::Segment> OccupancyTracker::getSegmentHistory(
+ bool forceFlush) {
+ if (forceFlush) {
+ recordPendingSegment();
+ }
+ std::vector<Segment> segments(mSegmentHistory.cbegin(),
+ mSegmentHistory.cend());
+ mSegmentHistory.clear();
+ return segments;
+}
+
+void OccupancyTracker::recordPendingSegment() {
+ // Only record longer segments to get a better measurement of actual double-
+ // vs. triple-buffered time
+ if (mPendingSegment.numFrames > LONG_SEGMENT_THRESHOLD) {
+ float occupancyAverage = 0.0f;
+ bool usedThirdBuffer = false;
+ for (const auto& timePair : mPendingSegment.mOccupancyTimes) {
+ size_t occupancy = timePair.first;
+ float timeRatio = static_cast<float>(timePair.second) /
+ mPendingSegment.totalTime;
+ occupancyAverage += timeRatio * occupancy;
+ usedThirdBuffer = usedThirdBuffer || (occupancy > 1);
+ }
+ mSegmentHistory.push_front({mPendingSegment.totalTime,
+ mPendingSegment.numFrames, occupancyAverage, usedThirdBuffer});
+ if (mSegmentHistory.size() > MAX_HISTORY_SIZE) {
+ mSegmentHistory.pop_back();
+ }
+ }
+ mPendingSegment.clear();
+}
+
+} // namespace android
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 1879f8a099..8e6ab1c73b 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -48,7 +48,8 @@ Surface::Surface(
mSharedBufferMode(false),
mAutoRefresh(false),
mSharedBufferSlot(BufferItem::INVALID_BUFFER_SLOT),
- mSharedBufferHasBeenQueued(false)
+ mSharedBufferHasBeenQueued(false),
+ mNextFrameNumber(1)
{
// Initialize the ANativeWindow function pointers.
ANativeWindow::setSwapInterval = hook_setSwapInterval;
@@ -116,7 +117,8 @@ status_t Surface::setGenerationNumber(uint32_t generation) {
}
uint64_t Surface::getNextFrameNumber() const {
- return mGraphicBufferProducer->getNextFrameNumber();
+ Mutex::Autolock lock(mMutex);
+ return mNextFrameNumber;
}
String8 Surface::getConsumerName() const {
@@ -133,6 +135,39 @@ status_t Surface::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
outTransformMatrix);
}
+bool Surface::getFrameTimestamps(uint64_t frameNumber, nsecs_t* outPostedTime,
+ nsecs_t* outAcquireTime, nsecs_t* outRefreshStartTime,
+ nsecs_t* outGlCompositionDoneTime, nsecs_t* outDisplayRetireTime,
+ nsecs_t* outReleaseTime) {
+ ATRACE_CALL();
+
+ FrameTimestamps timestamps;
+ bool found = mGraphicBufferProducer->getFrameTimestamps(frameNumber,
+ &timestamps);
+ if (found) {
+ if (outPostedTime) {
+ *outPostedTime = timestamps.postedTime;
+ }
+ if (outAcquireTime) {
+ *outAcquireTime = timestamps.acquireTime;
+ }
+ if (outRefreshStartTime) {
+ *outRefreshStartTime = timestamps.refreshStartTime;
+ }
+ if (outGlCompositionDoneTime) {
+ *outGlCompositionDoneTime = timestamps.glCompositionDoneTime;
+ }
+ if (outDisplayRetireTime) {
+ *outDisplayRetireTime = timestamps.displayRetireTime;
+ }
+ if (outReleaseTime) {
+ *outReleaseTime = timestamps.releaseTime;
+ }
+ return true;
+ }
+ return false;
+}
+
int Surface::hook_setSwapInterval(ANativeWindow* window, int interval) {
Surface* c = getSelf(window);
return c->setSwapInterval(interval);
@@ -259,8 +294,10 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
int buf = -1;
sp<Fence> fence;
+ nsecs_t now = systemTime();
status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence,
reqWidth, reqHeight, reqFormat, reqUsage);
+ mLastDequeueDuration = systemTime() - now;
if (result < 0) {
ALOGV("dequeueBuffer: IGraphicBufferProducer::dequeueBuffer"
@@ -463,7 +500,9 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
input.setSurfaceDamage(flippedRegion);
}
+ nsecs_t now = systemTime();
status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);
+ mLastQueueDuration = systemTime() - now;
if (err != OK) {
ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
}
@@ -471,7 +510,7 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
uint32_t numPendingBuffers = 0;
uint32_t hint = 0;
output.deflate(&mDefaultWidth, &mDefaultHeight, &hint,
- &numPendingBuffers);
+ &numPendingBuffers, &mNextFrameNumber);
// Disable transform hint if sticky transform is set.
if (mStickyTransform == 0) {
@@ -542,6 +581,20 @@ int Surface::query(int what, int* value) const {
}
return err;
}
+ case NATIVE_WINDOW_LAST_DEQUEUE_DURATION: {
+ int64_t durationUs = mLastDequeueDuration / 1000;
+ *value = durationUs > std::numeric_limits<int>::max() ?
+ std::numeric_limits<int>::max() :
+ static_cast<int>(durationUs);
+ return NO_ERROR;
+ }
+ case NATIVE_WINDOW_LAST_QUEUE_DURATION: {
+ int64_t durationUs = mLastQueueDuration / 1000;
+ *value = durationUs > std::numeric_limits<int>::max() ?
+ std::numeric_limits<int>::max() :
+ static_cast<int>(durationUs);
+ return NO_ERROR;
+ }
}
}
return mGraphicBufferProducer->query(what, value);
@@ -617,6 +670,9 @@ int Surface::perform(int operation, va_list args)
case NATIVE_WINDOW_SET_AUTO_REFRESH:
res = dispatchSetAutoRefresh(args);
break;
+ case NATIVE_WINDOW_GET_FRAME_TIMESTAMPS:
+ res = dispatchGetFrameTimestamps(args);
+ break;
default:
res = NAME_NOT_FOUND;
break;
@@ -737,6 +793,20 @@ int Surface::dispatchSetAutoRefresh(va_list args) {
return setAutoRefresh(autoRefresh);
}
+int Surface::dispatchGetFrameTimestamps(va_list args) {
+ uint32_t framesAgo = va_arg(args, uint32_t);
+ nsecs_t* outPostedTime = va_arg(args, int64_t*);
+ nsecs_t* outAcquireTime = va_arg(args, int64_t*);
+ nsecs_t* outRefreshStartTime = va_arg(args, int64_t*);
+ nsecs_t* outGlCompositionDoneTime = va_arg(args, int64_t*);
+ nsecs_t* outDisplayRetireTime = va_arg(args, int64_t*);
+ nsecs_t* outReleaseTime = va_arg(args, int64_t*);
+ bool ret = getFrameTimestamps(getNextFrameNumber() - 1 - framesAgo,
+ outPostedTime, outAcquireTime, outRefreshStartTime,
+ outGlCompositionDoneTime, outDisplayRetireTime, outReleaseTime);
+ return ret ? NO_ERROR : BAD_VALUE;
+}
+
int Surface::connect(int api) {
static sp<IProducerListener> listener = new DummyProducerListener();
return connect(api, listener);
@@ -752,7 +822,7 @@ int Surface::connect(int api, const sp<IProducerListener>& listener) {
uint32_t numPendingBuffers = 0;
uint32_t hint = 0;
output.deflate(&mDefaultWidth, &mDefaultHeight, &hint,
- &numPendingBuffers);
+ &numPendingBuffers, &mNextFrameNumber);
// Disable transform hint if sticky transform is set.
if (mStickyTransform == 0) {
@@ -774,14 +844,14 @@ int Surface::connect(int api, const sp<IProducerListener>& listener) {
}
-int Surface::disconnect(int api) {
+int Surface::disconnect(int api, IGraphicBufferProducer::DisconnectMode mode) {
ATRACE_CALL();
ALOGV("Surface::disconnect");
Mutex::Autolock lock(mMutex);
mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
mSharedBufferHasBeenQueued = false;
freeAllBuffers();
- int err = mGraphicBufferProducer->disconnect(api);
+ int err = mGraphicBufferProducer->disconnect(api, mode);
if (!err) {
mReqFormat = 0;
mReqWidth = 0;
@@ -1272,13 +1342,17 @@ status_t Surface::unlockAndPost()
bool Surface::waitForNextFrame(uint64_t lastFrame, nsecs_t timeout) {
Mutex::Autolock lock(mMutex);
- uint64_t currentFrame = mGraphicBufferProducer->getNextFrameNumber();
- if (currentFrame > lastFrame) {
+ if (mNextFrameNumber > lastFrame) {
return true;
}
return mQueueBufferCondition.waitRelative(mMutex, timeout) == OK;
}
+status_t Surface::getUniqueId(uint64_t* outId) const {
+ Mutex::Autolock lock(mMutex);
+ return mGraphicBufferProducer->getUniqueId(outId);
+}
+
namespace view {
status_t Surface::writeToParcel(Parcel* parcel) const {
@@ -1290,12 +1364,18 @@ status_t Surface::writeToParcel(Parcel* parcel, bool nameAlreadyWritten) const {
status_t res = OK;
- if (!nameAlreadyWritten) res = parcel->writeString16(name);
+ if (!nameAlreadyWritten) {
+ res = parcel->writeString16(name);
+ if (res != OK) return res;
- if (res == OK) {
- res = parcel->writeStrongBinder(
- IGraphicBufferProducer::asBinder(graphicBufferProducer));
+ /* isSingleBuffered defaults to no */
+ res = parcel->writeInt32(0);
+ if (res != OK) return res;
}
+
+ res = parcel->writeStrongBinder(
+ IGraphicBufferProducer::asBinder(graphicBufferProducer));
+
return res;
}
@@ -1306,13 +1386,20 @@ status_t Surface::readFromParcel(const Parcel* parcel) {
status_t Surface::readFromParcel(const Parcel* parcel, bool nameAlreadyRead) {
if (parcel == nullptr) return BAD_VALUE;
+ status_t res = OK;
if (!nameAlreadyRead) {
name = readMaybeEmptyString16(parcel);
+ // Discard this for now
+ int isSingleBuffered;
+ res = parcel->readInt32(&isSingleBuffered);
+ if (res != OK) {
+ return res;
+ }
}
sp<IBinder> binder;
- status_t res = parcel->readStrongBinder(&binder);
+ res = parcel->readStrongBinder(&binder);
if (res != OK) return res;
graphicBufferProducer = interface_cast<IGraphicBufferProducer>(binder);
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 2189047519..43506e9191 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -29,6 +29,8 @@
#include <binder/IMemory.h>
#include <binder/IServiceManager.h>
+#include <system/graphics.h>
+
#include <ui/DisplayInfo.h>
#include <gui/CpuConsumer.h>
@@ -165,11 +167,11 @@ public:
uint64_t frameNumber);
status_t setOverrideScalingMode(const sp<SurfaceComposerClient>& client,
const sp<IBinder>& id, int32_t overrideScalingMode);
- status_t setPositionAppliesWithResize(const sp<SurfaceComposerClient>& client,
+ status_t setGeometryAppliesWithResize(const sp<SurfaceComposerClient>& client,
const sp<IBinder>& id);
- void setDisplaySurface(const sp<IBinder>& token,
- const sp<IGraphicBufferProducer>& bufferProducer);
+ status_t setDisplaySurface(const sp<IBinder>& token,
+ sp<IGraphicBufferProducer> bufferProducer);
void setDisplayLayerStack(const sp<IBinder>& token, uint32_t layerStack);
void setDisplayProjection(const sp<IBinder>& token,
uint32_t orientation,
@@ -445,7 +447,7 @@ status_t Composer::setOverrideScalingMode(
return NO_ERROR;
}
-status_t Composer::setPositionAppliesWithResize(
+status_t Composer::setGeometryAppliesWithResize(
const sp<SurfaceComposerClient>& client,
const sp<IBinder>& id) {
Mutex::Autolock lock(mLock);
@@ -453,7 +455,7 @@ status_t Composer::setPositionAppliesWithResize(
if (!s) {
return BAD_INDEX;
}
- s->what |= layer_state_t::ePositionAppliesWithResize;
+ s->what |= layer_state_t::eGeometryAppliesWithResize;
return NO_ERROR;
}
@@ -471,12 +473,24 @@ DisplayState& Composer::getDisplayStateLocked(const sp<IBinder>& token) {
return mDisplayStates.editItemAt(static_cast<size_t>(index));
}
-void Composer::setDisplaySurface(const sp<IBinder>& token,
- const sp<IGraphicBufferProducer>& bufferProducer) {
+status_t Composer::setDisplaySurface(const sp<IBinder>& token,
+ sp<IGraphicBufferProducer> bufferProducer) {
+ if (bufferProducer.get() != nullptr) {
+ // Make sure that composition can never be stalled by a virtual display
+ // consumer that isn't processing buffers fast enough.
+ status_t err = bufferProducer->setAsyncMode(true);
+ if (err != NO_ERROR) {
+ ALOGE("Composer::setDisplaySurface Failed to enable async mode on the "
+ "BufferQueue. This BufferQueue cannot be used for virtual "
+ "display. (%d)", err);
+ return err;
+ }
+ }
Mutex::Autolock _l(mLock);
DisplayState& s(getDisplayStateLocked(token));
s.surface = bufferProducer;
s.what |= DisplayState::eSurfaceChanged;
+ return NO_ERROR;
}
void Composer::setDisplayLayerStack(const sp<IBinder>& token,
@@ -612,6 +626,14 @@ status_t SurfaceComposerClient::getLayerFrameStats(const sp<IBinder>& token,
return mClient->getLayerFrameStats(token, outStats);
}
+status_t SurfaceComposerClient::getTransformToDisplayInverse(const sp<IBinder>& token,
+ bool* outTransformToDisplayInverse) const {
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
+ return mClient->getTransformToDisplayInverse(token, outTransformToDisplayInverse);
+}
+
inline Composer& SurfaceComposerClient::getComposer() {
return mComposer;
}
@@ -699,16 +721,16 @@ status_t SurfaceComposerClient::setOverrideScalingMode(
this, id, overrideScalingMode);
}
-status_t SurfaceComposerClient::setPositionAppliesWithResize(
+status_t SurfaceComposerClient::setGeometryAppliesWithResize(
const sp<IBinder>& id) {
- return getComposer().setPositionAppliesWithResize(this, id);
+ return getComposer().setGeometryAppliesWithResize(this, id);
}
// ----------------------------------------------------------------------------
-void SurfaceComposerClient::setDisplaySurface(const sp<IBinder>& token,
- const sp<IGraphicBufferProducer>& bufferProducer) {
- Composer::getInstance().setDisplaySurface(token, bufferProducer);
+status_t SurfaceComposerClient::setDisplaySurface(const sp<IBinder>& token,
+ sp<IGraphicBufferProducer> bufferProducer) {
+ return Composer::getInstance().setDisplaySurface(token, bufferProducer);
}
void SurfaceComposerClient::setDisplayLayerStack(const sp<IBinder>& token,
@@ -763,6 +785,20 @@ status_t SurfaceComposerClient::setActiveConfig(const sp<IBinder>& display, int
return ComposerService::getComposerService()->setActiveConfig(display, id);
}
+status_t SurfaceComposerClient::getDisplayColorModes(const sp<IBinder>& display,
+ Vector<android_color_mode_t>* outColorModes) {
+ return ComposerService::getComposerService()->getDisplayColorModes(display, outColorModes);
+}
+
+android_color_mode_t SurfaceComposerClient::getActiveColorMode(const sp<IBinder>& display) {
+ return ComposerService::getComposerService()->getActiveColorMode(display);
+}
+
+status_t SurfaceComposerClient::setActiveColorMode(const sp<IBinder>& display,
+ android_color_mode_t colorMode) {
+ return ComposerService::getComposerService()->setActiveColorMode(display, colorMode);
+}
+
void SurfaceComposerClient::setDisplayPowerMode(const sp<IBinder>& token,
int mode) {
ComposerService::getComposerService()->setPowerMode(token, mode);
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index 4671e505aa..33c1d906e6 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -112,10 +112,10 @@ status_t SurfaceControl::setPosition(float x, float y) {
if (err < 0) return err;
return mClient->setPosition(mHandle, x, y);
}
-status_t SurfaceControl::setPositionAppliesWithResize() {
+status_t SurfaceControl::setGeometryAppliesWithResize() {
status_t err = validate();
if (err < 0) return err;
- return mClient->setPositionAppliesWithResize(mHandle);
+ return mClient->setGeometryAppliesWithResize(mHandle);
}
status_t SurfaceControl::setSize(uint32_t w, uint32_t h) {
status_t err = validate();
@@ -190,6 +190,13 @@ status_t SurfaceControl::getLayerFrameStats(FrameStats* outStats) const {
return client->getLayerFrameStats(mHandle, outStats);
}
+status_t SurfaceControl::getTransformToDisplayInverse(bool* outTransformToDisplayInverse) const {
+ status_t err = validate();
+ if (err < 0) return err;
+ const sp<SurfaceComposerClient>& client(mClient);
+ return client->getTransformToDisplayInverse(mHandle, outTransformToDisplayInverse);
+}
+
status_t SurfaceControl::validate() const
{
if (mHandle==0 || mClient==0) {
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 85d63b4ba2..65df7dc991 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -34,6 +34,10 @@
#include <gtest/gtest.h>
+#include <thread>
+
+using namespace std::chrono_literals;
+
namespace android {
class BufferQueueTest : public ::testing::Test {
@@ -850,4 +854,217 @@ TEST_F(BufferQueueTest, CanRetrieveLastQueuedBuffer) {
returnedBuffer->getNativeBuffer()->handle);
}
+TEST_F(BufferQueueTest, TestOccupancyHistory) {
+ createBufferQueue();
+ sp<DummyConsumer> dc(new DummyConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
+ IGraphicBufferProducer::QueueBufferOutput output;
+ ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
+ NATIVE_WINDOW_API_CPU, false, &output));
+
+ int slot = BufferQueue::INVALID_BUFFER_SLOT;
+ sp<Fence> fence = Fence::NO_FENCE;
+ sp<GraphicBuffer> buffer = nullptr;
+ IGraphicBufferProducer::QueueBufferInput input(0ull, true,
+ HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
+ BufferItem item{};
+
+ // Preallocate, dequeue, request, and cancel 3 buffers so we don't get
+ // BUFFER_NEEDS_REALLOCATION below
+ int slots[3] = {};
+ mProducer->setMaxDequeuedBufferCount(3);
+ for (size_t i = 0; i < 3; ++i) {
+ status_t result = mProducer->dequeueBuffer(&slots[i], &fence,
+ 0, 0, 0, 0);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
+ ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer));
+ }
+ for (size_t i = 0; i < 3; ++i) {
+ ASSERT_EQ(OK, mProducer->cancelBuffer(slots[i], Fence::NO_FENCE));
+ }
+
+ // Create 3 segments
+
+ // The first segment is a two-buffer segment, so we only put one buffer into
+ // the queue at a time
+ for (size_t i = 0; i < 5; ++i) {
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ std::this_thread::sleep_for(16ms);
+ }
+
+ // Sleep between segments
+ std::this_thread::sleep_for(500ms);
+
+ // The second segment is a double-buffer segment. It starts the same as the
+ // two-buffer segment, but then at the end, we put two buffers in the queue
+ // at the same time before draining it.
+ for (size_t i = 0; i < 5; ++i) {
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ std::this_thread::sleep_for(16ms);
+ }
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ std::this_thread::sleep_for(16ms);
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+
+ // Sleep between segments
+ std::this_thread::sleep_for(500ms);
+
+ // The third segment is a triple-buffer segment, so the queue is switching
+ // between one buffer and two buffers deep.
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+ for (size_t i = 0; i < 5; ++i) {
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ std::this_thread::sleep_for(16ms);
+ }
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+
+ // Now we read the segments
+ std::vector<OccupancyTracker::Segment> history;
+ ASSERT_EQ(OK, mConsumer->getOccupancyHistory(false, &history));
+
+ // Since we didn't force a flush, we should only get the first two segments
+ // (since the third segment hasn't been closed out by the appearance of a
+ // new segment yet)
+ ASSERT_EQ(2u, history.size());
+
+ // The first segment (which will be history[1], since the newest segment
+ // should be at the front of the vector) should be a two-buffer segment,
+ // which implies that the occupancy average should be between 0 and 1, and
+ // usedThirdBuffer should be false
+ const auto& firstSegment = history[1];
+ ASSERT_EQ(5u, firstSegment.numFrames);
+ ASSERT_LT(0, firstSegment.occupancyAverage);
+ ASSERT_GT(1, firstSegment.occupancyAverage);
+ ASSERT_EQ(false, firstSegment.usedThirdBuffer);
+
+ // The second segment should be a double-buffered segment, which implies that
+ // the occupancy average should be between 0 and 1, but usedThirdBuffer
+ // should be true
+ const auto& secondSegment = history[0];
+ ASSERT_EQ(7u, secondSegment.numFrames);
+ ASSERT_LT(0, secondSegment.occupancyAverage);
+ ASSERT_GT(1, secondSegment.occupancyAverage);
+ ASSERT_EQ(true, secondSegment.usedThirdBuffer);
+
+ // If we read the segments again without flushing, we shouldn't get any new
+ // segments
+ ASSERT_EQ(OK, mConsumer->getOccupancyHistory(false, &history));
+ ASSERT_EQ(0u, history.size());
+
+ // Read the segments again, this time forcing a flush so we get the third
+ // segment
+ ASSERT_EQ(OK, mConsumer->getOccupancyHistory(true, &history));
+ ASSERT_EQ(1u, history.size());
+
+ // This segment should be a triple-buffered segment, which implies that the
+ // occupancy average should be between 1 and 2, and usedThirdBuffer should
+ // be true
+ const auto& thirdSegment = history[0];
+ ASSERT_EQ(6u, thirdSegment.numFrames);
+ ASSERT_LT(1, thirdSegment.occupancyAverage);
+ ASSERT_GT(2, thirdSegment.occupancyAverage);
+ ASSERT_EQ(true, thirdSegment.usedThirdBuffer);
+}
+
+TEST_F(BufferQueueTest, TestDiscardFreeBuffers) {
+ createBufferQueue();
+ sp<DummyConsumer> dc(new DummyConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
+ IGraphicBufferProducer::QueueBufferOutput output;
+ ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
+ NATIVE_WINDOW_API_CPU, false, &output));
+
+ int slot = BufferQueue::INVALID_BUFFER_SLOT;
+ sp<Fence> fence = Fence::NO_FENCE;
+ sp<GraphicBuffer> buffer = nullptr;
+ IGraphicBufferProducer::QueueBufferInput input(0ull, true,
+ HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
+ BufferItem item{};
+
+ // Preallocate, dequeue, request, and cancel 4 buffers so we don't get
+ // BUFFER_NEEDS_REALLOCATION below
+ int slots[4] = {};
+ mProducer->setMaxDequeuedBufferCount(4);
+ for (size_t i = 0; i < 4; ++i) {
+ status_t result = mProducer->dequeueBuffer(&slots[i], &fence,
+ 0, 0, 0, 0);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
+ ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer));
+ }
+ for (size_t i = 0; i < 4; ++i) {
+ ASSERT_EQ(OK, mProducer->cancelBuffer(slots[i], Fence::NO_FENCE));
+ }
+
+ // Get buffers in all states: dequeued, filled, acquired, free
+
+ // Fill 3 buffers
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+ // Dequeue 1 buffer
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+
+ // Acquire and free 1 buffer
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ // Acquire 1 buffer, leaving 1 filled buffer in queue
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+
+ // Now discard the free buffers
+ ASSERT_EQ(OK, mConsumer->discardFreeBuffers());
+
+ // Check no free buffers in dump
+ String8 dumpString;
+ mConsumer->dumpState(dumpString, nullptr);
+
+ // Parse the dump to ensure that all buffer slots that are FREE also
+ // have a null GraphicBuffer
+ // Fragile - assumes the following format for the dump for a buffer entry:
+ // ":%p\][^:]*state=FREE" where %p is the buffer pointer in hex.
+ ssize_t idx = dumpString.find("state=FREE");
+ while (idx != -1) {
+ ssize_t bufferPtrIdx = idx - 1;
+ while (bufferPtrIdx > 0) {
+ if (dumpString[bufferPtrIdx] == ':') {
+ bufferPtrIdx++;
+ break;
+ }
+ bufferPtrIdx--;
+ }
+ ASSERT_GT(bufferPtrIdx, 0) << "Can't parse queue dump to validate";
+ ssize_t nullPtrIdx = dumpString.find("0x0]", bufferPtrIdx);
+ ASSERT_EQ(bufferPtrIdx, nullPtrIdx) << "Free buffer not discarded";
+ idx = dumpString.find("FREE", idx + 1);
+ }
+}
+
} // namespace android
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index 45b64639d2..9f3304731e 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -370,13 +370,16 @@ TEST_F(IGraphicBufferProducerTest, Queue_Succeeds) {
uint32_t height;
uint32_t transformHint;
uint32_t numPendingBuffers;
+ uint64_t nextFrameNumber;
- output.deflate(&width, &height, &transformHint, &numPendingBuffers);
+ output.deflate(&width, &height, &transformHint, &numPendingBuffers,
+ &nextFrameNumber);
EXPECT_EQ(DEFAULT_WIDTH, width);
EXPECT_EQ(DEFAULT_HEIGHT, height);
EXPECT_EQ(DEFAULT_TRANSFORM_HINT, transformHint);
EXPECT_EQ(1u, numPendingBuffers); // since queueBuffer was called exactly once
+ EXPECT_EQ(2u, nextFrameNumber);
}
// Buffer was not in the dequeued state
diff --git a/libs/gui/tests/SurfaceTextureGL_test.cpp b/libs/gui/tests/SurfaceTextureGL_test.cpp
index 81d5a57eb0..308bd7d69c 100644
--- a/libs/gui/tests/SurfaceTextureGL_test.cpp
+++ b/libs/gui/tests/SurfaceTextureGL_test.cpp
@@ -115,13 +115,13 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferPow2) {
EXPECT_TRUE(checkPixel(63, 63, 0, 133, 0, 255));
EXPECT_TRUE(checkPixel( 0, 63, 255, 127, 255, 255));
- EXPECT_TRUE(checkPixel(22, 19, 100, 255, 74, 255));
- EXPECT_TRUE(checkPixel(45, 11, 100, 255, 74, 255));
- EXPECT_TRUE(checkPixel(52, 12, 155, 0, 181, 255));
- EXPECT_TRUE(checkPixel( 7, 32, 150, 237, 170, 255));
- EXPECT_TRUE(checkPixel(31, 54, 0, 71, 117, 255));
- EXPECT_TRUE(checkPixel(29, 28, 0, 133, 0, 255));
- EXPECT_TRUE(checkPixel(36, 41, 100, 232, 255, 255));
+ EXPECT_TRUE(checkPixel(22, 19, 100, 255, 74, 255, 3));
+ EXPECT_TRUE(checkPixel(45, 11, 100, 255, 74, 255, 3));
+ EXPECT_TRUE(checkPixel(52, 12, 155, 0, 181, 255, 3));
+ EXPECT_TRUE(checkPixel( 7, 32, 150, 237, 170, 255, 3));
+ EXPECT_TRUE(checkPixel(31, 54, 0, 71, 117, 255, 3));
+ EXPECT_TRUE(checkPixel(29, 28, 0, 133, 0, 255, 3));
+ EXPECT_TRUE(checkPixel(36, 41, 100, 232, 255, 255, 3));
}
TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) {
diff --git a/libs/ui/Fence.cpp b/libs/ui/Fence.cpp
index bf24ffb7e0..7cf8233820 100644
--- a/libs/ui/Fence.cpp
+++ b/libs/ui/Fence.cpp
@@ -72,7 +72,7 @@ status_t Fence::waitForever(const char* logname) {
return err < 0 ? -errno : status_t(NO_ERROR);
}
-sp<Fence> Fence::merge(const String8& name, const sp<Fence>& f1,
+sp<Fence> Fence::merge(const char* name, const sp<Fence>& f1,
const sp<Fence>& f2) {
ATRACE_CALL();
int result;
@@ -80,24 +80,29 @@ sp<Fence> Fence::merge(const String8& name, const sp<Fence>& f1,
// valid fence (e.g. NO_FENCE) we merge the one valid fence with itself so
// that a new fence with the given name is created.
if (f1->isValid() && f2->isValid()) {
- result = sync_merge(name.string(), f1->mFenceFd, f2->mFenceFd);
+ result = sync_merge(name, f1->mFenceFd, f2->mFenceFd);
} else if (f1->isValid()) {
- result = sync_merge(name.string(), f1->mFenceFd, f1->mFenceFd);
+ result = sync_merge(name, f1->mFenceFd, f1->mFenceFd);
} else if (f2->isValid()) {
- result = sync_merge(name.string(), f2->mFenceFd, f2->mFenceFd);
+ result = sync_merge(name, f2->mFenceFd, f2->mFenceFd);
} else {
return NO_FENCE;
}
if (result == -1) {
status_t err = -errno;
ALOGE("merge: sync_merge(\"%s\", %d, %d) returned an error: %s (%d)",
- name.string(), f1->mFenceFd, f2->mFenceFd,
+ name, f1->mFenceFd, f2->mFenceFd,
strerror(-err), err);
return NO_FENCE;
}
return sp<Fence>(new Fence(result));
}
+sp<Fence> Fence::merge(const String8& name, const sp<Fence>& f1,
+ const sp<Fence>& f2) {
+ return merge(name.string(), f1, f2);
+}
+
int Fence::dup() const {
return ::dup(mFenceFd);
}
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index a430a31058..b53c563624 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -796,6 +796,11 @@ status_t Region::unflatten(void const* buffer, size_t size) {
return NO_MEMORY;
}
+ if (numRects > (UINT32_MAX / sizeof(Rect))) {
+ android_errorWriteWithInfoLog(0x534e4554, "29983260", -1, NULL, 0);
+ return NO_MEMORY;
+ }
+
Region result;
result.mStorage.clear();
for (size_t r = 0; r < numRects; ++r) {