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

Commit 277142c5 authored by Vishnu Nair's avatar Vishnu Nair
Browse files

SurfaceFlinger: Support out of order transactions

With BLAST, buffers are submitted via transactions and they
are subjected to the same transaction queueing logic - transactions
from a client must be applied in the order they came in.

This poses a problem when clients use multiple blast adapters.
Transactions from one window can get queued up if the buffer
is not ready to be presented, preventing transactions
from another window to be applied in a timely manner.

We fix this by using a different apply token for every bast adapter
so their transactions are handled independently.

Bug: b/176411039, b/168917217

Test: atest BlastBufferQueueTest
Test: YouTube minimize video and scroll, notice scrolling is at 60/90fps
Change-Id: I0005dfa3c221a2b33545e39af16ae4b1ef08d269
parent 9d441308
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -371,7 +371,7 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) {
                               mPendingTransactions.end());

    if (applyTransaction) {
        t->apply();
        t->setApplyToken(mApplyToken).apply();
    }

    BQA_LOGV("processNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64
+18 −3
Original line number Diff line number Diff line
@@ -396,7 +396,8 @@ SurfaceComposerClient::Transaction::Transaction(const Transaction& other)
        mContainsBuffer(other.mContainsBuffer),
        mDesiredPresentTime(other.mDesiredPresentTime),
        mIsAutoTimestamp(other.mIsAutoTimestamp),
        mFrameTimelineVsyncId(other.mFrameTimelineVsyncId) {
        mFrameTimelineVsyncId(other.mFrameTimelineVsyncId),
        mApplyToken(other.mApplyToken) {
    mDisplayStates = other.mDisplayStates;
    mComposerStates = other.mComposerStates;
    mInputWindowCommands = other.mInputWindowCommands;
@@ -427,7 +428,8 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel
    const int64_t desiredPresentTime = parcel->readInt64();
    const bool isAutoTimestamp = parcel->readBool();
    const int64_t frameTimelineVsyncId = parcel->readInt64();

    sp<IBinder> applyToken;
    parcel->readNullableStrongBinder(&applyToken);
    size_t count = static_cast<size_t>(parcel->readUint32());
    if (count > parcel->dataSize()) {
        return BAD_VALUE;
@@ -505,6 +507,7 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel
    mListenerCallbacks = listenerCallbacks;
    mComposerStates = composerStates;
    mInputWindowCommands = inputWindowCommands;
    mApplyToken = applyToken;
    return NO_ERROR;
}

@@ -532,6 +535,7 @@ status_t SurfaceComposerClient::Transaction::writeToParcel(Parcel* parcel) const
    parcel->writeInt64(mDesiredPresentTime);
    parcel->writeBool(mIsAutoTimestamp);
    parcel->writeInt64(mFrameTimelineVsyncId);
    parcel->writeStrongBinder(mApplyToken);
    parcel->writeUint32(static_cast<uint32_t>(mDisplayStates.size()));
    for (auto const& displayState : mDisplayStates) {
        displayState.write(*parcel);
@@ -607,6 +611,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Tr
    mEarlyWakeup = mEarlyWakeup || other.mEarlyWakeup;
    mExplicitEarlyWakeupStart = mExplicitEarlyWakeupStart || other.mExplicitEarlyWakeupStart;
    mExplicitEarlyWakeupEnd = mExplicitEarlyWakeupEnd || other.mExplicitEarlyWakeupEnd;
    mApplyToken = other.mApplyToken;

    // When merging vsync Ids we take the oldest one
    if (mFrameTimelineVsyncId != ISurfaceComposer::INVALID_VSYNC_ID &&
@@ -635,6 +640,7 @@ void SurfaceComposerClient::Transaction::clear() {
    mDesiredPresentTime = 0;
    mIsAutoTimestamp = true;
    mFrameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID;
    mApplyToken = nullptr;
}

void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) {
@@ -763,7 +769,10 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous) {
        flags |= ISurfaceComposer::eExplicitEarlyWakeupEnd;
    }

    sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
    sp<IBinder> applyToken = mApplyToken
            ? mApplyToken
            : IInterface::asBinder(TransactionCompletedListener::getIInstance());

    sf->setTransactionState(mFrameTimelineVsyncId, composerStates, displayStates, flags, applyToken,
                            mInputWindowCommands, mDesiredPresentTime, mIsAutoTimestamp,
                            {} /*uncacheBuffer - only set in doUncacheBufferTransaction*/,
@@ -1579,6 +1588,12 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setAutoR
    return *this;
}

SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setApplyToken(
        const sp<IBinder>& applyToken) {
    mApplyToken = applyToken;
    return *this;
}

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

DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
+4 −0
Original line number Diff line number Diff line
@@ -165,6 +165,10 @@ private:

    std::function<void(int64_t)> 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<IBinder> mApplyToken = new BBinder();
};

} // namespace android
+9 −0
Original line number Diff line number Diff line
@@ -388,6 +388,10 @@ public:
        // The vsync Id provided by Choreographer.getVsyncId
        int64_t mFrameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID;

        // If not null, transactions will be queued up using this token otherwise a common token
        // per process will be used.
        sp<IBinder> mApplyToken = nullptr;

        InputWindowCommands mInputWindowCommands;
        int mStatus = NO_ERROR;

@@ -555,6 +559,11 @@ public:
        // in shared buffer mode.
        Transaction& setAutoRefresh(const sp<SurfaceControl>& sc, bool autoRefresh);

        // Queues up transactions using this token in SurfaceFlinger.  By default, all transactions
        // from a client are placed on the same queue. This can be used to prevent multiple
        // transactions from blocking each other.
        Transaction& setApplyToken(const sp<IBinder>& token);

        status_t setDisplaySurface(const sp<IBinder>& token,
                const sp<IGraphicBufferProducer>& bufferProducer);

+61 −0
Original line number Diff line number Diff line
@@ -221,6 +221,32 @@ protected:
        return captureResults.result;
    }

    void queueBuffer(sp<IGraphicBufferProducer> igbp, uint8_t r, uint8_t g, uint8_t b,
                     nsecs_t presentTimeDelay) {
        int slot;
        sp<Fence> fence;
        sp<GraphicBuffer> buf;
        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_EQ(OK, igbp->requestBuffer(slot, &buf));

        uint32_t* bufData;
        buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
                  reinterpret_cast<void**>(&bufData));
        fillBuffer(bufData, Rect(buf->getWidth(), buf->getHeight() / 2), buf->getStride(), r, g, b);
        buf->unlock();

        IGraphicBufferProducer::QueueBufferOutput qbOutput;
        nsecs_t timestampNanos = systemTime() + presentTimeDelay;
        IGraphicBufferProducer::QueueBufferInput input(timestampNanos, false, HAL_DATASPACE_UNKNOWN,
                                                       Rect(mDisplayWidth, mDisplayHeight / 2),
                                                       NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
                                                       Fence::NO_FENCE);
        igbp->queueBuffer(slot, input, &qbOutput);
    }

    sp<SurfaceComposerClient> mClient;
    sp<ISurfaceComposer> mComposer;

@@ -528,6 +554,41 @@ TEST_F(BLASTBufferQueueTest, QueryNativeWindowQueuesToWindowComposer) {
    ASSERT_EQ(queuesToNativeWindow, 1);
}

TEST_F(BLASTBufferQueueTest, OutOfOrderTransactionTest) {
    sp<SurfaceControl> bgSurface =
            mClient->createSurface(String8("BGTest"), 0, 0, PIXEL_FORMAT_RGBA_8888,
                                   ISurfaceComposerClient::eFXSurfaceBufferState);
    ASSERT_NE(nullptr, bgSurface.get());
    Transaction t;
    t.setLayerStack(bgSurface, 0)
            .show(bgSurface)
            .setDataspace(bgSurface, ui::Dataspace::V0_SRGB)
            .setFrame(bgSurface, Rect(0, 0, mDisplayWidth, mDisplayHeight))
            .setLayer(bgSurface, std::numeric_limits<int32_t>::max() - 1)
            .apply();

    BLASTBufferQueueHelper slowAdapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
    sp<IGraphicBufferProducer> slowIgbProducer;
    setUpProducer(slowAdapter, slowIgbProducer);
    nsecs_t presentTimeDelay = std::chrono::nanoseconds(500ms).count();
    queueBuffer(slowIgbProducer, 0 /* r */, 0 /* g */, 0 /* b */, presentTimeDelay);

    BLASTBufferQueueHelper fastAdapter(bgSurface, mDisplayWidth, mDisplayHeight);
    sp<IGraphicBufferProducer> fastIgbProducer;
    setUpProducer(fastAdapter, fastIgbProducer);
    uint8_t r = 255;
    uint8_t g = 0;
    uint8_t b = 0;
    queueBuffer(fastIgbProducer, r, g, b, 0 /* presentTimeDelay */);
    fastAdapter.waitForCallbacks();

    // 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 BLASTBufferQueueTransformTest : public BLASTBufferQueueTest {
public:
    void test(uint32_t tr) {