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

Commit 79dc06a3 authored by Robert Carr's avatar Robert Carr Committed by Rob Carr
Browse files

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
parent de97b141
Loading
Loading
Loading
Loading
+15 −5
Original line number Original line Diff line number Diff line
@@ -182,7 +182,8 @@ BLASTBufferQueue::~BLASTBufferQueue() {
             static_cast<uint32_t>(mPendingTransactions.size()));
             static_cast<uint32_t>(mPendingTransactions.size()));
    SurfaceComposerClient::Transaction t;
    SurfaceComposerClient::Transaction t;
    mergePendingTransactions(&t, std::numeric_limits<uint64_t>::max() /* frameNumber */);
    mergePendingTransactions(&t, std::numeric_limits<uint64_t>::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<SurfaceControl>& surface, uint32_t width, uint32_t height,
void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height,
@@ -230,7 +231,8 @@ void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width,
        }
        }
    }
    }
    if (applyTransaction) {
    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);
    mergePendingTransactions(t, bufferItem.mFrameNumber);
    if (applyTransaction) {
    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
    BQA_LOGV("acquireNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64
@@ -857,7 +865,8 @@ void BLASTBufferQueue::applyPendingTransactions(uint64_t frameNumber) {


    SurfaceComposerClient::Transaction t;
    SurfaceComposerClient::Transaction t;
    mergePendingTransactions(&t, frameNumber);
    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,
void BLASTBufferQueue::mergePendingTransactions(SurfaceComposerClient::Transaction* t,
@@ -1050,7 +1059,8 @@ void BLASTBufferQueue::abandon() {
                 static_cast<uint32_t>(mPendingTransactions.size()));
                 static_cast<uint32_t>(mPendingTransactions.size()));
        SurfaceComposerClient::Transaction t;
        SurfaceComposerClient::Transaction t;
        mergePendingTransactions(&t, std::numeric_limits<uint64_t>::max() /* frameNumber */);
        mergePendingTransactions(&t, std::numeric_limits<uint64_t>::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
    // Clear sync states
+7 −1
Original line number Original line Diff line number Diff line
@@ -111,7 +111,13 @@ public:


        SAFE_PARCEL(data.writeUint64, transactionId);
        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 {
    void bootFinished() override {
+5 −0
Original line number Original line Diff line number Diff line
@@ -796,6 +796,8 @@ status_t BufferData::writeToParcel(Parcel* output) const {


    SAFE_PARCEL(output->writeStrongBinder, cachedBuffer.token.promote());
    SAFE_PARCEL(output->writeStrongBinder, cachedBuffer.token.promote());
    SAFE_PARCEL(output->writeUint64, cachedBuffer.id);
    SAFE_PARCEL(output->writeUint64, cachedBuffer.id);
    SAFE_PARCEL(output->writeBool, hasBarrier);
    SAFE_PARCEL(output->writeUint64, barrierFrameNumber);


    return NO_ERROR;
    return NO_ERROR;
}
}
@@ -832,6 +834,9 @@ status_t BufferData::readFromParcel(const Parcel* input) {
    cachedBuffer.token = tmpBinder;
    cachedBuffer.token = tmpBinder;
    SAFE_PARCEL(input->readUint64, &cachedBuffer.id);
    SAFE_PARCEL(input->readUint64, &cachedBuffer.id);


    SAFE_PARCEL(input->readBool, &hasBarrier);
    SAFE_PARCEL(input->readUint64, &barrierFrameNumber);

    return NO_ERROR;
    return NO_ERROR;
}
}


+21 −1
Original line number Original line Diff line number Diff line
@@ -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) {
    if (mStatus != NO_ERROR) {
        return mStatus;
        return mStatus;
    }
    }
@@ -984,6 +984,14 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous) {
    if (mAnimation) {
    if (mAnimation) {
        flags |= ISurfaceComposer::eAnimation;
        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
    // If both mEarlyWakeupStart and mEarlyWakeupEnd are set
    // it is equivalent for none
    // it is equivalent for none
@@ -1399,6 +1407,18 @@ std::shared_ptr<BufferData> SurfaceComposerClient::Transaction::getAndClearBuffe
    return bufferData;
    return bufferData;
}
}


SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBufferHasBarrier(
        const sp<SurfaceControl>& 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(
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffer(
        const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
        const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
        const std::optional<sp<Fence>>& fence, const std::optional<uint64_t>& frameNumber,
        const std::optional<sp<Fence>>& fence, const std::optional<uint64_t>& frameNumber,
+15 −0
Original line number Original line Diff line number Diff line
@@ -254,6 +254,21 @@ private:
    // surfacecontol. This is useful if the caller wants to synchronize the buffer scale with
    // surfacecontol. This is useful if the caller wants to synchronize the buffer scale with
    // additional scales in the hierarchy.
    // additional scales in the hierarchy.
    bool mUpdateDestinationFrame GUARDED_BY(mMutex) = true;
    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
} // namespace android
Loading