diff options
Diffstat (limited to 'libs')
| -rw-r--r-- | libs/gui/Android.bp | 1 | ||||
| -rw-r--r-- | libs/gui/BufferQueue.cpp | 9 | ||||
| -rw-r--r-- | libs/gui/BufferQueueConsumer.cpp | 14 | ||||
| -rw-r--r-- | libs/gui/BufferQueueCore.cpp | 20 | ||||
| -rw-r--r-- | libs/gui/BufferQueueProducer.cpp | 87 | ||||
| -rw-r--r-- | libs/gui/ConsumerBase.cpp | 24 | ||||
| -rw-r--r-- | libs/gui/IConsumerListener.cpp | 61 | ||||
| -rw-r--r-- | libs/gui/IGraphicBufferConsumer.cpp | 67 | ||||
| -rw-r--r-- | libs/gui/IGraphicBufferProducer.cpp | 151 | ||||
| -rw-r--r-- | libs/gui/IProducerListener.cpp | 26 | ||||
| -rw-r--r-- | libs/gui/ISurfaceComposer.cpp | 128 | ||||
| -rw-r--r-- | libs/gui/ISurfaceComposerClient.cpp | 51 | ||||
| -rw-r--r-- | libs/gui/OccupancyTracker.cpp | 117 | ||||
| -rw-r--r-- | libs/gui/Surface.cpp | 113 | ||||
| -rw-r--r-- | libs/gui/SurfaceComposerClient.cpp | 60 | ||||
| -rw-r--r-- | libs/gui/SurfaceControl.cpp | 11 | ||||
| -rw-r--r-- | libs/gui/tests/BufferQueue_test.cpp | 217 | ||||
| -rw-r--r-- | libs/gui/tests/IGraphicBufferProducer_test.cpp | 5 | ||||
| -rw-r--r-- | libs/gui/tests/SurfaceTextureGL_test.cpp | 14 | ||||
| -rw-r--r-- | libs/ui/Fence.cpp | 15 | ||||
| -rw-r--r-- | libs/ui/Region.cpp | 5 |
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, ×tamps); + 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, ×tamps); + 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, + ×tamps); + 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) { |