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

Commit f2b0478f authored by Vishnu Nair's avatar Vishnu Nair Committed by Android (Google) Code Review
Browse files

Merge "SF: Add TransactionHandler"

parents 5a1e9348 59f6d2df
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -634,7 +634,7 @@ bool layer_state_t::hasBufferChanges() const {
}

bool layer_state_t::hasValidBuffer() const {
    return bufferData && (bufferData->buffer || bufferData->cachedBuffer.isValid());
    return bufferData && (bufferData->hasBuffer() || bufferData->cachedBuffer.isValid());
}

status_t layer_state_t::matrix22_t::write(Parcel& output) const {
+51 −0
Original line number Diff line number Diff line
/*
 * Copyright 2022 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 <gui/LayerState.h>

namespace android::fake {

// Class which exposes buffer properties from BufferData without holding on to an actual buffer
class BufferData : public android::BufferData {
public:
    BufferData(uint64_t bufferId, uint32_t width, uint32_t height, int32_t pixelFormat,
               uint64_t outUsage)
          : mBufferId(bufferId),
            mWidth(width),
            mHeight(height),
            mPixelFormat(pixelFormat),
            mOutUsage(outUsage) {}
    bool hasBuffer() const override { return mBufferId != 0; }
    bool hasSameBuffer(const android::BufferData& other) const override {
        return getId() == other.getId() && frameNumber == other.frameNumber;
    }
    uint32_t getWidth() const override { return mWidth; }
    uint32_t getHeight() const override { return mHeight; }
    uint64_t getId() const override { return mBufferId; }
    PixelFormat getPixelFormat() const override { return mPixelFormat; }
    uint64_t getUsage() const override { return mOutUsage; }

private:
    uint64_t mBufferId;
    uint32_t mWidth;
    uint32_t mHeight;
    int32_t mPixelFormat;
    uint64_t mOutUsage;
};

} // namespace android::fake
+1 −0
Original line number Diff line number Diff line
@@ -194,6 +194,7 @@ filegroup {
        "Tracing/TransactionTracing.cpp",
        "Tracing/TransactionProtoParser.cpp",
        "TransactionCallbackInvoker.cpp",
        "TransactionHandler.cpp",
        "TunnelModeEnabledReporter.cpp",
    ],
}
+3 −1
Original line number Diff line number Diff line
@@ -881,7 +881,9 @@ public:

    bool mPendingHWCDestroy{false};

    bool backpressureEnabled() { return mDrawingState.flags & layer_state_t::eEnableBackpressure; }
    bool backpressureEnabled() const {
        return mDrawingState.flags & layer_state_t::eEnableBackpressure;
    }

    bool setStretchEffect(const StretchEffect& effect);
    StretchEffect getStretchEffect() const;
+112 −220
Original line number Diff line number Diff line
@@ -129,14 +129,11 @@
#include "LayerVector.h"
#include "MutexUtils.h"
#include "NativeWindowSurface.h"
#include "RefreshRateOverlay.h"
#include "RegionSamplingThread.h"
#include "Scheduler/DispSyncSource.h"
#include "Scheduler/EventThread.h"
#include "Scheduler/LayerHistory.h"
#include "Scheduler/Scheduler.h"
#include "Scheduler/VsyncConfiguration.h"
#include "Scheduler/VsyncController.h"
#include "StartPropertySetThread.h"
#include "SurfaceFlingerProperties.h"
#include "SurfaceInterceptor.h"
@@ -747,6 +744,7 @@ chooseRenderEngineTypeViaSysProp() {
void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) {
    ALOGI(  "SurfaceFlinger's main thread ready to run. "
            "Initializing graphics H/W...");
    addTransactionReadyFilters();
    Mutex::Autolock lock(mStateLock);

    // Get a RenderEngine for the given display / config (can't fail)
@@ -3649,124 +3647,119 @@ void SurfaceFlinger::setTransactionFlags(uint32_t mask, TransactionSchedule sche
    }
}

int SurfaceFlinger::flushPendingTransactionQueues(
        std::vector<TransactionState>& transactions,
        std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
        bool tryApplyUnsignaled) {
    std::unordered_set<sp<IBinder>, SpHash<IBinder>> applyTokensWithUnsignaledTransactions;
    int transactionsPendingBarrier = 0;
    auto it = mPendingTransactionQueues.begin();
    while (it != mPendingTransactionQueues.end()) {
        auto& [applyToken, transactionQueue] = *it;
        while (!transactionQueue.empty()) {
            // if we are in LatchUnsignaledConfig::AutoSingleLayer
            // then we should have only one applyToken for processing.
            // so we can stop further transactions on this applyToken.
            if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::AutoSingleLayer &&
                !applyTokensWithUnsignaledTransactions.empty()) {
                ATRACE_NAME("stopTransactionProcessing");
                break;
TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyTimelineCheck(
        const TransactionHandler::TransactionFlushState& flushState) {
    using TransactionReadiness = TransactionHandler::TransactionReadiness;
    const auto& transaction = *flushState.transaction;
    ATRACE_FORMAT("transactionIsReadyToBeApplied vsyncId: %" PRId64,
                  transaction.frameTimelineInfo.vsyncId);
    TimePoint desiredPresentTime = TimePoint::fromNs(transaction.desiredPresentTime);
    // Do not present if the desiredPresentTime has not passed unless it is more than
    // one second in the future. We ignore timestamps more than 1 second in the future
    // for stability reasons.
    if (!transaction.isAutoTimestamp && desiredPresentTime >= mExpectedPresentTime &&
        desiredPresentTime < mExpectedPresentTime + 1s) {
        ATRACE_NAME("not current");
        return TransactionReadiness::NotReady;
    }

            auto& transaction = transactionQueue.front();
            const auto ready =
                    transactionIsReadyToBeApplied(transaction, transaction.frameTimelineInfo,
                                                  transaction.isAutoTimestamp,
                                                  TimePoint::fromNs(transaction.desiredPresentTime),
                                                  transaction.originUid, transaction.states,
                                                  bufferLayersReadyToPresent, transactions.size(),
                                                  tryApplyUnsignaled);
            ATRACE_INT("TransactionReadiness", static_cast<int>(ready));
            if (ready == TransactionReadiness::NotReady) {
                setTransactionFlags(eTransactionFlushNeeded);
                break;
    if (!mScheduler->isVsyncValid(mExpectedPresentTime, transaction.originUid)) {
        ATRACE_NAME("!isVsyncValid");
        return TransactionReadiness::NotReady;
    }
            if (ready == TransactionReadiness::NotReadyBarrier) {
                transactionsPendingBarrier++;
                setTransactionFlags(eTransactionFlushNeeded);
                break;

    // If the client didn't specify desiredPresentTime, use the vsyncId to determine the
    // expected present time of this transaction.
    if (transaction.isAutoTimestamp &&
        frameIsEarly(mExpectedPresentTime, VsyncId{transaction.frameTimelineInfo.vsyncId})) {
        ATRACE_NAME("frameIsEarly");
        return TransactionReadiness::NotReady;
    }
            transaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
                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<uint64_t>::max();
    return TransactionReadiness::Ready;
}

TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferCheck(
        const TransactionHandler::TransactionFlushState& flushState) {
    using TransactionReadiness = TransactionHandler::TransactionReadiness;
    auto ready = TransactionReadiness::Ready;
    flushState.transaction->traverseStatesWithBuffersWhileTrue([&](const layer_state_t& s) -> bool {
        sp<Layer> layer = Layer::fromHandle(s.surface).promote();
        const auto& transaction = *flushState.transaction;
        // check for barrier frames
        if (s.bufferData->hasBarrier &&
            ((layer->getDrawingState().frameNumber) < s.bufferData->barrierFrameNumber)) {
            const bool willApplyBarrierFrame =
                    flushState.bufferLayersReadyToPresent.contains(s.surface.get()) &&
                    (flushState.bufferLayersReadyToPresent.get(s.surface.get()) >=
                     s.bufferData->barrierFrameNumber);
            if (!willApplyBarrierFrame) {
                ATRACE_NAME("NotReadyBarrier");
                ready = TransactionReadiness::NotReadyBarrier;
                return false;
            }
            });
            const bool appliedUnsignaled = (ready == TransactionReadiness::ReadyUnsignaled);
            if (appliedUnsignaled) {
                applyTokensWithUnsignaledTransactions.insert(transaction.applyToken);
        }

            transactions.emplace_back(std::move(transaction));
            transactionQueue.pop();
            mPendingTransactionCount--;
            ATRACE_INT("TransactionQueue", mPendingTransactionCount.load());
        // If backpressure is enabled and we already have a buffer to commit, keep
        // the transaction in the queue.
        const bool hasPendingBuffer =
                flushState.bufferLayersReadyToPresent.contains(s.surface.get());
        if (layer->backpressureEnabled() && hasPendingBuffer && transaction.isAutoTimestamp) {
            ATRACE_NAME("hasPendingBuffer");
            ready = TransactionReadiness::NotReady;
            return false;
        }

        if (transactionQueue.empty()) {
            it = mPendingTransactionQueues.erase(it);
        } else {
            it = std::next(it, 1);
        // check fence status
        const bool allowLatchUnsignaled = shouldLatchUnsignaled(layer, s, transaction.states.size(),
                                                                flushState.firstTransaction);
        ATRACE_FORMAT("%s allowLatchUnsignaled=%s", layer->getName().c_str(),
                      allowLatchUnsignaled ? "true" : "false");

        const bool acquireFenceChanged = s.bufferData &&
                s.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged) &&
                s.bufferData->acquireFence;
        const bool fenceSignaled =
                (!acquireFenceChanged ||
                 s.bufferData->acquireFence->getStatus() != Fence::Status::Unsignaled);
        if (!fenceSignaled) {
            if (!allowLatchUnsignaled) {
                ready = TransactionReadiness::NotReady;
                auto& listener = s.bufferData->releaseBufferListener;
                if (listener &&
                    (flushState.queueProcessTime - transaction.postTime) >
                            std::chrono::nanoseconds(4s).count()) {
                    mTransactionHandler.onTransactionQueueStalled(transaction, listener);
                }
                return false;
            }

            ready = enableLatchUnsignaledConfig == LatchUnsignaledConfig::AutoSingleLayer
                    ? TransactionReadiness::ReadyUnsignaledSingle
                    : TransactionReadiness::ReadyUnsignaled;
        }
    return transactionsPendingBarrier;
        return true;
    });
    ATRACE_INT("TransactionReadiness", static_cast<int>(ready));
    return ready;
}

void SurfaceFlinger::addTransactionReadyFilters() {
    mTransactionHandler.addTransactionReadyFilter(
            std::bind(&SurfaceFlinger::transactionReadyTimelineCheck, this, std::placeholders::_1));
    mTransactionHandler.addTransactionReadyFilter(
            std::bind(&SurfaceFlinger::transactionReadyBufferCheck, this, std::placeholders::_1));
}

bool SurfaceFlinger::flushTransactionQueues(VsyncId vsyncId) {
    // to prevent onHandleDestroyed from being called while the lock is held,
    // we must keep a copy of the transactions (specifically the composer
    // states) around outside the scope of the lock
    std::vector<TransactionState> transactions;
    // Layer handles that have transactions with buffers that are ready to be applied.
    std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>> bufferLayersReadyToPresent;
    std::vector<TransactionState> transactions = mTransactionHandler.flushTransactions();
    {
        Mutex::Autolock _l(mStateLock);
        {
            while (!mLocklessTransactionQueue.isEmpty()) {
                auto maybeTransaction = mLocklessTransactionQueue.pop();
                if (!maybeTransaction.has_value()) {
                    break;
                }
                auto transaction = maybeTransaction.value();
                mPendingTransactionQueues[transaction.applyToken].push(std::move(transaction));
            }

            // 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.
            int lastTransactionsPendingBarrier = 0;
            int transactionsPendingBarrier = 0;
            do {
                lastTransactionsPendingBarrier = transactionsPendingBarrier;
                transactionsPendingBarrier =
                        flushPendingTransactionQueues(transactions, bufferLayersReadyToPresent,
                                                      /*tryApplyUnsignaled*/ false);
            } while (lastTransactionsPendingBarrier != transactionsPendingBarrier);

            // 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.
            if (enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) {
                flushPendingTransactionQueues(transactions, bufferLayersReadyToPresent,
                                              /*tryApplyUnsignaled*/ true);
            }

        return applyTransactions(transactions, vsyncId);
    }
}
}

bool SurfaceFlinger::applyTransactions(std::vector<TransactionState>& transactions,
                                       VsyncId vsyncId) {
@@ -3791,7 +3784,7 @@ bool SurfaceFlinger::applyTransactions(std::vector<TransactionState>& transactio
}

bool SurfaceFlinger::transactionFlushNeeded() {
    return !mPendingTransactionQueues.empty() || !mLocklessTransactionQueue.isEmpty();
    return mTransactionHandler.hasPendingTransactions();
}

bool SurfaceFlinger::frameIsEarly(TimePoint expectedPresentTime, VsyncId vsyncId) const {
@@ -3817,7 +3810,7 @@ bool SurfaceFlinger::frameIsEarly(TimePoint expectedPresentTime, VsyncId vsyncId
}

bool SurfaceFlinger::shouldLatchUnsignaled(const sp<Layer>& layer, const layer_state_t& state,
                                           size_t numStates, size_t totalTXapplied) const {
                                           size_t numStates, bool firstTransaction) const {
    if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Disabled) {
        ALOGV("%s: false (LatchUnsignaledConfig::Disabled)", __func__);
        return false;
@@ -3836,9 +3829,9 @@ bool SurfaceFlinger::shouldLatchUnsignaled(const sp<Layer>& layer, const layer_s
    }

    if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::AutoSingleLayer) {
        if (totalTXapplied > 0) {
            ALOGV("%s: false (LatchUnsignaledConfig::AutoSingleLayer; totalTXapplied=%zu)",
                  __func__, totalTXapplied);
        if (!firstTransaction) {
            ALOGV("%s: false (LatchUnsignaledConfig::AutoSingleLayer; not first transaction)",
                  __func__);
            return false;
        }

@@ -3862,116 +3855,6 @@ bool SurfaceFlinger::shouldLatchUnsignaled(const sp<Layer>& layer, const layer_s
    return true;
}

auto SurfaceFlinger::transactionIsReadyToBeApplied(
        TransactionState& transaction, const FrameTimelineInfo& info, bool isAutoTimestamp,
        TimePoint desiredPresentTime, uid_t originUid, const Vector<ComposerState>& states,
        const std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>&
                bufferLayersReadyToPresent,
        size_t totalTXapplied, bool tryApplyUnsignaled) const -> TransactionReadiness {
    ATRACE_FORMAT("transactionIsReadyToBeApplied vsyncId: %" PRId64, info.vsyncId);
    // Do not present if the desiredPresentTime has not passed unless it is more than one second
    // in the future. We ignore timestamps more than 1 second in the future for stability reasons.
    if (!isAutoTimestamp && desiredPresentTime >= mExpectedPresentTime &&
        desiredPresentTime < mExpectedPresentTime + 1s) {
        ATRACE_NAME("not current");
        return TransactionReadiness::NotReady;
    }

    if (!mScheduler->isVsyncValid(mExpectedPresentTime, originUid)) {
        ATRACE_NAME("!isVsyncValid");
        return TransactionReadiness::NotReady;
    }

    // If the client didn't specify desiredPresentTime, use the vsyncId to determine the expected
    // present time of this transaction.
    if (isAutoTimestamp && frameIsEarly(mExpectedPresentTime, VsyncId{info.vsyncId})) {
        ATRACE_NAME("frameIsEarly");
        return TransactionReadiness::NotReady;
    }

    bool fenceUnsignaled = false;
    auto queueProcessTime = systemTime();
    for (const ComposerState& state : states) {
        const layer_state_t& s = state.state;

        sp<Layer> layer = nullptr;
        if (s.surface) {
            layer = fromHandle(s.surface).promote();
        } else if (s.hasBufferChanges()) {
            ALOGW("Transaction with buffer, but no Layer?");
            continue;
        }
        if (!layer) {
            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(),
                      allowLatchUnsignaled ? "true" : "false");

        const bool acquireFenceChanged = s.bufferData &&
                s.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged) &&
                s.bufferData->acquireFence;
        fenceUnsignaled = fenceUnsignaled ||
                (acquireFenceChanged &&
                 s.bufferData->acquireFence->getStatus() == Fence::Status::Unsignaled);

        if (fenceUnsignaled && !allowLatchUnsignaled) {
            if (!transaction.sentFenceTimeoutWarning &&
                queueProcessTime - transaction.postTime > std::chrono::nanoseconds(4s).count()) {
                transaction.sentFenceTimeoutWarning = true;
                auto listener = s.bufferData->releaseBufferListener;
                if (listener) {
                    listener->onTransactionQueueStalled();
                }
            }

            ATRACE_NAME("fence unsignaled");
            return TransactionReadiness::NotReady;
        }

        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();
            if (layer->backpressureEnabled() && hasPendingBuffer && isAutoTimestamp) {
                ATRACE_NAME("hasPendingBuffer");
                return TransactionReadiness::NotReady;
            }
        }
    }
    return fenceUnsignaled ? TransactionReadiness::ReadyUnsignaled : TransactionReadiness::Ready;
}

void SurfaceFlinger::queueTransaction(TransactionState& state) {
    mLocklessTransactionQueue.push(state);
    mPendingTransactionCount++;
    ATRACE_INT("TransactionQueue", mPendingTransactionCount.load());

    const auto schedule = [](uint32_t flags) {
        if (flags & eEarlyWakeupEnd) return TransactionSchedule::EarlyEnd;
        if (flags & eEarlyWakeupStart) return TransactionSchedule::EarlyStart;
        return TransactionSchedule::Late;
    }(state.flags);

    const auto frameHint = state.isFrameActive() ? FrameHint::kActive : FrameHint::kNone;

    setTransactionFlags(eTransactionFlushNeeded, schedule, state.applyToken, frameHint);
}

status_t SurfaceFlinger::setTransactionState(
        const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& states,
        const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
@@ -4022,7 +3905,16 @@ status_t SurfaceFlinger::setTransactionState(
    if (mTransactionTracing) {
        mTransactionTracing->addQueuedTransaction(state);
    }
    queueTransaction(state);

    const auto schedule = [](uint32_t flags) {
        if (flags & eEarlyWakeupEnd) return TransactionSchedule::EarlyEnd;
        if (flags & eEarlyWakeupStart) return TransactionSchedule::EarlyStart;
        return TransactionSchedule::Late;
    }(state.flags);

    const auto frameHint = state.isFrameActive() ? FrameHint::kActive : FrameHint::kNone;
    setTransactionFlags(eTransactionFlushNeeded, schedule, state.applyToken, frameHint);
    mTransactionHandler.queueTransaction(std::move(state));

    return NO_ERROR;
}
Loading