Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1506b181 authored by Vishnu Nair's avatar Vishnu Nair
Browse files

Introduce release buffer callback for BufferStateLayer

Currently BLAST clients use the TransactionCompleted callbacks to
determine when to release buffers. The TransactionCompleted callback
is overloaded. For transactions without buffers, the callback is
called when the transaction is applied on the server. If the
Transaction contains one or more buffers, the callback is called when
all the buffers are latched and ready to be presented. If we have
multiple buffers on multiple transactions, where one or more buffers
maybe dropped, the pending callbacks are called together. This may
delay signaling the client when a buffer can be released.

To fix this, we introduce a new buffer release callback that is
called as soon as a buffer is dropped by the server or when a new
buffer has been latched and the buffer will no longer be presented.
This new callback provides a graphic bufferid to identify the buffer
that can be released and a release fence to wait on.

BlastBufferQueue has been switched to use this new callback. Other
BLAST users continue to use the existing callback.

Test: go/wm-smoke
Test: atest ReleaseBufferCallbackTest
Bug: 178385281

Change-Id: Idd88e4994e543443198a5a8cfa0e3f5f67d5d482
parent 3fabf687
Loading
Loading
Loading
Loading
+60 −47
Original line number Diff line number Diff line
@@ -169,8 +169,6 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceCont

    mNumAcquired = 0;
    mNumFrameAvailable = 0;
    mPendingReleaseItem.item = BufferItem();
    mPendingReleaseItem.releaseFence = nullptr;
}

BLASTBufferQueue::~BLASTBufferQueue() {
@@ -242,7 +240,6 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence
        std::unique_lock _lock{mMutex};
        ATRACE_CALL();
        BQA_LOGV("transactionCallback");
        mInitialCallbackReceived = true;

        if (!stats.empty()) {
            mTransformHint = stats[0].transformHint;
@@ -255,38 +252,20 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence
                                            stats[0].frameEventStats.compositorTiming,
                                            stats[0].latchTime,
                                            stats[0].frameEventStats.dequeueReadyTime);
        }
        if (mPendingReleaseItem.item.mGraphicBuffer != nullptr) {
            if (!stats.empty()) {
                mPendingReleaseItem.releaseFence = stats[0].previousReleaseFence;
            } else {
                BQA_LOGE("Warning: no SurfaceControlStats returned in BLASTBufferQueue callback");
                mPendingReleaseItem.releaseFence = nullptr;
            }
            mBufferItemConsumer->releaseBuffer(mPendingReleaseItem.item,
                                               mPendingReleaseItem.releaseFence
                                                       ? mPendingReleaseItem.releaseFence
                                                       : Fence::NO_FENCE);
            mNumAcquired--;
            mPendingReleaseItem.item = BufferItem();
            mPendingReleaseItem.releaseFence = nullptr;
        }
            currFrameNumber = stats[0].frameEventStats.frameNumber;

        if (mSubmitted.empty()) {
            BQA_LOGE("ERROR: callback with no corresponding submitted buffer item");
            if (mTransactionCompleteCallback &&
                currFrameNumber >= mTransactionCompleteFrameNumber) {
                if (currFrameNumber > mTransactionCompleteFrameNumber) {
                    BQA_LOGE("transactionCallback received for a newer framenumber=%" PRIu64
                             " than expected=%" PRIu64,
                             currFrameNumber, mTransactionCompleteFrameNumber);
                }
        mPendingReleaseItem.item = std::move(mSubmitted.front());
        mSubmitted.pop();

        processNextBufferLocked(false /* useNextTransaction */);

        currFrameNumber = mPendingReleaseItem.item.mFrameNumber;
        if (mTransactionCompleteCallback && mTransactionCompleteFrameNumber == currFrameNumber) {
                transactionCompleteCallback = std::move(mTransactionCompleteCallback);
                mTransactionCompleteFrameNumber = 0;
            }
        }

        mCallbackCV.notify_all();
        decStrong((void*)transactionCallbackThunk);
    }

@@ -295,15 +274,46 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence
    }
}

void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) {
// Unlike transactionCallbackThunk the release buffer callback does not extend the life of the
// BBQ. This is because if the BBQ is destroyed, then the buffers will be released by the client.
// So we pass in a weak pointer to the BBQ and if it still alive, then we release the buffer.
// Otherwise, this is a no-op.
static void releaseBufferCallbackThunk(wp<BLASTBufferQueue> context, uint64_t graphicBufferId,
                                       const sp<Fence>& releaseFence) {
    sp<BLASTBufferQueue> blastBufferQueue = context.promote();
    ALOGV("releaseBufferCallbackThunk graphicBufferId=%" PRIu64 " blastBufferQueue=%s",
          graphicBufferId, blastBufferQueue ? "alive" : "dead");
    if (blastBufferQueue) {
        blastBufferQueue->releaseBufferCallback(graphicBufferId, releaseFence);
    }
}

void BLASTBufferQueue::releaseBufferCallback(uint64_t graphicBufferId,
                                             const sp<Fence>& releaseFence) {
    ATRACE_CALL();
    BQA_LOGV("processNextBufferLocked useNextTransaction=%s", toString(useNextTransaction));
    std::unique_lock _lock{mMutex};
    BQA_LOGV("releaseBufferCallback graphicBufferId=%" PRIu64, graphicBufferId);

    auto it = mSubmitted.find(graphicBufferId);
    if (it == mSubmitted.end()) {
        BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %" PRIu64,
                 graphicBufferId);
        return;
    }

    mBufferItemConsumer->releaseBuffer(it->second, releaseFence);
    mSubmitted.erase(it);
    mNumAcquired--;
    processNextBufferLocked(false /* useNextTransaction */);
    mCallbackCV.notify_all();
}

void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) {
    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)) {
        BQA_LOGV("processNextBufferLocked waiting for frame available or callback");
        mCallbackCV.notify_all();
        return;
    }
@@ -353,7 +363,7 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) {
    }

    mNumAcquired++;
    mSubmitted.push(bufferItem);
    mSubmitted[buffer->getId()] = bufferItem;

    bool needsDisconnect = false;
    mBufferItemConsumer->getConnectionEvents(bufferItem.mFrameNumber, &needsDisconnect);
@@ -369,7 +379,10 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) {
    mLastBufferScalingMode = bufferItem.mScalingMode;
    mLastAcquiredFrameNumber = bufferItem.mFrameNumber;

    t->setBuffer(mSurfaceControl, buffer);
    auto releaseBufferCallback =
            std::bind(releaseBufferCallbackThunk, wp<BLASTBufferQueue>(this) /* callbackContext */,
                      std::placeholders::_1, std::placeholders::_2);
    t->setBuffer(mSurfaceControl, buffer, releaseBufferCallback);
    t->setDataspace(mSurfaceControl, static_cast<ui::Dataspace>(bufferItem.mDataSpace));
    t->setHdrMetadata(mSurfaceControl, bufferItem.mHdrMetadata);
    t->setSurfaceDamageRegion(mSurfaceControl, bufferItem.mSurfaceDamage);
@@ -427,9 +440,12 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) {
    }

    BQA_LOGV("processNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64
             " applyTransaction=%s mTimestamp=%" PRId64 " mPendingTransactions.size=%d",
             " applyTransaction=%s mTimestamp=%" PRId64 "%s mPendingTransactions.size=%d"
             " graphicBufferId=%" PRIu64,
             mSize.width, mSize.height, bufferItem.mFrameNumber, toString(applyTransaction),
             bufferItem.mTimestamp, static_cast<uint32_t>(mPendingTransactions.size()));
             bufferItem.mTimestamp, bufferItem.mIsAutoTimestamp ? "(auto)" : "",
             static_cast<uint32_t>(mPendingTransactions.size()),
             bufferItem.mGraphicBuffer->getId());
}

Rect BLASTBufferQueue::computeCrop(const BufferItem& item) {
@@ -444,18 +460,17 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
    std::unique_lock _lock{mMutex};

    const bool nextTransactionSet = mNextTransaction != nullptr;
    BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " nextTransactionSet=%s mFlushShadowQueue=%s",
             item.mFrameNumber, toString(nextTransactionSet), toString(mFlushShadowQueue));

    if (nextTransactionSet || mFlushShadowQueue) {
    if (nextTransactionSet) {
        while (mNumFrameAvailable > 0 || maxBuffersAcquired(false /* includeExtraAcquire */)) {
            BQA_LOGV("waiting in onFrameAvailable...");
            mCallbackCV.wait(_lock);
        }
    }
    mFlushShadowQueue = false;
    // add to shadow queue
    mNumFrameAvailable++;

    BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " nextTransactionSet=%s", item.mFrameNumber,
             toString(nextTransactionSet));
    processNextBufferLocked(nextTransactionSet /* useNextTransaction */);
}

@@ -514,14 +529,12 @@ void BLASTBufferQueue::setTransactionCompleteCallback(
}

// Check if we have acquired the maximum number of buffers.
// As a special case, we wait for the first callback before acquiring the second buffer so we
// can ensure the first buffer is presented if multiple buffers are queued in succession.
// 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
// of the buffer, the next acquire may return with NO_BUFFER_AVAILABLE.
bool BLASTBufferQueue::maxBuffersAcquired(bool includeExtraAcquire) const {
    int maxAcquiredBuffers = MAX_ACQUIRED_BUFFERS + (includeExtraAcquire ? 2 : 1);
    return mNumAcquired == maxAcquiredBuffers || (!mInitialCallbackReceived && mNumAcquired == 1);
    return mNumAcquired == maxAcquiredBuffers;
}

class BBQSurface : public Surface {
+12 −1
Original line number Diff line number Diff line
@@ -27,7 +27,8 @@ namespace { // Anonymous

enum class Tag : uint32_t {
    ON_TRANSACTION_COMPLETED = IBinder::FIRST_CALL_TRANSACTION,
    LAST = ON_TRANSACTION_COMPLETED,
    ON_RELEASE_BUFFER,
    LAST = ON_RELEASE_BUFFER,
};

} // Anonymous namespace
@@ -122,6 +123,7 @@ status_t SurfaceStats::writeToParcel(Parcel* output) const {
    for (const auto& data : jankData) {
        SAFE_PARCEL(output->writeParcelable, data);
    }
    SAFE_PARCEL(output->writeUint64, previousBufferId);
    return NO_ERROR;
}

@@ -144,6 +146,7 @@ status_t SurfaceStats::readFromParcel(const Parcel* input) {
        SAFE_PARCEL(input->readParcelable, &data);
        jankData.push_back(data);
    }
    SAFE_PARCEL(input->readUint64, &previousBufferId);
    return NO_ERROR;
}

@@ -245,6 +248,12 @@ public:
                                         onTransactionCompleted)>(Tag::ON_TRANSACTION_COMPLETED,
                                                                  stats);
    }

    void onReleaseBuffer(uint64_t graphicBufferId, sp<Fence> releaseFence) override {
        callRemoteAsync<decltype(
                &ITransactionCompletedListener::onReleaseBuffer)>(Tag::ON_RELEASE_BUFFER,
                                                                  graphicBufferId, releaseFence);
    }
};

// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
@@ -263,6 +272,8 @@ status_t BnTransactionCompletedListener::onTransact(uint32_t code, const Parcel&
        case Tag::ON_TRANSACTION_COMPLETED:
            return callLocalAsync(data, reply,
                                  &ITransactionCompletedListener::onTransactionCompleted);
        case Tag::ON_RELEASE_BUFFER:
            return callLocalAsync(data, reply, &ITransactionCompletedListener::onReleaseBuffer);
    }
}

+16 −1
Original line number Diff line number Diff line
@@ -63,7 +63,8 @@ layer_state_t::layer_state_t()
        fixedTransformHint(ui::Transform::ROT_INVALID),
        frameNumber(0),
        frameTimelineInfo(),
        autoRefresh(false) {
        autoRefresh(false),
        releaseBufferListener(nullptr) {
    matrix.dsdx = matrix.dtdy = 1.0f;
    matrix.dsdy = matrix.dtdx = 0.0f;
    hdrMetadata.validTypes = 0;
@@ -152,6 +153,7 @@ status_t layer_state_t::write(Parcel& output) const
    SAFE_PARCEL(output.writeUint64, frameNumber);
    SAFE_PARCEL(frameTimelineInfo.write, output);
    SAFE_PARCEL(output.writeBool, autoRefresh);
    SAFE_PARCEL(output.writeStrongBinder, IInterface::asBinder(releaseBufferListener));

    SAFE_PARCEL(output.writeUint32, blurRegions.size());
    for (auto region : blurRegions) {
@@ -275,6 +277,12 @@ status_t layer_state_t::read(const Parcel& input)
    SAFE_PARCEL(frameTimelineInfo.read, input);
    SAFE_PARCEL(input.readBool, &autoRefresh);

    tmpBinder = nullptr;
    SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder);
    if (tmpBinder) {
        releaseBufferListener = checked_interface_cast<ITransactionCompletedListener>(tmpBinder);
    }

    uint32_t numRegions = 0;
    SAFE_PARCEL(input.readUint32, &numRegions);
    blurRegions.clear();
@@ -543,6 +551,13 @@ void layer_state_t::merge(const layer_state_t& other) {
        what |= eAutoRefreshChanged;
        autoRefresh = other.autoRefresh;
    }
    if (other.what & eReleaseBufferListenerChanged) {
        if (releaseBufferListener) {
            ALOGW("Overriding releaseBufferListener");
        }
        what |= eReleaseBufferListenerChanged;
        releaseBufferListener = other.releaseBufferListener;
    }
    if (other.what & eStretchChanged) {
        what |= eStretchChanged;
        stretchEffect = other.stretchEffect;
+83 −1
Original line number Diff line number Diff line
@@ -195,6 +195,17 @@ void TransactionCompletedListener::removeJankListener(const sp<JankDataListener>
    }
}

void TransactionCompletedListener::setReleaseBufferCallback(uint64_t graphicBufferId,
                                                            ReleaseBufferCallback listener) {
    std::scoped_lock<std::mutex> lock(mMutex);
    mReleaseBufferCallbacks[graphicBufferId] = listener;
}

void TransactionCompletedListener::removeReleaseBufferCallback(uint64_t graphicBufferId) {
    std::scoped_lock<std::mutex> lock(mMutex);
    mReleaseBufferCallbacks.erase(graphicBufferId);
}

void TransactionCompletedListener::addSurfaceStatsListener(void* context, void* cookie,
        sp<SurfaceControl> surfaceControl, SurfaceStatsCallback listener) {
    std::lock_guard<std::mutex> lock(mMutex);
@@ -275,6 +286,20 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener
                            .surfaceControls[surfaceStats.surfaceControl]
                            ->setTransformHint(surfaceStats.transformHint);
                }
                // If there is buffer id set, we look up any pending client release buffer callbacks
                // and call them. This is a performance optimization when we have a transaction
                // callback and a release buffer callback happening at the same time to avoid an
                // additional ipc call from the server.
                if (surfaceStats.previousBufferId) {
                    ReleaseBufferCallback callback =
                            popReleaseBufferCallbackLocked(surfaceStats.previousBufferId);
                    if (callback) {
                        callback(surfaceStats.previousBufferId,
                                 surfaceStats.previousReleaseFence
                                         ? surfaceStats.previousReleaseFence
                                         : Fence::NO_FENCE);
                    }
                }
            }

            callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
@@ -297,6 +322,32 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener
    }
}

void TransactionCompletedListener::onReleaseBuffer(uint64_t graphicBufferId,
                                                   sp<Fence> releaseFence) {
    ReleaseBufferCallback callback;
    {
        std::scoped_lock<std::mutex> lock(mMutex);
        callback = popReleaseBufferCallbackLocked(graphicBufferId);
    }
    if (!callback) {
        ALOGE("Could not call release buffer callback, buffer not found %" PRIu64, graphicBufferId);
        return;
    }
    callback(graphicBufferId, releaseFence);
}

ReleaseBufferCallback TransactionCompletedListener::popReleaseBufferCallbackLocked(
        uint64_t graphicBufferId) {
    ReleaseBufferCallback callback;
    auto itr = mReleaseBufferCallbacks.find(graphicBufferId);
    if (itr == mReleaseBufferCallbacks.end()) {
        return nullptr;
    }
    callback = itr->second;
    mReleaseBufferCallbacks.erase(itr);
    return callback;
}

// ---------------------------------------------------------------------------

void removeDeadBufferCallback(void* /*context*/, uint64_t graphicBufferId);
@@ -1219,17 +1270,20 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrame
}

SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffer(
        const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer) {
        const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
        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;
    if (mIsAutoTimestamp) {
        mDesiredPresentTime = systemTime();
    }
    setReleaseBufferCallback(s, callback);

    registerSurfaceControlForCallback(sc);

@@ -1237,6 +1291,34 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffe
    return *this;
}

void SurfaceComposerClient::Transaction::removeReleaseBufferCallback(layer_state_t* s) {
    if (!s->releaseBufferListener) {
        return;
    }

    s->what &= ~static_cast<uint64_t>(layer_state_t::eReleaseBufferListenerChanged);
    s->releaseBufferListener = nullptr;
    TransactionCompletedListener::getInstance()->removeReleaseBufferCallback(s->buffer->getId());
}

void SurfaceComposerClient::Transaction::setReleaseBufferCallback(layer_state_t* s,
                                                                  ReleaseBufferCallback callback) {
    if (!callback) {
        return;
    }

    if (!s->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();
    auto listener = TransactionCompletedListener::getInstance();
    listener->setReleaseBufferCallback(s->buffer->getId(), callback);
}

SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setAcquireFence(
        const sp<SurfaceControl>& sc, const sp<Fence>& fence) {
    layer_state_t* s = getLayerState(sc);
+5 −13
Original line number Diff line number Diff line
@@ -89,13 +89,14 @@ public:

    void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
            const std::vector<SurfaceControlStats>& stats);
    void releaseBufferCallback(uint64_t graphicBufferId, const sp<Fence>& releaseFence);
    void setNextTransaction(SurfaceComposerClient::Transaction *t);
    void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber);
    void setTransactionCompleteCallback(uint64_t frameNumber,
                                        std::function<void(int64_t)>&& transactionCompleteCallback);

    void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height, int32_t format);
    void flushShadowQueue() { mFlushShadowQueue = true; }
    void flushShadowQueue() {}

    status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless);
    status_t setFrameTimelineInfo(const FrameTimelineInfo& info);
@@ -132,16 +133,10 @@ private:

    int32_t mNumFrameAvailable GUARDED_BY(mMutex);
    int32_t mNumAcquired GUARDED_BY(mMutex);
    bool mInitialCallbackReceived GUARDED_BY(mMutex) = false;
    struct PendingReleaseItem {
        BufferItem item;
        sp<Fence> releaseFence;
    };

    std::queue<const BufferItem> mSubmitted GUARDED_BY(mMutex);
    // Keep a reference to the currently presented buffer so we can release it when the next buffer
    // is ready to be presented.
    PendingReleaseItem mPendingReleaseItem GUARDED_BY(mMutex);
    // 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.
    std::unordered_map<uint64_t /* bufferId */, BufferItem> mSubmitted GUARDED_BY(mMutex);

    ui::Size mSize GUARDED_BY(mMutex);
    ui::Size mRequestedSize GUARDED_BY(mMutex);
@@ -157,9 +152,6 @@ private:
    std::vector<std::tuple<uint64_t /* framenumber */, SurfaceComposerClient::Transaction>>
            mPendingTransactions GUARDED_BY(mMutex);

    // If set to true, the next queue buffer will wait until the shadow queue has been processed by
    // the adapter.
    bool mFlushShadowQueue = false;
    // 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.
Loading