From 98318de954ba00293cfd179266f09f266dc1c82b Mon Sep 17 00:00:00 2001 From: chaviw Date: Wed, 19 May 2021 16:45:23 -0500 Subject: Renamed and moved InputWindow and related files In preparation for the hierarchy listener interface, moved the InputWindow structs into libgui and have libinput dependant on libgui. Also renamed InputWindow to exclude Input since it will be used for more generic purposes. Test: Builds and flashes Bug: 188792659 Change-Id: I24262cbc14d409c00273de0024a672394a959e5f --- libs/gui/BLASTBufferQueue.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'libs/gui/BLASTBufferQueue.cpp') diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 29701168e7..dbb1cb0c17 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -38,7 +38,7 @@ using namespace std::chrono_literals; namespace { -inline const char* toString(bool b) { +inline const char* boolToString(bool b) { return b ? "true" : "false"; } } // namespace @@ -513,7 +513,7 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { BQA_LOGV("processNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64 " applyTransaction=%s mTimestamp=%" PRId64 "%s mPendingTransactions.size=%d" " graphicBufferId=%" PRIu64 "%s", - mSize.width, mSize.height, bufferItem.mFrameNumber, toString(applyTransaction), + mSize.width, mSize.height, bufferItem.mFrameNumber, boolToString(applyTransaction), bufferItem.mTimestamp, bufferItem.mIsAutoTimestamp ? "(auto)" : "", static_cast(mPendingTransactions.size()), bufferItem.mGraphicBuffer->getId(), bufferItem.mAutoRefresh ? " mAutoRefresh" : ""); @@ -543,7 +543,7 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { mNumFrameAvailable + mNumAcquired - mPendingRelease.size()); BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " nextTransactionSet=%s", item.mFrameNumber, - toString(nextTransactionSet)); + boolToString(nextTransactionSet)); processNextBufferLocked(nextTransactionSet /* useNextTransaction */); } -- cgit v1.2.3-59-g8ed1b From 0b020f857920d49b8fc421dceea40182865ef336 Mon Sep 17 00:00:00 2001 From: chaviw Date: Fri, 20 Aug 2021 12:00:47 -0500 Subject: Added getLastAcquiredFrameNum This will allow VRI to ask BBQ what buffer was actually acquired on the last draw. Test: blast sync Bug: 195262673 Change-Id: I492651e8e6d333ef11b682cec939d81057ae197d --- libs/gui/BLASTBufferQueue.cpp | 5 +++++ libs/gui/include/gui/BLASTBufferQueue.h | 1 + 2 files changed, 6 insertions(+) (limited to 'libs/gui/BLASTBufferQueue.cpp') diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index e5a2151f13..33335aacb5 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -837,4 +837,9 @@ uint32_t BLASTBufferQueue::getLastTransformHint() const { } } +uint64_t BLASTBufferQueue::getLastAcquiredFrameNum() { + std::unique_lock _lock{mMutex}; + return mLastAcquiredFrameNumber; +} + } // namespace android diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 6c5b2aa53c..be46180bf7 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -107,6 +107,7 @@ public: void setSidebandStream(const sp& stream); uint32_t getLastTransformHint() const; + uint64_t getLastAcquiredFrameNum(); virtual ~BLASTBufferQueue(); -- cgit v1.2.3-59-g8ed1b From 6a1952737262e9584098f37ade1f4406172c890f Mon Sep 17 00:00:00 2001 From: chaviw Date: Fri, 3 Sep 2021 16:14:25 -0500 Subject: Add applyPendingTransactions It's possible for a caller to request mergeWithNextTransaction, but the main frame had nothing new to draw. If that's the case, the mergeWithNextTransactions will be stuck and never applied (or applied much later). Since this could end up blocking Transactions, it's better to force apply these when we know a frame wasn't going to draw this vsync. Adding applyPendingTransactions to allows callers to force apply these transactions. Test: Existing tests pass Bug: 195262673 Change-Id: I3082b54e35dfae2b3f7fe589c6f665f781d8b07b --- libs/gui/BLASTBufferQueue.cpp | 42 +++++++++++++++++++++------------ libs/gui/include/gui/BLASTBufferQueue.h | 3 +++ 2 files changed, 30 insertions(+), 15 deletions(-) (limited to 'libs/gui/BLASTBufferQueue.cpp') diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 982dd63a8c..379b090c5b 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -508,21 +508,7 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { } } - auto mergeTransaction = - [&t, currentFrameNumber = bufferItem.mFrameNumber]( - std::tuple pendingTransaction) { - auto& [targetFrameNumber, transaction] = pendingTransaction; - if (currentFrameNumber < targetFrameNumber) { - return false; - } - t->merge(std::move(transaction)); - return true; - }; - - mPendingTransactions.erase(std::remove_if(mPendingTransactions.begin(), - mPendingTransactions.end(), mergeTransaction), - mPendingTransactions.end()); - + mergePendingTransactions(t, bufferItem.mFrameNumber); if (applyTransaction) { t->setApplyToken(mApplyToken).apply(); } @@ -726,6 +712,32 @@ void BLASTBufferQueue::mergeWithNextTransaction(SurfaceComposerClient::Transacti } } +void BLASTBufferQueue::applyPendingTransactions(uint64_t frameNumber) { + std::lock_guard _lock{mMutex}; + + SurfaceComposerClient::Transaction t; + mergePendingTransactions(&t, frameNumber); + t.setApplyToken(mApplyToken).apply(); +} + +void BLASTBufferQueue::mergePendingTransactions(SurfaceComposerClient::Transaction* t, + uint64_t frameNumber) { + auto mergeTransaction = + [&t, currentFrameNumber = frameNumber]( + std::tuple pendingTransaction) { + auto& [targetFrameNumber, transaction] = pendingTransaction; + if (currentFrameNumber < targetFrameNumber) { + return false; + } + t->merge(std::move(transaction)); + return true; + }; + + mPendingTransactions.erase(std::remove_if(mPendingTransactions.begin(), + mPendingTransactions.end(), mergeTransaction), + mPendingTransactions.end()); +} + // Maintains a single worker thread per process that services a list of runnables. class AsyncWorker : public Singleton { private: diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index be46180bf7..86480e36ae 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -94,6 +94,7 @@ public: uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount); void setNextTransaction(SurfaceComposerClient::Transaction *t); void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber); + void applyPendingTransactions(uint64_t frameNumber); void setTransactionCompleteCallback(uint64_t frameNumber, std::function&& transactionCompleteCallback); @@ -126,6 +127,8 @@ private: bool rejectBuffer(const BufferItem& item) REQUIRES(mMutex); bool maxBuffersAcquired(bool includeExtraAcquire) const REQUIRES(mMutex); static PixelFormat convertBufferFormat(PixelFormat& format); + void mergePendingTransactions(SurfaceComposerClient::Transaction* t, uint64_t frameNumber) + REQUIRES(mMutex); std::string mName; // Represents the queued buffer count from buffer queue, -- cgit v1.2.3-59-g8ed1b From ba4320c070e62f3ef7aea27c04684f5b53c5707d Mon Sep 17 00:00:00 2001 From: chaviw Date: Wed, 15 Sep 2021 15:20:53 -0500 Subject: Combine Buffer Properties when calling setBuffer There are several properties, like releaseCallback, acquireFence, frameNumber, that are tied to the buffer being set. If a new buffer is set, all those properties should also be overwritten, rather than through a separate call. 1. Remove Transaction.setAcquireFence and Transaction.setFrameNumber and added them to the setBuffer as optional 2. Combine all the buffer info into a struct called BufferData that's sent to SF as one. This will also help with merging or overwriting buffer data since the callback, frameNumber, and acquireFence should also be updated. 3. Combine the functions in SF so there's no longer a separate call to BSL to set fence. Instead, send all buffer related data to BSL.setBuffer Test: SurfaceFlinger_test Fixes: 200065015 Change-Id: I53ad12dd105cd4cac6c3a7ecd48279d1b3cd2b8f --- libs/gui/BLASTBufferQueue.cpp | 7 +- libs/gui/LayerState.cpp | 139 ++++++++------- libs/gui/SurfaceComposerClient.cpp | 86 ++++----- libs/gui/include/gui/LayerState.h | 71 +++++--- libs/gui/include/gui/SurfaceComposerClient.h | 9 +- services/surfaceflinger/BufferLayer.cpp | 8 - services/surfaceflinger/BufferLayer.h | 4 - services/surfaceflinger/BufferQueueLayer.cpp | 33 ---- services/surfaceflinger/BufferQueueLayer.h | 1 - services/surfaceflinger/BufferStateLayer.cpp | 111 ++++++------ services/surfaceflinger/BufferStateLayer.h | 16 +- services/surfaceflinger/Layer.h | 14 +- services/surfaceflinger/SurfaceFlinger.cpp | 52 +----- services/surfaceflinger/SurfaceFlinger.h | 8 +- services/surfaceflinger/tests/IPC_test.cpp | 6 +- .../surfaceflinger/tests/LayerCallback_test.cpp | 3 +- .../tests/LayerRenderTypeTransaction_test.cpp | 4 +- .../tests/ReleaseBufferCallback_test.cpp | 18 +- .../tests/unittests/TransactionFrameTracerTest.cpp | 22 +-- .../unittests/TransactionSurfaceFrameTest.cpp | 196 +++++++++++---------- 20 files changed, 361 insertions(+), 447 deletions(-) (limited to 'libs/gui/BLASTBufferQueue.cpp') diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 379b090c5b..cc209f39f5 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -468,12 +468,12 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { std::bind(releaseBufferCallbackThunk, wp(this) /* callbackContext */, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4); - t->setBuffer(mSurfaceControl, buffer, releaseCallbackId, releaseBufferCallback); + sp fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE; + t->setBuffer(mSurfaceControl, buffer, fence, bufferItem.mFrameNumber, releaseCallbackId, + releaseBufferCallback); t->setDataspace(mSurfaceControl, static_cast(bufferItem.mDataSpace)); t->setHdrMetadata(mSurfaceControl, bufferItem.mHdrMetadata); t->setSurfaceDamageRegion(mSurfaceControl, bufferItem.mSurfaceDamage); - t->setAcquireFence(mSurfaceControl, - bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE); t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast(this)); mSurfaceControlsWithPendingCallback.push(mSurfaceControl); @@ -486,7 +486,6 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { if (!bufferItem.mIsAutoTimestamp) { t->setDesiredPresentTime(bufferItem.mTimestamp); } - t->setFrameNumber(mSurfaceControl, bufferItem.mFrameNumber); if (!mNextFrameTimelineInfoQueue.empty()) { t->setFrameTimelineInfo(mNextFrameTimelineInfoQueue.front()); diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index a419a63056..cf61209d6c 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -64,12 +64,10 @@ layer_state_t::layer_state_t() frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT), changeFrameRateStrategy(ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS), fixedTransformHint(ui::Transform::ROT_INVALID), - frameNumber(0), autoRefresh(false), isTrustedOverlay(false), bufferCrop(Rect::INVALID_RECT), destinationFrame(Rect::INVALID_RECT), - releaseBufferListener(nullptr), dropInputMode(gui::DropInputMode::NONE) { matrix.dsdx = matrix.dtdy = 1.0f; matrix.dsdy = matrix.dtdx = 0.0f; @@ -104,20 +102,6 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.writeBool, transformToDisplayInverse); SAFE_PARCEL(output.write, orientedDisplaySpaceRect); - if (buffer) { - SAFE_PARCEL(output.writeBool, true); - SAFE_PARCEL(output.write, *buffer); - } else { - SAFE_PARCEL(output.writeBool, false); - } - - if (acquireFence) { - SAFE_PARCEL(output.writeBool, true); - SAFE_PARCEL(output.write, *acquireFence); - } else { - SAFE_PARCEL(output.writeBool, false); - } - SAFE_PARCEL(output.writeUint32, static_cast(dataspace)); SAFE_PARCEL(output.write, hdrMetadata); SAFE_PARCEL(output.write, surfaceDamageRegion); @@ -133,8 +117,6 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.write, colorTransform.asArray(), 16 * sizeof(float)); SAFE_PARCEL(output.writeFloat, cornerRadius); SAFE_PARCEL(output.writeUint32, backgroundBlurRadius); - SAFE_PARCEL(output.writeStrongBinder, cachedBuffer.token.promote()); - SAFE_PARCEL(output.writeUint64, cachedBuffer.id); SAFE_PARCEL(output.writeParcelable, metadata); SAFE_PARCEL(output.writeFloat, bgColorAlpha); SAFE_PARCEL(output.writeUint32, static_cast(bgColorDataspace)); @@ -151,9 +133,7 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.writeByte, frameRateCompatibility); SAFE_PARCEL(output.writeByte, changeFrameRateStrategy); SAFE_PARCEL(output.writeUint32, fixedTransformHint); - SAFE_PARCEL(output.writeUint64, frameNumber); SAFE_PARCEL(output.writeBool, autoRefresh); - SAFE_PARCEL(output.writeStrongBinder, IInterface::asBinder(releaseBufferListener)); SAFE_PARCEL(output.writeUint32, blurRegions.size()); for (auto region : blurRegions) { @@ -174,8 +154,8 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.write, destinationFrame); SAFE_PARCEL(output.writeBool, isTrustedOverlay); - SAFE_PARCEL(output.writeStrongBinder, releaseBufferEndpoint); SAFE_PARCEL(output.writeUint32, static_cast(dropInputMode)); + SAFE_PARCEL(bufferData.write, output); return NO_ERROR; } @@ -217,19 +197,6 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.readBool, &transformToDisplayInverse); SAFE_PARCEL(input.read, orientedDisplaySpaceRect); - bool tmpBool = false; - SAFE_PARCEL(input.readBool, &tmpBool); - if (tmpBool) { - buffer = new GraphicBuffer(); - SAFE_PARCEL(input.read, *buffer); - } - - SAFE_PARCEL(input.readBool, &tmpBool); - if (tmpBool) { - acquireFence = new Fence(); - SAFE_PARCEL(input.read, *acquireFence); - } - uint32_t tmpUint32 = 0; SAFE_PARCEL(input.readUint32, &tmpUint32); dataspace = static_cast(tmpUint32); @@ -237,6 +204,8 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.read, hdrMetadata); SAFE_PARCEL(input.read, surfaceDamageRegion); SAFE_PARCEL(input.readInt32, &api); + + bool tmpBool = false; SAFE_PARCEL(input.readBool, &tmpBool); if (tmpBool) { sidebandStream = NativeHandle::create(input.readNativeHandle(), true); @@ -245,10 +214,6 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.read, &colorTransform, 16 * sizeof(float)); SAFE_PARCEL(input.readFloat, &cornerRadius); SAFE_PARCEL(input.readUint32, &backgroundBlurRadius); - sp tmpBinder; - SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder); - cachedBuffer.token = tmpBinder; - SAFE_PARCEL(input.readUint64, &cachedBuffer.id); SAFE_PARCEL(input.readParcelable, &metadata); SAFE_PARCEL(input.readFloat, &bgColorAlpha); @@ -273,15 +238,8 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.readByte, &changeFrameRateStrategy); SAFE_PARCEL(input.readUint32, &tmpUint32); fixedTransformHint = static_cast(tmpUint32); - SAFE_PARCEL(input.readUint64, &frameNumber); SAFE_PARCEL(input.readBool, &autoRefresh); - tmpBinder = nullptr; - SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder); - if (tmpBinder) { - releaseBufferListener = checked_interface_cast(tmpBinder); - } - uint32_t numRegions = 0; SAFE_PARCEL(input.readUint32, &numRegions); blurRegions.clear(); @@ -305,11 +263,10 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.read, destinationFrame); SAFE_PARCEL(input.readBool, &isTrustedOverlay); - SAFE_PARCEL(input.readNullableStrongBinder, &releaseBufferEndpoint); - uint32_t mode; SAFE_PARCEL(input.readUint32, &mode); dropInputMode = static_cast(mode); + SAFE_PARCEL(bufferData.read, input); return NO_ERROR; } @@ -460,12 +417,7 @@ void layer_state_t::merge(const layer_state_t& other) { } if (other.what & eBufferChanged) { what |= eBufferChanged; - buffer = other.buffer; - releaseBufferEndpoint = other.releaseBufferEndpoint; - } - if (other.what & eAcquireFenceChanged) { - what |= eAcquireFenceChanged; - acquireFence = other.acquireFence; + bufferData = other.bufferData; } if (other.what & eDataspaceChanged) { what |= eDataspaceChanged; @@ -498,10 +450,6 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eInputInfoChanged; windowInfoHandle = new WindowInfoHandle(*other.windowInfoHandle); } - if (other.what & eCachedBufferChanged) { - what |= eCachedBufferChanged; - cachedBuffer = other.cachedBuffer; - } if (other.what & eBackgroundColorChanged) { what |= eBackgroundColorChanged; color = other.color; @@ -530,10 +478,6 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eFixedTransformHintChanged; fixedTransformHint = other.fixedTransformHint; } - if (other.what & eFrameNumberChanged) { - what |= eFrameNumberChanged; - frameNumber = other.frameNumber; - } if (other.what & eAutoRefreshChanged) { what |= eAutoRefreshChanged; autoRefresh = other.autoRefresh; @@ -542,13 +486,6 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eTrustedOverlayChanged; isTrustedOverlay = other.isTrustedOverlay; } - if (other.what & eReleaseBufferListenerChanged) { - if (releaseBufferListener) { - ALOGW("Overriding releaseBufferListener"); - } - what |= eReleaseBufferListenerChanged; - releaseBufferListener = other.releaseBufferListener; - } if (other.what & eStretchChanged) { what |= eStretchChanged; stretchEffect = other.stretchEffect; @@ -576,11 +513,11 @@ void layer_state_t::merge(const layer_state_t& other) { } bool layer_state_t::hasBufferChanges() const { - return (what & layer_state_t::eBufferChanged) || (what & layer_state_t::eCachedBufferChanged); + return what & layer_state_t::eBufferChanged; } bool layer_state_t::hasValidBuffer() const { - return buffer || cachedBuffer.isValid(); + return bufferData.buffer || bufferData.cachedBuffer.isValid(); } status_t layer_state_t::matrix22_t::write(Parcel& output) const { @@ -741,4 +678,66 @@ status_t LayerCaptureArgs::read(const Parcel& input) { return NO_ERROR; } +status_t BufferData::write(Parcel& output) const { + SAFE_PARCEL(output.writeInt32, flags.get()); + + if (buffer) { + SAFE_PARCEL(output.writeBool, true); + SAFE_PARCEL(output.write, *buffer); + } else { + SAFE_PARCEL(output.writeBool, false); + } + + if (acquireFence) { + SAFE_PARCEL(output.writeBool, true); + SAFE_PARCEL(output.write, *acquireFence); + } else { + SAFE_PARCEL(output.writeBool, false); + } + + SAFE_PARCEL(output.writeUint64, frameNumber); + SAFE_PARCEL(output.writeStrongBinder, IInterface::asBinder(releaseBufferListener)); + SAFE_PARCEL(output.writeStrongBinder, releaseBufferEndpoint); + + SAFE_PARCEL(output.writeStrongBinder, cachedBuffer.token.promote()); + SAFE_PARCEL(output.writeUint64, cachedBuffer.id); + + return NO_ERROR; +} + +status_t BufferData::read(const Parcel& input) { + int32_t tmpInt32; + SAFE_PARCEL(input.readInt32, &tmpInt32); + flags = Flags(tmpInt32); + + bool tmpBool = false; + SAFE_PARCEL(input.readBool, &tmpBool); + if (tmpBool) { + buffer = new GraphicBuffer(); + SAFE_PARCEL(input.read, *buffer); + } + + SAFE_PARCEL(input.readBool, &tmpBool); + if (tmpBool) { + acquireFence = new Fence(); + SAFE_PARCEL(input.read, *acquireFence); + } + + SAFE_PARCEL(input.readUint64, &frameNumber); + + sp tmpBinder = nullptr; + SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder); + if (tmpBinder) { + releaseBufferListener = checked_interface_cast(tmpBinder); + } + SAFE_PARCEL(input.readNullableStrongBinder, &releaseBufferEndpoint); + + tmpBinder = nullptr; + SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder); + cachedBuffer.token = tmpBinder; + SAFE_PARCEL(input.readUint64, &cachedBuffer.id); + + return NO_ERROR; +} + }; // namespace android diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index bbd3cca6ce..aca59b6d3b 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -805,7 +805,7 @@ void SurfaceComposerClient::Transaction::cacheBuffers() { layer_state_t* s = &(mComposerStates[handle].state); if (!(s->what & layer_state_t::eBufferChanged)) { continue; - } else if (s->what & layer_state_t::eCachedBufferChanged) { + } else if (s->bufferData.flags.test(BufferData::BufferDataChange::cachedBufferChanged)) { // If eBufferChanged and eCachedBufferChanged are both trued then that means // we already cached the buffer in a previous call to cacheBuffers, perhaps // from writeToParcel on a Transaction that was merged in to this one. @@ -814,23 +814,22 @@ void SurfaceComposerClient::Transaction::cacheBuffers() { // Don't try to cache a null buffer. Sending null buffers is cheap so we shouldn't waste // time trying to cache them. - if (!s->buffer) { + if (!s->bufferData.buffer) { continue; } uint64_t cacheId = 0; - status_t ret = BufferCache::getInstance().getCacheId(s->buffer, &cacheId); + status_t ret = BufferCache::getInstance().getCacheId(s->bufferData.buffer, &cacheId); if (ret == NO_ERROR) { // Cache-hit. Strip the buffer and send only the id. - s->what &= ~static_cast(layer_state_t::eBufferChanged); - s->buffer = nullptr; + s->bufferData.buffer = nullptr; } else { // Cache-miss. Include the buffer and send the new cacheId. - cacheId = BufferCache::getInstance().cache(s->buffer); + cacheId = BufferCache::getInstance().cache(s->bufferData.buffer); } - s->what |= layer_state_t::eCachedBufferChanged; - s->cachedBuffer.token = BufferCache::getInstance().getToken(); - s->cachedBuffer.id = cacheId; + s->bufferData.flags |= BufferData::BufferDataChange::cachedBufferChanged; + s->bufferData.cachedBuffer.token = BufferCache::getInstance().getToken(); + s->bufferData.cachedBuffer.id = cacheId; // If we have more buffers than the size of the cache, we should stop caching so we don't // evict other buffers in this transaction @@ -1289,22 +1288,33 @@ SurfaceComposerClient::Transaction::setTransformToDisplayInverse(const sp& sc, const sp& buffer, const ReleaseCallbackId& id, - ReleaseBufferCallback callback) { + const sp& sc, const sp& buffer, + const std::optional>& fence, const std::optional& frameNumber, + const ReleaseCallbackId& id, ReleaseBufferCallback callback) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } removeReleaseBufferCallback(s); - s->what |= layer_state_t::eBufferChanged; - s->buffer = buffer; - s->releaseBufferEndpoint = IInterface::asBinder(TransactionCompletedListener::getIInstance()); + BufferData bufferData; + bufferData.buffer = buffer; + if (frameNumber) { + bufferData.frameNumber = *frameNumber; + bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged; + } + if (fence) { + bufferData.acquireFence = *fence; + bufferData.flags |= BufferData::BufferDataChange::fenceChanged; + } + bufferData.releaseBufferEndpoint = + IInterface::asBinder(TransactionCompletedListener::getIInstance()); if (mIsAutoTimestamp) { mDesiredPresentTime = systemTime(); } - setReleaseBufferCallback(s, id, callback); - + setReleaseBufferCallback(&bufferData, id, callback); + s->what |= layer_state_t::eBufferChanged; + s->bufferData = bufferData; registerSurfaceControlForCallback(sc); mContainsBuffer = true; @@ -1312,51 +1322,33 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffe } void SurfaceComposerClient::Transaction::removeReleaseBufferCallback(layer_state_t* s) { - if (!s->releaseBufferListener) { + if (!(s->what & layer_state_t::eBufferChanged)) { return; } - s->what &= ~static_cast(layer_state_t::eReleaseBufferListenerChanged); - s->releaseBufferListener = nullptr; auto listener = TransactionCompletedListener::getInstance(); - listener->removeReleaseBufferCallback(s->releaseCallbackId); - s->releaseCallbackId = ReleaseCallbackId::INVALID_ID; + listener->removeReleaseBufferCallback(s->bufferData.releaseCallbackId); } -void SurfaceComposerClient::Transaction::setReleaseBufferCallback(layer_state_t* s, +void SurfaceComposerClient::Transaction::setReleaseBufferCallback(BufferData* bufferData, const ReleaseCallbackId& id, ReleaseBufferCallback callback) { if (!callback) { return; } - if (!s->buffer) { + if (!bufferData->buffer) { ALOGW("Transaction::setReleaseBufferCallback" "ignored trying to set a callback on a null buffer."); return; } - s->what |= layer_state_t::eReleaseBufferListenerChanged; - s->releaseBufferListener = TransactionCompletedListener::getIInstance(); - s->releaseCallbackId = id; + bufferData->releaseBufferListener = TransactionCompletedListener::getIInstance(); + bufferData->releaseCallbackId = id; auto listener = TransactionCompletedListener::getInstance(); listener->setReleaseBufferCallback(id, callback); } -SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setAcquireFence( - const sp& sc, const sp& fence) { - layer_state_t* s = getLayerState(sc); - if (!s) { - mStatus = BAD_INDEX; - return *this; - } - s->what |= layer_state_t::eAcquireFenceChanged; - s->acquireFence = fence; - - registerSurfaceControlForCallback(sc); - return *this; -} - SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDataspace( const sp& sc, ui::Dataspace dataspace) { layer_state_t* s = getLayerState(sc); @@ -1506,20 +1498,6 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::notifyPr return *this; } -SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameNumber( - const sp& sc, uint64_t frameNumber) { - layer_state_t* s = getLayerState(sc); - if (!s) { - mStatus = BAD_INDEX; - return *this; - } - - s->what |= layer_state_t::eFrameNumberChanged; - s->frameNumber = frameNumber; - - return *this; -} - SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInputWindowInfo( const sp& sc, const WindowInfo& info) { layer_state_t* s = getLayerState(sc); diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index b27102bcce..1f5d289a43 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -58,6 +58,44 @@ struct client_cache_t { bool isValid() const { return token != nullptr; } }; +struct BufferData { + enum class BufferDataChange : uint32_t { + fenceChanged = 0x01, + frameNumberChanged = 0x02, + cachedBufferChanged = 0x04, + }; + + sp buffer; + sp acquireFence; + + // Used by BlastBufferQueue to forward the framenumber generated by the + // graphics producer. + uint64_t frameNumber = 0; + + // Listens to when the buffer is safe to be released. This is used for blast + // layers only. The callback includes a release fence as well as the graphic + // buffer id to identify the buffer. + sp releaseBufferListener = nullptr; + + // Keeps track of the release callback id associated with the listener. This + // is not sent to the server since the id can be reconstructed there. This + // is used to remove the old callback from the client process map if it is + // overwritten by another setBuffer call. + ReleaseCallbackId releaseCallbackId = ReleaseCallbackId::INVALID_ID; + + // Stores which endpoint the release information should be sent to. We don't want to send the + // releaseCallbackId and release fence to all listeners so we store which listener the setBuffer + // was called with. + sp releaseBufferEndpoint; + + Flags flags; + + client_cache_t cachedBuffer; + + status_t write(Parcel& output) const; + status_t read(const Parcel& input); +}; + /* * Used to communicate layer information between SurfaceFlinger and its clients. */ @@ -82,7 +120,7 @@ struct layer_state_t { eTransparentRegionChanged = 0x00000020, eFlagsChanged = 0x00000040, eLayerStackChanged = 0x00000080, - eReleaseBufferListenerChanged = 0x00000400, + /* unused 0x00000400, */ eShadowRadiusChanged = 0x00000800, eLayerCreated = 0x00001000, eBufferCropChanged = 0x00002000, @@ -94,7 +132,7 @@ struct layer_state_t { eTransformToDisplayInverseChanged = 0x00080000, eCropChanged = 0x00100000, eBufferChanged = 0x00200000, - eAcquireFenceChanged = 0x00400000, + /* unused 0x00400000, */ eDataspaceChanged = 0x00800000, eHdrMetadataChanged = 0x01000000, eSurfaceDamageRegionChanged = 0x02000000, @@ -105,7 +143,7 @@ struct layer_state_t { eInputInfoChanged = 0x40000000, eCornerRadiusChanged = 0x80000000, eDestinationFrameChanged = 0x1'00000000, - eCachedBufferChanged = 0x2'00000000, + /* unused = 0x2'00000000, */ eBackgroundColorChanged = 0x4'00000000, eMetadataChanged = 0x8'00000000, eColorSpaceAgnosticChanged = 0x10'00000000, @@ -114,7 +152,7 @@ struct layer_state_t { eBackgroundBlurRadiusChanged = 0x80'00000000, eProducerDisconnect = 0x100'00000000, eFixedTransformHintChanged = 0x200'00000000, - eFrameNumberChanged = 0x400'00000000, + /* unused 0x400'00000000, */ eBlurRegionsChanged = 0x800'00000000, eAutoRefreshChanged = 0x1000'00000000, eStretchChanged = 0x2000'00000000, @@ -169,8 +207,7 @@ struct layer_state_t { bool transformToDisplayInverse; Rect crop; Rect orientedDisplaySpaceRect; - sp buffer; - sp acquireFence; + BufferData bufferData; ui::Dataspace dataspace; HdrMetadata hdrMetadata; Region surfaceDamageRegion; @@ -181,8 +218,6 @@ struct layer_state_t { sp windowInfoHandle = new gui::WindowInfoHandle(); - client_cache_t cachedBuffer; - LayerMetadata metadata; // The following refer to the alpha, and dataspace, respectively of @@ -216,10 +251,6 @@ struct layer_state_t { // otherwise the value will be a valid ui::Rotation. ui::Transform::RotationFlags fixedTransformHint; - // Used by BlastBufferQueue to forward the framenumber generated by the - // graphics producer. - uint64_t frameNumber; - // Indicates that the consumer should acquire the next frame as soon as it // can and not wait for a frame to become available. This is only relevant // in shared buffer mode. @@ -235,22 +266,6 @@ struct layer_state_t { Rect bufferCrop; Rect destinationFrame; - // Listens to when the buffer is safe to be released. This is used for blast - // layers only. The callback includes a release fence as well as the graphic - // buffer id to identify the buffer. - sp releaseBufferListener; - - // Keeps track of the release callback id associated with the listener. This - // is not sent to the server since the id can be reconstructed there. This - // is used to remove the old callback from the client process map if it is - // overwritten by another setBuffer call. - ReleaseCallbackId releaseCallbackId; - - // Stores which endpoint the release information should be sent to. We don't want to send the - // releaseCallbackId and release fence to all listeners so we store which listener the setBuffer - // was called with. - sp releaseBufferEndpoint; - // Force inputflinger to drop all input events for the layer and its children. gui::DropInputMode dropInputMode; }; diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 403ca0a166..82249a32f9 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -399,8 +399,7 @@ public: void cacheBuffers(); void registerSurfaceControlForCallback(const sp& sc); - void setReleaseBufferCallback(layer_state_t*, const ReleaseCallbackId&, - ReleaseBufferCallback); + void setReleaseBufferCallback(BufferData*, const ReleaseCallbackId&, ReleaseBufferCallback); void removeReleaseBufferCallback(layer_state_t*); public: @@ -473,10 +472,10 @@ public: Transaction& setTransformToDisplayInverse(const sp& sc, bool transformToDisplayInverse); Transaction& setBuffer(const sp& sc, const sp& buffer, + const std::optional>& fence = std::nullopt, + const std::optional& frameNumber = std::nullopt, const ReleaseCallbackId& id = ReleaseCallbackId::INVALID_ID, ReleaseBufferCallback callback = nullptr); - Transaction& setCachedBuffer(const sp& sc, int32_t bufferId); - Transaction& setAcquireFence(const sp& sc, const sp& fence); Transaction& setDataspace(const sp& sc, ui::Dataspace dataspace); Transaction& setHdrMetadata(const sp& sc, const HdrMetadata& hdrMetadata); Transaction& setSurfaceDamageRegion(const sp& sc, @@ -501,8 +500,6 @@ public: // ONLY FOR BLAST ADAPTER Transaction& notifyProducerDisconnect(const sp& sc); - // Set the framenumber generated by the graphics producer to mimic BufferQueue behaviour. - Transaction& setFrameNumber(const sp& sc, uint64_t frameNumber); Transaction& setInputWindowInfo(const sp& sc, const gui::WindowInfo& info); Transaction& setFocusedWindow(const gui::FocusRequest& request); diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp index d805294b07..ef05fe2138 100644 --- a/services/surfaceflinger/BufferLayer.cpp +++ b/services/surfaceflinger/BufferLayer.cpp @@ -620,14 +620,6 @@ bool BufferLayer::needsFilteringForScreenshots(const DisplayDevice* display, return sourceCrop.getHeight() != frameHeight || sourceCrop.getWidth() != frameWidth; } -uint64_t BufferLayer::getHeadFrameNumber(nsecs_t expectedPresentTime) const { - if (hasFrameUpdate()) { - return getFrameNumber(expectedPresentTime); - } else { - return mCurrentFrameNumber; - } -} - Rect BufferLayer::getBufferSize(const State& s) const { // If we have a sideband stream, or we are scaling the buffer then return the layer size since // we cannot determine the buffer size. diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h index 760c8b9f3c..2da73f81e5 100644 --- a/services/surfaceflinger/BufferLayer.h +++ b/services/surfaceflinger/BufferLayer.h @@ -164,8 +164,6 @@ protected: void updateCloneBufferInfo() override; uint64_t mPreviousFrameNumber = 0; - uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const override; - void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override; // Transform hint provided to the producer. This must be accessed holding @@ -189,8 +187,6 @@ protected: private: virtual bool fenceHasSignaled() const = 0; virtual bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const = 0; - virtual uint64_t getFrameNumber(nsecs_t expectedPresentTime) const = 0; - // Latch sideband stream and returns true if the dirty region should be updated. virtual bool latchSidebandStream(bool& recomputeVisibleRegions) = 0; diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp index d51db88662..8bbe43865a 100644 --- a/services/surfaceflinger/BufferQueueLayer.cpp +++ b/services/surfaceflinger/BufferQueueLayer.cpp @@ -161,39 +161,6 @@ bool BufferQueueLayer::framePresentTimeIsCurrent(nsecs_t expectedPresentTime) co return mQueueItems[0].item.mTimestamp <= expectedPresentTime; } -uint64_t BufferQueueLayer::getFrameNumber(nsecs_t expectedPresentTime) const { - Mutex::Autolock lock(mQueueItemLock); - uint64_t frameNumber = mQueueItems[0].item.mFrameNumber; - - // The head of the queue will be dropped if there are signaled and timely frames behind it - if (isRemovedFromCurrentState()) { - expectedPresentTime = 0; - } - - for (size_t i = 1; i < mQueueItems.size(); i++) { - const bool fenceSignaled = - mQueueItems[i].item.mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING; - if (!fenceSignaled) { - break; - } - - // We don't drop frames without explicit timestamps - if (mQueueItems[i].item.mIsAutoTimestamp) { - break; - } - - const nsecs_t desiredPresent = mQueueItems[i].item.mTimestamp; - if (desiredPresent < expectedPresentTime - BufferQueueConsumer::MAX_REASONABLE_NSEC || - desiredPresent > expectedPresentTime) { - break; - } - - frameNumber = mQueueItems[i].item.mFrameNumber; - } - - return frameNumber; -} - bool BufferQueueLayer::latchSidebandStream(bool& recomputeVisibleRegions) { // We need to update the sideband stream if the layer has both a buffer and a sideband stream. editCompositionState()->sidebandStreamHasFrame = hasFrameUpdate() && mSidebandStream.get(); diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h index b3b7948935..be2902bba6 100644 --- a/services/surfaceflinger/BufferQueueLayer.h +++ b/services/surfaceflinger/BufferQueueLayer.h @@ -89,7 +89,6 @@ protected: }; private: - uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override; bool latchSidebandStream(bool& recomputeVisibleRegions) override; void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override; diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp index 7466c0638c..82e5d4633c 100644 --- a/services/surfaceflinger/BufferStateLayer.cpp +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -406,15 +406,55 @@ bool BufferStateLayer::addFrameEvent(const sp& acquireFence, nsecs_t post return true; } -bool BufferStateLayer::setBuffer(const std::shared_ptr& buffer, - const sp& acquireFence, nsecs_t postTime, +std::shared_ptr BufferStateLayer::getBufferFromBufferData( + const BufferData& bufferData) { + bool cacheIdChanged = bufferData.flags.test(BufferData::BufferDataChange::cachedBufferChanged); + bool bufferSizeExceedsLimit = false; + std::shared_ptr buffer = nullptr; + if (cacheIdChanged && bufferData.buffer != nullptr) { + bufferSizeExceedsLimit = + mFlinger->exceedsMaxRenderTargetSize(bufferData.buffer->getWidth(), + bufferData.buffer->getHeight()); + if (!bufferSizeExceedsLimit) { + ClientCache::getInstance().add(bufferData.cachedBuffer, bufferData.buffer); + buffer = ClientCache::getInstance().get(bufferData.cachedBuffer); + } + } else if (cacheIdChanged) { + buffer = ClientCache::getInstance().get(bufferData.cachedBuffer); + } else if (bufferData.buffer != nullptr) { + bufferSizeExceedsLimit = + mFlinger->exceedsMaxRenderTargetSize(bufferData.buffer->getWidth(), + bufferData.buffer->getHeight()); + if (!bufferSizeExceedsLimit) { + buffer = std::make_shared< + renderengine::ExternalTexture>(bufferData.buffer, mFlinger->getRenderEngine(), + renderengine::ExternalTexture::Usage::READABLE); + } + } + ALOGE_IF(bufferSizeExceedsLimit, + "Attempted to create an ExternalTexture for layer %s that exceeds render target size " + "limit.", + getDebugName()); + return buffer; +} + +bool BufferStateLayer::setBuffer(const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime, bool isAutoTimestamp, - const client_cache_t& clientCacheId, uint64_t frameNumber, - std::optional dequeueTime, const FrameTimelineInfo& info, - const sp& releaseBufferListener, - const sp& releaseBufferEndpoint) { + std::optional dequeueTime, + const FrameTimelineInfo& info) { ATRACE_CALL(); + const std::shared_ptr& buffer = + getBufferFromBufferData(bufferData); + if (!buffer) { + return false; + } + + const bool frameNumberChanged = + bufferData.flags.test(BufferData::BufferDataChange::frameNumberChanged); + const uint64_t frameNumber = + frameNumberChanged ? bufferData.frameNumber : mDrawingState.frameNumber + 1; + if (mDrawingState.buffer) { mReleasePreviousBuffer = true; if (mDrawingState.buffer != mBufferInfo.mBuffer || @@ -438,9 +478,17 @@ bool BufferStateLayer::setBuffer(const std::shared_ptr(mDrawingState.acquireFence); + // The acquire fences of BufferStateLayers have already signaled before they are set + mCallbackHandleAcquireTime = mDrawingState.acquireFenceTime->getSignalTime(); + mDrawingState.modified = true; setTransactionFlags(eTransactionNeeded); @@ -462,7 +510,7 @@ bool BufferStateLayer::setBuffer(const std::shared_ptrmScheduler->recordLayerHistory(this, presentTime, LayerHistory::LayerUpdateType::Buffer); - addFrameEvent(acquireFence, postTime, isAutoTimestamp ? 0 : desiredPresentTime); + addFrameEvent(mDrawingState.acquireFence, postTime, isAutoTimestamp ? 0 : desiredPresentTime); setFrameTimelineVsyncForBufferTransaction(info, postTime); @@ -477,20 +525,7 @@ bool BufferStateLayer::setBuffer(const std::shared_ptrgetBuffer()->getWidth(); mDrawingState.height = mDrawingState.buffer->getBuffer()->getHeight(); - mDrawingState.releaseBufferEndpoint = releaseBufferEndpoint; - - return true; -} - -bool BufferStateLayer::setAcquireFence(const sp& fence) { - mDrawingState.acquireFence = fence; - mDrawingState.acquireFenceTime = std::make_unique(fence); - - // The acquire fences of BufferStateLayers have already signaled before they are set - mCallbackHandleAcquireTime = mDrawingState.acquireFenceTime->getSignalTime(); - - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); + mDrawingState.releaseBufferEndpoint = bufferData.releaseBufferEndpoint; return true; } @@ -659,36 +694,6 @@ bool BufferStateLayer::onPreComposition(nsecs_t refreshStartTime) { return BufferLayer::onPreComposition(refreshStartTime); } -uint64_t BufferStateLayer::getFrameNumber(nsecs_t /*expectedPresentTime*/) const { - return mDrawingState.frameNumber; -} - -/** - * This is the frameNumber used for deferred transaction signalling. We need to use this because - * of cases where we defer a transaction for a surface to itself. In the BLAST world this - * may not make a huge amount of sense (Why not just merge the Buffer transaction with the - * deferred transaction?) but this is an important legacy use case, for example moving - * a window at the same time it draws makes use of this kind of technique. So anyway - * imagine we have something like this: - * - * Transaction { // containing - * Buffer -> frameNumber = 2 - * DeferTransactionUntil -> frameNumber = 2 - * Random other stuff - * } - * Now imagine mFrameNumber returned mDrawingState.frameNumber (or mCurrentFrameNumber). - * Prior to doTransaction SurfaceFlinger will call notifyAvailableFrames, but because we - * haven't swapped mDrawingState to mDrawingState yet we will think the sync point - * is not ready. So we will return false from applyPendingState and not swap - * current state to drawing state. But because we don't swap current state - * to drawing state the number will never update and we will be stuck. This way - * we can see we need to return the frame number for the buffer we are about - * to apply. - */ -uint64_t BufferStateLayer::getHeadFrameNumber(nsecs_t /* expectedPresentTime */) const { - return mDrawingState.frameNumber; -} - void BufferStateLayer::setAutoRefresh(bool autoRefresh) { if (!mAutoRefresh.exchange(autoRefresh)) { mFlinger->signalLayerUpdate(); diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h index 124e91ac61..87b68ea71b 100644 --- a/services/surfaceflinger/BufferStateLayer.h +++ b/services/surfaceflinger/BufferStateLayer.h @@ -55,13 +55,9 @@ public: bool setTransform(uint32_t transform) override; bool setTransformToDisplayInverse(bool transformToDisplayInverse) override; bool setCrop(const Rect& crop) override; - bool setBuffer(const std::shared_ptr& buffer, - const sp& acquireFence, nsecs_t postTime, nsecs_t desiredPresentTime, - bool isAutoTimestamp, const client_cache_t& clientCacheId, uint64_t frameNumber, - std::optional dequeueTime, const FrameTimelineInfo& info, - const sp& transactionListener, - const sp& releaseBufferEndpoint) override; - bool setAcquireFence(const sp& fence) override; + bool setBuffer(const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime, + bool isAutoTimestamp, std::optional dequeueTime, + const FrameTimelineInfo& info) override; bool setDataspace(ui::Dataspace dataspace) override; bool setHdrMetadata(const HdrMetadata& hdrMetadata) override; bool setSurfaceDamageRegion(const Region& surfaceDamage) override; @@ -105,7 +101,6 @@ public: protected: void gatherBufferInfo() override; - uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const; void onSurfaceFrameCreated(const std::shared_ptr& surfaceFrame); ui::Transform getInputTransform() const override; Rect getInputBounds() const override; @@ -122,8 +117,6 @@ private: status_t addReleaseFence(const sp& ch, const sp& releaseFence); - uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override; - bool latchSidebandStream(bool& recomputeVisibleRegions) override; bool hasFrameUpdate() const override; @@ -143,6 +136,9 @@ private: bool bufferNeedsFiltering() const override; + std::shared_ptr getBufferFromBufferData( + const BufferData& bufferData); + sp mPreviousReleaseFence; ReleaseCallbackId mPreviousReleaseCallbackId = ReleaseCallbackId::INVALID_ID; uint64_t mPreviousReleasedFrameNumber = 0; diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 8c281d53e4..f29ebde66b 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -416,17 +416,11 @@ public: // Used only to set BufferStateLayer state virtual bool setTransform(uint32_t /*transform*/) { return false; }; virtual bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/) { return false; }; - virtual bool setBuffer(const std::shared_ptr& /*buffer*/, - const sp& /*acquireFence*/, nsecs_t /*postTime*/, - nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/, - const client_cache_t& /*clientCacheId*/, uint64_t /* frameNumber */, - std::optional /* dequeueTime */, - const FrameTimelineInfo& /*info*/, - const sp& /* releaseBufferListener */, - const sp& /* releaseBufferEndpoint */) { + virtual bool setBuffer(const BufferData&, nsecs_t /*postTime*/, nsecs_t /*desiredPresentTime*/, + bool /*isAutoTimestamp*/, std::optional /* dequeueTime */, + const FrameTimelineInfo& /*info*/) { return false; }; - virtual bool setAcquireFence(const sp& /*fence*/) { return false; }; virtual bool setDataspace(ui::Dataspace /*dataspace*/) { return false; }; virtual bool setHdrMetadata(const HdrMetadata& /*hdrMetadata*/) { return false; }; virtual bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/) { return false; }; @@ -527,8 +521,6 @@ public: virtual bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const { return false; } - virtual uint64_t getHeadFrameNumber(nsecs_t /* expectedPresentTime */) const { return 0; } - /* * called after composition. * returns true if the layer latched a new buffer this frame. diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 88715e36aa..da3d22e318 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -3570,9 +3570,10 @@ bool SurfaceFlinger::transactionIsReadyToBeApplied( for (const ComposerState& state : states) { const layer_state_t& s = state.state; - const bool acquireFenceChanged = (s.what & layer_state_t::eAcquireFenceChanged); - if (acquireFenceChanged && s.acquireFence && !enableLatchUnsignaled && - s.acquireFence->getStatus() == Fence::Status::Unsignaled) { + const bool acquireFenceChanged = + s.bufferData.flags.test(BufferData::BufferDataChange::fenceChanged); + if (acquireFenceChanged && s.bufferData.acquireFence && !enableLatchUnsignaled && + s.bufferData.acquireFence->getStatus() == Fence::Status::Unsignaled) { ATRACE_NAME("fence unsignaled"); return false; } @@ -4069,9 +4070,6 @@ uint32_t SurfaceFlinger::setClientStateLocked( if (what & layer_state_t::eCropChanged) { if (layer->setCrop(s.crop)) flags |= eTraversalNeeded; } - if (what & layer_state_t::eAcquireFenceChanged) { - if (layer->setAcquireFence(s.acquireFence)) flags |= eTraversalNeeded; - } if (what & layer_state_t::eDataspaceChanged) { if (layer->setDataspace(s.dataspace)) flags |= eTraversalNeeded; } @@ -4198,43 +4196,11 @@ uint32_t SurfaceFlinger::setClientStateLocked( callbackHandles.emplace_back(new CallbackHandle(listener, callbackIds, s.surface)); } } - bool bufferChanged = what & layer_state_t::eBufferChanged; - bool cacheIdChanged = what & layer_state_t::eCachedBufferChanged; - bool bufferSizeExceedsLimit = false; - std::shared_ptr buffer; - if (bufferChanged && cacheIdChanged && s.buffer != nullptr) { - bufferSizeExceedsLimit = - exceedsMaxRenderTargetSize(s.buffer->getWidth(), s.buffer->getHeight()); - if (!bufferSizeExceedsLimit) { - ClientCache::getInstance().add(s.cachedBuffer, s.buffer); - buffer = ClientCache::getInstance().get(s.cachedBuffer); - } - } else if (cacheIdChanged) { - buffer = ClientCache::getInstance().get(s.cachedBuffer); - } else if (bufferChanged && s.buffer != nullptr) { - bufferSizeExceedsLimit = - exceedsMaxRenderTargetSize(s.buffer->getWidth(), s.buffer->getHeight()); - if (!bufferSizeExceedsLimit) { - buffer = std::make_shared< - renderengine::ExternalTexture>(s.buffer, getRenderEngine(), - renderengine::ExternalTexture::Usage::READABLE); - } - } - ALOGE_IF(bufferSizeExceedsLimit, - "Attempted to create an ExternalTexture for layer %s that exceeds render target size " - "limit.", - layer->getDebugName()); - if (buffer) { - const bool frameNumberChanged = what & layer_state_t::eFrameNumberChanged; - const uint64_t frameNumber = frameNumberChanged - ? s.frameNumber - : layer->getHeadFrameNumber(-1 /* expectedPresentTime */) + 1; - - if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime, isAutoTimestamp, - s.cachedBuffer, frameNumber, dequeueBufferTimestamp, frameTimelineInfo, - s.releaseBufferListener, s.releaseBufferEndpoint)) { - flags |= eTraversalNeeded; - } + + if (what & layer_state_t::eBufferChanged && + layer->setBuffer(s.bufferData, postTime, desiredPresentTime, isAutoTimestamp, + dequeueBufferTimestamp, frameTimelineInfo)) { + flags |= eTraversalNeeded; } else if (frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) { layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime); } diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 141d7ac34f..5ef3fffece 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -363,6 +363,10 @@ protected: return static_cast(findDisplay(p)); } + bool exceedsMaxRenderTargetSize(uint32_t width, uint32_t height) const { + return width > mMaxRenderTargetSize || height > mMaxRenderTargetSize; + } + private: friend class BufferLayer; friend class BufferQueueLayer; @@ -939,10 +943,6 @@ private: void readPersistentProperties(); - bool exceedsMaxRenderTargetSize(uint32_t width, uint32_t height) const { - return width > mMaxRenderTargetSize || height > mMaxRenderTargetSize; - } - uint32_t getMaxAcquiredBufferCountForCurrentRefreshRate(uid_t uid) const; /* diff --git a/services/surfaceflinger/tests/IPC_test.cpp b/services/surfaceflinger/tests/IPC_test.cpp index 8d7e175cec..94e1e0c95c 100644 --- a/services/surfaceflinger/tests/IPC_test.cpp +++ b/services/surfaceflinger/tests/IPC_test.cpp @@ -161,8 +161,7 @@ public: Color::RED); transaction->setLayerStack(mSurfaceControl, ui::DEFAULT_LAYER_STACK) .setLayer(mSurfaceControl, std::numeric_limits::max()) - .setBuffer(mSurfaceControl, gb) - .setAcquireFence(mSurfaceControl, fence) + .setBuffer(mSurfaceControl, gb, fence) .show(mSurfaceControl) .addTransactionCompletedCallback(mCallbackHelper.function, mCallbackHelper.getContext()); @@ -312,8 +311,7 @@ TEST_F(IPCTest, MergeBasic) { Transaction transaction; transaction.setLayerStack(sc, ui::DEFAULT_LAYER_STACK) .setLayer(sc, std::numeric_limits::max() - 1) - .setBuffer(sc, gb) - .setAcquireFence(sc, fence) + .setBuffer(sc, gb, fence) .show(sc) .addTransactionCompletedCallback(helper1.function, helper1.getContext()); diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp index 965aac301d..e8759e503b 100644 --- a/services/surfaceflinger/tests/LayerCallback_test.cpp +++ b/services/surfaceflinger/tests/LayerCallback_test.cpp @@ -66,8 +66,7 @@ public: return err; } - transaction.setBuffer(layer, buffer); - transaction.setAcquireFence(layer, fence); + transaction.setBuffer(layer, buffer, fence); } if (setBackgroundColor) { diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp index c8eeac66e9..0e2bc3df0e 100644 --- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp +++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp @@ -1353,7 +1353,7 @@ TEST_P(LayerRenderTypeTransactionTest, DISABLED_SetFenceBasic_BufferState) { return; } - Transaction().setBuffer(layer, buffer).setAcquireFence(layer, fence).apply(); + Transaction().setBuffer(layer, buffer, fence).apply(); status_t status = fence->wait(1000); ASSERT_NE(static_cast(Fence::Status::Unsignaled), status); @@ -1375,7 +1375,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetFenceNull_BufferState) { sp fence = Fence::NO_FENCE; - Transaction().setBuffer(layer, buffer).setAcquireFence(layer, fence).apply(); + Transaction().setBuffer(layer, buffer, fence).apply(); auto shot = getScreenCapture(); shot->expectColor(Rect(0, 0, 32, 32), Color::RED); diff --git a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp index c4d42fad36..3847a51b7c 100644 --- a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp +++ b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp @@ -85,9 +85,7 @@ public: sp fence, CallbackHelper& callback, const ReleaseCallbackId& id, ReleaseBufferCallbackHelper& releaseCallback) { Transaction t; - t.setFrameNumber(layer, id.framenumber); - t.setBuffer(layer, buffer, id, releaseCallback.getCallback()); - t.setAcquireFence(layer, fence); + t.setBuffer(layer, buffer, fence, id.framenumber, id, releaseCallback.getCallback()); t.addTransactionCompletedCallback(callback.function, callback.getContext()); t.apply(); } @@ -302,8 +300,8 @@ TEST_F(ReleaseBufferCallbackTest, DISABLED_FrameDropping) { nsecs_t time = systemTime() + std::chrono::nanoseconds(100ms).count(); Transaction t; - t.setBuffer(layer, firstBuffer, firstBufferCallbackId, releaseCallback->getCallback()); - t.setAcquireFence(layer, Fence::NO_FENCE); + t.setBuffer(layer, firstBuffer, std::nullopt, std::nullopt, firstBufferCallbackId, + releaseCallback->getCallback()); t.addTransactionCompletedCallback(transactionCallback.function, transactionCallback.getContext()); t.setDesiredPresentTime(time); @@ -318,8 +316,8 @@ TEST_F(ReleaseBufferCallbackTest, DISABLED_FrameDropping) { // Dropping frames in transaction queue emits a callback sp secondBuffer = getBuffer(); ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber()); - t.setBuffer(layer, secondBuffer, secondBufferCallbackId, releaseCallback->getCallback()); - t.setAcquireFence(layer, Fence::NO_FENCE); + t.setBuffer(layer, secondBuffer, std::nullopt, std::nullopt, secondBufferCallbackId, + releaseCallback->getCallback()); t.addTransactionCompletedCallback(transactionCallback.function, transactionCallback.getContext()); t.setDesiredPresentTime(time); @@ -361,10 +359,8 @@ TEST_F(ReleaseBufferCallbackTest, DISABLED_Merge_Different_Processes) { ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber()); Transaction transaction1; - transaction1.setFrameNumber(layer, secondBufferCallbackId.framenumber); - transaction1.setBuffer(layer, secondBuffer, secondBufferCallbackId, - releaseCallback->getCallback()); - transaction1.setAcquireFence(layer, Fence::NO_FENCE); + transaction1.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber, + secondBufferCallbackId, releaseCallback->getCallback()); transaction1.addTransactionCompletedCallback(callback1.function, callback1.getContext()); // Set a different TransactionCompletedListener to mimic a second process diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp index a749ece835..bd6a7805ec 100644 --- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp @@ -46,6 +46,7 @@ public: ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); setupScheduler(); mFlinger.setupComposer(std::make_unique()); + mFlinger.setupRenderEngine(std::unique_ptr(mRenderEngine)); } ~TransactionFrameTracerTest() { @@ -92,21 +93,17 @@ public: } TestableSurfaceFlinger mFlinger; - renderengine::mock::RenderEngine mRenderEngine; + renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); FenceToFenceTimeMap fenceFactory; - client_cache_t mClientCache; void BLASTTransactionSendsFrameTracerEvents() { sp layer = createBufferStateLayer(); sp fence(new Fence()); - const auto buffer = std::make_shared< - renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, - 1, 0), - mRenderEngine, false); + const auto buffer = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0); int32_t layerId = layer->getSequence(); - uint64_t bufferId = buffer->getBuffer()->getId(); + uint64_t bufferId = buffer->getId(); uint64_t frameNumber = 5; nsecs_t dequeueTime = 10; nsecs_t postTime = 20; @@ -117,9 +114,14 @@ public: EXPECT_CALL(*mFlinger.getFrameTracer(), traceTimestamp(layerId, bufferId, frameNumber, postTime, FrameTracer::FrameEvent::QUEUE, /*duration*/ 0)); - layer->setBuffer(buffer, fence, postTime, /*desiredPresentTime*/ 30, false, mClientCache, - frameNumber, dequeueTime, FrameTimelineInfo{}, - nullptr /* releaseBufferCallback */, nullptr /* releaseBufferEndpoint*/); + BufferData bufferData; + bufferData.buffer = buffer; + bufferData.acquireFence = fence; + bufferData.frameNumber = frameNumber; + bufferData.flags |= BufferData::BufferDataChange::fenceChanged; + bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged; + layer->setBuffer(bufferData, postTime, /*desiredPresentTime*/ 30, false, dequeueTime, + FrameTimelineInfo{}); commitTransaction(layer.get()); bool computeVisisbleRegions; diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp index 2a7921f661..2b5b7b5a5b 100644 --- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp @@ -46,6 +46,7 @@ public: ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); setupScheduler(); mFlinger.setupComposer(std::make_unique()); + mFlinger.setupRenderEngine(std::unique_ptr(mRenderEngine)); } ~TransactionSurfaceFrameTest() { @@ -92,10 +93,9 @@ public: } TestableSurfaceFlinger mFlinger; - renderengine::mock::RenderEngine mRenderEngine; + renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); FenceToFenceTimeMap fenceFactory; - client_cache_t mClientCache; void PresentedSurfaceFrameForBufferlessTransaction() { sp layer = createBufferStateLayer(); @@ -114,13 +114,15 @@ public: sp layer = createBufferStateLayer(); sp fence(new Fence()); auto acquireFence = fenceFactory.createFenceTimeForTest(fence); - const auto buffer = std::make_shared< - renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, - 1, 0), - mRenderEngine, false); - layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, - nullptr /* releaseBufferEndpoint */); + const auto buffer = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0); + BufferData bufferData; + bufferData.buffer = buffer; + bufferData.acquireFence = fence; + bufferData.frameNumber = 1; + bufferData.flags |= BufferData::BufferDataChange::fenceChanged; + bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged; + layer->setBuffer(bufferData, 10, 20, false, std::nullopt, + {/*vsyncId*/ 1, /*inputEventId*/ 0}); acquireFence->signalForTest(12); commitTransaction(layer.get()); @@ -143,27 +145,30 @@ public: sp fence1(new Fence()); auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1); - const auto buffer1 = std::make_shared< - renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, - 1, 0), - mRenderEngine, false); - layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, - nullptr /* releaseBufferEndpoint */); + const auto buffer1 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0); + BufferData bufferData; + bufferData.buffer = buffer1; + bufferData.acquireFence = fence1; + bufferData.frameNumber = 1; + bufferData.flags |= BufferData::BufferDataChange::fenceChanged; + bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged; + layer->setBuffer(bufferData, 10, 20, false, std::nullopt, + {/*vsyncId*/ 1, /*inputEventId*/ 0}); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX; sp fence2(new Fence()); auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2); - const auto buffer2 = std::make_shared< - renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, - 1, 0), - mRenderEngine, false); + const auto buffer2 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0); nsecs_t start = systemTime(); - layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, - nullptr /* releaseBufferEndpoint */); + bufferData.buffer = buffer2; + bufferData.acquireFence = fence2; + bufferData.frameNumber = 1; + bufferData.flags |= BufferData::BufferDataChange::fenceChanged; + bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged; + layer->setBuffer(bufferData, 10, 20, false, std::nullopt, + {/*vsyncId*/ 1, /*inputEventId*/ 0}); nsecs_t end = systemTime(); acquireFence2->signalForTest(12); @@ -198,13 +203,15 @@ public: sp fence(new Fence()); auto acquireFence = fenceFactory.createFenceTimeForTest(fence); - const auto buffer = std::make_shared< - renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, - 1, 0), - mRenderEngine, false); - layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, - nullptr /* releaseBufferEndpoint */); + const auto buffer = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0); + BufferData bufferData; + bufferData.buffer = buffer; + bufferData.acquireFence = fence; + bufferData.frameNumber = 1; + bufferData.flags |= BufferData::BufferDataChange::fenceChanged; + bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged; + layer->setBuffer(bufferData, 10, 20, false, std::nullopt, + {/*vsyncId*/ 1, /*inputEventId*/ 0}); acquireFence->signalForTest(12); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); @@ -227,13 +234,15 @@ public: sp layer = createBufferStateLayer(); sp fence(new Fence()); auto acquireFence = fenceFactory.createFenceTimeForTest(fence); - const auto buffer = std::make_shared< - renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, - 1, 0), - mRenderEngine, false); - layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, - nullptr /* releaseBufferEndpoint */); + const auto buffer = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0); + BufferData bufferData; + bufferData.buffer = buffer; + bufferData.acquireFence = fence; + bufferData.frameNumber = 1; + bufferData.flags |= BufferData::BufferDataChange::fenceChanged; + bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged; + layer->setBuffer(bufferData, 10, 20, false, std::nullopt, + {/*vsyncId*/ 1, /*inputEventId*/ 0}); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); @@ -260,13 +269,15 @@ public: sp fence(new Fence()); auto acquireFence = fenceFactory.createFenceTimeForTest(fence); - const auto buffer = std::make_shared< - renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, - 1, 0), - mRenderEngine, false); - layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 3, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, - nullptr /* releaseBufferEndpoint */); + const auto buffer = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0); + BufferData bufferData; + bufferData.buffer = buffer; + bufferData.acquireFence = fence; + bufferData.frameNumber = 1; + bufferData.flags |= BufferData::BufferDataChange::fenceChanged; + bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged; + layer->setBuffer(bufferData, 10, 20, false, std::nullopt, + {/*vsyncId*/ 3, /*inputEventId*/ 0}); EXPECT_EQ(2u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); const auto bufferSurfaceFrameTX = layer->mDrawingState.bufferSurfaceFrameTX; @@ -299,25 +310,28 @@ public: sp fence1(new Fence()); auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1); - const auto buffer1 = std::make_shared< - renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, - 1, 0), - mRenderEngine, false); - layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, - nullptr /* releaseBufferEndpoint */); + const auto buffer1 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0); + BufferData bufferData; + bufferData.buffer = buffer1; + bufferData.acquireFence = fence1; + bufferData.frameNumber = 1; + bufferData.flags |= BufferData::BufferDataChange::fenceChanged; + bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged; + layer->setBuffer(bufferData, 10, 20, false, std::nullopt, + {/*vsyncId*/ 1, /*inputEventId*/ 0}); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX; sp fence2(new Fence()); auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2); - const auto buffer2 = std::make_shared< - renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, - 1, 0), - mRenderEngine, false); - layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, - nullptr /* releaseBufferEndpoint */); + const auto buffer2 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0); + bufferData.buffer = buffer2; + bufferData.acquireFence = fence2; + bufferData.frameNumber = 1; + bufferData.flags |= BufferData::BufferDataChange::fenceChanged; + bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged; + layer->setBuffer(bufferData, 10, 20, false, std::nullopt, + {/*vsyncId*/ 1, /*inputEventId*/ 0}); acquireFence2->signalForTest(12); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); @@ -342,27 +356,30 @@ public: sp fence1(new Fence()); auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1); - const auto buffer1 = std::make_shared< - renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, - 1, 0), - mRenderEngine, false); - layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, - nullptr /* releaseBufferEndpoint */); + const auto buffer1 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0); + BufferData bufferData; + bufferData.buffer = buffer1; + bufferData.acquireFence = fence1; + bufferData.frameNumber = 1; + bufferData.flags |= BufferData::BufferDataChange::fenceChanged; + bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged; + layer->setBuffer(bufferData, 10, 20, false, std::nullopt, + {/*vsyncId*/ 1, /*inputEventId*/ 0}); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); const auto droppedSurfaceFrame1 = layer->mDrawingState.bufferSurfaceFrameTX; sp fence2(new Fence()); auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2); - const auto buffer2 = std::make_shared< - renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, - 1, 0), - mRenderEngine, false); + const auto buffer2 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0); auto dropStartTime1 = systemTime(); - layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ FrameTimelineInfo::INVALID_VSYNC_ID, /*inputEventId*/ 0}, - nullptr /* releaseBufferCallback */, nullptr /* releaseBufferEndpoint */); + bufferData.buffer = buffer2; + bufferData.acquireFence = fence2; + bufferData.frameNumber = 1; + bufferData.flags |= BufferData::BufferDataChange::fenceChanged; + bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged; + layer->setBuffer(bufferData, 10, 20, false, std::nullopt, + {/*vsyncId*/ FrameTimelineInfo::INVALID_VSYNC_ID, /*inputEventId*/ 0}); auto dropEndTime1 = systemTime(); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); @@ -370,14 +387,15 @@ public: sp fence3(new Fence()); auto acquireFence3 = fenceFactory.createFenceTimeForTest(fence3); - const auto buffer3 = std::make_shared< - renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, - 1, 0), - mRenderEngine, false); + const auto buffer3 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0); auto dropStartTime2 = systemTime(); - layer->setBuffer(buffer3, fence3, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 2, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, - nullptr /* releaseBufferEndpoint */); + bufferData.buffer = buffer3; + bufferData.acquireFence = fence3; + bufferData.frameNumber = 1; + bufferData.flags |= BufferData::BufferDataChange::fenceChanged; + bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged; + layer->setBuffer(bufferData, 10, 20, false, std::nullopt, + {/*vsyncId*/ 2, /*inputEventId*/ 0}); auto dropEndTime2 = systemTime(); acquireFence3->signalForTest(12); @@ -413,16 +431,16 @@ public: uint32_t surfaceFramesPendingClassification = 0; std::vector> bufferlessSurfaceFrames; for (int i = 0; i < 10; i += 2) { - sp fence1(new Fence()); - const auto buffer1 = std::make_shared< - renderengine::ExternalTexture>(new GraphicBuffer(1, 1, - HAL_PIXEL_FORMAT_RGBA_8888, 1, - 0), - mRenderEngine, false); - layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, - nullptr /* releaseBufferCallback */, - nullptr /* releaseBufferEndpoint */); + sp fence(new Fence()); + const auto buffer = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0); + BufferData bufferData; + bufferData.buffer = buffer; + bufferData.acquireFence = fence; + bufferData.frameNumber = 1; + bufferData.flags |= BufferData::BufferDataChange::fenceChanged; + bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged; + layer->setBuffer(bufferData, 10, 20, false, std::nullopt, + {/*vsyncId*/ 1, /*inputEventId*/ 0}); layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 2, /*inputEventId*/ 0}, 10); -- cgit v1.2.3-59-g8ed1b From 69058fb42b64794bb86a8ed23693eaf3dcc686ea Mon Sep 17 00:00:00 2001 From: chaviw Date: Mon, 27 Sep 2021 09:37:30 -0500 Subject: Call release buffer if the buffer is overwritten in the client Clients can overwrite the buffer in a transaction before it's applied. When this happens, the buffer is then replaced, but the client doesn't get a callback that the buffer was released. This could cause the client to run out of buffers since the dropped ones become lost. This can happen in two situations: 1. Merging two transactions where each has a buffer for the same SurfaceControl 2. Transaction.setBuffer is called multiple times before an apply In both cases, call the release callback so the client can properly handle the dropped buffers. It's also possible that the merging occurs in a different process than the one that set the buffer. Because of that, we need to ensure we call the correct releaseBufferListener that's associated with the one that set the buffer. The transformHint is removed from the releaseCallback since it's already provided in the transaction callback. It was originally added in the release callback to give it to BBQ early so it can know the new transform hint before rendering a new frame. However, the transform hint is now provided by VRI to BBQ so it no longer needs to be sent back via release callback. Test: ReleaseBufferCallbackTest Change-Id: I9df0c713bf841f53e1a3d092f33179573a680dc4 Bug: 200285149 --- libs/gui/BLASTBufferQueue.cpp | 32 ++++---- libs/gui/ITransactionCompletedListener.cpp | 3 +- libs/gui/SurfaceComposerClient.cpp | 90 +++++++++++++++++----- libs/gui/include/gui/BLASTBufferQueue.h | 4 +- .../include/gui/ITransactionCompletedListener.h | 1 - libs/gui/include/gui/SurfaceComposerClient.h | 27 ++++++- services/surfaceflinger/BufferStateLayer.cpp | 12 +-- .../tests/ReleaseBufferCallback_test.cpp | 82 +++++++++++++++++++- 8 files changed, 199 insertions(+), 52 deletions(-) (limited to 'libs/gui/BLASTBufferQueue.cpp') diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 2b17616eda..36de581e38 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -162,6 +162,7 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const spgetMaxAcquiredBufferCount(&mMaxAcquiredBuffers); mBufferItemConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBuffers); + mCurrentMaxAcquiredBufferCount = mMaxAcquiredBuffers; mTransformHint = mSurfaceControl->getTransformHint(); mBufferItemConsumer->setTransformHint(mTransformHint); @@ -325,31 +326,23 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp context, const ReleaseCallbackId& id, - const sp& releaseFence, uint32_t transformHint, - uint32_t currentMaxAcquiredBufferCount) { + const sp& releaseFence, + std::optional currentMaxAcquiredBufferCount) { sp blastBufferQueue = context.promote(); if (blastBufferQueue) { - blastBufferQueue->releaseBufferCallback(id, releaseFence, transformHint, - currentMaxAcquiredBufferCount); + blastBufferQueue->releaseBufferCallback(id, releaseFence, currentMaxAcquiredBufferCount); } else { ALOGV("releaseBufferCallbackThunk %s blastBufferQueue is dead", id.to_string().c_str()); } } -void BLASTBufferQueue::releaseBufferCallback(const ReleaseCallbackId& id, - const sp& releaseFence, uint32_t transformHint, - uint32_t currentMaxAcquiredBufferCount) { +void BLASTBufferQueue::releaseBufferCallback( + const ReleaseCallbackId& id, const sp& releaseFence, + std::optional currentMaxAcquiredBufferCount) { ATRACE_CALL(); std::unique_lock _lock{mMutex}; BQA_LOGV("releaseBufferCallback %s", id.to_string().c_str()); - if (mSurfaceControl != nullptr) { - mTransformHint = transformHint; - mSurfaceControl->setTransformHint(transformHint); - mBufferItemConsumer->setTransformHint(mTransformHint); - BQA_LOGV("updated mTransformHint=%d", mTransformHint); - } - // Calculate how many buffers we need to hold before we release them back // to the buffer queue. This will prevent higher latency when we are running // on a lower refresh rate than the max supported. We only do that for EGL @@ -359,8 +352,12 @@ void BLASTBufferQueue::releaseBufferCallback(const ReleaseCallbackId& id, return it != mSubmitted.end() && it->second.mApi == NATIVE_WINDOW_API_EGL; }(); + if (currentMaxAcquiredBufferCount) { + mCurrentMaxAcquiredBufferCount = *currentMaxAcquiredBufferCount; + } + const auto numPendingBuffersToHold = - isEGL ? std::max(0u, mMaxAcquiredBuffers - currentMaxAcquiredBufferCount) : 0; + isEGL ? std::max(0u, mMaxAcquiredBuffers - mCurrentMaxAcquiredBufferCount) : 0; mPendingRelease.emplace_back(ReleasedBuffer{id, releaseFence}); // Release all buffers that are beyond the ones that we need to hold @@ -374,7 +371,7 @@ void BLASTBufferQueue::releaseBufferCallback(const ReleaseCallbackId& id, return; } mNumAcquired--; - BQA_LOGV("released %s", id.to_string().c_str()); + BQA_LOGV("released %s", releaseBuffer.callbackId.to_string().c_str()); mBufferItemConsumer->releaseBuffer(it->second, releaseBuffer.releaseFence); mSubmitted.erase(it); processNextBufferLocked(false /* useNextTransaction */); @@ -466,8 +463,7 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { auto releaseBufferCallback = std::bind(releaseBufferCallbackThunk, wp(this) /* callbackContext */, - std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, - std::placeholders::_4); + std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); sp fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE; t->setBuffer(mSurfaceControl, buffer, fence, bufferItem.mFrameNumber, releaseCallbackId, releaseBufferCallback); diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp index 98e8b548bd..aa7ebc9eb3 100644 --- a/libs/gui/ITransactionCompletedListener.cpp +++ b/libs/gui/ITransactionCompletedListener.cpp @@ -254,11 +254,10 @@ public: } void onReleaseBuffer(ReleaseCallbackId callbackId, sp releaseFence, - uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount) override { + uint32_t currentMaxAcquiredBufferCount) override { callRemoteAsync(Tag::ON_RELEASE_BUFFER, callbackId, releaseFence, - transformHint, currentMaxAcquiredBufferCount); } }; diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index aca59b6d3b..2713be060a 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -214,12 +214,6 @@ void TransactionCompletedListener::setReleaseBufferCallback(const ReleaseCallbac mReleaseBufferCallbacks[callbackId] = listener; } -void TransactionCompletedListener::removeReleaseBufferCallback( - const ReleaseCallbackId& callbackId) { - std::scoped_lock lock(mMutex); - mReleaseBufferCallbacks.erase(callbackId); -} - void TransactionCompletedListener::addSurfaceStatsListener(void* context, void* cookie, sp surfaceControl, SurfaceStatsCallback listener) { std::scoped_lock lock(mSurfaceStatsListenerMutex); @@ -343,7 +337,6 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener surfaceStats.previousReleaseFence ? surfaceStats.previousReleaseFence : Fence::NO_FENCE, - surfaceStats.transformHint, surfaceStats.currentMaxAcquiredBufferCount); } } @@ -389,7 +382,7 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener } void TransactionCompletedListener::onReleaseBuffer(ReleaseCallbackId callbackId, - sp releaseFence, uint32_t transformHint, + sp releaseFence, uint32_t currentMaxAcquiredBufferCount) { ReleaseBufferCallback callback; { @@ -401,7 +394,11 @@ void TransactionCompletedListener::onReleaseBuffer(ReleaseCallbackId callbackId, callbackId.to_string().c_str()); return; } - callback(callbackId, releaseFence, transformHint, currentMaxAcquiredBufferCount); + std::optional optionalMaxAcquiredBufferCount = + currentMaxAcquiredBufferCount == UINT_MAX + ? std::nullopt + : std::make_optional(currentMaxAcquiredBufferCount); + callback(callbackId, releaseFence, optionalMaxAcquiredBufferCount); } ReleaseBufferCallback TransactionCompletedListener::popReleaseBufferCallbackLocked( @@ -712,11 +709,34 @@ status_t SurfaceComposerClient::Transaction::writeToParcel(Parcel* parcel) const return NO_ERROR; } +void SurfaceComposerClient::Transaction::releaseBufferIfOverwriting(const layer_state_t& state) { + if (!(state.what & layer_state_t::eBufferChanged)) { + return; + } + + auto listener = state.bufferData.releaseBufferListener; + sp fence = + state.bufferData.acquireFence ? state.bufferData.acquireFence : Fence::NO_FENCE; + if (state.bufferData.releaseBufferEndpoint == + IInterface::asBinder(TransactionCompletedListener::getIInstance())) { + // if the callback is in process, run on a different thread to avoid any lock contigency + // issues in the client. + SurfaceComposerClient::getDefault() + ->mReleaseCallbackThread.addReleaseCallback(state.bufferData.releaseCallbackId, + fence); + } else { + listener->onReleaseBuffer(state.bufferData.releaseCallbackId, fence, UINT_MAX); + } +} + SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Transaction&& other) { for (auto const& [handle, composerState] : other.mComposerStates) { if (mComposerStates.count(handle) == 0) { mComposerStates[handle] = composerState; } else { + if (composerState.state.what & layer_state_t::eBufferChanged) { + releaseBufferIfOverwriting(mComposerStates[handle].state); + } mComposerStates[handle].state.merge(composerState.state); } } @@ -1296,7 +1316,9 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffe mStatus = BAD_INDEX; return *this; } - removeReleaseBufferCallback(s); + + releaseBufferIfOverwriting(*s); + BufferData bufferData; bufferData.buffer = buffer; if (frameNumber) { @@ -1321,15 +1343,6 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffe return *this; } -void SurfaceComposerClient::Transaction::removeReleaseBufferCallback(layer_state_t* s) { - if (!(s->what & layer_state_t::eBufferChanged)) { - return; - } - - auto listener = TransactionCompletedListener::getInstance(); - listener->removeReleaseBufferCallback(s->bufferData.releaseCallbackId); -} - void SurfaceComposerClient::Transaction::setReleaseBufferCallback(BufferData* bufferData, const ReleaseCallbackId& id, ReleaseBufferCallback callback) { @@ -2210,4 +2223,43 @@ status_t ScreenshotClient::captureLayers(const LayerCaptureArgs& captureArgs, return s->captureLayers(captureArgs, captureListener); } +// --------------------------------------------------------------------------------- + +void ReleaseCallbackThread::addReleaseCallback(const ReleaseCallbackId callbackId, + sp releaseFence) { + std::scoped_lock lock(mMutex); + if (!mStarted) { + mThread = std::thread(&ReleaseCallbackThread::threadMain, this); + mStarted = true; + } + + mCallbackInfos.emplace(callbackId, std::move(releaseFence)); + mReleaseCallbackPending.notify_one(); +} + +void ReleaseCallbackThread::threadMain() { + const auto listener = TransactionCompletedListener::getInstance(); + std::queue>> callbackInfos; + while (true) { + { + std::unique_lock lock(mMutex); + callbackInfos = std::move(mCallbackInfos); + mCallbackInfos = {}; + } + + while (!callbackInfos.empty()) { + auto [callbackId, releaseFence] = callbackInfos.front(); + listener->onReleaseBuffer(callbackId, std::move(releaseFence), UINT_MAX); + callbackInfos.pop(); + } + + { + std::unique_lock lock(mMutex); + if (mCallbackInfos.size() == 0) { + mReleaseCallbackPending.wait(lock); + } + } + } +} + } // namespace android diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 1f517f65d6..49ece6e052 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -91,7 +91,7 @@ public: void transactionCallback(nsecs_t latchTime, const sp& presentFence, const std::vector& stats); void releaseBufferCallback(const ReleaseCallbackId& id, const sp& releaseFence, - uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount); + std::optional currentMaxAcquiredBufferCount); void setNextTransaction(SurfaceComposerClient::Transaction *t); void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber); void applyPendingTransactions(uint64_t frameNumber); @@ -236,6 +236,8 @@ private: // Keep track of SurfaceControls that have submitted a transaction and BBQ is waiting on a // callback for them. std::queue> mSurfaceControlsWithPendingCallback GUARDED_BY(mMutex); + + uint32_t mCurrentMaxAcquiredBufferCount; }; } // namespace android diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h index 937095c543..0df5822597 100644 --- a/libs/gui/include/gui/ITransactionCompletedListener.h +++ b/libs/gui/include/gui/ITransactionCompletedListener.h @@ -192,7 +192,6 @@ public: virtual void onTransactionCompleted(ListenerStats stats) = 0; virtual void onReleaseBuffer(ReleaseCallbackId callbackId, sp releaseFence, - uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount) = 0; }; diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 82249a32f9..e62c76ed80 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -84,7 +85,7 @@ using TransactionCompletedCallback = const std::vector& /*stats*/)>; using ReleaseBufferCallback = std::function& /*releaseFence*/, - uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount)>; + std::optional currentMaxAcquiredBufferCount)>; using SurfaceStatsCallback = std::function); + void threadMain(); + +private: + std::thread mThread; + std::mutex mMutex; + bool mStarted GUARDED_BY(mMutex) = false; + std::condition_variable mReleaseCallbackPending; + std::queue>> mCallbackInfos + GUARDED_BY(mMutex); +}; + +// --------------------------------------------------------------------------- + class SurfaceComposerClient : public RefBase { friend class Composer; @@ -350,6 +367,7 @@ public: private: static std::atomic idCounter; int64_t generateId(); + void releaseBufferIfOverwriting(const layer_state_t& state); protected: std::unordered_map, ComposerState, IBinderHash> mComposerStates; @@ -400,7 +418,6 @@ public: void cacheBuffers(); void registerSurfaceControlForCallback(const sp& sc); void setReleaseBufferCallback(BufferData*, const ReleaseCallbackId&, ReleaseBufferCallback); - void removeReleaseBufferCallback(layer_state_t*); public: Transaction(); @@ -625,6 +642,9 @@ public: status_t addWindowInfosListener(const sp& windowInfosListener); status_t removeWindowInfosListener(const sp& windowInfosListener); +protected: + ReleaseCallbackThread mReleaseCallbackThread; + private: virtual void onFirstRef(); @@ -725,11 +745,10 @@ public: void removeSurfaceStatsListener(void* context, void* cookie); void setReleaseBufferCallback(const ReleaseCallbackId&, ReleaseBufferCallback); - void removeReleaseBufferCallback(const ReleaseCallbackId&); // BnTransactionCompletedListener overrides void onTransactionCompleted(ListenerStats stats) override; - void onReleaseBuffer(ReleaseCallbackId, sp releaseFence, uint32_t transformHint, + void onReleaseBuffer(ReleaseCallbackId, sp releaseFence, uint32_t currentMaxAcquiredBufferCount) override; // For Testing Only diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp index 4eeaba154f..099b85b99f 100644 --- a/services/surfaceflinger/BufferStateLayer.cpp +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -40,13 +40,13 @@ using PresentState = frametimeline::SurfaceFrame::PresentState; namespace { void callReleaseBufferCallback(const sp& listener, const sp& buffer, uint64_t framenumber, - const sp& releaseFence, uint32_t transformHint, + const sp& releaseFence, uint32_t currentMaxAcquiredBufferCount) { if (!listener) { return; } listener->onReleaseBuffer({buffer->getId(), framenumber}, - releaseFence ? releaseFence : Fence::NO_FENCE, transformHint, + releaseFence ? releaseFence : Fence::NO_FENCE, currentMaxAcquiredBufferCount); } } // namespace @@ -64,7 +64,7 @@ BufferStateLayer::~BufferStateLayer() { if (mBufferInfo.mBuffer != nullptr && !isClone()) { callReleaseBufferCallback(mDrawingState.releaseBufferListener, mBufferInfo.mBuffer->getBuffer(), mBufferInfo.mFrameNumber, - mBufferInfo.mFence, mTransformHint, + mBufferInfo.mFence, mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate( mOwnerUid)); } @@ -479,7 +479,7 @@ bool BufferStateLayer::setBuffer(const BufferData& bufferData, nsecs_t postTime, // call any release buffer callbacks if set. callReleaseBufferCallback(mDrawingState.releaseBufferListener, mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber, - mDrawingState.acquireFence, mTransformHint, + mDrawingState.acquireFence, mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate( mOwnerUid)); decrementPendingBufferCount(); @@ -491,9 +491,9 @@ bool BufferStateLayer::setBuffer(const BufferData& bufferData, nsecs_t postTime, } else if (mLastClientCompositionFence != nullptr) { callReleaseBufferCallback(mDrawingState.releaseBufferListener, mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber, - mLastClientCompositionFence, mTransformHint, + mLastClientCompositionFence, mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate( - mOwnerUid)); + mOwnerUid)); mLastClientCompositionFence = nullptr; } } diff --git a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp index 3847a51b7c..e50c2fce56 100644 --- a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp +++ b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp @@ -31,7 +31,7 @@ class ReleaseBufferCallbackHelper { public: static void function(void* callbackContext, ReleaseCallbackId callbackId, const sp& releaseFence, - uint32_t /*currentMaxAcquiredBufferCount*/) { + std::optional /*currentMaxAcquiredBufferCount*/) { if (!callbackContext) { FAIL() << "failed to get callback context"; } @@ -385,4 +385,84 @@ TEST_F(ReleaseBufferCallbackTest, DISABLED_Merge_Different_Processes) { ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId)); } +TEST_F(ReleaseBufferCallbackTest, DISABLED_SetBuffer_OverwriteBuffers) { + sp layer = createBufferStateLayer(); + ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper(); + + sp firstBuffer = getBuffer(); + ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber()); + + // Create transaction with a buffer. + Transaction transaction; + transaction.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber, + firstBufferCallbackId, releaseCallback->getCallback()); + + sp secondBuffer = getBuffer(); + ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber()); + + // Call setBuffer on the same transaction with a different buffer. + transaction.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber, + secondBufferCallbackId, releaseCallback->getCallback()); + + ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId)); +} + +TEST_F(ReleaseBufferCallbackTest, DISABLED_Merge_Transactions_OverwriteBuffers) { + sp layer = createBufferStateLayer(); + ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper(); + + sp firstBuffer = getBuffer(); + ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber()); + + // Create transaction with a buffer. + Transaction transaction1; + transaction1.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber, + firstBufferCallbackId, releaseCallback->getCallback()); + + sp secondBuffer = getBuffer(); + ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber()); + + // Create a second transaction with a new buffer for the same layer. + Transaction transaction2; + transaction2.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber, + secondBufferCallbackId, releaseCallback->getCallback()); + + // merge transaction1 into transaction2 so ensure we get a proper buffer release callback. + transaction1.merge(std::move(transaction2)); + ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId)); +} + +TEST_F(ReleaseBufferCallbackTest, DISABLED_MergeBuffers_Different_Processes) { + sp firstCompletedListener = new TransactionCompletedListener(); + sp secondCompletedListener = new TransactionCompletedListener(); + + TransactionCompletedListener::setInstance(firstCompletedListener); + + sp layer = createBufferStateLayer(); + ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper(); + + sp firstBuffer = getBuffer(); + ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber()); + + Transaction transaction1; + transaction1.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber, + firstBufferCallbackId, releaseCallback->getCallback()); + + // Sent a second buffer to allow the first buffer to get released. + sp secondBuffer = getBuffer(); + ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber()); + + Transaction transaction2; + transaction2.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber, + secondBufferCallbackId, releaseCallback->getCallback()); + + // Set a different TransactionCompletedListener to mimic a second process + TransactionCompletedListener::setInstance(secondCompletedListener); + Transaction().merge(std::move(transaction1)).merge(std::move(transaction2)).apply(); + + // Make sure we can still get the release callback even though the merge happened in a different + // process. + ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId)); +} + } // namespace android -- cgit v1.2.3-59-g8ed1b From d7deef7278f934a1750738b600c11c1771ae7ac6 Mon Sep 17 00:00:00 2001 From: chaviw Date: Wed, 6 Oct 2021 11:53:40 -0500 Subject: Move blast sync handling to BBQ Add logic in BBQ so it can handle waiting the transaction callback vs a sync transaction request. The following behavior will occur 1. If a nextTransaction (sync) was set, we will wait until the transaction callback for that frame before continuing to acquire new frames. Once the transaction callback for the sync transaction is invoked, BBQ will flush the shadow queue. It will try to process as many frames as it can that were queued up during the time BBQ was blocked from processing. 2. If BBQ is waiting on a sync transaction callback and then another sync transaction is requested afterwards, BBQ will allow it to acquire a buffer instead of just adding to the shadow queue. It will acquire the new frame in the new sync transaction and allow the caller that requested the sync to apply it. At this point, it's up to the callers to ensure they apply the two sync transactions in order to ensure frames are applied in order. 3. Similar to 2, but if there are queue requests in between the two sync requests that aren't trying to be synced. When the second sync frame is getting acquired, BBQ will acquire and release any frames that were requested in between. This is so we don't skip or have to wait in the first sync transaction callback. Test: BLASTBufferQueueTest Bug: 200285149 Change-Id: I8da8de1a3fe2a44ca2199ff92cfd4b60c7f01183 --- libs/gui/BLASTBufferQueue.cpp | 155 ++++++++++-- libs/gui/include/gui/BLASTBufferQueue.h | 9 +- libs/gui/include/gui/test/CallbackUtils.h | 211 ++++++++++++++++ libs/gui/tests/BLASTBufferQueue_test.cpp | 272 ++++++++++++++++++++- services/surfaceflinger/tests/IPC_test.cpp | 2 +- .../surfaceflinger/tests/LayerCallback_test.cpp | 2 +- .../tests/ReleaseBufferCallback_test.cpp | 4 +- .../surfaceflinger/tests/utils/CallbackUtils.h | 213 ---------------- 8 files changed, 619 insertions(+), 249 deletions(-) create mode 100644 libs/gui/include/gui/test/CallbackUtils.h delete mode 100644 services/surfaceflinger/tests/utils/CallbackUtils.h (limited to 'libs/gui/BLASTBufferQueue.cpp') diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 36de581e38..2d2b6b26f6 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -46,6 +46,8 @@ inline const char* boolToString(bool b) { namespace android { // Macros to include adapter info in log messages +#define BQA_LOGD(x, ...) \ + ALOGD("[%s](f:%u,a:%u) " x, mName.c_str(), mNumFrameAvailable, mNumAcquired, ##__VA_ARGS__) #define BQA_LOGV(x, ...) \ ALOGV("[%s](f:%u,a:%u) " x, mName.c_str(), mNumFrameAvailable, mNumAcquired, ##__VA_ARGS__) // enable logs for a single layer @@ -244,6 +246,67 @@ void BLASTBufferQueue::update(const sp& surface, uint32_t width, } } +static std::optional findMatchingStat( + const std::vector& stats, const sp& sc) { + for (auto stat : stats) { + if (SurfaceControl::isSameSurface(sc, stat.surfaceControl)) { + return stat; + } + } + return std::nullopt; +} + +static void transactionCommittedCallbackThunk(void* context, nsecs_t latchTime, + const sp& presentFence, + const std::vector& stats) { + if (context == nullptr) { + return; + } + sp bq = static_cast(context); + bq->transactionCommittedCallback(latchTime, presentFence, stats); +} + +void BLASTBufferQueue::transactionCommittedCallback(nsecs_t /*latchTime*/, + const sp& /*presentFence*/, + const std::vector& stats) { + { + std::unique_lock _lock{mMutex}; + ATRACE_CALL(); + BQA_LOGV("transactionCommittedCallback"); + if (!mSurfaceControlsWithPendingCallback.empty()) { + sp pendingSC = mSurfaceControlsWithPendingCallback.front(); + std::optional stat = findMatchingStat(stats, pendingSC); + if (stat) { + uint64_t currFrameNumber = stat->frameEventStats.frameNumber; + + // We need to check if we were waiting for a transaction callback in order to + // process any pending buffers and unblock. It's possible to get transaction + // callbacks for previous requests so we need to ensure the frame from this + // transaction callback matches the last acquired buffer. Since acquireNextBuffer + // will stop processing buffers when mWaitForTransactionCallback is set, we know + // that mLastAcquiredFrameNumber is the frame we're waiting on. + // We also want to check if mNextTransaction is null because it's possible another + // sync request came in while waiting, but it hasn't started processing yet. In that + // case, we don't actually want to flush the frames in between since they will get + // processed and merged with the sync transaction and released earlier than if they + // were sent to SF + if (mWaitForTransactionCallback && mNextTransaction == nullptr && + currFrameNumber >= mLastAcquiredFrameNumber) { + mWaitForTransactionCallback = false; + flushShadowQueue(); + } + } else { + BQA_LOGE("Failed to find matching SurfaceControl in transaction callback"); + } + } else { + BQA_LOGE("No matching SurfaceControls found: mSurfaceControlsWithPendingCallback was " + "empty."); + } + + decStrong((void*)transactionCommittedCallbackThunk); + } +} + static void transactionCallbackThunk(void* context, nsecs_t latchTime, const sp& presentFence, const std::vector& stats) { @@ -267,12 +330,9 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp pendingSC = mSurfaceControlsWithPendingCallback.front(); mSurfaceControlsWithPendingCallback.pop(); - bool found = false; - for (auto stat : stats) { - if (!SurfaceControl::isSameSurface(pendingSC, stat.surfaceControl)) { - continue; - } - + std::optional statsOptional = findMatchingStat(stats, pendingSC); + if (statsOptional) { + SurfaceControlStats stat = *statsOptional; mTransformHint = stat.transformHint; mBufferItemConsumer->setTransformHint(mTransformHint); BQA_LOGV("updated mTransformHint=%d", mTransformHint); @@ -300,12 +360,7 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp context, const Relea } } +void BLASTBufferQueue::flushShadowQueue() { + BQA_LOGV("flushShadowQueue"); + int numFramesToFlush = mNumFrameAvailable; + while (numFramesToFlush > 0) { + acquireNextBufferLocked(std::nullopt); + numFramesToFlush--; + } +} + void BLASTBufferQueue::releaseBufferCallback( const ReleaseCallbackId& id, const sp& releaseFence, std::optional currentMaxAcquiredBufferCount) { @@ -374,7 +438,11 @@ void BLASTBufferQueue::releaseBufferCallback( BQA_LOGV("released %s", releaseBuffer.callbackId.to_string().c_str()); mBufferItemConsumer->releaseBuffer(it->second, releaseBuffer.releaseFence); mSubmitted.erase(it); - processNextBufferLocked(false /* useNextTransaction */); + // Don't process the transactions here if mWaitForTransactionCallback is set. Instead, let + // onFrameAvailable handle processing them since it will merge with the nextTransaction. + if (!mWaitForTransactionCallback) { + acquireNextBufferLocked(std::nullopt); + } } ATRACE_INT("PendingRelease", mPendingRelease.size()); @@ -383,13 +451,15 @@ void BLASTBufferQueue::releaseBufferCallback( mCallbackCV.notify_all(); } -void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { +void BLASTBufferQueue::acquireNextBufferLocked( + const std::optional transaction) { ATRACE_CALL(); // If the next transaction is set, we want to guarantee the our acquire will not fail, so don't // include the extra buffer when checking if we can acquire the next buffer. - const bool includeExtraAcquire = !useNextTransaction; - if (mNumFrameAvailable == 0 || maxBuffersAcquired(includeExtraAcquire)) { - mCallbackCV.notify_all(); + const bool includeExtraAcquire = !transaction; + const bool maxAcquired = maxBuffersAcquired(includeExtraAcquire); + if (mNumFrameAvailable == 0 || maxAcquired) { + BQA_LOGV("Can't process next buffer maxBuffersAcquired=%s", boolToString(maxAcquired)); return; } @@ -401,9 +471,8 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { SurfaceComposerClient::Transaction localTransaction; bool applyTransaction = true; SurfaceComposerClient::Transaction* t = &localTransaction; - if (mNextTransaction != nullptr && useNextTransaction) { - t = mNextTransaction; - mNextTransaction = nullptr; + if (transaction) { + t = *transaction; applyTransaction = false; } @@ -433,7 +502,7 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { mSize.width, mSize.height, mRequestedSize.width, mRequestedSize.height, buffer->getWidth(), buffer->getHeight(), bufferItem.mTransform); mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE); - processNextBufferLocked(useNextTransaction); + acquireNextBufferLocked(transaction); return; } @@ -452,6 +521,7 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { // Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback. incStrong((void*)transactionCallbackThunk); + incStrong((void*)transactionCommittedCallbackThunk); const bool sizeHasChanged = mRequestedSize != mSize; mSize = mRequestedSize; @@ -471,6 +541,7 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { t->setHdrMetadata(mSurfaceControl, bufferItem.mHdrMetadata); t->setSurfaceDamageRegion(mSurfaceControl, bufferItem.mSurfaceDamage); t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast(this)); + t->addTransactionCommittedCallback(transactionCommittedCallbackThunk, static_cast(this)); mSurfaceControlsWithPendingCallback.push(mSurfaceControl); if (updateDestinationFrame) { @@ -508,7 +579,7 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { t->setApplyToken(mApplyToken).apply(); } - BQA_LOGV("processNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64 + BQA_LOGV("acquireNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64 " applyTransaction=%s mTimestamp=%" PRId64 "%s mPendingTransactions.size=%d" " graphicBufferId=%" PRIu64 "%s transform=%d", mSize.width, mSize.height, bufferItem.mFrameNumber, boolToString(applyTransaction), @@ -524,17 +595,44 @@ Rect BLASTBufferQueue::computeCrop(const BufferItem& item) { return item.mCrop; } +void BLASTBufferQueue::acquireAndReleaseBuffer() { + BufferItem bufferItem; + mBufferItemConsumer->acquireBuffer(&bufferItem, 0 /* expectedPresent */, false); + mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE); + mNumFrameAvailable--; +} + void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { ATRACE_CALL(); std::unique_lock _lock{mMutex}; const bool nextTransactionSet = mNextTransaction != nullptr; + BQA_LOGV("onFrameAvailable-start nextTransactionSet=%s", boolToString(nextTransactionSet)); if (nextTransactionSet) { - while (mNumFrameAvailable > 0 || maxBuffersAcquired(false /* includeExtraAcquire */)) { - BQA_LOGV("waiting in onFrameAvailable..."); + if (mWaitForTransactionCallback) { + // We are waiting on a previous sync's transaction callback so allow another sync + // transaction to proceed. + // + // We need to first flush out the transactions that were in between the two syncs. + // We do this by merging them into mNextTransaction so any buffer merging will get + // a release callback invoked. The release callback will be async so we need to wait + // on max acquired to make sure we have the capacity to acquire another buffer. + if (maxBuffersAcquired(false /* includeExtraAcquire */)) { + BQA_LOGD("waiting to flush shadow queue..."); + mCallbackCV.wait(_lock); + } + while (mNumFrameAvailable > 0) { + // flush out the shadow queue + acquireAndReleaseBuffer(); + } + } + + while (maxBuffersAcquired(false /* includeExtraAcquire */)) { + BQA_LOGD("waiting for free buffer."); mCallbackCV.wait(_lock); } } + // add to shadow queue mNumFrameAvailable++; ATRACE_INT(mQueuedBufferTrace.c_str(), @@ -542,7 +640,14 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " nextTransactionSet=%s", item.mFrameNumber, boolToString(nextTransactionSet)); - processNextBufferLocked(nextTransactionSet /* useNextTransaction */); + + if (nextTransactionSet) { + acquireNextBufferLocked(std::move(mNextTransaction)); + mNextTransaction = nullptr; + mWaitForTransactionCallback = true; + } else if (!mWaitForTransactionCallback) { + acquireNextBufferLocked(std::nullopt); + } } void BLASTBufferQueue::onFrameReplaced(const BufferItem& item) { diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 49ece6e052..4a1d4752bc 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -88,6 +88,8 @@ public: void onFrameDequeued(const uint64_t) override; void onFrameCancelled(const uint64_t) override; + void transactionCommittedCallback(nsecs_t latchTime, const sp& presentFence, + const std::vector& stats); void transactionCallback(nsecs_t latchTime, const sp& presentFence, const std::vector& stats); void releaseBufferCallback(const ReleaseCallbackId& id, const sp& releaseFence, @@ -120,7 +122,8 @@ private: void createBufferQueue(sp* outProducer, sp* outConsumer); - void processNextBufferLocked(bool useNextTransaction) REQUIRES(mMutex); + void acquireNextBufferLocked( + const std::optional transaction) REQUIRES(mMutex); Rect computeCrop(const BufferItem& item) REQUIRES(mMutex); // Return true if we need to reject the buffer based on the scaling mode and the buffer size. bool rejectBuffer(const BufferItem& item) REQUIRES(mMutex); @@ -129,6 +132,9 @@ private: void mergePendingTransactions(SurfaceComposerClient::Transaction* t, uint64_t frameNumber) REQUIRES(mMutex); + void flushShadowQueue() REQUIRES(mMutex); + void acquireAndReleaseBuffer() REQUIRES(mMutex); + std::string mName; // Represents the queued buffer count from buffer queue, // pre-BLAST. This is mNumFrameAvailable (buffers that queued to blast) + @@ -238,6 +244,7 @@ private: std::queue> mSurfaceControlsWithPendingCallback GUARDED_BY(mMutex); uint32_t mCurrentMaxAcquiredBufferCount; + bool mWaitForTransactionCallback = false; }; } // namespace android diff --git a/libs/gui/include/gui/test/CallbackUtils.h b/libs/gui/include/gui/test/CallbackUtils.h new file mode 100644 index 0000000000..64032087eb --- /dev/null +++ b/libs/gui/include/gui/test/CallbackUtils.h @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2019 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. + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +using ::std::literals::chrono_literals::operator""ms; +using ::std::literals::chrono_literals::operator""s; + +namespace android { + +namespace { + +struct CallbackData { + CallbackData() = default; + CallbackData(nsecs_t time, const sp& fence, + const std::vector& stats) + : latchTime(time), presentFence(fence), surfaceControlStats(stats) {} + + nsecs_t latchTime; + sp presentFence; + std::vector surfaceControlStats; +}; + +class ExpectedResult { +public: + enum Transaction { + NOT_PRESENTED = 0, + PRESENTED, + }; + + enum Buffer { + NOT_ACQUIRED = 0, + ACQUIRED, + }; + + enum PreviousBuffer { + NOT_RELEASED = 0, + RELEASED, + UNKNOWN, + }; + + void reset() { + mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED; + mExpectedSurfaceResults.clear(); + } + + void addSurface(ExpectedResult::Transaction transactionResult, const sp& layer, + ExpectedResult::Buffer bufferResult = ACQUIRED, + ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) { + mTransactionResult = transactionResult; + mExpectedSurfaceResults.emplace(std::piecewise_construct, std::forward_as_tuple(layer), + std::forward_as_tuple(bufferResult, previousBufferResult)); + } + + void addSurfaces(ExpectedResult::Transaction transactionResult, + const std::vector>& layers, + ExpectedResult::Buffer bufferResult = ACQUIRED, + ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) { + for (const auto& layer : layers) { + addSurface(transactionResult, layer, bufferResult, previousBufferResult); + } + } + + void addExpectedPresentTime(nsecs_t expectedPresentTime) { + mExpectedPresentTime = expectedPresentTime; + } + + void addExpectedPresentTimeForVsyncId(nsecs_t expectedPresentTime) { + mExpectedPresentTimeForVsyncId = expectedPresentTime; + } + + void verifyCallbackData(const CallbackData& callbackData) const { + const auto& [latchTime, presentFence, surfaceControlStats] = callbackData; + if (mTransactionResult == ExpectedResult::Transaction::PRESENTED) { + ASSERT_GE(latchTime, 0) << "bad latch time"; + ASSERT_NE(presentFence, nullptr); + if (mExpectedPresentTime >= 0) { + ASSERT_EQ(presentFence->wait(3000), NO_ERROR); + ASSERT_GE(presentFence->getSignalTime(), mExpectedPresentTime - nsecs_t(5 * 1e6)); + // if the panel is running at 30 hz, at the worst case, our expected time just + // misses vsync and we have to wait another 33.3ms + ASSERT_LE(presentFence->getSignalTime(), + mExpectedPresentTime + nsecs_t(66.666666 * 1e6)); + } else if (mExpectedPresentTimeForVsyncId >= 0) { + ASSERT_EQ(presentFence->wait(3000), NO_ERROR); + // We give 4ms for prediction error + ASSERT_GE(presentFence->getSignalTime(), + mExpectedPresentTimeForVsyncId - 4'000'000); + } + } else { + ASSERT_EQ(presentFence, nullptr) << "transaction shouldn't have been presented"; + ASSERT_EQ(latchTime, -1) << "unpresented transactions shouldn't be latched"; + } + + ASSERT_EQ(surfaceControlStats.size(), mExpectedSurfaceResults.size()) + << "wrong number of surfaces"; + + for (const auto& stats : surfaceControlStats) { + ASSERT_NE(stats.surfaceControl, nullptr) << "returned null surface control"; + + const auto& expectedSurfaceResult = mExpectedSurfaceResults.find(stats.surfaceControl); + ASSERT_NE(expectedSurfaceResult, mExpectedSurfaceResults.end()) + << "unexpected surface control"; + expectedSurfaceResult->second.verifySurfaceControlStats(stats, latchTime); + } + } + +private: + class ExpectedSurfaceResult { + public: + ExpectedSurfaceResult(ExpectedResult::Buffer bufferResult, + ExpectedResult::PreviousBuffer previousBufferResult) + : mBufferResult(bufferResult), mPreviousBufferResult(previousBufferResult) {} + + void verifySurfaceControlStats(const SurfaceControlStats& surfaceControlStats, + nsecs_t latchTime) const { + const auto& [surfaceControl, latch, acquireTime, presentFence, previousReleaseFence, + transformHint, frameEvents] = surfaceControlStats; + + ASSERT_EQ(acquireTime > 0, mBufferResult == ExpectedResult::Buffer::ACQUIRED) + << "bad acquire time"; + ASSERT_LE(acquireTime, latchTime) << "acquire time should be <= latch time"; + + if (mPreviousBufferResult == ExpectedResult::PreviousBuffer::RELEASED) { + ASSERT_NE(previousReleaseFence, nullptr) + << "failed to set release prev buffer fence"; + } else if (mPreviousBufferResult == ExpectedResult::PreviousBuffer::NOT_RELEASED) { + ASSERT_EQ(previousReleaseFence, nullptr) + << "should not have set released prev buffer fence"; + } + } + + private: + ExpectedResult::Buffer mBufferResult; + ExpectedResult::PreviousBuffer mPreviousBufferResult; + }; + + struct SCHash { + std::size_t operator()(const sp& sc) const { + return std::hash{}(sc->getHandle().get()); + } + }; + ExpectedResult::Transaction mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED; + nsecs_t mExpectedPresentTime = -1; + nsecs_t mExpectedPresentTimeForVsyncId = -1; + std::unordered_map, ExpectedSurfaceResult, SCHash> mExpectedSurfaceResults; +}; + +class CallbackHelper { +public: + static void function(void* callbackContext, nsecs_t latchTime, const sp& presentFence, + const std::vector& stats) { + if (!callbackContext) { + ALOGE("failed to get callback context"); + } + CallbackHelper* helper = static_cast(callbackContext); + std::lock_guard lock(helper->mMutex); + helper->mCallbackDataQueue.emplace(latchTime, presentFence, stats); + helper->mConditionVariable.notify_all(); + } + + void getCallbackData(CallbackData* outData) { + std::unique_lock lock(mMutex); + + if (mCallbackDataQueue.empty()) { + ASSERT_NE(mConditionVariable.wait_for(lock, std::chrono::seconds(3)), + std::cv_status::timeout) + << "did not receive callback"; + } + + *outData = std::move(mCallbackDataQueue.front()); + mCallbackDataQueue.pop(); + } + + void verifyFinalState() { + // Wait to see if there are extra callbacks + std::this_thread::sleep_for(500ms); + + std::lock_guard lock(mMutex); + EXPECT_EQ(mCallbackDataQueue.size(), 0U) << "extra callbacks received"; + mCallbackDataQueue = {}; + } + + void* getContext() { return static_cast(this); } + + std::mutex mMutex; + std::condition_variable mConditionVariable; + std::queue mCallbackDataQueue; +}; +} // namespace +} // namespace android diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp index b474086d1d..8e4898d680 100644 --- a/libs/gui/tests/BLASTBufferQueue_test.cpp +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -42,6 +43,29 @@ namespace android { using Transaction = SurfaceComposerClient::Transaction; using android::hardware::graphics::common::V1_2::BufferUsage; +class CountProducerListener : public BnProducerListener { +public: + void onBufferReleased() override { + std::scoped_lock lock(mMutex); + mNumReleased++; + mReleaseCallback.notify_one(); + } + + void waitOnNumberReleased(int32_t expectedNumReleased) { + std::unique_lock lock(mMutex); + while (mNumReleased < expectedNumReleased) { + ASSERT_NE(mReleaseCallback.wait_for(lock, std::chrono::seconds(3)), + std::cv_status::timeout) + << "did not receive release"; + } + } + +private: + std::mutex mMutex; + std::condition_variable mReleaseCallback; + int32_t mNumReleased GUARDED_BY(mMutex) = 0; +}; + class BLASTBufferQueueHelper { public: BLASTBufferQueueHelper(const sp& sc, int width, int height) { @@ -152,18 +176,19 @@ protected: mCaptureArgs.dataspace = ui::Dataspace::V0_SRGB; } - void setUpProducer(BLASTBufferQueueHelper& adapter, sp& producer) { + void setUpProducer(BLASTBufferQueueHelper& adapter, sp& producer, + int32_t maxBufferCount = 2) { producer = adapter.getIGraphicBufferProducer(); - setUpProducer(producer); + setUpProducer(producer, maxBufferCount); } - void setUpProducer(sp& igbProducer) { + void setUpProducer(sp& igbProducer, int32_t maxBufferCount) { ASSERT_NE(nullptr, igbProducer.get()); - ASSERT_EQ(NO_ERROR, igbProducer->setMaxDequeuedBufferCount(2)); + ASSERT_EQ(NO_ERROR, igbProducer->setMaxDequeuedBufferCount(maxBufferCount)); IGraphicBufferProducer::QueueBufferOutput qbOutput; + mProducerListener = new CountProducerListener(); ASSERT_EQ(NO_ERROR, - igbProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, - &qbOutput)); + igbProducer->connect(mProducerListener, NATIVE_WINDOW_API_CPU, false, &qbOutput)); ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint); } @@ -287,6 +312,7 @@ protected: DisplayCaptureArgs mCaptureArgs; ScreenCaptureResults mCaptureResults; + sp mProducerListener; }; TEST_F(BLASTBufferQueueTest, CreateBLASTBufferQueue) { @@ -749,6 +775,240 @@ TEST_F(BLASTBufferQueueTest, ScalingModeChanges) { {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight / 2})); } +TEST_F(BLASTBufferQueueTest, SyncThenNoSync) { + uint8_t r = 255; + uint8_t g = 0; + uint8_t b = 0; + + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + + sp igbProducer; + setUpProducer(adapter, igbProducer); + + Transaction next; + adapter.setNextTransaction(&next); + queueBuffer(igbProducer, 0, 255, 0, 0); + + // queue non sync buffer, so this one should get blocked + // Add a present delay to allow the first screenshot to get taken. + nsecs_t presentTimeDelay = std::chrono::nanoseconds(500ms).count(); + queueBuffer(igbProducer, r, g, b, presentTimeDelay); + + CallbackHelper transactionCallback; + next.addTransactionCompletedCallback(transactionCallback.function, + transactionCallback.getContext()) + .apply(); + + CallbackData callbackData; + transactionCallback.getCallbackData(&callbackData); + + // capture screen and verify that it is red + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + ASSERT_NO_FATAL_FAILURE( + checkScreenCapture(0, 255, 0, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); + + mProducerListener->waitOnNumberReleased(1); + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + ASSERT_NO_FATAL_FAILURE( + checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); +} + +TEST_F(BLASTBufferQueueTest, MultipleSyncTransactions) { + uint8_t r = 255; + uint8_t g = 0; + uint8_t b = 0; + + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + + sp igbProducer; + setUpProducer(adapter, igbProducer); + + Transaction mainTransaction; + + Transaction next; + adapter.setNextTransaction(&next); + queueBuffer(igbProducer, 0, 255, 0, 0); + + mainTransaction.merge(std::move(next)); + + adapter.setNextTransaction(&next); + queueBuffer(igbProducer, r, g, b, 0); + + mainTransaction.merge(std::move(next)); + // Expect 1 buffer to be released even before sending to SurfaceFlinger + mProducerListener->waitOnNumberReleased(1); + + CallbackHelper transactionCallback; + mainTransaction + .addTransactionCompletedCallback(transactionCallback.function, + transactionCallback.getContext()) + .apply(); + + CallbackData callbackData; + transactionCallback.getCallbackData(&callbackData); + + // capture screen and verify that it is red + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + ASSERT_NO_FATAL_FAILURE( + checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); +} + +TEST_F(BLASTBufferQueueTest, MultipleSyncTransactionWithNonSync) { + uint8_t r = 255; + uint8_t g = 0; + uint8_t b = 0; + + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + + sp igbProducer; + setUpProducer(adapter, igbProducer); + + Transaction mainTransaction; + + Transaction next; + // queue a sync transaction + adapter.setNextTransaction(&next); + queueBuffer(igbProducer, 0, 255, 0, 0); + + mainTransaction.merge(std::move(next)); + + // queue another buffer without setting next transaction + queueBuffer(igbProducer, 0, 0, 255, 0); + + // queue another sync transaction + adapter.setNextTransaction(&next); + queueBuffer(igbProducer, r, g, b, 0); + // Expect 1 buffer to be released because the non sync transaction should merge + // with the sync + mProducerListener->waitOnNumberReleased(1); + + mainTransaction.merge(std::move(next)); + // Expect 2 buffers to be released due to merging the two syncs. + mProducerListener->waitOnNumberReleased(2); + + CallbackHelper transactionCallback; + mainTransaction + .addTransactionCompletedCallback(transactionCallback.function, + transactionCallback.getContext()) + .apply(); + + CallbackData callbackData; + transactionCallback.getCallbackData(&callbackData); + + // capture screen and verify that it is red + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + ASSERT_NO_FATAL_FAILURE( + checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); +} + +TEST_F(BLASTBufferQueueTest, MultipleSyncRunOutOfBuffers) { + uint8_t r = 255; + uint8_t g = 0; + uint8_t b = 0; + + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + + sp igbProducer; + setUpProducer(adapter, igbProducer, 3); + + Transaction mainTransaction; + + Transaction next; + // queue a sync transaction + adapter.setNextTransaction(&next); + queueBuffer(igbProducer, 0, 255, 0, 0); + + mainTransaction.merge(std::move(next)); + + // queue a few buffers without setting next transaction + queueBuffer(igbProducer, 0, 0, 255, 0); + queueBuffer(igbProducer, 0, 0, 255, 0); + queueBuffer(igbProducer, 0, 0, 255, 0); + + // queue another sync transaction + adapter.setNextTransaction(&next); + queueBuffer(igbProducer, r, g, b, 0); + // Expect 3 buffers to be released because the non sync transactions should merge + // with the sync + mProducerListener->waitOnNumberReleased(3); + + mainTransaction.merge(std::move(next)); + // Expect 4 buffers to be released due to merging the two syncs. + mProducerListener->waitOnNumberReleased(4); + + CallbackHelper transactionCallback; + mainTransaction + .addTransactionCompletedCallback(transactionCallback.function, + transactionCallback.getContext()) + .apply(); + + CallbackData callbackData; + transactionCallback.getCallbackData(&callbackData); + + // capture screen and verify that it is red + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + ASSERT_NO_FATAL_FAILURE( + checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); +} + +// Tests BBQ with a sync transaction when the buffers acquired reaches max and the only way to +// continue processing is for a release callback from SurfaceFlinger. +// This is done by sending a buffer to SF so it can release the previous one and allow BBQ to +// continue acquiring buffers. +TEST_F(BLASTBufferQueueTest, RunOutOfBuffersWaitingOnSF) { + uint8_t r = 255; + uint8_t g = 0; + uint8_t b = 0; + + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + + sp igbProducer; + setUpProducer(adapter, igbProducer, 4); + + Transaction mainTransaction; + + // Send a buffer to SF + queueBuffer(igbProducer, 0, 255, 0, 0); + + Transaction next; + // queue a sync transaction + adapter.setNextTransaction(&next); + queueBuffer(igbProducer, 0, 255, 0, 0); + + mainTransaction.merge(std::move(next)); + + // queue a few buffers without setting next transaction + queueBuffer(igbProducer, 0, 0, 255, 0); + queueBuffer(igbProducer, 0, 0, 255, 0); + queueBuffer(igbProducer, 0, 0, 255, 0); + + // apply the first synced buffer to ensure we have to wait on SF + mainTransaction.apply(); + + // queue another sync transaction + adapter.setNextTransaction(&next); + queueBuffer(igbProducer, r, g, b, 0); + // Expect 2 buffers to be released because the non sync transactions should merge + // with the sync + mProducerListener->waitOnNumberReleased(3); + + mainTransaction.merge(std::move(next)); + + CallbackHelper transactionCallback; + mainTransaction + .addTransactionCompletedCallback(transactionCallback.function, + transactionCallback.getContext()) + .apply(); + + CallbackData callbackData; + transactionCallback.getCallbackData(&callbackData); + + // capture screen and verify that it is red + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + ASSERT_NO_FATAL_FAILURE( + checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); +} + class TestProducerListener : public BnProducerListener { public: sp mIgbp; diff --git a/services/surfaceflinger/tests/IPC_test.cpp b/services/surfaceflinger/tests/IPC_test.cpp index 94e1e0c95c..ce94dab907 100644 --- a/services/surfaceflinger/tests/IPC_test.cpp +++ b/services/surfaceflinger/tests/IPC_test.cpp @@ -28,8 +28,8 @@ #include +#include #include "BufferGenerator.h" -#include "utils/CallbackUtils.h" #include "utils/ColorUtils.h" #include "utils/TransactionUtils.h" diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp index e8759e503b..9ddbed21d8 100644 --- a/services/surfaceflinger/tests/LayerCallback_test.cpp +++ b/services/surfaceflinger/tests/LayerCallback_test.cpp @@ -18,8 +18,8 @@ #include +#include #include "LayerTransactionTest.h" -#include "utils/CallbackUtils.h" using namespace std::chrono_literals; diff --git a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp index e50c2fce56..f6b0def2f7 100644 --- a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp +++ b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ +#include #include "LayerTransactionTest.h" -#include "utils/CallbackUtils.h" using namespace std::chrono_literals; @@ -61,7 +61,7 @@ public: std::this_thread::sleep_for(300ms); std::lock_guard lock(mMutex); - EXPECT_EQ(mCallbackDataQueue.size(), 0) << "extra callbacks received"; + EXPECT_EQ(mCallbackDataQueue.size(), 0U) << "extra callbacks received"; mCallbackDataQueue = {}; } diff --git a/services/surfaceflinger/tests/utils/CallbackUtils.h b/services/surfaceflinger/tests/utils/CallbackUtils.h deleted file mode 100644 index f4a3425b6a..0000000000 --- a/services/surfaceflinger/tests/utils/CallbackUtils.h +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright (C) 2019 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. - */ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -using ::std::literals::chrono_literals::operator""ms; -using ::std::literals::chrono_literals::operator""s; - -namespace android { - -namespace { - -struct CallbackData { - CallbackData() = default; - CallbackData(nsecs_t time, const sp& fence, - const std::vector& stats) - : latchTime(time), presentFence(fence), surfaceControlStats(stats) {} - - nsecs_t latchTime; - sp presentFence; - std::vector surfaceControlStats; -}; - -class ExpectedResult { -public: - enum Transaction { - NOT_PRESENTED = 0, - PRESENTED, - }; - - enum Buffer { - NOT_ACQUIRED = 0, - ACQUIRED, - }; - - enum PreviousBuffer { - NOT_RELEASED = 0, - RELEASED, - UNKNOWN, - }; - - void reset() { - mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED; - mExpectedSurfaceResults.clear(); - } - - void addSurface(ExpectedResult::Transaction transactionResult, const sp& layer, - ExpectedResult::Buffer bufferResult = ACQUIRED, - ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) { - mTransactionResult = transactionResult; - mExpectedSurfaceResults.emplace(std::piecewise_construct, std::forward_as_tuple(layer), - std::forward_as_tuple(bufferResult, previousBufferResult)); - } - - void addSurfaces(ExpectedResult::Transaction transactionResult, - const std::vector>& layers, - ExpectedResult::Buffer bufferResult = ACQUIRED, - ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) { - for (const auto& layer : layers) { - addSurface(transactionResult, layer, bufferResult, previousBufferResult); - } - } - - void addExpectedPresentTime(nsecs_t expectedPresentTime) { - mExpectedPresentTime = expectedPresentTime; - } - - void addExpectedPresentTimeForVsyncId(nsecs_t expectedPresentTime) { - mExpectedPresentTimeForVsyncId = expectedPresentTime; - } - - void verifyCallbackData(const CallbackData& callbackData) const { - const auto& [latchTime, presentFence, surfaceControlStats] = callbackData; - if (mTransactionResult == ExpectedResult::Transaction::PRESENTED) { - ASSERT_GE(latchTime, 0) << "bad latch time"; - ASSERT_NE(presentFence, nullptr); - if (mExpectedPresentTime >= 0) { - ASSERT_EQ(presentFence->wait(3000), NO_ERROR); - ASSERT_GE(presentFence->getSignalTime(), mExpectedPresentTime - nsecs_t(5 * 1e6)); - // if the panel is running at 30 hz, at the worst case, our expected time just - // misses vsync and we have to wait another 33.3ms - ASSERT_LE(presentFence->getSignalTime(), - mExpectedPresentTime + nsecs_t(66.666666 * 1e6)); - } else if (mExpectedPresentTimeForVsyncId >= 0) { - ASSERT_EQ(presentFence->wait(3000), NO_ERROR); - // We give 4ms for prediction error - ASSERT_GE(presentFence->getSignalTime(), - mExpectedPresentTimeForVsyncId - 4'000'000); - } - } else { - ASSERT_EQ(presentFence, nullptr) << "transaction shouldn't have been presented"; - ASSERT_EQ(latchTime, -1) << "unpresented transactions shouldn't be latched"; - } - - ASSERT_EQ(surfaceControlStats.size(), mExpectedSurfaceResults.size()) - << "wrong number of surfaces"; - - for (const auto& stats : surfaceControlStats) { - ASSERT_NE(stats.surfaceControl, nullptr) << "returned null surface control"; - - const auto& expectedSurfaceResult = mExpectedSurfaceResults.find(stats.surfaceControl); - ASSERT_NE(expectedSurfaceResult, mExpectedSurfaceResults.end()) - << "unexpected surface control"; - expectedSurfaceResult->second.verifySurfaceControlStats(stats, latchTime); - } - } - -private: - class ExpectedSurfaceResult { - public: - ExpectedSurfaceResult(ExpectedResult::Buffer bufferResult, - ExpectedResult::PreviousBuffer previousBufferResult) - : mBufferResult(bufferResult), mPreviousBufferResult(previousBufferResult) {} - - void verifySurfaceControlStats(const SurfaceControlStats& surfaceControlStats, - nsecs_t latchTime) const { - const auto& - [surfaceControl, latch, acquireTime, presentFence, previousReleaseFence, - transformHint, - frameEvents] = surfaceControlStats; - - ASSERT_EQ(acquireTime > 0, mBufferResult == ExpectedResult::Buffer::ACQUIRED) - << "bad acquire time"; - ASSERT_LE(acquireTime, latchTime) << "acquire time should be <= latch time"; - - if (mPreviousBufferResult == ExpectedResult::PreviousBuffer::RELEASED) { - ASSERT_NE(previousReleaseFence, nullptr) - << "failed to set release prev buffer fence"; - } else if (mPreviousBufferResult == ExpectedResult::PreviousBuffer::NOT_RELEASED) { - ASSERT_EQ(previousReleaseFence, nullptr) - << "should not have set released prev buffer fence"; - } - } - - private: - ExpectedResult::Buffer mBufferResult; - ExpectedResult::PreviousBuffer mPreviousBufferResult; - }; - - struct SCHash { - std::size_t operator()(const sp& sc) const { - return std::hash{}(sc->getHandle().get()); - } - }; - ExpectedResult::Transaction mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED; - nsecs_t mExpectedPresentTime = -1; - nsecs_t mExpectedPresentTimeForVsyncId = -1; - std::unordered_map, ExpectedSurfaceResult, SCHash> mExpectedSurfaceResults; -}; - -class CallbackHelper { -public: - static void function(void* callbackContext, nsecs_t latchTime, const sp& presentFence, - const std::vector& stats) { - if (!callbackContext) { - ALOGE("failed to get callback context"); - } - CallbackHelper* helper = static_cast(callbackContext); - std::lock_guard lock(helper->mMutex); - helper->mCallbackDataQueue.emplace(latchTime, presentFence, stats); - helper->mConditionVariable.notify_all(); - } - - void getCallbackData(CallbackData* outData) { - std::unique_lock lock(mMutex); - - if (mCallbackDataQueue.empty()) { - ASSERT_NE(mConditionVariable.wait_for(lock, std::chrono::seconds(3)), - std::cv_status::timeout) - << "did not receive callback"; - } - - *outData = std::move(mCallbackDataQueue.front()); - mCallbackDataQueue.pop(); - } - - void verifyFinalState() { - // Wait to see if there are extra callbacks - std::this_thread::sleep_for(500ms); - - std::lock_guard lock(mMutex); - EXPECT_EQ(mCallbackDataQueue.size(), 0) << "extra callbacks received"; - mCallbackDataQueue = {}; - } - - void* getContext() { return static_cast(this); } - - std::mutex mMutex; - std::condition_variable mConditionVariable; - std::queue mCallbackDataQueue; -}; -} -} // namespace android -- cgit v1.2.3-59-g8ed1b From 6ebdf5f0f7ca5354a1a9a0e6419275c7b873849d Mon Sep 17 00:00:00 2001 From: chaviw Date: Thu, 14 Oct 2021 11:57:22 -0500 Subject: Use bufferItem's fence when calling release When we acquire and release immediately, we want to use the bufferItem's release fence instead of NO_FENCE. Also add an error log if there's a failure in acquireBuffer, which really should never happen. Test: BLASTBufferQueueTest Bug: 200285149 Change-Id: I5e869b0f66f37b15b9317985b30f539dbfea831c --- libs/gui/BLASTBufferQueue.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'libs/gui/BLASTBufferQueue.cpp') diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 2d2b6b26f6..dfbb8630c0 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -597,9 +597,15 @@ Rect BLASTBufferQueue::computeCrop(const BufferItem& item) { void BLASTBufferQueue::acquireAndReleaseBuffer() { BufferItem bufferItem; - mBufferItemConsumer->acquireBuffer(&bufferItem, 0 /* expectedPresent */, false); - mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE); + status_t status = + mBufferItemConsumer->acquireBuffer(&bufferItem, 0 /* expectedPresent */, false); + if (status != OK) { + BQA_LOGE("Failed to acquire a buffer in acquireAndReleaseBuffer, err=%s", + statusToString(status).c_str()); + return; + } mNumFrameAvailable--; + mBufferItemConsumer->releaseBuffer(bufferItem, bufferItem.mFence); } void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { -- cgit v1.2.3-59-g8ed1b From f10b9044af6604542a567c8cf6848e8c0fbbfa4d Mon Sep 17 00:00:00 2001 From: chaviw Date: Wed, 13 Oct 2021 15:48:59 -0500 Subject: Remove setTransactionCompleteCallback The transaction complete callback is no longer used so delete the code Test: BLASTBufferQueueTest Bug: 200285149 Change-Id: I91c49de3a68fed3fe8261555852b1ca6d7aad546 --- libs/gui/BLASTBufferQueue.cpp | 30 ---------------- libs/gui/include/gui/BLASTBufferQueue.h | 9 ++--- libs/gui/tests/BLASTBufferQueue_test.cpp | 60 ++++++++++++++++++++------------ 3 files changed, 39 insertions(+), 60 deletions(-) (limited to 'libs/gui/BLASTBufferQueue.cpp') diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 2d2b6b26f6..703360fb23 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -319,9 +319,6 @@ static void transactionCallbackThunk(void* context, nsecs_t latchTime, void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp& /*presentFence*/, const std::vector& stats) { - std::function transactionCompleteCallback = nullptr; - uint64_t currFrameNumber = 0; - { std::unique_lock _lock{mMutex}; ATRACE_CALL(); @@ -348,18 +345,6 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp= mTransactionCompleteFrameNumber) { - if (currFrameNumber > mTransactionCompleteFrameNumber) { - BQA_LOGE("transactionCallback received for a newer framenumber=%" PRIu64 - " than expected=%" PRIu64, - currFrameNumber, mTransactionCompleteFrameNumber); - } - transactionCompleteCallback = std::move(mTransactionCompleteCallback); - mTransactionCompleteFrameNumber = 0; - } } else { BQA_LOGE("Failed to find matching SurfaceControl in transaction callback"); } @@ -370,10 +355,6 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp&& transactionCompleteCallback) { - std::lock_guard _lock{mMutex}; - if (transactionCompleteCallback == nullptr) { - mTransactionCompleteCallback = nullptr; - } else { - mTransactionCompleteCallback = std::move(transactionCompleteCallback); - mTransactionCompleteFrameNumber = frameNumber; - } -} - // Check if we have acquired the maximum number of buffers. // Consumer can acquire an additional buffer if that buffer is not droppable. Set // includeExtraAcquire is true to include this buffer to the count. Since this depends on the state diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 4a1d4752bc..3881620f3d 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -88,8 +88,8 @@ public: void onFrameDequeued(const uint64_t) override; void onFrameCancelled(const uint64_t) override; - void transactionCommittedCallback(nsecs_t latchTime, const sp& presentFence, - const std::vector& stats); + virtual void transactionCommittedCallback(nsecs_t latchTime, const sp& presentFence, + const std::vector& stats); void transactionCallback(nsecs_t latchTime, const sp& presentFence, const std::vector& stats); void releaseBufferCallback(const ReleaseCallbackId& id, const sp& releaseFence, @@ -97,8 +97,6 @@ public: void setNextTransaction(SurfaceComposerClient::Transaction *t); void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber); void applyPendingTransactions(uint64_t frameNumber); - void setTransactionCompleteCallback(uint64_t frameNumber, - std::function&& transactionCompleteCallback); void update(const sp& surface, uint32_t width, uint32_t height, int32_t format, SurfaceComposerClient::Transaction* outTransaction = nullptr); @@ -224,9 +222,6 @@ private: // Tracks the last acquired frame number uint64_t mLastAcquiredFrameNumber GUARDED_BY(mMutex) = 0; - std::function mTransactionCompleteCallback GUARDED_BY(mMutex) = nullptr; - uint64_t mTransactionCompleteFrameNumber GUARDED_BY(mMutex){0}; - // Queues up transactions using this token in SurfaceFlinger. This prevents queued up // transactions from other parts of the client from blocking this transaction. const sp mApplyToken GUARDED_BY(mMutex) = new BBinder(); diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp index 8e4898d680..b2d50482a5 100644 --- a/libs/gui/tests/BLASTBufferQueue_test.cpp +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -66,11 +66,44 @@ private: int32_t mNumReleased GUARDED_BY(mMutex) = 0; }; +class TestBLASTBufferQueue : public BLASTBufferQueue { +public: + TestBLASTBufferQueue(const std::string& name, const sp& surface, int width, + int height, int32_t format) + : BLASTBufferQueue(name, surface, width, height, format) {} + + void transactionCommittedCallback(nsecs_t latchTime, const sp& presentFence, + const std::vector& stats) override { + BLASTBufferQueue::transactionCommittedCallback(latchTime, presentFence, stats); + + uint64_t frameNumber = stats[0].frameEventStats.frameNumber; + + { + std::unique_lock lock{frameNumberMutex}; + mLastTransactionCommittedFrameNumber = frameNumber; + mCommittedCV.notify_all(); + } + } + + void waitForCallback(int64_t frameNumber) { + std::unique_lock lock{frameNumberMutex}; + // Wait until all but one of the submitted buffers have been released. + while (mLastTransactionCommittedFrameNumber < frameNumber) { + mCommittedCV.wait(lock); + } + } + +private: + std::mutex frameNumberMutex; + std::condition_variable mCommittedCV; + int64_t mLastTransactionCommittedFrameNumber = -1; +}; + class BLASTBufferQueueHelper { public: BLASTBufferQueueHelper(const sp& sc, int width, int height) { - mBlastBufferQueueAdapter = new BLASTBufferQueue("TestBLASTBufferQueue", sc, width, height, - PIXEL_FORMAT_RGBA_8888); + mBlastBufferQueueAdapter = new TestBLASTBufferQueue("TestBLASTBufferQueue", sc, width, + height, PIXEL_FORMAT_RGBA_8888); } void update(const sp& sc, int width, int height) { @@ -107,28 +140,12 @@ public: } } - void setTransactionCompleteCallback(int64_t frameNumber) { - mBlastBufferQueueAdapter->setTransactionCompleteCallback(frameNumber, [&](int64_t frame) { - std::unique_lock lock{mMutex}; - mLastTransactionCompleteFrameNumber = frame; - mCallbackCV.notify_all(); - }); - } - void waitForCallback(int64_t frameNumber) { - std::unique_lock lock{mMutex}; - // Wait until all but one of the submitted buffers have been released. - while (mLastTransactionCompleteFrameNumber < frameNumber) { - mCallbackCV.wait(lock); - } + mBlastBufferQueueAdapter->waitForCallback(frameNumber); } private: - sp mBlastBufferQueueAdapter; - - std::mutex mMutex; - std::condition_variable mCallbackCV; - int64_t mLastTransactionCompleteFrameNumber = -1; + sp mBlastBufferQueueAdapter; }; class BLASTBufferQueueTest : public ::testing::Test { @@ -1366,7 +1383,6 @@ TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_Basic) { IGraphicBufferProducer::QueueBufferOutput qbOutput; nsecs_t requestedPresentTimeA = 0; nsecs_t postedTimeA = 0; - adapter.setTransactionCompleteCallback(1); setUpAndQueueBuffer(igbProducer, &requestedPresentTimeA, &postedTimeA, &qbOutput, true); history.applyDelta(qbOutput.frameTimestamps); @@ -1435,7 +1451,6 @@ TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_DroppedFrame) { // queue another buffer so the first can be dropped nsecs_t requestedPresentTimeB = 0; nsecs_t postedTimeB = 0; - adapter.setTransactionCompleteCallback(2); presentTime = systemTime() + std::chrono::nanoseconds(1ms).count(); setUpAndQueueBuffer(igbProducer, &requestedPresentTimeB, &postedTimeB, &qbOutput, true, presentTime); @@ -1501,7 +1516,6 @@ TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_CompositorTimings) { IGraphicBufferProducer::QueueBufferOutput qbOutput; nsecs_t requestedPresentTimeA = 0; nsecs_t postedTimeA = 0; - adapter.setTransactionCompleteCallback(1); setUpAndQueueBuffer(igbProducer, &requestedPresentTimeA, &postedTimeA, &qbOutput, true); history.applyDelta(qbOutput.frameTimestamps); adapter.waitForCallback(1); -- cgit v1.2.3-59-g8ed1b From 768bfa07e598608f135140f1001bf09c7de35b1e Mon Sep 17 00:00:00 2001 From: chaviw Date: Mon, 1 Nov 2021 09:50:57 -0500 Subject: Change log to match with function it's coming from Added the function name in the error log when SurfaceControl can't be found in the callbacks. Test: Log Bug: 200285149 Change-Id: I6c721a699c25c8659f5aa5e703de134c8c0a31b7 --- libs/gui/BLASTBufferQueue.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'libs/gui/BLASTBufferQueue.cpp') diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 1ae90f34f5..9080822f92 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -296,7 +296,7 @@ void BLASTBufferQueue::transactionCommittedCallback(nsecs_t /*latchTime*/, flushShadowQueue(); } } else { - BQA_LOGE("Failed to find matching SurfaceControl in transaction callback"); + BQA_LOGE("Failed to find matching SurfaceControl in transactionCommittedCallback"); } } else { BQA_LOGE("No matching SurfaceControls found: mSurfaceControlsWithPendingCallback was " @@ -346,7 +346,7 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp Date: Fri, 5 Nov 2021 16:21:06 -0700 Subject: BBQ: Clean up acquire states on BQ disconnect When the producer disconnects, all buffers in the queue will be freed. So clean up the bbq acquire state and handle any pending release callbacks. If we do get a release callback for a pending buffer for a disconnected queue, we cannot release the buffer back to the queue. So track these separately and drop the release callbacks as they come. Transaction callbacks are still expected to come in the order they were submitted regardless of buffer queue state. So we can continue to handle the pending transactions and transaction complete callbacks. When the queue is reconnected, the queue will increment the framenumbers starting from the last queued framenumber. Bug: 201482894 Test: atest BLASTBufferQueueTest Change-Id: I110530c2135804fc7b9545602dd6430014cad85c --- libs/gui/BLASTBufferQueue.cpp | 73 +++++++++++++++++++++++++--- libs/gui/include/gui/BLASTBufferQueue.h | 12 +++++ services/surfaceflinger/BufferStateLayer.cpp | 2 +- services/surfaceflinger/BufferStateLayer.h | 1 + 4 files changed, 80 insertions(+), 8 deletions(-) (limited to 'libs/gui/BLASTBufferQueue.cpp') diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 9080822f92..9cb7c88956 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -58,13 +58,22 @@ namespace android { ALOGE("[%s](f:%u,a:%u) " x, mName.c_str(), mNumFrameAvailable, mNumAcquired, ##__VA_ARGS__) void BLASTBufferItemConsumer::onDisconnect() { - Mutex::Autolock lock(mMutex); - mPreviouslyConnected = mCurrentlyConnected; - mCurrentlyConnected = false; - if (mPreviouslyConnected) { - mDisconnectEvents.push(mCurrentFrameNumber); + { + Mutex::Autolock lock(mMutex); + mPreviouslyConnected = mCurrentlyConnected; + mCurrentlyConnected = false; + if (mPreviouslyConnected) { + mDisconnectEvents.push(mCurrentFrameNumber); + } + mFrameEventHistory.onDisconnect(); + } + + { + std::scoped_lock lock(mBufferQueueMutex); + if (mBLASTBufferQueue != nullptr) { + mBLASTBufferQueue->onProducerDisconnect(); + } } - mFrameEventHistory.onDisconnect(); } void BLASTBufferItemConsumer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps, @@ -202,7 +211,11 @@ void BLASTBufferQueue::update(const sp& surface, uint32_t width, } SurfaceComposerClient::Transaction t; - const bool setBackpressureFlag = !SurfaceControl::isSameSurface(mSurfaceControl, surface); + bool setBackpressureFlag = false; + if (!SurfaceControl::isSameSurface(mSurfaceControl, surface)) { + mSurfaceControlSwapCount++; + setBackpressureFlag = true; + } bool applyTransaction = false; // Always update the native object even though they might have the same layer handle, so we can @@ -388,6 +401,19 @@ void BLASTBufferQueue::releaseBufferCallback( std::unique_lock _lock{mMutex}; BQA_LOGV("releaseBufferCallback %s", id.to_string().c_str()); + const auto it = mFreedBuffers.find(id); + if (it != mFreedBuffers.end()) { + mFreedBuffers.erase(it); + BQA_LOGV("releaseBufferCallback ignoring freed buffer %s", id.to_string().c_str()); + return; + } + + if (mFreedBuffers.size() != 0 && mLogMissingReleaseCallback) { + BQA_LOGD("Unexpected out of order buffer release. mFreedBuffer count=%d", + static_cast(mFreedBuffers.size())); + mLogMissingReleaseCallback = false; + } + // Calculate how many buffers we need to hold before we release them back // to the buffer queue. This will prevent higher latency when we are running // on a lower refresh rate than the max supported. We only do that for EGL @@ -593,6 +619,12 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { ATRACE_CALL(); std::unique_lock _lock{mMutex}; + if ((mSurfaceControlSwapCount > mProducerDisconnectCount) && mLogScSwap) { + BQA_LOGD("Expected producer disconnect sc swap count=%d bq disconnect count=%d", + mSurfaceControlSwapCount, mProducerDisconnectCount); + mLogScSwap = false; + } + const bool nextTransactionSet = mNextTransaction != nullptr; BQA_LOGV("onFrameAvailable-start nextTransactionSet=%s", boolToString(nextTransactionSet)); if (nextTransactionSet) { @@ -959,4 +991,31 @@ uint64_t BLASTBufferQueue::getLastAcquiredFrameNum() { return mLastAcquiredFrameNumber; } +// When the producer disconnects, all buffers in the queue will be freed. So clean up the bbq +// acquire state and handle any pending release callbacks. If we do get a release callback for a +// pending buffer for a disconnected queue, we cannot release the buffer back to the queue. So track +// these separately and drop the release callbacks as they come. + +// Transaction callbacks are still expected to come in the order they were submitted regardless of +// buffer queue state. So we can continue to handles the pending transactions and transaction +// complete callbacks. When the queue is reconnected, the queue will increment the framenumbers +// starting from the last queued framenumber. +void BLASTBufferQueue::onProducerDisconnect() { + BQA_LOGV("onProducerDisconnect"); + std::scoped_lock _lock{mMutex}; + // reset counts since the queue has been disconnected and all buffers have been freed. + mNumFrameAvailable = 0; + mNumAcquired = 0; + + // Track submitted buffers in a different container so we can handle any pending release buffer + // callbacks without affecting the BBQ acquire state. + mFreedBuffers.insert(mSubmitted.begin(), mSubmitted.end()); + mSubmitted.clear(); + mPendingRelease.clear(); + mProducerDisconnectCount++; + mCallbackCV.notify_all(); + mLogMissingReleaseCallback = true; + mLogScSwap = true; +} + } // namespace android diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 3881620f3d..72567e3f93 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -109,6 +109,8 @@ public: uint32_t getLastTransformHint() const; uint64_t getLastAcquiredFrameNum(); + void onProducerDisconnect(); + virtual ~BLASTBufferQueue(); private: @@ -156,6 +158,12 @@ private: std::unordered_map mSubmitted GUARDED_BY(mMutex); + // Keep a reference to the submitted buffers that were freed so we can drop the buffer quietly + // when we get the release callback from flinger. This can happen if the client had disconnected + // from the queue. + std::unordered_map mFreedBuffers + GUARDED_BY(mMutex); + // Keep a queue of the released buffers instead of immediately releasing // the buffers back to the buffer queue. This would be controlled by SF // setting the max acquired buffer count. @@ -240,6 +248,10 @@ private: uint32_t mCurrentMaxAcquiredBufferCount; bool mWaitForTransactionCallback = false; + bool mLogScSwap = true; + bool mLogMissingReleaseCallback = true; + uint32_t mSurfaceControlSwapCount = 0; + uint32_t mProducerDisconnectCount = 0; }; } // namespace android diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp index c0753f9d47..25fb6ce2ae 100644 --- a/services/surfaceflinger/BufferStateLayer.cpp +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -404,7 +404,7 @@ bool BufferStateLayer::setBuffer(const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime, bool isAutoTimestamp, std::optional dequeueTime, const FrameTimelineInfo& info) { - ATRACE_CALL(); + ATRACE_NAME(mSetBufferTraceTag.c_str()); const std::shared_ptr& buffer = getBufferFromBufferData(bufferData); diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h index eea700cf7b..3aa60826d0 100644 --- a/services/surfaceflinger/BufferStateLayer.h +++ b/services/surfaceflinger/BufferStateLayer.h @@ -153,6 +153,7 @@ private: static constexpr int kPendingClassificationMaxSurfaceFrames = 25; const std::string mBlastTransactionName{"BufferTX - " + mName}; + const std::string mSetBufferTraceTag{"setBuffer - " + mName}; // This integer is incremented everytime a buffer arrives at the server for this layer, // and decremented when a buffer is dropped or latched. When changed the integer is exported // to systrace with ATRACE_INT and mBlastTransactionName. This way when debugging perf it is -- cgit v1.2.3-59-g8ed1b From c33c63aba9b234eff8a3487c14946bf5bbea3bb5 Mon Sep 17 00:00:00 2001 From: Jiakai Zhang Date: Tue, 9 Nov 2021 11:24:04 +0000 Subject: Revert "BBQ: Clean up acquire states on BQ disconnect" This reverts commit 9051fb1885df913eecfa9072155797bdaf05d278. Reason for revert: Potential culprit CL for broken test b/205665388 Change-Id: Iba58d8de19851beedf46511fbefa837e3f3f7ad2 --- libs/gui/BLASTBufferQueue.cpp | 73 +++------------------------- libs/gui/include/gui/BLASTBufferQueue.h | 12 ----- services/surfaceflinger/BufferStateLayer.cpp | 2 +- services/surfaceflinger/BufferStateLayer.h | 1 - 4 files changed, 8 insertions(+), 80 deletions(-) (limited to 'libs/gui/BLASTBufferQueue.cpp') diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 9cb7c88956..9080822f92 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -58,22 +58,13 @@ namespace android { ALOGE("[%s](f:%u,a:%u) " x, mName.c_str(), mNumFrameAvailable, mNumAcquired, ##__VA_ARGS__) void BLASTBufferItemConsumer::onDisconnect() { - { - Mutex::Autolock lock(mMutex); - mPreviouslyConnected = mCurrentlyConnected; - mCurrentlyConnected = false; - if (mPreviouslyConnected) { - mDisconnectEvents.push(mCurrentFrameNumber); - } - mFrameEventHistory.onDisconnect(); - } - - { - std::scoped_lock lock(mBufferQueueMutex); - if (mBLASTBufferQueue != nullptr) { - mBLASTBufferQueue->onProducerDisconnect(); - } + Mutex::Autolock lock(mMutex); + mPreviouslyConnected = mCurrentlyConnected; + mCurrentlyConnected = false; + if (mPreviouslyConnected) { + mDisconnectEvents.push(mCurrentFrameNumber); } + mFrameEventHistory.onDisconnect(); } void BLASTBufferItemConsumer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps, @@ -211,11 +202,7 @@ void BLASTBufferQueue::update(const sp& surface, uint32_t width, } SurfaceComposerClient::Transaction t; - bool setBackpressureFlag = false; - if (!SurfaceControl::isSameSurface(mSurfaceControl, surface)) { - mSurfaceControlSwapCount++; - setBackpressureFlag = true; - } + const bool setBackpressureFlag = !SurfaceControl::isSameSurface(mSurfaceControl, surface); bool applyTransaction = false; // Always update the native object even though they might have the same layer handle, so we can @@ -401,19 +388,6 @@ void BLASTBufferQueue::releaseBufferCallback( std::unique_lock _lock{mMutex}; BQA_LOGV("releaseBufferCallback %s", id.to_string().c_str()); - const auto it = mFreedBuffers.find(id); - if (it != mFreedBuffers.end()) { - mFreedBuffers.erase(it); - BQA_LOGV("releaseBufferCallback ignoring freed buffer %s", id.to_string().c_str()); - return; - } - - if (mFreedBuffers.size() != 0 && mLogMissingReleaseCallback) { - BQA_LOGD("Unexpected out of order buffer release. mFreedBuffer count=%d", - static_cast(mFreedBuffers.size())); - mLogMissingReleaseCallback = false; - } - // Calculate how many buffers we need to hold before we release them back // to the buffer queue. This will prevent higher latency when we are running // on a lower refresh rate than the max supported. We only do that for EGL @@ -619,12 +593,6 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { ATRACE_CALL(); std::unique_lock _lock{mMutex}; - if ((mSurfaceControlSwapCount > mProducerDisconnectCount) && mLogScSwap) { - BQA_LOGD("Expected producer disconnect sc swap count=%d bq disconnect count=%d", - mSurfaceControlSwapCount, mProducerDisconnectCount); - mLogScSwap = false; - } - const bool nextTransactionSet = mNextTransaction != nullptr; BQA_LOGV("onFrameAvailable-start nextTransactionSet=%s", boolToString(nextTransactionSet)); if (nextTransactionSet) { @@ -991,31 +959,4 @@ uint64_t BLASTBufferQueue::getLastAcquiredFrameNum() { return mLastAcquiredFrameNumber; } -// When the producer disconnects, all buffers in the queue will be freed. So clean up the bbq -// acquire state and handle any pending release callbacks. If we do get a release callback for a -// pending buffer for a disconnected queue, we cannot release the buffer back to the queue. So track -// these separately and drop the release callbacks as they come. - -// Transaction callbacks are still expected to come in the order they were submitted regardless of -// buffer queue state. So we can continue to handles the pending transactions and transaction -// complete callbacks. When the queue is reconnected, the queue will increment the framenumbers -// starting from the last queued framenumber. -void BLASTBufferQueue::onProducerDisconnect() { - BQA_LOGV("onProducerDisconnect"); - std::scoped_lock _lock{mMutex}; - // reset counts since the queue has been disconnected and all buffers have been freed. - mNumFrameAvailable = 0; - mNumAcquired = 0; - - // Track submitted buffers in a different container so we can handle any pending release buffer - // callbacks without affecting the BBQ acquire state. - mFreedBuffers.insert(mSubmitted.begin(), mSubmitted.end()); - mSubmitted.clear(); - mPendingRelease.clear(); - mProducerDisconnectCount++; - mCallbackCV.notify_all(); - mLogMissingReleaseCallback = true; - mLogScSwap = true; -} - } // namespace android diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 72567e3f93..3881620f3d 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -109,8 +109,6 @@ public: uint32_t getLastTransformHint() const; uint64_t getLastAcquiredFrameNum(); - void onProducerDisconnect(); - virtual ~BLASTBufferQueue(); private: @@ -158,12 +156,6 @@ private: std::unordered_map mSubmitted GUARDED_BY(mMutex); - // Keep a reference to the submitted buffers that were freed so we can drop the buffer quietly - // when we get the release callback from flinger. This can happen if the client had disconnected - // from the queue. - std::unordered_map mFreedBuffers - GUARDED_BY(mMutex); - // Keep a queue of the released buffers instead of immediately releasing // the buffers back to the buffer queue. This would be controlled by SF // setting the max acquired buffer count. @@ -248,10 +240,6 @@ private: uint32_t mCurrentMaxAcquiredBufferCount; bool mWaitForTransactionCallback = false; - bool mLogScSwap = true; - bool mLogMissingReleaseCallback = true; - uint32_t mSurfaceControlSwapCount = 0; - uint32_t mProducerDisconnectCount = 0; }; } // namespace android diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp index 25fb6ce2ae..c0753f9d47 100644 --- a/services/surfaceflinger/BufferStateLayer.cpp +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -404,7 +404,7 @@ bool BufferStateLayer::setBuffer(const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime, bool isAutoTimestamp, std::optional dequeueTime, const FrameTimelineInfo& info) { - ATRACE_NAME(mSetBufferTraceTag.c_str()); + ATRACE_CALL(); const std::shared_ptr& buffer = getBufferFromBufferData(bufferData); diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h index 3aa60826d0..eea700cf7b 100644 --- a/services/surfaceflinger/BufferStateLayer.h +++ b/services/surfaceflinger/BufferStateLayer.h @@ -153,7 +153,6 @@ private: static constexpr int kPendingClassificationMaxSurfaceFrames = 25; const std::string mBlastTransactionName{"BufferTX - " + mName}; - const std::string mSetBufferTraceTag{"setBuffer - " + mName}; // This integer is incremented everytime a buffer arrives at the server for this layer, // and decremented when a buffer is dropped or latched. When changed the integer is exported // to systrace with ATRACE_INT and mBlastTransactionName. This way when debugging perf it is -- cgit v1.2.3-59-g8ed1b From a1c4c829a1ac5fd2a5ae583424fd35ae7a9a292c Mon Sep 17 00:00:00 2001 From: chaviw Date: Wed, 10 Nov 2021 18:11:58 -0600 Subject: Rename nextTransaction to syncTransaction The variable nextTransaction is abigious and doesn't represent its purpose. Rename to syncTransaction Test: BLASTBufferQueueTest Bug: 200285149 Change-Id: I1c1e151a63e88127178a92a369e82bcc6770ab90 --- libs/gui/BLASTBufferQueue.cpp | 28 ++++++------- libs/gui/include/gui/BLASTBufferQueue.h | 4 +- libs/gui/tests/BLASTBufferQueue_test.cpp | 68 ++++++++++++++++---------------- 3 files changed, 50 insertions(+), 50 deletions(-) (limited to 'libs/gui/BLASTBufferQueue.cpp') diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 9080822f92..e9149f3a3e 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -138,7 +138,7 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp= mLastAcquiredFrameNumber) { mWaitForTransactionCallback = false; flushShadowQueue(); @@ -420,7 +420,7 @@ void BLASTBufferQueue::releaseBufferCallback( mBufferItemConsumer->releaseBuffer(it->second, releaseBuffer.releaseFence); mSubmitted.erase(it); // Don't process the transactions here if mWaitForTransactionCallback is set. Instead, let - // onFrameAvailable handle processing them since it will merge with the nextTransaction. + // onFrameAvailable handle processing them since it will merge with the syncTransaction. if (!mWaitForTransactionCallback) { acquireNextBufferLocked(std::nullopt); } @@ -593,15 +593,15 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { ATRACE_CALL(); std::unique_lock _lock{mMutex}; - const bool nextTransactionSet = mNextTransaction != nullptr; - BQA_LOGV("onFrameAvailable-start nextTransactionSet=%s", boolToString(nextTransactionSet)); - if (nextTransactionSet) { + const bool syncTransactionSet = mSyncTransaction != nullptr; + BQA_LOGV("onFrameAvailable-start syncTransactionSet=%s", boolToString(syncTransactionSet)); + if (syncTransactionSet) { if (mWaitForTransactionCallback) { // We are waiting on a previous sync's transaction callback so allow another sync // transaction to proceed. // // We need to first flush out the transactions that were in between the two syncs. - // We do this by merging them into mNextTransaction so any buffer merging will get + // We do this by merging them into mSyncTransaction so any buffer merging will get // a release callback invoked. The release callback will be async so we need to wait // on max acquired to make sure we have the capacity to acquire another buffer. if (maxBuffersAcquired(false /* includeExtraAcquire */)) { @@ -625,12 +625,12 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { ATRACE_INT(mQueuedBufferTrace.c_str(), mNumFrameAvailable + mNumAcquired - mPendingRelease.size()); - BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " nextTransactionSet=%s", item.mFrameNumber, - boolToString(nextTransactionSet)); + BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " syncTransactionSet=%s", item.mFrameNumber, + boolToString(syncTransactionSet)); - if (nextTransactionSet) { - acquireNextBufferLocked(std::move(mNextTransaction)); - mNextTransaction = nullptr; + if (syncTransactionSet) { + acquireNextBufferLocked(std::move(mSyncTransaction)); + mSyncTransaction = nullptr; mWaitForTransactionCallback = true; } else if (!mWaitForTransactionCallback) { acquireNextBufferLocked(std::nullopt); @@ -652,9 +652,9 @@ void BLASTBufferQueue::onFrameCancelled(const uint64_t bufferId) { mDequeueTimestamps.erase(bufferId); }; -void BLASTBufferQueue::setNextTransaction(SurfaceComposerClient::Transaction* t) { +void BLASTBufferQueue::setSyncTransaction(SurfaceComposerClient::Transaction* t) { std::lock_guard _lock{mMutex}; - mNextTransaction = t; + mSyncTransaction = t; } bool BLASTBufferQueue::rejectBuffer(const BufferItem& item) { diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 4a63544464..f718de8c57 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -94,7 +94,7 @@ public: const std::vector& stats); void releaseBufferCallback(const ReleaseCallbackId& id, const sp& releaseFence, std::optional currentMaxAcquiredBufferCount); - void setNextTransaction(SurfaceComposerClient::Transaction *t); + void setSyncTransaction(SurfaceComposerClient::Transaction* t); void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber); void applyPendingTransactions(uint64_t frameNumber); @@ -208,7 +208,7 @@ private: sp mProducer; sp mBufferItemConsumer; - SurfaceComposerClient::Transaction* mNextTransaction GUARDED_BY(mMutex); + SurfaceComposerClient::Transaction* mSyncTransaction GUARDED_BY(mMutex); std::vector> mPendingTransactions GUARDED_BY(mMutex); diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp index 8607e1d83c..194757fe6b 100644 --- a/libs/gui/tests/BLASTBufferQueue_test.cpp +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -109,15 +109,15 @@ public: mBlastBufferQueueAdapter->update(sc, width, height, PIXEL_FORMAT_RGBA_8888); } - void setNextTransaction(Transaction* next) { - mBlastBufferQueueAdapter->setNextTransaction(next); + void setSyncTransaction(Transaction* sync) { + mBlastBufferQueueAdapter->setSyncTransaction(sync); } int getWidth() { return mBlastBufferQueueAdapter->mSize.width; } int getHeight() { return mBlastBufferQueueAdapter->mSize.height; } - Transaction* getNextTransaction() { return mBlastBufferQueueAdapter->mNextTransaction; } + Transaction* getSyncTransaction() { return mBlastBufferQueueAdapter->mSyncTransaction; } sp getIGraphicBufferProducer() { return mBlastBufferQueueAdapter->getIGraphicBufferProducer(); @@ -337,7 +337,7 @@ TEST_F(BLASTBufferQueueTest, CreateBLASTBufferQueue) { ASSERT_EQ(mSurfaceControl, adapter.getSurfaceControl()); ASSERT_EQ(mDisplayWidth, adapter.getWidth()); ASSERT_EQ(mDisplayHeight, adapter.getHeight()); - ASSERT_EQ(nullptr, adapter.getNextTransaction()); + ASSERT_EQ(nullptr, adapter.getSyncTransaction()); } TEST_F(BLASTBufferQueueTest, Update) { @@ -358,11 +358,11 @@ TEST_F(BLASTBufferQueueTest, Update) { ASSERT_EQ(mDisplayHeight / 2, height); } -TEST_F(BLASTBufferQueueTest, SetNextTransaction) { +TEST_F(BLASTBufferQueueTest, SetSyncTransaction) { BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); - Transaction next; - adapter.setNextTransaction(&next); - ASSERT_EQ(&next, adapter.getNextTransaction()); + Transaction sync; + adapter.setSyncTransaction(&sync); + ASSERT_EQ(&sync, adapter.getSyncTransaction()); } TEST_F(BLASTBufferQueueTest, DISABLED_onFrameAvailable_ApplyDesiredPresentTime) { @@ -801,8 +801,8 @@ TEST_F(BLASTBufferQueueTest, SyncThenNoSync) { sp igbProducer; setUpProducer(adapter, igbProducer); - Transaction next; - adapter.setNextTransaction(&next); + Transaction sync; + adapter.setSyncTransaction(&sync); queueBuffer(igbProducer, 0, 255, 0, 0); // queue non sync buffer, so this one should get blocked @@ -811,7 +811,7 @@ TEST_F(BLASTBufferQueueTest, SyncThenNoSync) { queueBuffer(igbProducer, r, g, b, presentTimeDelay); CallbackHelper transactionCallback; - next.addTransactionCompletedCallback(transactionCallback.function, + sync.addTransactionCompletedCallback(transactionCallback.function, transactionCallback.getContext()) .apply(); @@ -841,16 +841,16 @@ TEST_F(BLASTBufferQueueTest, MultipleSyncTransactions) { Transaction mainTransaction; - Transaction next; - adapter.setNextTransaction(&next); + Transaction sync; + adapter.setSyncTransaction(&sync); queueBuffer(igbProducer, 0, 255, 0, 0); - mainTransaction.merge(std::move(next)); + mainTransaction.merge(std::move(sync)); - adapter.setNextTransaction(&next); + adapter.setSyncTransaction(&sync); queueBuffer(igbProducer, r, g, b, 0); - mainTransaction.merge(std::move(next)); + mainTransaction.merge(std::move(sync)); // Expect 1 buffer to be released even before sending to SurfaceFlinger mProducerListener->waitOnNumberReleased(1); @@ -881,24 +881,24 @@ TEST_F(BLASTBufferQueueTest, MultipleSyncTransactionWithNonSync) { Transaction mainTransaction; - Transaction next; + Transaction sync; // queue a sync transaction - adapter.setNextTransaction(&next); + adapter.setSyncTransaction(&sync); queueBuffer(igbProducer, 0, 255, 0, 0); - mainTransaction.merge(std::move(next)); + mainTransaction.merge(std::move(sync)); - // queue another buffer without setting next transaction + // queue another buffer without setting sync transaction queueBuffer(igbProducer, 0, 0, 255, 0); // queue another sync transaction - adapter.setNextTransaction(&next); + adapter.setSyncTransaction(&sync); queueBuffer(igbProducer, r, g, b, 0); // Expect 1 buffer to be released because the non sync transaction should merge // with the sync mProducerListener->waitOnNumberReleased(1); - mainTransaction.merge(std::move(next)); + mainTransaction.merge(std::move(sync)); // Expect 2 buffers to be released due to merging the two syncs. mProducerListener->waitOnNumberReleased(2); @@ -929,26 +929,26 @@ TEST_F(BLASTBufferQueueTest, MultipleSyncRunOutOfBuffers) { Transaction mainTransaction; - Transaction next; + Transaction sync; // queue a sync transaction - adapter.setNextTransaction(&next); + adapter.setSyncTransaction(&sync); queueBuffer(igbProducer, 0, 255, 0, 0); - mainTransaction.merge(std::move(next)); + mainTransaction.merge(std::move(sync)); - // queue a few buffers without setting next transaction + // queue a few buffers without setting sync transaction queueBuffer(igbProducer, 0, 0, 255, 0); queueBuffer(igbProducer, 0, 0, 255, 0); queueBuffer(igbProducer, 0, 0, 255, 0); // queue another sync transaction - adapter.setNextTransaction(&next); + adapter.setSyncTransaction(&sync); queueBuffer(igbProducer, r, g, b, 0); // Expect 3 buffers to be released because the non sync transactions should merge // with the sync mProducerListener->waitOnNumberReleased(3); - mainTransaction.merge(std::move(next)); + mainTransaction.merge(std::move(sync)); // Expect 4 buffers to be released due to merging the two syncs. mProducerListener->waitOnNumberReleased(4); @@ -986,14 +986,14 @@ TEST_F(BLASTBufferQueueTest, RunOutOfBuffersWaitingOnSF) { // Send a buffer to SF queueBuffer(igbProducer, 0, 255, 0, 0); - Transaction next; + Transaction sync; // queue a sync transaction - adapter.setNextTransaction(&next); + adapter.setSyncTransaction(&sync); queueBuffer(igbProducer, 0, 255, 0, 0); - mainTransaction.merge(std::move(next)); + mainTransaction.merge(std::move(sync)); - // queue a few buffers without setting next transaction + // queue a few buffers without setting sync transaction queueBuffer(igbProducer, 0, 0, 255, 0); queueBuffer(igbProducer, 0, 0, 255, 0); queueBuffer(igbProducer, 0, 0, 255, 0); @@ -1002,13 +1002,13 @@ TEST_F(BLASTBufferQueueTest, RunOutOfBuffersWaitingOnSF) { mainTransaction.apply(); // queue another sync transaction - adapter.setNextTransaction(&next); + adapter.setSyncTransaction(&sync); queueBuffer(igbProducer, r, g, b, 0); // Expect 2 buffers to be released because the non sync transactions should merge // with the sync mProducerListener->waitOnNumberReleased(3); - mainTransaction.merge(std::move(next)); + mainTransaction.merge(std::move(sync)); CallbackHelper transactionCallback; mainTransaction -- cgit v1.2.3-59-g8ed1b From 0acd33a338821d3f3e30677f5f5505cae69d527d Mon Sep 17 00:00:00 2001 From: chaviw Date: Tue, 2 Nov 2021 11:55:37 -0500 Subject: Add ability to process buffers into the same syncTransaction In a normal sync case, we send the syncTransaction and then clear it as soon as a buffer is acquired and placed in the transaction. However, in some cases, we want to continue processing buffers into the sync transaction until we get the signal to stop. This is for cases when we don't have a clear signal which buffer wants to be synced, but know when it's done waiting for the correct buffer. This also adds a way to retrieve and clear the buffer from the transaction to allow BBQ to release the buffer when it knows it's ready to acquire a new buffer. This speeds up the process so BBQ doesn't need to wait for the asynchronous callback to release the previous buffer. This new machanism can be used to synchronize SurfaceViews because it knows when something has been changed and to wait until the app has notified that it's drawn. Once it's notified, we can stop processing frames into the sync transaction and allow the transaction to be applied with the main window. Bug: 200284684 Test: BLASTBufferQueueTest Change-Id: Ibdb524270de368033cf8835f0c305e8a5756ff6e --- libs/gui/BLASTBufferQueue.cpp | 96 ++++++++++++++++++---------- libs/gui/SurfaceComposerClient.cpp | 30 +++++++++ libs/gui/include/gui/BLASTBufferQueue.h | 11 +++- libs/gui/include/gui/SurfaceComposerClient.h | 3 + libs/gui/tests/BLASTBufferQueue_test.cpp | 52 +++++++++++++-- 5 files changed, 154 insertions(+), 38 deletions(-) (limited to 'libs/gui/BLASTBufferQueue.cpp') diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index e9149f3a3e..55703214a5 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -407,18 +407,9 @@ void BLASTBufferQueue::releaseBufferCallback( // Release all buffers that are beyond the ones that we need to hold while (mPendingRelease.size() > numPendingBuffersToHold) { - const auto releaseBuffer = mPendingRelease.front(); + const auto releasedBuffer = mPendingRelease.front(); mPendingRelease.pop_front(); - auto it = mSubmitted.find(releaseBuffer.callbackId); - if (it == mSubmitted.end()) { - BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %s", - releaseBuffer.callbackId.to_string().c_str()); - return; - } - mNumAcquired--; - BQA_LOGV("released %s", releaseBuffer.callbackId.to_string().c_str()); - mBufferItemConsumer->releaseBuffer(it->second, releaseBuffer.releaseFence); - mSubmitted.erase(it); + releaseBuffer(releasedBuffer.callbackId, releasedBuffer.releaseFence); // Don't process the transactions here if mWaitForTransactionCallback is set. Instead, let // onFrameAvailable handle processing them since it will merge with the syncTransaction. if (!mWaitForTransactionCallback) { @@ -432,6 +423,20 @@ void BLASTBufferQueue::releaseBufferCallback( mCallbackCV.notify_all(); } +void BLASTBufferQueue::releaseBuffer(const ReleaseCallbackId& callbackId, + const sp& releaseFence) { + auto it = mSubmitted.find(callbackId); + if (it == mSubmitted.end()) { + BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %s", + callbackId.to_string().c_str()); + return; + } + mNumAcquired--; + BQA_LOGV("released %s", callbackId.to_string().c_str()); + mBufferItemConsumer->releaseBuffer(it->second, releaseFence); + mSubmitted.erase(it); +} + void BLASTBufferQueue::acquireNextBufferLocked( const std::optional transaction) { ATRACE_CALL(); @@ -589,34 +594,57 @@ void BLASTBufferQueue::acquireAndReleaseBuffer() { mBufferItemConsumer->releaseBuffer(bufferItem, bufferItem.mFence); } +void BLASTBufferQueue::flushAndWaitForFreeBuffer(std::unique_lock& lock) { + if (mWaitForTransactionCallback && mNumFrameAvailable > 0) { + // We are waiting on a previous sync's transaction callback so allow another sync + // transaction to proceed. + // + // We need to first flush out the transactions that were in between the two syncs. + // We do this by merging them into mSyncTransaction so any buffer merging will get + // a release callback invoked. The release callback will be async so we need to wait + // on max acquired to make sure we have the capacity to acquire another buffer. + if (maxBuffersAcquired(false /* includeExtraAcquire */)) { + BQA_LOGD("waiting to flush shadow queue..."); + mCallbackCV.wait(lock); + } + while (mNumFrameAvailable > 0) { + // flush out the shadow queue + acquireAndReleaseBuffer(); + } + } + + while (maxBuffersAcquired(false /* includeExtraAcquire */)) { + BQA_LOGD("waiting for free buffer."); + mCallbackCV.wait(lock); + } +} + void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { ATRACE_CALL(); std::unique_lock _lock{mMutex}; const bool syncTransactionSet = mSyncTransaction != nullptr; BQA_LOGV("onFrameAvailable-start syncTransactionSet=%s", boolToString(syncTransactionSet)); + if (syncTransactionSet) { - if (mWaitForTransactionCallback) { - // We are waiting on a previous sync's transaction callback so allow another sync - // transaction to proceed. - // - // We need to first flush out the transactions that were in between the two syncs. - // We do this by merging them into mSyncTransaction so any buffer merging will get - // a release callback invoked. The release callback will be async so we need to wait - // on max acquired to make sure we have the capacity to acquire another buffer. - if (maxBuffersAcquired(false /* includeExtraAcquire */)) { - BQA_LOGD("waiting to flush shadow queue..."); - mCallbackCV.wait(_lock); - } - while (mNumFrameAvailable > 0) { - // flush out the shadow queue - acquireAndReleaseBuffer(); + bool mayNeedToWaitForBuffer = true; + // If we are going to re-use the same mSyncTransaction, release the buffer that may already + // be set in the Transaction. This is to allow us a free slot early to continue processing + // a new buffer. + if (!mAcquireSingleBuffer) { + auto bufferData = mSyncTransaction->getAndClearBuffer(mSurfaceControl); + if (bufferData) { + BQA_LOGD("Releasing previous buffer when syncing: framenumber=%" PRIu64, + bufferData->frameNumber); + releaseBuffer(bufferData->releaseCallbackId, bufferData->acquireFence); + // Because we just released a buffer, we know there's no need to wait for a free + // buffer. + mayNeedToWaitForBuffer = false; } } - while (maxBuffersAcquired(false /* includeExtraAcquire */)) { - BQA_LOGD("waiting for free buffer."); - mCallbackCV.wait(_lock); + if (mayNeedToWaitForBuffer) { + flushAndWaitForFreeBuffer(_lock); } } @@ -629,8 +657,10 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { boolToString(syncTransactionSet)); if (syncTransactionSet) { - acquireNextBufferLocked(std::move(mSyncTransaction)); - mSyncTransaction = nullptr; + acquireNextBufferLocked(mSyncTransaction); + if (mAcquireSingleBuffer) { + mSyncTransaction = nullptr; + } mWaitForTransactionCallback = true; } else if (!mWaitForTransactionCallback) { acquireNextBufferLocked(std::nullopt); @@ -652,9 +682,11 @@ void BLASTBufferQueue::onFrameCancelled(const uint64_t bufferId) { mDequeueTimestamps.erase(bufferId); }; -void BLASTBufferQueue::setSyncTransaction(SurfaceComposerClient::Transaction* t) { +void BLASTBufferQueue::setSyncTransaction(SurfaceComposerClient::Transaction* t, + bool acquireSingleBuffer) { std::lock_guard _lock{mMutex}; mSyncTransaction = t; + mAcquireSingleBuffer = mSyncTransaction ? acquireSingleBuffer : true; } bool BLASTBufferQueue::rejectBuffer(const BufferItem& item) { diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 2713be060a..b139cf126c 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -413,6 +413,14 @@ ReleaseBufferCallback TransactionCompletedListener::popReleaseBufferCallbackLock return callback; } +void TransactionCompletedListener::removeReleaseBufferCallback( + const ReleaseCallbackId& callbackId) { + { + std::scoped_lock lock(mMutex); + popReleaseBufferCallbackLocked(callbackId); + } +} + // --------------------------------------------------------------------------- void removeDeadBufferCallback(void* /*context*/, uint64_t graphicBufferId); @@ -1307,6 +1315,28 @@ SurfaceComposerClient::Transaction::setTransformToDisplayInverse(const sp SurfaceComposerClient::Transaction::getAndClearBuffer( + const sp& sc) { + layer_state_t* s = getLayerState(sc); + if (!s) { + return std::nullopt; + } + if (!(s->what & layer_state_t::eBufferChanged)) { + return std::nullopt; + } + + BufferData bufferData = s->bufferData; + + TransactionCompletedListener::getInstance()->removeReleaseBufferCallback( + bufferData.releaseCallbackId); + BufferData emptyBufferData; + s->what &= ~layer_state_t::eBufferChanged; + s->bufferData = emptyBufferData; + + mContainsBuffer = false; + return bufferData; +} + SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffer( const sp& sc, const sp& buffer, const std::optional>& fence, const std::optional& frameNumber, diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index f718de8c57..d76617373a 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -94,7 +94,7 @@ public: const std::vector& stats); void releaseBufferCallback(const ReleaseCallbackId& id, const sp& releaseFence, std::optional currentMaxAcquiredBufferCount); - void setSyncTransaction(SurfaceComposerClient::Transaction* t); + void setSyncTransaction(SurfaceComposerClient::Transaction* t, bool acquireSingleBuffer = true); void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber); void applyPendingTransactions(uint64_t frameNumber); @@ -132,6 +132,9 @@ private: void flushShadowQueue() REQUIRES(mMutex); void acquireAndReleaseBuffer() REQUIRES(mMutex); + void releaseBuffer(const ReleaseCallbackId& callbackId, const sp& releaseFence) + REQUIRES(mMutex); + void flushAndWaitForFreeBuffer(std::unique_lock& lock); std::string mName; // Represents the queued buffer count from buffer queue, @@ -239,7 +242,11 @@ private: std::queue> mSurfaceControlsWithPendingCallback GUARDED_BY(mMutex); uint32_t mCurrentMaxAcquiredBufferCount; - bool mWaitForTransactionCallback = false; + bool mWaitForTransactionCallback GUARDED_BY(mMutex) = false; + + // Flag to determine if syncTransaction should only acquire a single buffer and then clear or + // continue to acquire buffers until explicitly cleared + bool mAcquireSingleBuffer GUARDED_BY(mMutex) = true; }; } // namespace android diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index e62c76ed80..e05c3646c6 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -493,6 +493,7 @@ public: const std::optional& frameNumber = std::nullopt, const ReleaseCallbackId& id = ReleaseCallbackId::INVALID_ID, ReleaseBufferCallback callback = nullptr); + std::optional getAndClearBuffer(const sp& sc); Transaction& setDataspace(const sp& sc, ui::Dataspace dataspace); Transaction& setHdrMetadata(const sp& sc, const HdrMetadata& hdrMetadata); Transaction& setSurfaceDamageRegion(const sp& sc, @@ -751,6 +752,8 @@ public: void onReleaseBuffer(ReleaseCallbackId, sp releaseFence, uint32_t currentMaxAcquiredBufferCount) override; + void removeReleaseBufferCallback(const ReleaseCallbackId& callbackId); + // For Testing Only static void setInstance(const sp&); diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp index 194757fe6b..48b8621be1 100644 --- a/libs/gui/tests/BLASTBufferQueue_test.cpp +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -109,8 +109,8 @@ public: mBlastBufferQueueAdapter->update(sc, width, height, PIXEL_FORMAT_RGBA_8888); } - void setSyncTransaction(Transaction* sync) { - mBlastBufferQueueAdapter->setSyncTransaction(sync); + void setSyncTransaction(Transaction* next, bool acquireSingleBuffer = true) { + mBlastBufferQueueAdapter->setSyncTransaction(next, acquireSingleBuffer); } int getWidth() { return mBlastBufferQueueAdapter->mSize.width; } @@ -143,6 +143,11 @@ public: mBlastBufferQueueAdapter->waitForCallback(frameNumber); } + void validateNumFramesSubmitted(int64_t numFramesSubmitted) { + std::unique_lock lock{mBlastBufferQueueAdapter->mMutex}; + ASSERT_EQ(numFramesSubmitted, mBlastBufferQueueAdapter->mSubmitted.size()); + } + private: sp mBlastBufferQueueAdapter; }; @@ -298,7 +303,7 @@ protected: auto ret = igbp->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight, PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr, nullptr); - ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret); + ASSERT_TRUE(ret == IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION || ret == NO_ERROR); ASSERT_EQ(OK, igbp->requestBuffer(slot, &buf)); uint32_t* bufData; @@ -818,7 +823,7 @@ TEST_F(BLASTBufferQueueTest, SyncThenNoSync) { CallbackData callbackData; transactionCallback.getCallbackData(&callbackData); - // capture screen and verify that it is red + // capture screen and verify that it is green ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); ASSERT_NO_FATAL_FAILURE( checkScreenCapture(0, 255, 0, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); @@ -1025,6 +1030,45 @@ TEST_F(BLASTBufferQueueTest, RunOutOfBuffersWaitingOnSF) { checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); } +TEST_F(BLASTBufferQueueTest, SetSyncTransactionAcquireMultipleBuffers) { + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + + sp igbProducer; + setUpProducer(adapter, igbProducer); + + Transaction next; + adapter.setSyncTransaction(&next, false); + queueBuffer(igbProducer, 0, 255, 0, 0); + queueBuffer(igbProducer, 0, 0, 255, 0); + // There should only be one frame submitted since the first frame will be released. + adapter.validateNumFramesSubmitted(1); + adapter.setSyncTransaction(nullptr); + + // queue non sync buffer, so this one should get blocked + // Add a present delay to allow the first screenshot to get taken. + nsecs_t presentTimeDelay = std::chrono::nanoseconds(500ms).count(); + queueBuffer(igbProducer, 255, 0, 0, presentTimeDelay); + + CallbackHelper transactionCallback; + next.addTransactionCompletedCallback(transactionCallback.function, + transactionCallback.getContext()) + .apply(); + + CallbackData callbackData; + transactionCallback.getCallbackData(&callbackData); + + // capture screen and verify that it is blue + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + ASSERT_NO_FATAL_FAILURE( + checkScreenCapture(0, 0, 255, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); + + mProducerListener->waitOnNumberReleased(2); + // capture screen and verify that it is red + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + ASSERT_NO_FATAL_FAILURE( + checkScreenCapture(255, 0, 0, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); +} + class TestProducerListener : public BnProducerListener { public: sp mIgbp; -- cgit v1.2.3-59-g8ed1b From f2dace7ba5f849f14fd63b5bfe873056d3e44721 Mon Sep 17 00:00:00 2001 From: chaviw Date: Wed, 17 Nov 2021 17:36:50 -0600 Subject: Only add commit callback when using sync transaction There's no need to add a commit callback when we're not syncing since the callback won't actually do anything. Instead only add a commit callback when a sync transaction has been requested. Test: BLASTBufferQueueTest Bug: 205278630 Change-Id: Ib7345f2581b6e4ce8923531aebcd457c14d86027 --- libs/gui/BLASTBufferQueue.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'libs/gui/BLASTBufferQueue.cpp') diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 55703214a5..f05426f7cf 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -507,7 +507,6 @@ void BLASTBufferQueue::acquireNextBufferLocked( // Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback. incStrong((void*)transactionCallbackThunk); - incStrong((void*)transactionCommittedCallbackThunk); const bool sizeHasChanged = mRequestedSize != mSize; mSize = mRequestedSize; @@ -527,7 +526,7 @@ void BLASTBufferQueue::acquireNextBufferLocked( t->setHdrMetadata(mSurfaceControl, bufferItem.mHdrMetadata); t->setSurfaceDamageRegion(mSurfaceControl, bufferItem.mSurfaceDamage); t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast(this)); - t->addTransactionCommittedCallback(transactionCommittedCallbackThunk, static_cast(this)); + mSurfaceControlsWithPendingCallback.push(mSurfaceControl); if (updateDestinationFrame) { @@ -658,6 +657,13 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { if (syncTransactionSet) { acquireNextBufferLocked(mSyncTransaction); + + // Only need a commit callback when syncing to ensure the buffer that's synced has been sent + // to SF + incStrong((void*)transactionCommittedCallbackThunk); + mSyncTransaction->addTransactionCommittedCallback(transactionCommittedCallbackThunk, + static_cast(this)); + if (mAcquireSingleBuffer) { mSyncTransaction = nullptr; } -- cgit v1.2.3-59-g8ed1b From cf2f21fd82d1f95b20077fd9a10aeaa6eb4c202e Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Tue, 30 Nov 2021 14:47:02 -0800 Subject: BLASTBufferQueue: Cap shadow queue size during sync While waiting for the transaction commit callback on a previous sync transaction BLASTBufferQueue will halt buffer processing to ensure later frames do not arrive at SurfaceFlinger first. These buffers wait in the shadow queue (tracked by mNumFrameAvailable) to be acquired later. If we end up in a situation where all the buffers are either in a sync transaction or in the shadow queue then dequeue buffer will begin to block. This isn't ideal, as dequeue buffer blocking can cause UI thread to block, aka UI thread can block on RenderThread. However completing the sync transaction (from a previous frame) can also depend on UI thread, aka RenderThread can now block on UI thread (since we need the transaction to apply in order to thaw the shadow queue). In this CL we try and avoid that situation by only keeping 1 frame in the shadow queue while waiting for the sync to complete. If a second frame comes in we will acquire and release the first before acquiring the second. Bug: 200285149 Change-Id: I5072765e7b94820b3e66c557f5a96172ccef8172 --- libs/gui/BLASTBufferQueue.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'libs/gui/BLASTBufferQueue.cpp') diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index f05426f7cf..dd50ca7fff 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -649,6 +649,9 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { // add to shadow queue mNumFrameAvailable++; + if (mWaitForTransactionCallback && mNumFrameAvailable == 2) { + acquireAndReleaseBuffer(); + } ATRACE_INT(mQueuedBufferTrace.c_str(), mNumFrameAvailable + mNumAcquired - mPendingRelease.size()); -- cgit v1.2.3-59-g8ed1b From 1cb8e89776024a103e54e71bbfe64e2e9428d5dd Mon Sep 17 00:00:00 2001 From: Vishnu Nair Date: Mon, 6 Dec 2021 16:45:48 -0800 Subject: BBQ: Capture initial destframe change from BBQ Allow the caller to pass in a transaction that can capture the initial destination frame changes. This allows the caller to apply other destination frame changes via BBQ#update in order. Bug: 195443440 Test: atest BLASTBufferQueueTest Test: repro steps from bug Change-Id: Ibf53a0efdebb87291d081e48633c373a98d347b1 --- libs/gui/BLASTBufferQueue.cpp | 37 +++++++++++++-------------------- libs/gui/include/gui/BLASTBufferQueue.h | 1 + 2 files changed, 15 insertions(+), 23 deletions(-) (limited to 'libs/gui/BLASTBufferQueue.cpp') diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index dd50ca7fff..85a4b67f34 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -132,12 +132,11 @@ void BLASTBufferItemConsumer::onSidebandStreamChanged() { } } -BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp& surface, - int width, int height, int32_t format) - : mSurfaceControl(surface), - mSize(width, height), +BLASTBufferQueue::BLASTBufferQueue(const std::string& name) + : mSurfaceControl(nullptr), + mSize(1, 1), mRequestedSize(mSize), - mFormat(format), + mFormat(PIXEL_FORMAT_RGBA_8888), mSyncTransaction(nullptr) { createBufferQueue(&mProducer, &mConsumer); // since the adapter is in the client process, set dequeue timeout @@ -158,25 +157,21 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const spsetName(String8(consumerName.c_str())); mBufferItemConsumer->setFrameAvailableListener(this); mBufferItemConsumer->setBufferFreedListener(this); - mBufferItemConsumer->setDefaultBufferSize(mSize.width, mSize.height); - mBufferItemConsumer->setDefaultBufferFormat(convertBufferFormat(format)); mBufferItemConsumer->setBlastBufferQueue(this); ComposerService::getComposerService()->getMaxAcquiredBufferCount(&mMaxAcquiredBuffers); mBufferItemConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBuffers); mCurrentMaxAcquiredBufferCount = mMaxAcquiredBuffers; - - mTransformHint = mSurfaceControl->getTransformHint(); - mBufferItemConsumer->setTransformHint(mTransformHint); - SurfaceComposerClient::Transaction() - .setFlags(surface, layer_state_t::eEnableBackpressure, - layer_state_t::eEnableBackpressure) - .setApplyToken(mApplyToken) - .apply(); mNumAcquired = 0; mNumFrameAvailable = 0; - BQA_LOGV("BLASTBufferQueue created width=%d height=%d format=%d mTransformHint=%d", width, - height, format, mTransformHint); + BQA_LOGV("BLASTBufferQueue created width=%d height=%d format=%d mTransformHint=%d", mSize.width, + mSize.height, mFormat, mTransformHint); +} + +BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp& surface, + int width, int height, int32_t format) + : BLASTBufferQueue(name) { + update(surface, width, height, format); } BLASTBufferQueue::~BLASTBufferQueue() { @@ -228,12 +223,9 @@ void BLASTBufferQueue::update(const sp& surface, uint32_t width, // If the buffer supports scaling, update the frame immediately since the client may // want to scale the existing buffer to the new size. mSize = mRequestedSize; - // We only need to update the scale if we've received at least one buffer. The reason - // for this is the scale is calculated based on the requested size and buffer size. - // If there's no buffer, the scale will always be 1. SurfaceComposerClient::Transaction* destFrameTransaction = (outTransaction) ? outTransaction : &t; - if (mSurfaceControl != nullptr && mLastBufferInfo.hasBuffer) { + if (mSurfaceControl != nullptr) { destFrameTransaction->setDestinationFrame(mSurfaceControl, Rect(0, 0, newSize.getWidth(), newSize.getHeight())); @@ -508,9 +500,8 @@ void BLASTBufferQueue::acquireNextBufferLocked( // Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback. incStrong((void*)transactionCallbackThunk); - const bool sizeHasChanged = mRequestedSize != mSize; + const bool updateDestinationFrame = mRequestedSize != mSize; mSize = mRequestedSize; - const bool updateDestinationFrame = sizeHasChanged || !mLastBufferInfo.hasBuffer; Rect crop = computeCrop(bufferItem); mLastBufferInfo.update(true /* hasBuffer */, bufferItem.mGraphicBuffer->getWidth(), bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform, diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index d76617373a..8a2f392e35 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -74,6 +74,7 @@ class BLASTBufferQueue : public ConsumerBase::FrameAvailableListener, public BufferItemConsumer::BufferFreedListener { public: + BLASTBufferQueue(const std::string& name); BLASTBufferQueue(const std::string& name, const sp& surface, int width, int height, int32_t format); -- cgit v1.2.3-59-g8ed1b From 1e8bf10f0ddb7531034bb687ec118c9b6b16dfa3 Mon Sep 17 00:00:00 2001 From: Vishnu Nair Date: Tue, 28 Dec 2021 14:36:59 -0800 Subject: BBQ: Recreate BBQ when SurfaceControl changes 1/2 Alternative approach to fab15e55446080bdcfc05ba315e8ef914b0a6f65 which was racy because the disconnect callback is called without the BQ lock and the client can continue to modify the BQ state after disconnecting from the queue. This approach resets the BQ and BBQ states when the SurfaceControl is updated. This solves one concrete problem of not relying on the old SurfaceControl to be destroyed in order to release the currently presented buffer back to BQ. In addition this change resets the sync state in BBQ with the rationale the system does not want to sync on buffers presented on an older SurfaceControl. Bug: 197269223 Test: atest BLASTBufferQueueTest Test: labtest ag/16407859 cf-foldable * 3 (b/197269223#comment40) Change-Id: Id7049c3fcb7f68ed1bcaed8b82bd13b4397af000 --- libs/gui/BLASTBufferQueue.cpp | 90 ++++++++++++++++++++++++-------- libs/gui/include/gui/BLASTBufferQueue.h | 8 +-- libs/gui/tests/BLASTBufferQueue_test.cpp | 88 +++++++++++++++++++++++++++++++ 3 files changed, 161 insertions(+), 25 deletions(-) (limited to 'libs/gui/BLASTBufferQueue.cpp') diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 85a4b67f34..34faf87546 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -164,8 +164,7 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name) mCurrentMaxAcquiredBufferCount = mMaxAcquiredBuffers; mNumAcquired = 0; mNumFrameAvailable = 0; - BQA_LOGV("BLASTBufferQueue created width=%d height=%d format=%d mTransformHint=%d", mSize.width, - mSize.height, mFormat, mTransformHint); + BQA_LOGV("BLASTBufferQueue created"); } BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp& surface, @@ -182,14 +181,14 @@ BLASTBufferQueue::~BLASTBufferQueue() { BQA_LOGE("Applying pending transactions on dtor %d", static_cast(mPendingTransactions.size())); SurfaceComposerClient::Transaction t; - for (auto& [targetFrameNumber, transaction] : mPendingTransactions) { - t.merge(std::move(transaction)); - } - t.apply(); + mergePendingTransactions(&t, std::numeric_limits::max() /* frameNumber */); + t.setApplyToken(mApplyToken).apply(); } void BLASTBufferQueue::update(const sp& surface, uint32_t width, uint32_t height, int32_t format, SurfaceComposerClient::Transaction* outTransaction) { + LOG_ALWAYS_FATAL_IF(surface == nullptr, "BLASTBufferQueue: mSurfaceControl must not be NULL"); + std::unique_lock _lock{mMutex}; if (mFormat != format) { mFormat = format; @@ -197,21 +196,20 @@ void BLASTBufferQueue::update(const sp& surface, uint32_t width, } SurfaceComposerClient::Transaction t; - const bool setBackpressureFlag = !SurfaceControl::isSameSurface(mSurfaceControl, surface); + const bool surfaceControlChanged = !SurfaceControl::isSameSurface(mSurfaceControl, surface); bool applyTransaction = false; // Always update the native object even though they might have the same layer handle, so we can // get the updated transform hint from WM. mSurfaceControl = surface; - if (mSurfaceControl != nullptr) { - if (setBackpressureFlag) { - t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure, - layer_state_t::eEnableBackpressure); - applyTransaction = true; - } - mTransformHint = mSurfaceControl->getTransformHint(); - mBufferItemConsumer->setTransformHint(mTransformHint); + if (surfaceControlChanged) { + BQA_LOGD("Updating SurfaceControl without recreating BBQ"); + t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure, + layer_state_t::eEnableBackpressure); + applyTransaction = true; } + mTransformHint = mSurfaceControl->getTransformHint(); + mBufferItemConsumer->setTransformHint(mTransformHint); BQA_LOGV("update width=%d height=%d format=%d mTransformHint=%d", width, height, format, mTransformHint); @@ -225,11 +223,9 @@ void BLASTBufferQueue::update(const sp& surface, uint32_t width, mSize = mRequestedSize; SurfaceComposerClient::Transaction* destFrameTransaction = (outTransaction) ? outTransaction : &t; - if (mSurfaceControl != nullptr) { - destFrameTransaction->setDestinationFrame(mSurfaceControl, - Rect(0, 0, newSize.getWidth(), - newSize.getHeight())); - } + destFrameTransaction->setDestinationFrame(mSurfaceControl, + Rect(0, 0, newSize.getWidth(), + newSize.getHeight())); applyTransaction = true; } } @@ -640,7 +636,7 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { // add to shadow queue mNumFrameAvailable++; - if (mWaitForTransactionCallback && mNumFrameAvailable == 2) { + if (mWaitForTransactionCallback && mNumFrameAvailable >= 2) { acquireAndReleaseBuffer(); } ATRACE_INT(mQueuedBufferTrace.c_str(), @@ -717,7 +713,7 @@ bool BLASTBufferQueue::rejectBuffer(const BufferItem& item) { // of the buffer, the next acquire may return with NO_BUFFER_AVAILABLE. bool BLASTBufferQueue::maxBuffersAcquired(bool includeExtraAcquire) const { int maxAcquiredBuffers = mMaxAcquiredBuffers + (includeExtraAcquire ? 2 : 1); - return mNumAcquired == maxAcquiredBuffers; + return mNumAcquired >= maxAcquiredBuffers; } class BBQSurface : public Surface { @@ -991,4 +987,54 @@ uint64_t BLASTBufferQueue::getLastAcquiredFrameNum() { return mLastAcquiredFrameNumber; } +void BLASTBufferQueue::abandon() { + std::unique_lock _lock{mMutex}; + // flush out the shadow queue + while (mNumFrameAvailable > 0) { + acquireAndReleaseBuffer(); + } + + // Clear submitted buffer states + mNumAcquired = 0; + mSubmitted.clear(); + mPendingRelease.clear(); + + if (!mPendingTransactions.empty()) { + BQA_LOGD("Applying pending transactions on abandon %d", + static_cast(mPendingTransactions.size())); + SurfaceComposerClient::Transaction t; + mergePendingTransactions(&t, std::numeric_limits::max() /* frameNumber */); + t.setApplyToken(mApplyToken).apply(); + } + + // Clear sync states + if (mWaitForTransactionCallback) { + BQA_LOGD("mWaitForTransactionCallback cleared"); + mWaitForTransactionCallback = false; + } + + if (mSyncTransaction != nullptr) { + BQA_LOGD("mSyncTransaction cleared mAcquireSingleBuffer=%s", + mAcquireSingleBuffer ? "true" : "false"); + mSyncTransaction = nullptr; + mAcquireSingleBuffer = false; + } + + // abandon buffer queue + if (mBufferItemConsumer != nullptr) { + mBufferItemConsumer->abandon(); + mBufferItemConsumer->setFrameAvailableListener(nullptr); + mBufferItemConsumer->setBufferFreedListener(nullptr); + mBufferItemConsumer->setBlastBufferQueue(nullptr); + } + mBufferItemConsumer = nullptr; + mConsumer = nullptr; + mProducer = nullptr; +} + +bool BLASTBufferQueue::isSameSurfaceControl(const sp& surfaceControl) const { + std::unique_lock _lock{mMutex}; + return SurfaceControl::isSameSurface(mSurfaceControl, surfaceControl); +} + } // namespace android diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 8a2f392e35..8b2310dc8e 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -82,6 +82,7 @@ public: return mProducer; } sp getSurface(bool includeSurfaceControlHandle); + bool isSameSurfaceControl(const sp& surfaceControl) const; void onBufferFreed(const wp&/* graphicBuffer*/) override { /* TODO */ } void onFrameReplaced(const BufferItem& item) override; @@ -109,6 +110,7 @@ public: uint32_t getLastTransformHint() const; uint64_t getLastAcquiredFrameNum(); + void abandon(); virtual ~BLASTBufferQueue(); @@ -145,15 +147,15 @@ private: std::string mQueuedBufferTrace; sp mSurfaceControl; - std::mutex mMutex; + mutable std::mutex mMutex; std::condition_variable mCallbackCV; // BufferQueue internally allows 1 more than // the max to be acquired int32_t mMaxAcquiredBuffers = 1; - int32_t mNumFrameAvailable GUARDED_BY(mMutex); - int32_t mNumAcquired GUARDED_BY(mMutex); + int32_t mNumFrameAvailable GUARDED_BY(mMutex) = 0; + int32_t mNumAcquired GUARDED_BY(mMutex) = 0; // Keep a reference to the submitted buffers so we can release when surfaceflinger drops the // buffer or the buffer has been presented and a new buffer is ready to be presented. diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp index 48b8621be1..42a32f3b42 100644 --- a/libs/gui/tests/BLASTBufferQueue_test.cpp +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -1069,6 +1069,94 @@ TEST_F(BLASTBufferQueueTest, SetSyncTransactionAcquireMultipleBuffers) { checkScreenCapture(255, 0, 0, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); } +// This test will currently fail because the old surfacecontrol will steal the last presented buffer +// until the old surface control is destroyed. This is not necessarily a bug but to document a +// limitation with the update API and to test any changes to make the api more robust. The current +// approach for the client is to recreate the blastbufferqueue when the surfacecontrol updates. +TEST_F(BLASTBufferQueueTest, DISABLED_DisconnectProducerTest) { + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + std::vector> surfaceControls; + sp igbProducer; + for (int i = 0; i < 10; i++) { + sp sc = + mClient->createSurface(String8("TestSurface"), mDisplayWidth, mDisplayHeight, + PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceBufferState, + /*parent*/ nullptr); + Transaction() + .setLayerStack(mSurfaceControl, ui::DEFAULT_LAYER_STACK) + .setLayer(mSurfaceControl, std::numeric_limits::max()) + .show(mSurfaceControl) + .setDataspace(mSurfaceControl, ui::Dataspace::V0_SRGB) + .apply(true); + surfaceControls.push_back(sc); + adapter.update(sc, mDisplayWidth, mDisplayHeight); + + setUpProducer(adapter, igbProducer); + Transaction next; + queueBuffer(igbProducer, 0, 255, 0, 0); + queueBuffer(igbProducer, 0, 0, 255, 0); + adapter.setSyncTransaction(&next, false); + queueBuffer(igbProducer, 255, 0, 0, 0); + + CallbackHelper transactionCallback; + next.addTransactionCompletedCallback(transactionCallback.function, + transactionCallback.getContext()) + .apply(); + + CallbackData callbackData; + transactionCallback.getCallbackData(&callbackData); + // capture screen and verify that it is red + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + ASSERT_NO_FATAL_FAILURE( + checkScreenCapture(255, 0, 0, + {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); + igbProducer->disconnect(NATIVE_WINDOW_API_CPU); + } +} + +// See DISABLED_DisconnectProducerTest +TEST_F(BLASTBufferQueueTest, DISABLED_UpdateSurfaceControlTest) { + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + std::vector> surfaceControls; + sp igbProducer; + for (int i = 0; i < 10; i++) { + sp sc = + mClient->createSurface(String8("TestSurface"), mDisplayWidth, mDisplayHeight, + PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceBufferState, + /*parent*/ nullptr); + Transaction() + .setLayerStack(mSurfaceControl, ui::DEFAULT_LAYER_STACK) + .setLayer(mSurfaceControl, std::numeric_limits::max()) + .show(mSurfaceControl) + .setDataspace(mSurfaceControl, ui::Dataspace::V0_SRGB) + .apply(true); + surfaceControls.push_back(sc); + adapter.update(sc, mDisplayWidth, mDisplayHeight); + setUpProducer(adapter, igbProducer); + + Transaction next; + queueBuffer(igbProducer, 0, 255, 0, 0); + queueBuffer(igbProducer, 0, 0, 255, 0); + adapter.setSyncTransaction(&next, false); + queueBuffer(igbProducer, 255, 0, 0, 0); + + CallbackHelper transactionCallback; + next.addTransactionCompletedCallback(transactionCallback.function, + transactionCallback.getContext()) + .apply(); + + CallbackData callbackData; + transactionCallback.getCallbackData(&callbackData); + // capture screen and verify that it is red + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + ASSERT_NO_FATAL_FAILURE( + checkScreenCapture(255, 0, 0, + {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); + } +} + class TestProducerListener : public BnProducerListener { public: sp mIgbp; -- cgit v1.2.3-59-g8ed1b From ab066515cbe0e0a9e61efeb76afbaa63da169752 Mon Sep 17 00:00:00 2001 From: Vishnu Nair Date: Tue, 4 Jan 2022 22:28:00 +0000 Subject: BBQ: Fix log spam when creating a new BBQ Change-Id: I48ecc1f89c9fdfe1e76ac9972d275d9d37c98eda Test: logcat on boot Bug: n/a --- libs/gui/BLASTBufferQueue.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'libs/gui/BLASTBufferQueue.cpp') diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 34faf87546..038e23df4c 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -197,13 +197,15 @@ void BLASTBufferQueue::update(const sp& surface, uint32_t width, SurfaceComposerClient::Transaction t; const bool surfaceControlChanged = !SurfaceControl::isSameSurface(mSurfaceControl, surface); + if (surfaceControlChanged && mSurfaceControl != nullptr) { + BQA_LOGD("Updating SurfaceControl without recreating BBQ"); + } bool applyTransaction = false; // Always update the native object even though they might have the same layer handle, so we can // get the updated transform hint from WM. mSurfaceControl = surface; if (surfaceControlChanged) { - BQA_LOGD("Updating SurfaceControl without recreating BBQ"); t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure, layer_state_t::eEnableBackpressure); applyTransaction = true; -- cgit v1.2.3-59-g8ed1b From dbca1353f2c660ec4815da6497ee161cdda721ce Mon Sep 17 00:00:00 2001 From: Ady Abraham Date: Wed, 15 Dec 2021 11:58:56 -0800 Subject: keep a wp in BufferItemConsumer Use a wp<> instead of raw pointer and remove the mutex around it to prevent potential deadlocks. Bug: 208121127 Test: stress test by partner Test: atest BLASTBufferQueueTest Change-Id: Iffed80410aeffc9b724d5c01ca2ec589c9622990 --- libs/gui/BLASTBufferQueue.cpp | 16 ++++------------ libs/gui/include/gui/BLASTBufferQueue.h | 11 +++++------ 2 files changed, 9 insertions(+), 18 deletions(-) (limited to 'libs/gui/BLASTBufferQueue.cpp') diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 34faf87546..46de2cb222 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -119,16 +119,11 @@ void BLASTBufferItemConsumer::getConnectionEvents(uint64_t frameNumber, bool* ne if (needsDisconnect != nullptr) *needsDisconnect = disconnect; } -void BLASTBufferItemConsumer::setBlastBufferQueue(BLASTBufferQueue* blastbufferqueue) { - std::scoped_lock lock(mBufferQueueMutex); - mBLASTBufferQueue = blastbufferqueue; -} - void BLASTBufferItemConsumer::onSidebandStreamChanged() { - std::scoped_lock lock(mBufferQueueMutex); - if (mBLASTBufferQueue != nullptr) { + sp bbq = mBLASTBufferQueue.promote(); + if (bbq != nullptr) { sp stream = getSidebandStream(); - mBLASTBufferQueue->setSidebandStream(stream); + bbq->setSidebandStream(stream); } } @@ -148,7 +143,7 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name) mBufferItemConsumer = new BLASTBufferItemConsumer(mConsumer, GraphicBuffer::USAGE_HW_COMPOSER | GraphicBuffer::USAGE_HW_TEXTURE, - 1, false); + 1, false, this); static int32_t id = 0; mName = name + "#" + std::to_string(id); auto consumerName = mName + "(BLAST Consumer)" + std::to_string(id); @@ -157,7 +152,6 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name) mBufferItemConsumer->setName(String8(consumerName.c_str())); mBufferItemConsumer->setFrameAvailableListener(this); mBufferItemConsumer->setBufferFreedListener(this); - mBufferItemConsumer->setBlastBufferQueue(this); ComposerService::getComposerService()->getMaxAcquiredBufferCount(&mMaxAcquiredBuffers); mBufferItemConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBuffers); @@ -174,7 +168,6 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const spsetBlastBufferQueue(nullptr); if (mPendingTransactions.empty()) { return; } @@ -1025,7 +1018,6 @@ void BLASTBufferQueue::abandon() { mBufferItemConsumer->abandon(); mBufferItemConsumer->setFrameAvailableListener(nullptr); mBufferItemConsumer->setBufferFreedListener(nullptr); - mBufferItemConsumer->setBlastBufferQueue(nullptr); } mBufferItemConsumer = nullptr; mConsumer = nullptr; diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 8b2310dc8e..f77cfe6a69 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -38,11 +38,11 @@ class BufferItemConsumer; class BLASTBufferItemConsumer : public BufferItemConsumer { public: BLASTBufferItemConsumer(const sp& consumer, uint64_t consumerUsage, - int bufferCount, bool controlledByApp) + int bufferCount, bool controlledByApp, wp bbq) : BufferItemConsumer(consumer, consumerUsage, bufferCount, controlledByApp), + mBLASTBufferQueue(std::move(bbq)), mCurrentlyConnected(false), - mPreviouslyConnected(false), - mBLASTBufferQueue(nullptr) {} + mPreviouslyConnected(false) {} void onDisconnect() override; void addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps, @@ -53,21 +53,20 @@ public: CompositorTiming compositorTiming, nsecs_t latchTime, nsecs_t dequeueReadyTime) REQUIRES(mMutex); void getConnectionEvents(uint64_t frameNumber, bool* needsDisconnect); - void setBlastBufferQueue(BLASTBufferQueue* blastbufferqueue) REQUIRES(mMutex); protected: void onSidebandStreamChanged() override REQUIRES(mMutex); private: + const wp mBLASTBufferQueue; + uint64_t mCurrentFrameNumber = 0; Mutex mMutex; - std::mutex mBufferQueueMutex; ConsumerFrameEventHistory mFrameEventHistory GUARDED_BY(mMutex); std::queue mDisconnectEvents GUARDED_BY(mMutex); bool mCurrentlyConnected GUARDED_BY(mMutex); bool mPreviouslyConnected GUARDED_BY(mMutex); - BLASTBufferQueue* mBLASTBufferQueue GUARDED_BY(mBufferQueueMutex); }; class BLASTBufferQueue -- cgit v1.2.3-59-g8ed1b From 8dd181f3e8274bf89ebde94c1c7d57c9c83b0a49 Mon Sep 17 00:00:00 2001 From: chaviw Date: Wed, 5 Jan 2022 18:36:46 -0600 Subject: Replace releaseCallbackId with generateReleaseCallbackId in BufferData releaseCallbackId was confusing data in BufferData because it wasn't being parceled, even though the rest of the object was. This is because the ReleaseCallbackId can be generated from info in BufferData. Therefore, remove releaseCallbackId and instead replace with a function that generates the ReleaseCallbackId This fixes an issue when Transactions that contained buffers for the same layer were being merged. The merge was expected to release the old buffer. However, if the Transaction was parceled before it was merged, the ReleaseCallbackId would be lost and the release callback would send an invalid callback id, resulting in a lost buffer that never got released. By using generateReleaseCallbackId, the callback id is recreated when the release callback needs to be invoked since the buffer and framenumber are already being parceled. Test: ReleaseBufferCallbackTest Test: AppConfigurationTests Bug: 209920544 Change-Id: I2a24b8a9764959173c960048dc82e68f4c083898 --- libs/gui/BLASTBufferQueue.cpp | 5 ++--- libs/gui/LayerState.cpp | 4 ++++ libs/gui/SurfaceComposerClient.cpp | 16 +++++++--------- libs/gui/include/gui/LayerState.h | 9 +++------ libs/gui/include/gui/SurfaceComposerClient.h | 3 +-- .../tests/ReleaseBufferCallback_test.cpp | 20 ++++++++++---------- 6 files changed, 27 insertions(+), 30 deletions(-) (limited to 'libs/gui/BLASTBufferQueue.cpp') diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 34faf87546..a726fee66d 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -507,8 +507,7 @@ void BLASTBufferQueue::acquireNextBufferLocked( std::bind(releaseBufferCallbackThunk, wp(this) /* callbackContext */, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); sp fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE; - t->setBuffer(mSurfaceControl, buffer, fence, bufferItem.mFrameNumber, releaseCallbackId, - releaseBufferCallback); + t->setBuffer(mSurfaceControl, buffer, fence, bufferItem.mFrameNumber, releaseBufferCallback); t->setDataspace(mSurfaceControl, static_cast(bufferItem.mDataSpace)); t->setHdrMetadata(mSurfaceControl, bufferItem.mHdrMetadata); t->setSurfaceDamageRegion(mSurfaceControl, bufferItem.mSurfaceDamage); @@ -622,7 +621,7 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { if (bufferData) { BQA_LOGD("Releasing previous buffer when syncing: framenumber=%" PRIu64, bufferData->frameNumber); - releaseBuffer(bufferData->releaseCallbackId, bufferData->acquireFence); + releaseBuffer(bufferData->generateReleaseCallbackId(), bufferData->acquireFence); // Because we just released a buffer, we know there's no need to wait for a free // buffer. mayNeedToWaitForBuffer = false; diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index ec0573a1a9..acd9ac5a93 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -677,6 +677,10 @@ status_t LayerCaptureArgs::read(const Parcel& input) { return NO_ERROR; } +ReleaseCallbackId BufferData::generateReleaseCallbackId() const { + return {buffer->getId(), frameNumber}; +} + status_t BufferData::write(Parcel& output) const { SAFE_PARCEL(output.writeInt32, flags.get()); diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index cf04ec8051..3ec347f22c 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -736,10 +736,10 @@ void SurfaceComposerClient::Transaction::releaseBufferIfOverwriting(const layer_ // if the callback is in process, run on a different thread to avoid any lock contigency // issues in the client. SurfaceComposerClient::getDefault() - ->mReleaseCallbackThread.addReleaseCallback(state.bufferData.releaseCallbackId, - fence); + ->mReleaseCallbackThread + .addReleaseCallback(state.bufferData.generateReleaseCallbackId(), fence); } else { - listener->onReleaseBuffer(state.bufferData.releaseCallbackId, fence, UINT_MAX); + listener->onReleaseBuffer(state.bufferData.generateReleaseCallbackId(), fence, UINT_MAX); } } @@ -1335,7 +1335,7 @@ std::optional SurfaceComposerClient::Transaction::getAndClearBuffer( BufferData bufferData = s->bufferData; TransactionCompletedListener::getInstance()->removeReleaseBufferCallback( - bufferData.releaseCallbackId); + bufferData.generateReleaseCallbackId()); BufferData emptyBufferData; s->what &= ~layer_state_t::eBufferChanged; s->bufferData = emptyBufferData; @@ -1347,7 +1347,7 @@ std::optional SurfaceComposerClient::Transaction::getAndClearBuffer( SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffer( const sp& sc, const sp& buffer, const std::optional>& fence, const std::optional& frameNumber, - const ReleaseCallbackId& id, ReleaseBufferCallback callback) { + ReleaseBufferCallback callback) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; @@ -1371,7 +1371,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffe if (mIsAutoTimestamp) { mDesiredPresentTime = systemTime(); } - setReleaseBufferCallback(&bufferData, id, callback); + setReleaseBufferCallback(&bufferData, callback); s->what |= layer_state_t::eBufferChanged; s->bufferData = bufferData; registerSurfaceControlForCallback(sc); @@ -1381,7 +1381,6 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffe } void SurfaceComposerClient::Transaction::setReleaseBufferCallback(BufferData* bufferData, - const ReleaseCallbackId& id, ReleaseBufferCallback callback) { if (!callback) { return; @@ -1394,9 +1393,8 @@ void SurfaceComposerClient::Transaction::setReleaseBufferCallback(BufferData* bu } bufferData->releaseBufferListener = TransactionCompletedListener::getIInstance(); - bufferData->releaseCallbackId = id; auto listener = TransactionCompletedListener::getInstance(); - listener->setReleaseBufferCallback(id, callback); + listener->setReleaseBufferCallback(bufferData->generateReleaseCallbackId(), callback); } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDataspace( diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index a0d3162afe..e48f52d13d 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -77,12 +77,6 @@ struct BufferData { // buffer id to identify the buffer. sp releaseBufferListener = nullptr; - // Keeps track of the release callback id associated with the listener. This - // is not sent to the server since the id can be reconstructed there. This - // is used to remove the old callback from the client process map if it is - // overwritten by another setBuffer call. - ReleaseCallbackId releaseCallbackId = ReleaseCallbackId::INVALID_ID; - // Stores which endpoint the release information should be sent to. We don't want to send the // releaseCallbackId and release fence to all listeners so we store which listener the setBuffer // was called with. @@ -92,6 +86,9 @@ struct BufferData { client_cache_t cachedBuffer; + // Generates the release callback id based on the buffer id and frame number. + // This is used as an identifier when release callbacks are invoked. + ReleaseCallbackId generateReleaseCallbackId() const; status_t write(Parcel& output) const; status_t read(const Parcel& input); }; diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 17b4846921..4798678338 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -416,7 +416,7 @@ public: void cacheBuffers(); void registerSurfaceControlForCallback(const sp& sc); - void setReleaseBufferCallback(BufferData*, const ReleaseCallbackId&, ReleaseBufferCallback); + void setReleaseBufferCallback(BufferData*, ReleaseBufferCallback); public: Transaction(); @@ -490,7 +490,6 @@ public: Transaction& setBuffer(const sp& sc, const sp& buffer, const std::optional>& fence = std::nullopt, const std::optional& frameNumber = std::nullopt, - const ReleaseCallbackId& id = ReleaseCallbackId::INVALID_ID, ReleaseBufferCallback callback = nullptr); std::optional getAndClearBuffer(const sp& sc); Transaction& setDataspace(const sp& sc, ui::Dataspace dataspace); diff --git a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp index f6b0def2f7..027a15e3f6 100644 --- a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp +++ b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp @@ -85,7 +85,7 @@ public: sp fence, CallbackHelper& callback, const ReleaseCallbackId& id, ReleaseBufferCallbackHelper& releaseCallback) { Transaction t; - t.setBuffer(layer, buffer, fence, id.framenumber, id, releaseCallback.getCallback()); + t.setBuffer(layer, buffer, fence, id.framenumber, releaseCallback.getCallback()); t.addTransactionCompletedCallback(callback.function, callback.getContext()); t.apply(); } @@ -300,7 +300,7 @@ TEST_F(ReleaseBufferCallbackTest, DISABLED_FrameDropping) { nsecs_t time = systemTime() + std::chrono::nanoseconds(100ms).count(); Transaction t; - t.setBuffer(layer, firstBuffer, std::nullopt, std::nullopt, firstBufferCallbackId, + t.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber, releaseCallback->getCallback()); t.addTransactionCompletedCallback(transactionCallback.function, transactionCallback.getContext()); @@ -316,7 +316,7 @@ TEST_F(ReleaseBufferCallbackTest, DISABLED_FrameDropping) { // Dropping frames in transaction queue emits a callback sp secondBuffer = getBuffer(); ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber()); - t.setBuffer(layer, secondBuffer, std::nullopt, std::nullopt, secondBufferCallbackId, + t.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber, releaseCallback->getCallback()); t.addTransactionCompletedCallback(transactionCallback.function, transactionCallback.getContext()); @@ -360,7 +360,7 @@ TEST_F(ReleaseBufferCallbackTest, DISABLED_Merge_Different_Processes) { Transaction transaction1; transaction1.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber, - secondBufferCallbackId, releaseCallback->getCallback()); + releaseCallback->getCallback()); transaction1.addTransactionCompletedCallback(callback1.function, callback1.getContext()); // Set a different TransactionCompletedListener to mimic a second process @@ -395,14 +395,14 @@ TEST_F(ReleaseBufferCallbackTest, DISABLED_SetBuffer_OverwriteBuffers) { // Create transaction with a buffer. Transaction transaction; transaction.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber, - firstBufferCallbackId, releaseCallback->getCallback()); + releaseCallback->getCallback()); sp secondBuffer = getBuffer(); ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber()); // Call setBuffer on the same transaction with a different buffer. transaction.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber, - secondBufferCallbackId, releaseCallback->getCallback()); + releaseCallback->getCallback()); ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId)); } @@ -417,7 +417,7 @@ TEST_F(ReleaseBufferCallbackTest, DISABLED_Merge_Transactions_OverwriteBuffers) // Create transaction with a buffer. Transaction transaction1; transaction1.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber, - firstBufferCallbackId, releaseCallback->getCallback()); + releaseCallback->getCallback()); sp secondBuffer = getBuffer(); ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber()); @@ -425,7 +425,7 @@ TEST_F(ReleaseBufferCallbackTest, DISABLED_Merge_Transactions_OverwriteBuffers) // Create a second transaction with a new buffer for the same layer. Transaction transaction2; transaction2.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber, - secondBufferCallbackId, releaseCallback->getCallback()); + releaseCallback->getCallback()); // merge transaction1 into transaction2 so ensure we get a proper buffer release callback. transaction1.merge(std::move(transaction2)); @@ -446,7 +446,7 @@ TEST_F(ReleaseBufferCallbackTest, DISABLED_MergeBuffers_Different_Processes) { Transaction transaction1; transaction1.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber, - firstBufferCallbackId, releaseCallback->getCallback()); + releaseCallback->getCallback()); // Sent a second buffer to allow the first buffer to get released. sp secondBuffer = getBuffer(); @@ -454,7 +454,7 @@ TEST_F(ReleaseBufferCallbackTest, DISABLED_MergeBuffers_Different_Processes) { Transaction transaction2; transaction2.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber, - secondBufferCallbackId, releaseCallback->getCallback()); + releaseCallback->getCallback()); // Set a different TransactionCompletedListener to mimic a second process TransactionCompletedListener::setInstance(secondCompletedListener); -- cgit v1.2.3-59-g8ed1b From 57ae4b20546df7666303fd649bd49ab89a3bcc7f Mon Sep 17 00:00:00 2001 From: chaviw Date: Thu, 3 Feb 2022 16:51:39 -0600 Subject: Add more info in BBQ Tracing Added the buffer name, mNumFrameAvailable, mNumAcquired to all traces in BBQ. Also added frameNum for acquire and release to help match up when the release is called. Test: Trace shows new data Bug: 217621394 Change-Id: I86bf0c12741a2651bb3802c6142ac341b0fb362e --- libs/gui/BLASTBufferQueue.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'libs/gui/BLASTBufferQueue.cpp') diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index dd966837f4..aeb237deb4 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -57,6 +58,10 @@ namespace android { #define BQA_LOGE(x, ...) \ ALOGE("[%s](f:%u,a:%u) " x, mName.c_str(), mNumFrameAvailable, mNumAcquired, ##__VA_ARGS__) +#define BBQ_TRACE(x, ...) \ + ATRACE_FORMAT("%s - %s(f:%u,a:%u)" x, __FUNCTION__, mName.c_str(), mNumFrameAvailable, \ + mNumAcquired, ##__VA_ARGS__) + void BLASTBufferItemConsumer::onDisconnect() { Mutex::Autolock lock(mMutex); mPreviouslyConnected = mCurrentlyConnected; @@ -254,7 +259,7 @@ void BLASTBufferQueue::transactionCommittedCallback(nsecs_t /*latchTime*/, const std::vector& stats) { { std::unique_lock _lock{mMutex}; - ATRACE_CALL(); + BBQ_TRACE(); BQA_LOGV("transactionCommittedCallback"); if (!mSurfaceControlsWithPendingCallback.empty()) { sp pendingSC = mSurfaceControlsWithPendingCallback.front(); @@ -304,7 +309,7 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp& stats) { { std::unique_lock _lock{mMutex}; - ATRACE_CALL(); + BBQ_TRACE(); BQA_LOGV("transactionCallback"); if (!mSurfaceControlsWithPendingCallback.empty()) { @@ -367,7 +372,7 @@ void BLASTBufferQueue::flushShadowQueue() { void BLASTBufferQueue::releaseBufferCallback( const ReleaseCallbackId& id, const sp& releaseFence, std::optional currentMaxAcquiredBufferCount) { - ATRACE_CALL(); + BBQ_TRACE(); std::unique_lock _lock{mMutex}; BQA_LOGV("releaseBufferCallback %s", id.to_string().c_str()); @@ -415,6 +420,7 @@ void BLASTBufferQueue::releaseBuffer(const ReleaseCallbackId& callbackId, return; } mNumAcquired--; + BBQ_TRACE("frame=%" PRIu64, callbackId.framenumber); BQA_LOGV("released %s", callbackId.to_string().c_str()); mBufferItemConsumer->releaseBuffer(it->second, releaseFence); mSubmitted.erase(it); @@ -422,7 +428,6 @@ void BLASTBufferQueue::releaseBuffer(const ReleaseCallbackId& callbackId, void BLASTBufferQueue::acquireNextBufferLocked( const std::optional transaction) { - ATRACE_CALL(); // If the next transaction is set, we want to guarantee the our acquire will not fail, so don't // include the extra buffer when checking if we can acquire the next buffer. const bool includeExtraAcquire = !transaction; @@ -456,8 +461,10 @@ void BLASTBufferQueue::acquireNextBufferLocked( BQA_LOGE("Failed to acquire a buffer, err=%s", statusToString(status).c_str()); return; } + auto buffer = bufferItem.mGraphicBuffer; mNumFrameAvailable--; + BBQ_TRACE("frame=%" PRIu64, bufferItem.mFrameNumber); if (buffer == nullptr) { mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE); @@ -600,7 +607,7 @@ void BLASTBufferQueue::flushAndWaitForFreeBuffer(std::unique_lock& l } void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { - ATRACE_CALL(); + BBQ_TRACE(); std::unique_lock _lock{mMutex}; const bool syncTransactionSet = mSyncTransaction != nullptr; @@ -674,6 +681,7 @@ void BLASTBufferQueue::onFrameCancelled(const uint64_t bufferId) { void BLASTBufferQueue::setSyncTransaction(SurfaceComposerClient::Transaction* t, bool acquireSingleBuffer) { + BBQ_TRACE(); std::lock_guard _lock{mMutex}; mSyncTransaction = t; mAcquireSingleBuffer = mSyncTransaction ? acquireSingleBuffer : true; -- cgit v1.2.3-59-g8ed1b From d84085a2bab2019124cd5fd35dede59e39e5682f Mon Sep 17 00:00:00 2001 From: chaviw Date: Tue, 8 Feb 2022 11:07:04 -0600 Subject: Add gatherPendingTransactions in BBQ Add a function to gather the transactions that were sent to BBQ via the mergeWithNextTransaction. This is to allow the caller to get the transaction in case a frame isn't drawn and we need to apply or merge these transactions anyway. Test: Builds and runs Bug: 200284684 Change-Id: Ic9f3afb48e00212a6488a6a8f9cd4403ed1cb7d6 --- libs/gui/BLASTBufferQueue.cpp | 8 ++++++++ libs/gui/include/gui/BLASTBufferQueue.h | 1 + 2 files changed, 9 insertions(+) (limited to 'libs/gui/BLASTBufferQueue.cpp') diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index dd966837f4..6bcbea10d7 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -834,6 +834,14 @@ void BLASTBufferQueue::mergePendingTransactions(SurfaceComposerClient::Transacti mPendingTransactions.end()); } +SurfaceComposerClient::Transaction* BLASTBufferQueue::gatherPendingTransactions( + uint64_t frameNumber) { + std::lock_guard _lock{mMutex}; + SurfaceComposerClient::Transaction* t = new SurfaceComposerClient::Transaction(); + mergePendingTransactions(t, frameNumber); + return t; +} + // Maintains a single worker thread per process that services a list of runnables. class AsyncWorker : public Singleton { private: diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index f77cfe6a69..1ed592b506 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -98,6 +98,7 @@ public: void setSyncTransaction(SurfaceComposerClient::Transaction* t, bool acquireSingleBuffer = true); void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber); void applyPendingTransactions(uint64_t frameNumber); + SurfaceComposerClient::Transaction* gatherPendingTransactions(uint64_t frameNumber); void update(const sp& surface, uint32_t width, uint32_t height, int32_t format, SurfaceComposerClient::Transaction* outTransaction = nullptr); -- cgit v1.2.3-59-g8ed1b From d2aaab14d2fd52a6c6a0c928a55bf000f6c538ca Mon Sep 17 00:00:00 2001 From: Vishnu Nair Date: Thu, 10 Feb 2022 14:49:09 -0800 Subject: SurfaceView: Avoid destination frame updates on multiple threads 2/2 The caller such as SurfaceView can optionally apply destination frame changes if they wish to synchronize buffer scale with other scales in the hierarchy. This is hard to synchronize if the buffer scale changes, since in fixed scaling mode we want the destination frame to be applied when a buffer of the new size is queued. This approach is brittle because SurfaceView does not have control over the buffer production and the app can get into a scenario where there is scaled incorrectly. Fix this by configuring BBQ to always apply destination frame changes or always defer to the caller. If the scaling mode is freeze, then BBQ will set a flag to ignore the destination frame. This allows us to synchronize destination frame changes with scale applied by a parent and avoid unwanted scaling if the scaling mode changes to freeze. Test: atest SurfaceViewTest Test: go/wm-smoke Bug: 217973491 Change-Id: I0d214e3f7e45c38e6222a2947ca354c36a202ff5 --- libs/gui/BLASTBufferQueue.cpp | 35 +++++++++++++------------ libs/gui/include/gui/BLASTBufferQueue.h | 16 +++++------ libs/gui/include/gui/LayerState.h | 9 +++++-- services/surfaceflinger/BufferStateLayer.cpp | 3 ++- services/surfaceflinger/Layer.cpp | 3 +++ services/surfaceflinger/layerproto/layers.proto | 2 ++ 6 files changed, 40 insertions(+), 28 deletions(-) (limited to 'libs/gui/BLASTBufferQueue.cpp') diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 7ce72ffa59..b7a7aa0ccb 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -132,12 +132,13 @@ void BLASTBufferItemConsumer::onSidebandStreamChanged() { } } -BLASTBufferQueue::BLASTBufferQueue(const std::string& name) +BLASTBufferQueue::BLASTBufferQueue(const std::string& name, bool updateDestinationFrame) : mSurfaceControl(nullptr), mSize(1, 1), mRequestedSize(mSize), mFormat(PIXEL_FORMAT_RGBA_8888), - mSyncTransaction(nullptr) { + mSyncTransaction(nullptr), + mUpdateDestinationFrame(updateDestinationFrame) { createBufferQueue(&mProducer, &mConsumer); // since the adapter is in the client process, set dequeue timeout // explicitly so that dequeueBuffer will block @@ -184,7 +185,7 @@ BLASTBufferQueue::~BLASTBufferQueue() { } void BLASTBufferQueue::update(const sp& surface, uint32_t width, uint32_t height, - int32_t format, SurfaceComposerClient::Transaction* outTransaction) { + int32_t format) { LOG_ALWAYS_FATAL_IF(surface == nullptr, "BLASTBufferQueue: mSurfaceControl must not be NULL"); std::unique_lock _lock{mMutex}; @@ -193,7 +194,6 @@ void BLASTBufferQueue::update(const sp& surface, uint32_t width, mBufferItemConsumer->setDefaultBufferFormat(convertBufferFormat(format)); } - SurfaceComposerClient::Transaction t; const bool surfaceControlChanged = !SurfaceControl::isSameSurface(mSurfaceControl, surface); if (surfaceControlChanged && mSurfaceControl != nullptr) { BQA_LOGD("Updating SurfaceControl without recreating BBQ"); @@ -203,6 +203,7 @@ void BLASTBufferQueue::update(const sp& surface, uint32_t width, // Always update the native object even though they might have the same layer handle, so we can // get the updated transform hint from WM. mSurfaceControl = surface; + SurfaceComposerClient::Transaction t; if (surfaceControlChanged) { t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure, layer_state_t::eEnableBackpressure); @@ -221,12 +222,10 @@ void BLASTBufferQueue::update(const sp& surface, uint32_t width, // If the buffer supports scaling, update the frame immediately since the client may // want to scale the existing buffer to the new size. mSize = mRequestedSize; - SurfaceComposerClient::Transaction* destFrameTransaction = - (outTransaction) ? outTransaction : &t; - destFrameTransaction->setDestinationFrame(mSurfaceControl, - Rect(0, 0, newSize.getWidth(), - newSize.getHeight())); - applyTransaction = true; + if (mUpdateDestinationFrame) { + t.setDestinationFrame(mSurfaceControl, Rect(newSize)); + applyTransaction = true; + } } } if (applyTransaction) { @@ -498,7 +497,6 @@ void BLASTBufferQueue::acquireNextBufferLocked( // Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback. incStrong((void*)transactionCallbackThunk); - const bool updateDestinationFrame = mRequestedSize != mSize; mSize = mRequestedSize; Rect crop = computeCrop(bufferItem); mLastBufferInfo.update(true /* hasBuffer */, bufferItem.mGraphicBuffer->getWidth(), @@ -517,12 +515,19 @@ void BLASTBufferQueue::acquireNextBufferLocked( mSurfaceControlsWithPendingCallback.push(mSurfaceControl); - if (updateDestinationFrame) { - t->setDestinationFrame(mSurfaceControl, Rect(0, 0, mSize.getWidth(), mSize.getHeight())); + if (mUpdateDestinationFrame) { + t->setDestinationFrame(mSurfaceControl, Rect(mSize)); + } else { + const bool ignoreDestinationFrame = + bufferItem.mScalingMode == NATIVE_WINDOW_SCALING_MODE_FREEZE; + t->setFlags(mSurfaceControl, + ignoreDestinationFrame ? layer_state_t::eIgnoreDestinationFrame : 0, + layer_state_t::eIgnoreDestinationFrame); } t->setBufferCrop(mSurfaceControl, crop); t->setTransform(mSurfaceControl, bufferItem.mTransform); t->setTransformToDisplayInverse(mSurfaceControl, bufferItem.mTransformToDisplayInverse); + t->setAutoRefresh(mSurfaceControl, bufferItem.mAutoRefresh); if (!bufferItem.mIsAutoTimestamp) { t->setDesiredPresentTime(bufferItem.mTimestamp); } @@ -532,10 +537,6 @@ void BLASTBufferQueue::acquireNextBufferLocked( mNextFrameTimelineInfoQueue.pop(); } - if (mAutoRefresh != bufferItem.mAutoRefresh) { - t->setAutoRefresh(mSurfaceControl, bufferItem.mAutoRefresh); - mAutoRefresh = bufferItem.mAutoRefresh; - } { std::unique_lock _lock{mTimestampMutex}; auto dequeueTime = mDequeueTimestamps.find(buffer->getId()); diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 1ed592b506..74addeac4a 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -73,7 +73,7 @@ class BLASTBufferQueue : public ConsumerBase::FrameAvailableListener, public BufferItemConsumer::BufferFreedListener { public: - BLASTBufferQueue(const std::string& name); + BLASTBufferQueue(const std::string& name, bool updateDestinationFrame = true); BLASTBufferQueue(const std::string& name, const sp& surface, int width, int height, int32_t format); @@ -100,8 +100,7 @@ public: void applyPendingTransactions(uint64_t frameNumber); SurfaceComposerClient::Transaction* gatherPendingTransactions(uint64_t frameNumber); - void update(const sp& surface, uint32_t width, uint32_t height, int32_t format, - SurfaceComposerClient::Transaction* outTransaction = nullptr); + void update(const sp& surface, uint32_t width, uint32_t height, int32_t format); status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless); status_t setFrameTimelineInfo(const FrameTimelineInfo& info); @@ -218,11 +217,6 @@ private: std::vector> mPendingTransactions GUARDED_BY(mMutex); - // Last requested auto refresh state set by the producer. The state indicates that the consumer - // should acquire the next frame as soon as it can and not wait for a frame to become available. - // This is only relevant for shared buffer mode. - bool mAutoRefresh GUARDED_BY(mMutex) = false; - std::queue mNextFrameTimelineInfoQueue GUARDED_BY(mMutex); // Tracks the last acquired frame number @@ -250,6 +244,12 @@ private: // Flag to determine if syncTransaction should only acquire a single buffer and then clear or // continue to acquire buffers until explicitly cleared bool mAcquireSingleBuffer GUARDED_BY(mMutex) = true; + + // True if BBQ will update the destination frame used to scale the buffer to the requested size. + // If false, the caller is responsible for updating the destination frame on the BBQ + // surfacecontol. This is useful if the caller wants to synchronize the buffer scale with + // additional scales in the hierarchy. + bool mUpdateDestinationFrame GUARDED_BY(mMutex) = true; }; } // namespace android diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index f7206193cd..ac9e428c9d 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -128,8 +128,13 @@ struct layer_state_t { // Queue up BufferStateLayer buffers instead of dropping the oldest buffer when this flag is // set. This blocks the client until all the buffers have been presented. If the buffers // have presentation timestamps, then we may drop buffers. - eEnableBackpressure = 0x100, // ENABLE_BACKPRESSURE - eLayerIsDisplayDecoration = 0x200, // DISPLAY_DECORATION + eEnableBackpressure = 0x100, // ENABLE_BACKPRESSURE + eLayerIsDisplayDecoration = 0x200, // DISPLAY_DECORATION + // Ignore any destination frame set on the layer. This is used when the buffer scaling mode + // is freeze and the destination frame is applied asynchronously with the buffer submission. + // This is needed to maintain compatibility for SurfaceView scaling behavior. + // See SurfaceView scaling behavior for more details. + eIgnoreDestinationFrame = 0x400, }; enum { diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp index 02e444df8e..e6a76e8ea9 100644 --- a/services/surfaceflinger/BufferStateLayer.cpp +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -271,7 +271,8 @@ static bool assignTransform(ui::Transform* dst, ui::Transform& from) { // Translate destination frame into scale and position. If a destination frame is not set, use the // provided scale and position bool BufferStateLayer::updateGeometry() { - if (mDrawingState.destinationFrame.isEmpty()) { + if ((mDrawingState.flags & layer_state_t::eIgnoreDestinationFrame) || + mDrawingState.destinationFrame.isEmpty()) { // If destination frame is not set, use the requested transform set via // BufferStateLayer::setPosition and BufferStateLayer::setMatrix. return assignTransform(&mDrawingState.transform, mRequestedTransform); diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 808109603f..533acfdefe 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -2142,6 +2142,9 @@ void Layer::writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet (*protoMap)[entry.first] = std::string(entry.second.cbegin(), entry.second.cend()); } } + + LayerProtoHelper::writeToProto(state.destinationFrame, + [&]() { return layerInfo->mutable_destination_frame(); }); } bool Layer::isRemovedFromCurrentState() const { diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto index 2e9e659880..3598308338 100644 --- a/services/surfaceflinger/layerproto/layers.proto +++ b/services/surfaceflinger/layerproto/layers.proto @@ -136,6 +136,8 @@ message LayerProto { // Corner radius explicitly set on layer rather than inherited float requested_corner_radius = 56; + + RectProto destination_frame = 57; } message PositionProto { -- cgit v1.2.3-59-g8ed1b From 4861b10c044ff3c6edd71ca9971bde5c8a8fde0e Mon Sep 17 00:00:00 2001 From: Tianhao Yao Date: Thu, 3 Feb 2022 20:18:35 +0000 Subject: Changed setSyncTransaction to syncNextTransaction with callback logic. Replaced setSyncTransation with syncNextTransaciton in BBQ. BBQ will no longer take in transaction pointers but create transactions inside of itself and invoke the callback when the transaction is ready. Also added stopContinuousSyncTransction method to inform BBQ to cease acquiring buffers into the transaction. Test: go/wm-smoke. Test: BLASTBufferQueueTest Bug: 210714235. Change-Id: I7103a95046d0251e5bdaec21b2741ff194f8e81e --- libs/gui/BLASTBufferQueue.cpp | 139 +++++++++++++++++++------------ libs/gui/include/gui/BLASTBufferQueue.h | 6 +- libs/gui/tests/BLASTBufferQueue_test.cpp | 57 ++++++++----- 3 files changed, 127 insertions(+), 75 deletions(-) (limited to 'libs/gui/BLASTBufferQueue.cpp') diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index b7a7aa0ccb..8e23eb8766 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -137,6 +137,7 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name, bool updateDestinati mSize(1, 1), mRequestedSize(mSize), mFormat(PIXEL_FORMAT_RGBA_8888), + mTransactionReadyCallback(nullptr), mSyncTransaction(nullptr), mUpdateDestinationFrame(updateDestinationFrame) { createBufferQueue(&mProducer, &mConsumer); @@ -608,60 +609,69 @@ void BLASTBufferQueue::flushAndWaitForFreeBuffer(std::unique_lock& l } void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { - BBQ_TRACE(); - std::unique_lock _lock{mMutex}; + std::function prevCallback = nullptr; + SurfaceComposerClient::Transaction* prevTransaction = nullptr; + { + BBQ_TRACE(); + std::unique_lock _lock{mMutex}; + const bool syncTransactionSet = mTransactionReadyCallback != nullptr; + BQA_LOGV("onFrameAvailable-start syncTransactionSet=%s", boolToString(syncTransactionSet)); + + if (syncTransactionSet) { + bool mayNeedToWaitForBuffer = true; + // If we are going to re-use the same mSyncTransaction, release the buffer that may + // already be set in the Transaction. This is to allow us a free slot early to continue + // processing a new buffer. + if (!mAcquireSingleBuffer) { + auto bufferData = mSyncTransaction->getAndClearBuffer(mSurfaceControl); + if (bufferData) { + BQA_LOGD("Releasing previous buffer when syncing: framenumber=%" PRIu64, + bufferData->frameNumber); + releaseBuffer(bufferData->generateReleaseCallbackId(), + bufferData->acquireFence); + // Because we just released a buffer, we know there's no need to wait for a free + // buffer. + mayNeedToWaitForBuffer = false; + } + } - const bool syncTransactionSet = mSyncTransaction != nullptr; - BQA_LOGV("onFrameAvailable-start syncTransactionSet=%s", boolToString(syncTransactionSet)); - - if (syncTransactionSet) { - bool mayNeedToWaitForBuffer = true; - // If we are going to re-use the same mSyncTransaction, release the buffer that may already - // be set in the Transaction. This is to allow us a free slot early to continue processing - // a new buffer. - if (!mAcquireSingleBuffer) { - auto bufferData = mSyncTransaction->getAndClearBuffer(mSurfaceControl); - if (bufferData) { - BQA_LOGD("Releasing previous buffer when syncing: framenumber=%" PRIu64, - bufferData->frameNumber); - releaseBuffer(bufferData->generateReleaseCallbackId(), bufferData->acquireFence); - // Because we just released a buffer, we know there's no need to wait for a free - // buffer. - mayNeedToWaitForBuffer = false; + if (mayNeedToWaitForBuffer) { + flushAndWaitForFreeBuffer(_lock); } } - if (mayNeedToWaitForBuffer) { - flushAndWaitForFreeBuffer(_lock); + // add to shadow queue + mNumFrameAvailable++; + if (mWaitForTransactionCallback && mNumFrameAvailable >= 2) { + acquireAndReleaseBuffer(); } - } - - // add to shadow queue - mNumFrameAvailable++; - if (mWaitForTransactionCallback && mNumFrameAvailable >= 2) { - acquireAndReleaseBuffer(); - } - ATRACE_INT(mQueuedBufferTrace.c_str(), - mNumFrameAvailable + mNumAcquired - mPendingRelease.size()); - - BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " syncTransactionSet=%s", item.mFrameNumber, - boolToString(syncTransactionSet)); - - if (syncTransactionSet) { - acquireNextBufferLocked(mSyncTransaction); - - // Only need a commit callback when syncing to ensure the buffer that's synced has been sent - // to SF - incStrong((void*)transactionCommittedCallbackThunk); - mSyncTransaction->addTransactionCommittedCallback(transactionCommittedCallbackThunk, - static_cast(this)); - - if (mAcquireSingleBuffer) { - mSyncTransaction = nullptr; + ATRACE_INT(mQueuedBufferTrace.c_str(), + mNumFrameAvailable + mNumAcquired - mPendingRelease.size()); + + BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " syncTransactionSet=%s", + item.mFrameNumber, boolToString(syncTransactionSet)); + + if (syncTransactionSet) { + acquireNextBufferLocked(mSyncTransaction); + + // Only need a commit callback when syncing to ensure the buffer that's synced has been + // sent to SF + incStrong((void*)transactionCommittedCallbackThunk); + mSyncTransaction->addTransactionCommittedCallback(transactionCommittedCallbackThunk, + static_cast(this)); + mWaitForTransactionCallback = true; + if (mAcquireSingleBuffer) { + prevCallback = mTransactionReadyCallback; + prevTransaction = mSyncTransaction; + mTransactionReadyCallback = nullptr; + mSyncTransaction = nullptr; + } + } else if (!mWaitForTransactionCallback) { + acquireNextBufferLocked(std::nullopt); } - mWaitForTransactionCallback = true; - } else if (!mWaitForTransactionCallback) { - acquireNextBufferLocked(std::nullopt); + } + if (prevCallback) { + prevCallback(prevTransaction); } } @@ -680,12 +690,37 @@ void BLASTBufferQueue::onFrameCancelled(const uint64_t bufferId) { mDequeueTimestamps.erase(bufferId); }; -void BLASTBufferQueue::setSyncTransaction(SurfaceComposerClient::Transaction* t, - bool acquireSingleBuffer) { +void BLASTBufferQueue::syncNextTransaction( + std::function callback, + bool acquireSingleBuffer) { BBQ_TRACE(); std::lock_guard _lock{mMutex}; - mSyncTransaction = t; - mAcquireSingleBuffer = mSyncTransaction ? acquireSingleBuffer : true; + mTransactionReadyCallback = callback; + if (callback) { + mSyncTransaction = new SurfaceComposerClient::Transaction(); + } else { + mSyncTransaction = nullptr; + } + mAcquireSingleBuffer = mTransactionReadyCallback ? acquireSingleBuffer : true; +} + +void BLASTBufferQueue::stopContinuousSyncTransaction() { + std::function prevCallback = nullptr; + SurfaceComposerClient::Transaction* prevTransaction = nullptr; + { + std::lock_guard _lock{mMutex}; + bool invokeCallback = mTransactionReadyCallback && !mAcquireSingleBuffer; + if (invokeCallback) { + prevCallback = mTransactionReadyCallback; + prevTransaction = mSyncTransaction; + } + mTransactionReadyCallback = nullptr; + mSyncTransaction = nullptr; + mAcquireSingleBuffer = true; + } + if (prevCallback) { + prevCallback(prevTransaction); + } } bool BLASTBufferQueue::rejectBuffer(const BufferItem& item) { diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 74addeac4a..265ae24255 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -95,7 +95,9 @@ public: const std::vector& stats); void releaseBufferCallback(const ReleaseCallbackId& id, const sp& releaseFence, std::optional currentMaxAcquiredBufferCount); - void setSyncTransaction(SurfaceComposerClient::Transaction* t, bool acquireSingleBuffer = true); + void syncNextTransaction(std::function callback, + bool acquireSingleBuffer = true); + void stopContinuousSyncTransaction(); void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber); void applyPendingTransactions(uint64_t frameNumber); SurfaceComposerClient::Transaction* gatherPendingTransactions(uint64_t frameNumber); @@ -213,6 +215,8 @@ private: sp mProducer; sp mBufferItemConsumer; + std::function mTransactionReadyCallback + GUARDED_BY(mMutex); SurfaceComposerClient::Transaction* mSyncTransaction GUARDED_BY(mMutex); std::vector> mPendingTransactions GUARDED_BY(mMutex); diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp index 179bdd76aa..0c3236c9ce 100644 --- a/libs/gui/tests/BLASTBufferQueue_test.cpp +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -110,15 +110,27 @@ public: mBlastBufferQueueAdapter->update(sc, width, height, PIXEL_FORMAT_RGBA_8888); } - void setSyncTransaction(Transaction* next, bool acquireSingleBuffer = true) { - mBlastBufferQueueAdapter->setSyncTransaction(next, acquireSingleBuffer); + void setSyncTransaction(Transaction& next, bool acquireSingleBuffer = true) { + auto callback = [&next](Transaction* t) { next.merge(std::move(*t)); }; + mBlastBufferQueueAdapter->syncNextTransaction(callback, acquireSingleBuffer); + } + + void syncNextTransaction(std::function callback, + bool acquireSingleBuffer = true) { + mBlastBufferQueueAdapter->syncNextTransaction(callback, acquireSingleBuffer); + } + + void stopContinuousSyncTransaction() { + mBlastBufferQueueAdapter->stopContinuousSyncTransaction(); } int getWidth() { return mBlastBufferQueueAdapter->mSize.width; } int getHeight() { return mBlastBufferQueueAdapter->mSize.height; } - Transaction* getSyncTransaction() { return mBlastBufferQueueAdapter->mSyncTransaction; } + std::function getTransactionReadyCallback() { + return mBlastBufferQueueAdapter->mTransactionReadyCallback; + } sp getIGraphicBufferProducer() { return mBlastBufferQueueAdapter->getIGraphicBufferProducer(); @@ -343,7 +355,7 @@ TEST_F(BLASTBufferQueueTest, CreateBLASTBufferQueue) { ASSERT_EQ(mSurfaceControl, adapter.getSurfaceControl()); ASSERT_EQ(mDisplayWidth, adapter.getWidth()); ASSERT_EQ(mDisplayHeight, adapter.getHeight()); - ASSERT_EQ(nullptr, adapter.getSyncTransaction()); + ASSERT_EQ(nullptr, adapter.getTransactionReadyCallback()); } TEST_F(BLASTBufferQueueTest, Update) { @@ -364,11 +376,12 @@ TEST_F(BLASTBufferQueueTest, Update) { ASSERT_EQ(mDisplayHeight / 2, height); } -TEST_F(BLASTBufferQueueTest, SetSyncTransaction) { +TEST_F(BLASTBufferQueueTest, SyncNextTransaction) { BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); - Transaction sync; - adapter.setSyncTransaction(&sync); - ASSERT_EQ(&sync, adapter.getSyncTransaction()); + ASSERT_EQ(nullptr, adapter.getTransactionReadyCallback()); + auto callback = [](Transaction*) {}; + adapter.syncNextTransaction(callback); + ASSERT_NE(nullptr, adapter.getTransactionReadyCallback()); } TEST_F(BLASTBufferQueueTest, DISABLED_onFrameAvailable_ApplyDesiredPresentTime) { @@ -808,7 +821,7 @@ TEST_F(BLASTBufferQueueTest, SyncThenNoSync) { setUpProducer(adapter, igbProducer); Transaction sync; - adapter.setSyncTransaction(&sync); + adapter.setSyncTransaction(sync); queueBuffer(igbProducer, 0, 255, 0, 0); // queue non sync buffer, so this one should get blocked @@ -848,12 +861,12 @@ TEST_F(BLASTBufferQueueTest, MultipleSyncTransactions) { Transaction mainTransaction; Transaction sync; - adapter.setSyncTransaction(&sync); + adapter.setSyncTransaction(sync); queueBuffer(igbProducer, 0, 255, 0, 0); mainTransaction.merge(std::move(sync)); - adapter.setSyncTransaction(&sync); + adapter.setSyncTransaction(sync); queueBuffer(igbProducer, r, g, b, 0); mainTransaction.merge(std::move(sync)); @@ -889,7 +902,7 @@ TEST_F(BLASTBufferQueueTest, MultipleSyncTransactionWithNonSync) { Transaction sync; // queue a sync transaction - adapter.setSyncTransaction(&sync); + adapter.setSyncTransaction(sync); queueBuffer(igbProducer, 0, 255, 0, 0); mainTransaction.merge(std::move(sync)); @@ -898,7 +911,7 @@ TEST_F(BLASTBufferQueueTest, MultipleSyncTransactionWithNonSync) { queueBuffer(igbProducer, 0, 0, 255, 0); // queue another sync transaction - adapter.setSyncTransaction(&sync); + adapter.setSyncTransaction(sync); queueBuffer(igbProducer, r, g, b, 0); // Expect 1 buffer to be released because the non sync transaction should merge // with the sync @@ -937,7 +950,7 @@ TEST_F(BLASTBufferQueueTest, MultipleSyncRunOutOfBuffers) { Transaction sync; // queue a sync transaction - adapter.setSyncTransaction(&sync); + adapter.setSyncTransaction(sync); queueBuffer(igbProducer, 0, 255, 0, 0); mainTransaction.merge(std::move(sync)); @@ -948,7 +961,7 @@ TEST_F(BLASTBufferQueueTest, MultipleSyncRunOutOfBuffers) { queueBuffer(igbProducer, 0, 0, 255, 0); // queue another sync transaction - adapter.setSyncTransaction(&sync); + adapter.setSyncTransaction(sync); queueBuffer(igbProducer, r, g, b, 0); // Expect 3 buffers to be released because the non sync transactions should merge // with the sync @@ -994,7 +1007,7 @@ TEST_F(BLASTBufferQueueTest, RunOutOfBuffersWaitingOnSF) { Transaction sync; // queue a sync transaction - adapter.setSyncTransaction(&sync); + adapter.setSyncTransaction(sync); queueBuffer(igbProducer, 0, 255, 0, 0); mainTransaction.merge(std::move(sync)); @@ -1008,7 +1021,7 @@ TEST_F(BLASTBufferQueueTest, RunOutOfBuffersWaitingOnSF) { mainTransaction.apply(); // queue another sync transaction - adapter.setSyncTransaction(&sync); + adapter.setSyncTransaction(sync); queueBuffer(igbProducer, r, g, b, 0); // Expect 2 buffers to be released because the non sync transactions should merge // with the sync @@ -1031,19 +1044,19 @@ TEST_F(BLASTBufferQueueTest, RunOutOfBuffersWaitingOnSF) { checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); } -TEST_F(BLASTBufferQueueTest, SetSyncTransactionAcquireMultipleBuffers) { +TEST_F(BLASTBufferQueueTest, SyncNextTransactionAcquireMultipleBuffers) { BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); sp igbProducer; setUpProducer(adapter, igbProducer); Transaction next; - adapter.setSyncTransaction(&next, false); + adapter.setSyncTransaction(next, false); queueBuffer(igbProducer, 0, 255, 0, 0); queueBuffer(igbProducer, 0, 0, 255, 0); // There should only be one frame submitted since the first frame will be released. adapter.validateNumFramesSubmitted(1); - adapter.setSyncTransaction(nullptr); + adapter.stopContinuousSyncTransaction(); // queue non sync buffer, so this one should get blocked // Add a present delay to allow the first screenshot to get taken. @@ -1097,7 +1110,7 @@ TEST_F(BLASTBufferQueueTest, DISABLED_DisconnectProducerTest) { Transaction next; queueBuffer(igbProducer, 0, 255, 0, 0); queueBuffer(igbProducer, 0, 0, 255, 0); - adapter.setSyncTransaction(&next, false); + adapter.setSyncTransaction(next, true); queueBuffer(igbProducer, 255, 0, 0, 0); CallbackHelper transactionCallback; @@ -1140,7 +1153,7 @@ TEST_F(BLASTBufferQueueTest, DISABLED_UpdateSurfaceControlTest) { Transaction next; queueBuffer(igbProducer, 0, 255, 0, 0); queueBuffer(igbProducer, 0, 0, 255, 0); - adapter.setSyncTransaction(&next, false); + adapter.setSyncTransaction(next, true); queueBuffer(igbProducer, 255, 0, 0, 0); CallbackHelper transactionCallback; -- cgit v1.2.3-59-g8ed1b From 79dc06a39f4a31b65873271f22aecd91b37b75af Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Tue, 22 Feb 2022 15:28:59 -0800 Subject: BLASTBufferQueue/SF: apply transactions with one-way binder This CL has three main components: 1. Expose a flag through Transaction::apply that results in one-way calls to setTransactionState 2. Use this flag in all transactions from BBQ which use its own apply token. 3. Implement and use a new transaction barrier system to preserve ordering when switching apply tokens (e.g. BLASTSync) We can see that this CL is safe by making a few assumptions and then arguing that transaction ordering is preserved. We assume: 1. All transactions on the BBQ apply token will remain in order, since they are one-way calls from a single process to a single interface on another. AND 2. The ordering of transactions applied on seperate apply tokens is undefined 3. When preparing transactions for sync, BBQ will set a commit callback, and wait on it before applying another frame of it's own. But when preparing transactions to apply directly, it will not set a callback, and can not (for performance reasons) 4. The ordering of consecutive transactions in a sync is the responsibility of the sync consumer, e.g. SysUI or WM, who will be using their own apply token. Now imagine there were two transactions for frames N and N+1 which were applied in order before this CL, but out of order after. They can't both be applied by BBQ, by assumption one. They can't both be sync transactions (by assumption 4). It can't be a sync transaction applied by a bbq transaction, because we will wait on the callback, and we didn't modify the sync transaction to be applied one-way anyway. So our hypothetically disordered frame must be frame N (applied by BBQ) and frame N+1 (sync). When we analyze this case, we can see that we actually have a bug in the existing code. By assumption 2 and 4, frame N and N+1 will be applied on different apply tokens and so their ordering is undefined. We can solve the existing issue and enable one-way transactions at the same time. When BBQ prepares a transaction for sync following a transaction that has been directly applied (e.g. our aforementioned potentially undefined N,N+1 case) it uses a new API (setBufferHasBarrier) to set a barrier on the previous frame number. SurfaceFlinger interprets this barrier by forcing the barrier dependent transaction to remain in the transaction queue until a buffer with frameNumber >= the barrier frame has arrived. We chose >= because by assumption 4, the sync consumer may choose (probably incorrectly) to apply transactions out of order and we wouldn't want this to deadlock the transaction queue. The implementation itself is relatively well commented in SurfaceFlinger.cpp and so for details refer there. We see that use of this barrier system ensures our frame sequence was in order, since we tried every combination of (Sync, NotSync) and every frame is either sync or not sync, we can see that there are no frames which are not in order. We can apply similar analysis to slowly make setTransactionState async everywhere but we will need to eliminate the several "sync transaction" usages that remain (e.g. syncInputTransactions, etc). Most of these can be replaced with commit callbacks, but there is a lot to go through. Bug: 220929926 Test: Existing tests pass Change-Id: I3fd622966a9d12e4a197cf8560040f492dff996c --- libs/gui/BLASTBufferQueue.cpp | 20 +++++-- libs/gui/ISurfaceComposer.cpp | 8 ++- libs/gui/LayerState.cpp | 5 ++ libs/gui/SurfaceComposerClient.cpp | 22 ++++++- libs/gui/include/gui/BLASTBufferQueue.h | 15 +++++ libs/gui/include/gui/ISurfaceComposer.h | 1 + libs/gui/include/gui/LayerState.h | 2 + libs/gui/include/gui/SurfaceComposerClient.h | 23 +++++++- services/surfaceflinger/BufferStateLayer.h | 2 + services/surfaceflinger/SurfaceFlinger.cpp | 85 +++++++++++++++++++++++----- services/surfaceflinger/SurfaceFlinger.h | 8 ++- 11 files changed, 167 insertions(+), 24 deletions(-) (limited to 'libs/gui/BLASTBufferQueue.cpp') diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 8e23eb8766..ec4c7c10a4 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -182,7 +182,8 @@ BLASTBufferQueue::~BLASTBufferQueue() { static_cast(mPendingTransactions.size())); SurfaceComposerClient::Transaction t; mergePendingTransactions(&t, std::numeric_limits::max() /* frameNumber */); - t.setApplyToken(mApplyToken).apply(); + // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction + t.setApplyToken(mApplyToken).apply(false, true); } void BLASTBufferQueue::update(const sp& surface, uint32_t width, uint32_t height, @@ -230,7 +231,8 @@ void BLASTBufferQueue::update(const sp& surface, uint32_t width, } } if (applyTransaction) { - t.setApplyToken(mApplyToken).apply(); + // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction + t.setApplyToken(mApplyToken).apply(false, true); } } @@ -551,7 +553,13 @@ void BLASTBufferQueue::acquireNextBufferLocked( mergePendingTransactions(t, bufferItem.mFrameNumber); if (applyTransaction) { - t->setApplyToken(mApplyToken).apply(); + // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction + t->setApplyToken(mApplyToken).apply(false, true); + mAppliedLastTransaction = true; + mLastAppliedFrameNumber = bufferItem.mFrameNumber; + } else { + t->setBufferHasBarrier(mSurfaceControl, mLastAppliedFrameNumber); + mAppliedLastTransaction = false; } BQA_LOGV("acquireNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64 @@ -857,7 +865,8 @@ void BLASTBufferQueue::applyPendingTransactions(uint64_t frameNumber) { SurfaceComposerClient::Transaction t; mergePendingTransactions(&t, frameNumber); - t.setApplyToken(mApplyToken).apply(); + // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction + t.setApplyToken(mApplyToken).apply(false, true); } void BLASTBufferQueue::mergePendingTransactions(SurfaceComposerClient::Transaction* t, @@ -1050,7 +1059,8 @@ void BLASTBufferQueue::abandon() { static_cast(mPendingTransactions.size())); SurfaceComposerClient::Transaction t; mergePendingTransactions(&t, std::numeric_limits::max() /* frameNumber */); - t.setApplyToken(mApplyToken).apply(); + // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction + t.setApplyToken(mApplyToken).apply(false, true); } // Clear sync states diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index 5ab0abc561..5532c6e747 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -111,7 +111,13 @@ public: SAFE_PARCEL(data.writeUint64, transactionId); - return remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply); + if (flags & ISurfaceComposer::eOneWay) { + return remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, + data, &reply, IBinder::FLAG_ONEWAY); + } else { + return remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, + data, &reply); + } } void bootFinished() override { diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 6944d38dfe..338ff1114f 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -796,6 +796,8 @@ status_t BufferData::writeToParcel(Parcel* output) const { SAFE_PARCEL(output->writeStrongBinder, cachedBuffer.token.promote()); SAFE_PARCEL(output->writeUint64, cachedBuffer.id); + SAFE_PARCEL(output->writeBool, hasBarrier); + SAFE_PARCEL(output->writeUint64, barrierFrameNumber); return NO_ERROR; } @@ -832,6 +834,9 @@ status_t BufferData::readFromParcel(const Parcel* input) { cachedBuffer.token = tmpBinder; SAFE_PARCEL(input->readUint64, &cachedBuffer.id); + SAFE_PARCEL(input->readBool, &hasBarrier); + SAFE_PARCEL(input->readUint64, &barrierFrameNumber); + return NO_ERROR; } diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 26ccda580a..27856cef13 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -930,7 +930,7 @@ void SurfaceComposerClient::Transaction::cacheBuffers() { } } -status_t SurfaceComposerClient::Transaction::apply(bool synchronous) { +status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay) { if (mStatus != NO_ERROR) { return mStatus; } @@ -984,6 +984,14 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous) { if (mAnimation) { flags |= ISurfaceComposer::eAnimation; } + if (oneWay) { + if (mForceSynchronous) { + ALOGE("Transaction attempted to set synchronous and one way at the same time" + " this is an invalid request. Synchronous will win for safety"); + } else { + flags |= ISurfaceComposer::eOneWay; + } + } // If both mEarlyWakeupStart and mEarlyWakeupEnd are set // it is equivalent for none @@ -1399,6 +1407,18 @@ std::shared_ptr SurfaceComposerClient::Transaction::getAndClearBuffe return bufferData; } +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBufferHasBarrier( + const sp& sc, uint64_t barrierFrameNumber) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->bufferData->hasBarrier = true; + s->bufferData->barrierFrameNumber = barrierFrameNumber; + return *this; +} + SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffer( const sp& sc, const sp& buffer, const std::optional>& fence, const std::optional& frameNumber, diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 265ae24255..65fc04df1a 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -254,6 +254,21 @@ private: // surfacecontol. This is useful if the caller wants to synchronize the buffer scale with // additional scales in the hierarchy. bool mUpdateDestinationFrame GUARDED_BY(mMutex) = true; + + // We send all transactions on our apply token over one-way binder calls to avoid blocking + // client threads. All of our transactions remain in order, since they are one-way binder calls + // from a single process, to a single interface. However once we give up a Transaction for sync + // we can start to have ordering issues. When we return from sync to normal frame production, + // we wait on the commit callback of sync frames ensuring ordering, however we don't want to + // wait on the commit callback for every normal frame (since even emitting them has a + // performance cost) this means we need a method to ensure frames are in order when switching + // from one-way application on our apply token, to application on some other apply token. We + // make use of setBufferHasBarrier to declare this ordering. This boolean simply tracks when we + // need to set this flag, notably only in the case where we are transitioning from a previous + // transaction applied by us (one way, may not yet have reached server) and an upcoming + // transaction that will be applied by some sync consumer. + bool mAppliedLastTransaction = false; + uint64_t mLastAppliedFrameNumber = 0; }; } // namespace android diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index 4dfc383b57..0a3cc19a13 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -113,6 +113,7 @@ public: // android.permission.ACCESS_SURFACE_FLINGER eEarlyWakeupStart = 0x08, eEarlyWakeupEnd = 0x10, + eOneWay = 0x20 }; enum VsyncSource { diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 885b4ae55c..0f37dab53c 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -89,6 +89,8 @@ public: // Used by BlastBufferQueue to forward the framenumber generated by the // graphics producer. uint64_t frameNumber = 0; + bool hasBarrier = false; + uint64_t barrierFrameNumber = 0; // Listens to when the buffer is safe to be released. This is used for blast // layers only. The callback includes a release fence as well as the graphic diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 6c79b5bbbc..c8ac1662ad 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -461,7 +461,7 @@ public: // Clears the contents of the transaction without applying it. void clear(); - status_t apply(bool synchronous = false); + status_t apply(bool synchronous = false, bool oneWay = false); // Merge another transaction in to this one, clearing other // as if it had been applied. Transaction& merge(Transaction&& other); @@ -521,6 +521,27 @@ public: const std::optional& frameNumber = std::nullopt, ReleaseBufferCallback callback = nullptr); std::shared_ptr getAndClearBuffer(const sp& sc); + + /** + * If this transaction, has a a buffer set for the given SurfaceControl + * mark that buffer as ordered after a given barrierFrameNumber. + * + * SurfaceFlinger will refuse to apply this transaction until after + * the frame in barrierFrameNumber has been applied. This transaction may + * be applied in the same frame as the barrier buffer or after. + * + * This is only designed to be used to handle switches between multiple + * apply tokens, as explained in the comment for BLASTBufferQueue::mAppliedLastTransaction. + * + * Has to be called after setBuffer. + * + * WARNING: + * This API is very dangerous to the caller, as if you invoke it without + * a frameNumber you have not yet submitted, you can dead-lock your + * SurfaceControl's transaction queue. + */ + Transaction& setBufferHasBarrier(const sp& sc, + uint64_t barrierFrameNumber); Transaction& setDataspace(const sp& sc, ui::Dataspace dataspace); Transaction& setHdrMetadata(const sp& sc, const HdrMetadata& hdrMetadata); Transaction& setSurfaceDamageRegion(const sp& sc, diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h index 669eaadfec..8a696f11b4 100644 --- a/services/surfaceflinger/BufferStateLayer.h +++ b/services/surfaceflinger/BufferStateLayer.h @@ -141,6 +141,8 @@ private: ReleaseCallbackId mPreviousReleaseCallbackId = ReleaseCallbackId::INVALID_ID; uint64_t mPreviousReleasedFrameNumber = 0; + uint64_t mPreviousBarrierFrameNumber = 0; + bool mReleasePreviousBuffer = false; // Stores the last set acquire fence signal time used to populate the callback handle's acquire diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index e8034de52c..9a5d5b2aa9 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -3674,11 +3674,12 @@ bool SurfaceFlinger::stopTransactionProcessing( return false; } -void SurfaceFlinger::flushPendingTransactionQueues( +int SurfaceFlinger::flushPendingTransactionQueues( std::vector& transactions, - std::unordered_set, SpHash>& bufferLayersReadyToPresent, + std::unordered_map, uint64_t, SpHash>& bufferLayersReadyToPresent, std::unordered_set, SpHash>& applyTokensWithUnsignaledTransactions, bool tryApplyUnsignaled) { + int transactionsPendingBarrier = 0; auto it = mPendingTransactionQueues.begin(); while (it != mPendingTransactionQueues.end()) { auto& [applyToken, transactionQueue] = *it; @@ -3701,8 +3702,21 @@ void SurfaceFlinger::flushPendingTransactionQueues( setTransactionFlags(eTransactionFlushNeeded); break; } + if (ready == TransactionReadiness::NotReadyBarrier) { + transactionsPendingBarrier++; + setTransactionFlags(eTransactionFlushNeeded); + break; + } transaction.traverseStatesWithBuffers([&](const layer_state_t& state) { - bufferLayersReadyToPresent.insert(state.surface); + const bool frameNumberChanged = + state.bufferData->flags.test(BufferData::BufferDataChange::frameNumberChanged); + if (frameNumberChanged) { + bufferLayersReadyToPresent[state.surface] = state.bufferData->frameNumber; + } else { + // Barrier function only used for BBQ which always includes a frame number + bufferLayersReadyToPresent[state.surface] = + std::numeric_limits::max(); + } }); const bool appliedUnsignaled = (ready == TransactionReadiness::ReadyUnsignaled); if (appliedUnsignaled) { @@ -3720,6 +3734,7 @@ void SurfaceFlinger::flushPendingTransactionQueues( it = std::next(it, 1); } } + return transactionsPendingBarrier; } bool SurfaceFlinger::flushTransactionQueues(int64_t vsyncId) { @@ -3728,19 +3743,21 @@ bool SurfaceFlinger::flushTransactionQueues(int64_t vsyncId) { // states) around outside the scope of the lock std::vector transactions; // Layer handles that have transactions with buffers that are ready to be applied. - std::unordered_set, SpHash> bufferLayersReadyToPresent; + std::unordered_map, uint64_t, SpHash> bufferLayersReadyToPresent; std::unordered_set, SpHash> applyTokensWithUnsignaledTransactions; { Mutex::Autolock _l(mStateLock); { Mutex::Autolock _l(mQueueLock); + int lastTransactionsPendingBarrier = 0; + int transactionsPendingBarrier = 0; // First collect transactions from the pending transaction queues. // We are not allowing unsignaled buffers here as we want to // collect all the transactions from applyTokens that are ready first. - flushPendingTransactionQueues(transactions, bufferLayersReadyToPresent, - applyTokensWithUnsignaledTransactions, - /*tryApplyUnsignaled*/ false); + transactionsPendingBarrier = + flushPendingTransactionQueues(transactions, bufferLayersReadyToPresent, + applyTokensWithUnsignaledTransactions, /*tryApplyUnsignaled*/ false); // Second, collect transactions from the transaction queue. // Here as well we are not allowing unsignaled buffers for the same @@ -3765,18 +3782,48 @@ bool SurfaceFlinger::flushTransactionQueues(int64_t vsyncId) { /*tryApplyUnsignaled*/ false); }(); ATRACE_INT("TransactionReadiness", static_cast(ready)); - if (ready == TransactionReadiness::NotReady) { + if (ready != TransactionReadiness::Ready) { + if (ready == TransactionReadiness::NotReadyBarrier) { + transactionsPendingBarrier++; + } mPendingTransactionQueues[transaction.applyToken].push(std::move(transaction)); } else { transaction.traverseStatesWithBuffers([&](const layer_state_t& state) { - bufferLayersReadyToPresent.insert(state.surface); - }); + const bool frameNumberChanged = + state.bufferData->flags.test(BufferData::BufferDataChange::frameNumberChanged); + if (frameNumberChanged) { + bufferLayersReadyToPresent[state.surface] = state.bufferData->frameNumber; + } else { + // Barrier function only used for BBQ which always includes a frame number. + // This value only used for barrier logic. + bufferLayersReadyToPresent[state.surface] = + std::numeric_limits::max(); + } + }); transactions.emplace_back(std::move(transaction)); } mTransactionQueue.pop_front(); ATRACE_INT("TransactionQueue", mTransactionQueue.size()); } + // Transactions with a buffer pending on a barrier may be on a different applyToken + // than the transaction which satisfies our barrier. In fact this is the exact use case + // that the primitive is designed for. This means we may first process + // the barrier dependent transaction, determine it ineligible to complete + // and then satisfy in a later inner iteration of flushPendingTransactionQueues. + // The barrier dependent transaction was eligible to be presented in this frame + // but we would have prevented it without case. To fix this we continually + // loop through flushPendingTransactionQueues until we perform an iteration + // where the number of transactionsPendingBarrier doesn't change. This way + // we can continue to resolve dependency chains of barriers as far as possible. + while (lastTransactionsPendingBarrier != transactionsPendingBarrier) { + lastTransactionsPendingBarrier = transactionsPendingBarrier; + transactionsPendingBarrier = + flushPendingTransactionQueues(transactions, bufferLayersReadyToPresent, + applyTokensWithUnsignaledTransactions, + /*tryApplyUnsignaled*/ false); + } + // We collected all transactions that could apply without latching unsignaled buffers. // If we are allowing latch unsignaled of some form, now it's the time to go over the // transactions that were not applied and try to apply them unsignaled. @@ -3892,7 +3939,8 @@ bool SurfaceFlinger::shouldLatchUnsignaled(const sp& layer, const layer_s auto SurfaceFlinger::transactionIsReadyToBeApplied( const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime, uid_t originUid, const Vector& states, - const std::unordered_set, SpHash>& bufferLayersReadyToPresent, + const std::unordered_map< + sp, uint64_t, SpHash>& bufferLayersReadyToPresent, size_t totalTXapplied, bool tryApplyUnsignaled) const -> TransactionReadiness { ATRACE_FORMAT("transactionIsReadyToBeApplied vsyncId: %" PRId64, info.vsyncId); const nsecs_t expectedPresentTime = mExpectedPresentTime.load(); @@ -3930,6 +3978,17 @@ auto SurfaceFlinger::transactionIsReadyToBeApplied( continue; } + if (s.hasBufferChanges() && s.bufferData->hasBarrier && + ((layer->getDrawingState().frameNumber) < s.bufferData->barrierFrameNumber)) { + const bool willApplyBarrierFrame = + (bufferLayersReadyToPresent.find(s.surface) != bufferLayersReadyToPresent.end()) && + (bufferLayersReadyToPresent.at(s.surface) >= s.bufferData->barrierFrameNumber); + if (!willApplyBarrierFrame) { + ATRACE_NAME("NotReadyBarrier"); + return TransactionReadiness::NotReadyBarrier; + } + } + const bool allowLatchUnsignaled = tryApplyUnsignaled && shouldLatchUnsignaled(layer, s, states.size(), totalTXapplied); ATRACE_FORMAT("%s allowLatchUnsignaled=%s", layer->getName().c_str(), @@ -3950,8 +4009,8 @@ auto SurfaceFlinger::transactionIsReadyToBeApplied( if (s.hasBufferChanges()) { // If backpressure is enabled and we already have a buffer to commit, keep the // transaction in the queue. - const bool hasPendingBuffer = - bufferLayersReadyToPresent.find(s.surface) != bufferLayersReadyToPresent.end(); + const bool hasPendingBuffer = bufferLayersReadyToPresent.find(s.surface) != + bufferLayersReadyToPresent.end(); if (layer->backpressureEnabled() && hasPendingBuffer && isAutoTimestamp) { ATRACE_NAME("hasPendingBuffer"); return TransactionReadiness::NotReady; diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 58298387c1..0365b3744c 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -760,9 +760,9 @@ private: // Returns true if there is at least one transaction that needs to be flushed bool transactionFlushNeeded(); - void flushPendingTransactionQueues( + int flushPendingTransactionQueues( std::vector& transactions, - std::unordered_set, SpHash>& bufferLayersReadyToPresent, + std::unordered_map, uint64_t, SpHash>& bufferLayersReadyToPresent, std::unordered_set, SpHash>& applyTokensWithUnsignaledTransactions, bool tryApplyUnsignaled) REQUIRES(mStateLock, mQueueLock); @@ -789,13 +789,15 @@ private: void commitOffscreenLayers(); enum class TransactionReadiness { NotReady, + NotReadyBarrier, Ready, ReadyUnsignaled, }; TransactionReadiness transactionIsReadyToBeApplied( const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime, uid_t originUid, const Vector& states, - const std::unordered_set, SpHash>& bufferLayersReadyToPresent, + const std::unordered_map< + sp, uint64_t, SpHash>& bufferLayersReadyToPresent, size_t totalTXapplied, bool tryApplyUnsignaled) const REQUIRES(mStateLock); static LatchUnsignaledConfig getLatchUnsignaledConfig(); bool shouldLatchUnsignaled(const sp& layer, const layer_state_t&, size_t numStates, -- cgit v1.2.3-59-g8ed1b From 3b4bdcf601ef90634c172ce239fc75ddd5b63b84 Mon Sep 17 00:00:00 2001 From: chaviw Date: Thu, 17 Mar 2022 09:27:03 -0500 Subject: Invoke sync transaction callback if overwritten If a caller invokes the syncNextTransaction it will overwrite any callback that was set before. This can break since the caller expects to receive the callback. The change invokes the old callback when a new callback overwrites it. It also invokes the callback when the blast buffer queue is torn down in case there's one still pending. Test: Chrome in split + rotation Test: BLASTBufferQueueTest Fixes: 223329500 Change-Id: I91d1d7c47f4eca014c66ba8c2f773ef8be5c674e --- libs/gui/BLASTBufferQueue.cpp | 35 +++++++++++++++++++++++++------- libs/gui/tests/BLASTBufferQueue_test.cpp | 28 +++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 7 deletions(-) (limited to 'libs/gui/BLASTBufferQueue.cpp') diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index ec4c7c10a4..c2793ac5de 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -184,6 +184,10 @@ BLASTBufferQueue::~BLASTBufferQueue() { mergePendingTransactions(&t, std::numeric_limits::max() /* frameNumber */); // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction t.setApplyToken(mApplyToken).apply(false, true); + + if (mTransactionReadyCallback) { + mTransactionReadyCallback(mSyncTransaction); + } } void BLASTBufferQueue::update(const sp& surface, uint32_t width, uint32_t height, @@ -702,14 +706,31 @@ void BLASTBufferQueue::syncNextTransaction( std::function callback, bool acquireSingleBuffer) { BBQ_TRACE(); - std::lock_guard _lock{mMutex}; - mTransactionReadyCallback = callback; - if (callback) { - mSyncTransaction = new SurfaceComposerClient::Transaction(); - } else { - mSyncTransaction = nullptr; + + std::function prevCallback = nullptr; + SurfaceComposerClient::Transaction* prevTransaction = nullptr; + + { + std::lock_guard _lock{mMutex}; + // We're about to overwrite the previous call so we should invoke that callback + // immediately. + if (mTransactionReadyCallback) { + prevCallback = mTransactionReadyCallback; + prevTransaction = mSyncTransaction; + } + + mTransactionReadyCallback = callback; + if (callback) { + mSyncTransaction = new SurfaceComposerClient::Transaction(); + } else { + mSyncTransaction = nullptr; + } + mAcquireSingleBuffer = mTransactionReadyCallback ? acquireSingleBuffer : true; + } + + if (prevCallback) { + prevCallback(prevTransaction); } - mAcquireSingleBuffer = mTransactionReadyCallback ? acquireSingleBuffer : true; } void BLASTBufferQueue::stopContinuousSyncTransaction() { diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp index 0c3236c9ce..cb7e94c932 100644 --- a/libs/gui/tests/BLASTBufferQueue_test.cpp +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -1083,6 +1083,34 @@ TEST_F(BLASTBufferQueueTest, SyncNextTransactionAcquireMultipleBuffers) { checkScreenCapture(255, 0, 0, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); } +TEST_F(BLASTBufferQueueTest, SyncNextTransactionOverwrite) { + std::mutex mutex; + std::condition_variable callbackReceivedCv; + bool receivedCallback = false; + + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + ASSERT_EQ(nullptr, adapter.getTransactionReadyCallback()); + auto callback = [&](Transaction*) { + std::unique_lock lock(mutex); + receivedCallback = true; + callbackReceivedCv.notify_one(); + }; + adapter.syncNextTransaction(callback); + ASSERT_NE(nullptr, adapter.getTransactionReadyCallback()); + + auto callback2 = [](Transaction*) {}; + adapter.syncNextTransaction(callback2); + + std::unique_lock lock(mutex); + if (!receivedCallback) { + ASSERT_NE(callbackReceivedCv.wait_for(lock, std::chrono::seconds(3)), + std::cv_status::timeout) + << "did not receive callback"; + } + + ASSERT_TRUE(receivedCallback); +} + // This test will currently fail because the old surfacecontrol will steal the last presented buffer // until the old surface control is destroyed. This is not necessarily a bug but to document a // limitation with the update API and to test any changes to make the api more robust. The current -- cgit v1.2.3-59-g8ed1b From 4c1b646994a5878b792eaf6358756206d8fe1433 Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Tue, 21 Dec 2021 10:30:50 -0800 Subject: SurfaceFlinger: Report stuck fences to client It's ocassionally observed in traces that the GPU is hung resulting in fences failing to fire. This normally propagates back up as an ANR in dequeueBuffers or onFrameAvailable. The ANR might then pass from the app team, to the HWUI team, to the WM team, and then if we are lucky enough to get a trace, to the GPU team. In this CL we allow the client process to monitor this situation itself, and proactively trigger an ANR with a more useful and informative message than "stuck in dequeueBuffers" Bug: 216160569 Test: Existing tests pass Change-Id: I3ff93bf57d24268ac57734dfed2df185985ffe1f --- libs/gui/BLASTBufferQueue.cpp | 17 ++++++++++++ libs/gui/ITransactionCompletedListener.cpp | 9 +++++++ libs/gui/SurfaceComposerClient.cpp | 21 +++++++++++++++ libs/gui/include/gui/BLASTBufferQueue.h | 10 ++++++++ .../include/gui/ITransactionCompletedListener.h | 1 + libs/gui/include/gui/SurfaceComposerClient.h | 6 +++++ services/surfaceflinger/SurfaceFlinger.cpp | 30 ++++++++++++++++------ services/surfaceflinger/SurfaceFlinger.h | 2 +- services/surfaceflinger/TransactionState.h | 2 ++ 9 files changed, 89 insertions(+), 9 deletions(-) (limited to 'libs/gui/BLASTBufferQueue.cpp') diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index c2793ac5de..dbccf30fae 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -165,6 +165,17 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name, bool updateDestinati mCurrentMaxAcquiredBufferCount = mMaxAcquiredBuffers; mNumAcquired = 0; mNumFrameAvailable = 0; + + TransactionCompletedListener::getInstance()->addQueueStallListener( + [&]() { + std::function callbackCopy; + { + std::unique_lock _lock{mMutex}; + callbackCopy = mTransactionHangCallback; + } + if (callbackCopy) callbackCopy(true); + }, this); + BQA_LOGV("BLASTBufferQueue created"); } @@ -175,6 +186,7 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const spremoveQueueStallListener(this); if (mPendingTransactions.empty()) { return; } @@ -1113,4 +1125,9 @@ bool BLASTBufferQueue::isSameSurfaceControl(const sp& surfaceCon return SurfaceControl::isSameSurface(mSurfaceControl, surfaceControl); } +void BLASTBufferQueue::setTransactionHangCallback(std::function callback) { + std::unique_lock _lock{mMutex}; + mTransactionHangCallback = callback; +} + } // namespace android diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp index f7392d4969..e4b8bad8f8 100644 --- a/libs/gui/ITransactionCompletedListener.cpp +++ b/libs/gui/ITransactionCompletedListener.cpp @@ -29,6 +29,7 @@ namespace { // Anonymous enum class Tag : uint32_t { ON_TRANSACTION_COMPLETED = IBinder::FIRST_CALL_TRANSACTION, ON_RELEASE_BUFFER, + ON_TRANSACTION_QUEUE_STALLED, LAST = ON_RELEASE_BUFFER, }; @@ -277,6 +278,11 @@ public: callbackId, releaseFence, currentMaxAcquiredBufferCount); } + + void onTransactionQueueStalled() override { + callRemoteAsync( + Tag::ON_TRANSACTION_QUEUE_STALLED); + } }; // Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see @@ -297,6 +303,9 @@ status_t BnTransactionCompletedListener::onTransact(uint32_t code, const Parcel& &ITransactionCompletedListener::onTransactionCompleted); case Tag::ON_RELEASE_BUFFER: return callLocalAsync(data, reply, &ITransactionCompletedListener::onReleaseBuffer); + case Tag::ON_TRANSACTION_QUEUE_STALLED: + return callLocalAsync(data, reply, + &ITransactionCompletedListener::onTransactionQueueStalled); } } diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 7a63af0ed3..4ce5e4fe59 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -447,6 +447,27 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener } } +void TransactionCompletedListener::onTransactionQueueStalled() { + std::unordered_map> callbackCopy; + { + std::scoped_lock lock(mMutex); + callbackCopy = mQueueStallListeners; + } + for (auto const& it : callbackCopy) { + it.second(); + } +} + +void TransactionCompletedListener::addQueueStallListener(std::function stallListener, + void* id) { + std::scoped_lock lock(mMutex); + mQueueStallListeners[id] = stallListener; +} +void TransactionCompletedListener::removeQueueStallListener(void *id) { + std::scoped_lock lock(mMutex); + mQueueStallListeners.erase(id); +} + void TransactionCompletedListener::onReleaseBuffer(ReleaseCallbackId callbackId, sp releaseFence, uint32_t currentMaxAcquiredBufferCount) { diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 65fc04df1a..9328a54184 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -113,6 +113,14 @@ public: uint64_t getLastAcquiredFrameNum(); void abandon(); + /** + * Set a callback to be invoked when we are hung. The boolean parameter + * indicates whether the hang is due to an unfired fence. + * TODO: The boolean is always true atm, unfired fence is + * the only case we detect. + */ + void setTransactionHangCallback(std::function callback); + virtual ~BLASTBufferQueue(); private: @@ -269,6 +277,8 @@ private: // transaction that will be applied by some sync consumer. bool mAppliedLastTransaction = false; uint64_t mLastAppliedFrameNumber = 0; + + std::function mTransactionHangCallback; }; } // namespace android diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h index a791c665e1..cc136bb40a 100644 --- a/libs/gui/include/gui/ITransactionCompletedListener.h +++ b/libs/gui/include/gui/ITransactionCompletedListener.h @@ -194,6 +194,7 @@ public: virtual void onReleaseBuffer(ReleaseCallbackId callbackId, sp releaseFence, uint32_t currentMaxAcquiredBufferCount) = 0; + virtual void onTransactionQueueStalled() = 0; }; class BnTransactionCompletedListener : public SafeBnInterface { diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 0cc43d85bf..efbdb36fef 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -772,6 +772,7 @@ protected: // This is protected by mSurfaceStatsListenerMutex, but GUARDED_BY isn't supported for // std::recursive_mutex std::multimap mSurfaceStatsListeners; + std::unordered_map> mQueueStallListeners; public: static sp getInstance(); @@ -789,6 +790,9 @@ public: const sp& surfaceControl, const std::unordered_set& callbackIds); + void addQueueStallListener(std::function stallListener, void* id); + void removeQueueStallListener(void *id); + /* * Adds a jank listener to be informed about SurfaceFlinger's jank classification for a specific * surface. Jank classifications arrive as part of the transaction callbacks about previous @@ -817,6 +821,8 @@ public: // For Testing Only static void setInstance(const sp&); + void onTransactionQueueStalled() override; + private: ReleaseBufferCallback popReleaseBufferCallbackLocked(const ReleaseCallbackId&); static sp sInstance; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 3bfc2cc2a3..f6f3805620 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -3697,12 +3697,13 @@ int SurfaceFlinger::flushPendingTransactionQueues( auto& transaction = transactionQueue.front(); const auto ready = - transactionIsReadyToBeApplied(transaction.frameTimelineInfo, - transaction.isAutoTimestamp, - transaction.desiredPresentTime, - transaction.originUid, transaction.states, - bufferLayersReadyToPresent, transactions.size(), - tryApplyUnsignaled); + transactionIsReadyToBeApplied(transaction, + transaction.frameTimelineInfo, + transaction.isAutoTimestamp, + transaction.desiredPresentTime, + transaction.originUid, transaction.states, + bufferLayersReadyToPresent, transactions.size(), + tryApplyUnsignaled); ATRACE_INT("TransactionReadiness", static_cast(ready)); if (ready == TransactionReadiness::NotReady) { setTransactionFlags(eTransactionFlushNeeded); @@ -3779,7 +3780,7 @@ bool SurfaceFlinger::flushTransactionQueues(int64_t vsyncId) { return TransactionReadiness::NotReady; } - return transactionIsReadyToBeApplied(transaction.frameTimelineInfo, + return transactionIsReadyToBeApplied(transaction, transaction.frameTimelineInfo, transaction.isAutoTimestamp, transaction.desiredPresentTime, transaction.originUid, transaction.states, @@ -3941,7 +3942,7 @@ bool SurfaceFlinger::shouldLatchUnsignaled(const sp& layer, const layer_s return true; } -auto SurfaceFlinger::transactionIsReadyToBeApplied( +auto SurfaceFlinger::transactionIsReadyToBeApplied(TransactionState& transaction, const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime, uid_t originUid, const Vector& states, const std::unordered_map< @@ -3970,8 +3971,10 @@ auto SurfaceFlinger::transactionIsReadyToBeApplied( } bool fenceUnsignaled = false; + auto queueProcessTime = systemTime(); for (const ComposerState& state : states) { const layer_state_t& s = state.state; + sp layer = nullptr; if (s.surface) { layer = fromHandle(s.surface).promote(); @@ -4007,6 +4010,15 @@ auto SurfaceFlinger::transactionIsReadyToBeApplied( s.bufferData->acquireFence->getStatus() == Fence::Status::Unsignaled); if (fenceUnsignaled && !allowLatchUnsignaled) { + if (!transaction.sentFenceTimeoutWarning && + queueProcessTime - transaction.queueTime > std::chrono::nanoseconds(4s).count()) { + transaction.sentFenceTimeoutWarning = true; + auto listener = s.bufferData->releaseBufferListener; + if (listener) { + listener->onTransactionQueueStalled(); + } + } + ATRACE_NAME("fence unsignaled"); return TransactionReadiness::NotReady; } @@ -4026,6 +4038,8 @@ auto SurfaceFlinger::transactionIsReadyToBeApplied( } void SurfaceFlinger::queueTransaction(TransactionState& state) { + state.queueTime = systemTime(); + Mutex::Autolock lock(mQueueLock); // Generate a CountDownLatch pending state if this is a synchronous transaction. diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 011aaef961..0b013d4474 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -816,7 +816,7 @@ private: Ready, ReadyUnsignaled, }; - TransactionReadiness transactionIsReadyToBeApplied( + TransactionReadiness transactionIsReadyToBeApplied(TransactionState& state, const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime, uid_t originUid, const Vector& states, const std::unordered_map< diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h index bab5326093..900d566942 100644 --- a/services/surfaceflinger/TransactionState.h +++ b/services/surfaceflinger/TransactionState.h @@ -98,6 +98,8 @@ struct TransactionState { int originUid; uint64_t id; std::shared_ptr transactionCommittedSignal; + int64_t queueTime = 0; + bool sentFenceTimeoutWarning = false; }; class CountDownLatch { -- cgit v1.2.3-59-g8ed1b