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

Commit d56514e9 authored by Pascal Mütschard's avatar Pascal Mütschard
Browse files

Jank callback API refactor.

Removes the old work-arounds for missing jank callbacks.
Removes the jank data from the transaction completed callback.
Adds new function to ISurfaceComposer to register jank listeners.

With the new API, jank data is only sent over binder periodically
(every ~50 frames) and on a background thread. It is also only tracked
for layers where there is a listener registered.

Test: manual, libsurfaceflinger_unittest
Bug: http://b/336461947
Flag: EXEMPT refactor
Change-Id: I3238ce604571832523525cf098832c7352879826
parent 293ea2be
Loading
Loading
Loading
Loading
+2 −46
Original line number Diff line number Diff line
@@ -43,12 +43,6 @@ enum class Tag : uint32_t {

} // Anonymous namespace

namespace { // Anonymous

constexpr int32_t kSerializedCallbackTypeOnCompelteWithJankData = 2;

} // Anonymous namespace

status_t FrameEventHistoryStats::writeToParcel(Parcel* output) const {
    status_t err = output->writeUint64(frameNumber);
    if (err != NO_ERROR) return err;
@@ -119,23 +113,6 @@ status_t FrameEventHistoryStats::readFromParcel(const Parcel* input) {
    return err;
}

JankData::JankData()
      : frameVsyncId(FrameTimelineInfo::INVALID_VSYNC_ID), jankType(JankType::None) {}

status_t JankData::writeToParcel(Parcel* output) const {
    SAFE_PARCEL(output->writeInt64, frameVsyncId);
    SAFE_PARCEL(output->writeInt32, jankType);
    SAFE_PARCEL(output->writeInt64, frameIntervalNs);
    return NO_ERROR;
}

status_t JankData::readFromParcel(const Parcel* input) {
    SAFE_PARCEL(input->readInt64, &frameVsyncId);
    SAFE_PARCEL(input->readInt32, &jankType);
    SAFE_PARCEL(input->readInt64, &frameIntervalNs);
    return NO_ERROR;
}

status_t SurfaceStats::writeToParcel(Parcel* output) const {
    SAFE_PARCEL(output->writeStrongBinder, surfaceControl);
    if (const auto* acquireFence = std::get_if<sp<Fence>>(&acquireTimeOrFence)) {
@@ -160,10 +137,6 @@ status_t SurfaceStats::writeToParcel(Parcel* output) const {

    SAFE_PARCEL(output->writeUint32, currentMaxAcquiredBufferCount);
    SAFE_PARCEL(output->writeParcelable, eventStats);
    SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(jankData.size()));
    for (const auto& data : jankData) {
        SAFE_PARCEL(output->writeParcelable, data);
    }
    SAFE_PARCEL(output->writeParcelable, previousReleaseCallbackId);
    return NO_ERROR;
}
@@ -200,13 +173,6 @@ status_t SurfaceStats::readFromParcel(const Parcel* input) {
    SAFE_PARCEL(input->readUint32, &currentMaxAcquiredBufferCount);
    SAFE_PARCEL(input->readParcelable, &eventStats);

    int32_t jankData_size = 0;
    SAFE_PARCEL_READ_SIZE(input->readInt32, &jankData_size, input->dataSize());
    for (int i = 0; i < jankData_size; i++) {
        JankData data;
        SAFE_PARCEL(input->readParcelable, &data);
        jankData.push_back(data);
    }
    SAFE_PARCEL(input->readParcelable, &previousReleaseCallbackId);
    return NO_ERROR;
}
@@ -371,11 +337,7 @@ ListenerCallbacks ListenerCallbacks::filter(CallbackId::Type type) const {

status_t CallbackId::writeToParcel(Parcel* output) const {
    SAFE_PARCEL(output->writeInt64, id);
    if (type == Type::ON_COMPLETE && includeJankData) {
        SAFE_PARCEL(output->writeInt32, kSerializedCallbackTypeOnCompelteWithJankData);
    } else {
    SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(type));
    }
    return NO_ERROR;
}

@@ -383,13 +345,7 @@ status_t CallbackId::readFromParcel(const Parcel* input) {
    SAFE_PARCEL(input->readInt64, &id);
    int32_t typeAsInt;
    SAFE_PARCEL(input->readInt32, &typeAsInt);
    if (typeAsInt == kSerializedCallbackTypeOnCompelteWithJankData) {
        type = Type::ON_COMPLETE;
        includeJankData = true;
    } else {
    type = static_cast<CallbackId::Type>(typeAsInt);
        includeJankData = false;
    }
    return NO_ERROR;
}

+168 −56
Original line number Diff line number Diff line
@@ -86,7 +86,8 @@ int64_t generateId() {
    return (((int64_t)getpid()) << 32) | ++idCounter;
}

void emptyCallback(nsecs_t, const sp<Fence>&, const std::vector<SurfaceControlStats>&) {}
constexpr int64_t INVALID_VSYNC = -1;

} // namespace

const std::string SurfaceComposerClient::kEmpty{};
@@ -207,9 +208,168 @@ sp<SurfaceComposerClient> SurfaceComposerClient::getDefault() {
    return DefaultComposerClient::getComposerClient();
}

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

JankDataListener::~JankDataListener() {
}

status_t JankDataListener::flushJankData() {
    if (mLayerId == -1) {
        return INVALID_OPERATION;
    }

    binder::Status status = ComposerServiceAIDL::getComposerService()->flushJankData(mLayerId);
    return statusTFromBinderStatus(status);
}

std::mutex JankDataListenerFanOut::sFanoutInstanceMutex;
std::unordered_map<int32_t, sp<JankDataListenerFanOut>> JankDataListenerFanOut::sFanoutInstances;

binder::Status JankDataListenerFanOut::onJankData(const std::vector<gui::JankData>& jankData) {
    // Find the highest VSync ID.
    int64_t lastVsync = jankData.empty()
            ? 0
            : std::max_element(jankData.begin(), jankData.end(),
                               [](const gui::JankData& jd1, const gui::JankData& jd2) {
                                   return jd1.frameVsyncId < jd2.frameVsyncId;
                               })
                      ->frameVsyncId;

    // Fan out the jank data callback.
    std::vector<wp<JankDataListener>> listenersToRemove;
    for (auto listener : getActiveListeners()) {
        if (!listener->onJankDataAvailable(jankData) ||
            (listener->mRemoveAfter >= 0 && listener->mRemoveAfter <= lastVsync)) {
            listenersToRemove.push_back(listener);
        }
    }

    return removeListeners(listenersToRemove)
            ? binder::Status::ok()
            : binder::Status::fromExceptionCode(binder::Status::EX_NULL_POINTER);
}

status_t JankDataListenerFanOut::addListener(sp<SurfaceControl> sc, sp<JankDataListener> listener) {
    sp<IBinder> layer = sc->getHandle();
    if (layer == nullptr) {
        return UNEXPECTED_NULL;
    }
    int32_t layerId = sc->getLayerId();

    sFanoutInstanceMutex.lock();
    auto it = sFanoutInstances.find(layerId);
    bool registerNeeded = it == sFanoutInstances.end();
    sp<JankDataListenerFanOut> fanout;
    if (registerNeeded) {
        fanout = sp<JankDataListenerFanOut>::make(layerId);
        sFanoutInstances.insert({layerId, fanout});
    } else {
        fanout = it->second;
    }

    fanout->mMutex.lock();
    fanout->mListeners.insert(listener);
    fanout->mMutex.unlock();

    sFanoutInstanceMutex.unlock();

    if (registerNeeded) {
        binder::Status status =
                ComposerServiceAIDL::getComposerService()->addJankListener(layer, fanout);
        return statusTFromBinderStatus(status);
    }
    return OK;
}

status_t JankDataListenerFanOut::removeListener(sp<JankDataListener> listener) {
    int32_t layerId = listener->mLayerId;
    if (layerId == -1) {
        return INVALID_OPERATION;
    }

    int64_t removeAfter = INVALID_VSYNC;
    sp<JankDataListenerFanOut> fanout;

    sFanoutInstanceMutex.lock();
    auto it = sFanoutInstances.find(layerId);
    if (it != sFanoutInstances.end()) {
        fanout = it->second;
        removeAfter = fanout->updateAndGetRemovalVSync();
    }

    if (removeAfter != INVALID_VSYNC) {
        // Remove this instance from the map, so that no new listeners are added
        // while we're scheduled to be removed.
        sFanoutInstances.erase(layerId);
    }
    sFanoutInstanceMutex.unlock();

    if (removeAfter < 0) {
        return OK;
    }

    binder::Status status =
            ComposerServiceAIDL::getComposerService()->removeJankListener(layerId, fanout,
                                                                          removeAfter);
    return statusTFromBinderStatus(status);
}

std::vector<sp<JankDataListener>> JankDataListenerFanOut::getActiveListeners() {
    std::scoped_lock<std::mutex> lock(mMutex);

    std::vector<sp<JankDataListener>> listeners;
    for (auto it = mListeners.begin(); it != mListeners.end();) {
        auto listener = it->promote();
        if (!listener) {
            it = mListeners.erase(it);
        } else {
            listeners.push_back(std::move(listener));
            it++;
        }
    }
    return listeners;
}

bool JankDataListenerFanOut::removeListeners(const std::vector<wp<JankDataListener>>& listeners) {
    std::scoped_lock<std::mutex> fanoutLock(sFanoutInstanceMutex);
    std::scoped_lock<std::mutex> listenersLock(mMutex);

    for (auto listener : listeners) {
        mListeners.erase(listener);
    }

    if (mListeners.empty()) {
        sFanoutInstances.erase(mLayerId);
        return false;
    }
    return true;
}

int64_t JankDataListenerFanOut::updateAndGetRemovalVSync() {
    std::scoped_lock<std::mutex> lock(mMutex);
    if (mRemoveAfter >= 0) {
        // We've already been scheduled to be removed. Don't schedule again.
        return INVALID_VSYNC;
    }

    int64_t removeAfter = 0;
    for (auto it = mListeners.begin(); it != mListeners.end();) {
        auto listener = it->promote();
        if (!listener) {
            it = mListeners.erase(it);
        } else if (listener->mRemoveAfter < 0) {
            // We have at least one listener that's still interested. Don't remove.
            return INVALID_VSYNC;
        } else {
            removeAfter = std::max(removeAfter, listener->mRemoveAfter);
            it++;
        }
    }

    mRemoveAfter = removeAfter;
    return removeAfter;
}

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

// TransactionCompletedListener does not use ANDROID_SINGLETON_STATIC_INSTANCE because it needs
@@ -256,14 +416,6 @@ CallbackId TransactionCompletedListener::addCallbackFunction(
                surfaceControls,
        CallbackId::Type callbackType) {
    std::lock_guard<std::mutex> lock(mMutex);
    return addCallbackFunctionLocked(callbackFunction, surfaceControls, callbackType);
}

CallbackId TransactionCompletedListener::addCallbackFunctionLocked(
        const TransactionCompletedCallback& callbackFunction,
        const std::unordered_set<sp<SurfaceControl>, SurfaceComposerClient::SCHash>&
                surfaceControls,
        CallbackId::Type callbackType) {
    startListeningLocked();

    CallbackId callbackId(getNextIdLocked(), callbackType);
@@ -272,33 +424,11 @@ CallbackId TransactionCompletedListener::addCallbackFunctionLocked(

    for (const auto& surfaceControl : surfaceControls) {
        callbackSurfaceControls[surfaceControl->getHandle()] = surfaceControl;

        if (callbackType == CallbackId::Type::ON_COMPLETE &&
            mJankListeners.count(surfaceControl->getLayerId()) != 0) {
            callbackId.includeJankData = true;
        }
    }

    return callbackId;
}

void TransactionCompletedListener::addJankListener(const sp<JankDataListener>& listener,
                                                   sp<SurfaceControl> surfaceControl) {
    std::lock_guard<std::mutex> lock(mMutex);
    mJankListeners.insert({surfaceControl->getLayerId(), listener});
}

void TransactionCompletedListener::removeJankListener(const sp<JankDataListener>& listener) {
    std::lock_guard<std::mutex> lock(mMutex);
    for (auto it = mJankListeners.begin(); it != mJankListeners.end();) {
        if (it->second == listener) {
            it = mJankListeners.erase(it);
        } else {
            it++;
        }
    }
}

void TransactionCompletedListener::setReleaseBufferCallback(const ReleaseCallbackId& callbackId,
                                                            ReleaseBufferCallback listener) {
    std::scoped_lock<std::mutex> lock(mMutex);
@@ -325,32 +455,20 @@ void TransactionCompletedListener::removeSurfaceStatsListener(void* context, voi
}

void TransactionCompletedListener::addSurfaceControlToCallbacks(
        SurfaceComposerClient::CallbackInfo& callbackInfo,
        const sp<SurfaceControl>& surfaceControl) {
        const sp<SurfaceControl>& surfaceControl,
        const std::unordered_set<CallbackId, CallbackIdHash>& callbackIds) {
    std::lock_guard<std::mutex> lock(mMutex);

    bool includingJankData = false;
    for (auto callbackId : callbackInfo.callbackIds) {
    for (auto callbackId : callbackIds) {
        mCallbacks[callbackId].surfaceControls.emplace(std::piecewise_construct,
                                                       std::forward_as_tuple(
                                                               surfaceControl->getHandle()),
                                                       std::forward_as_tuple(surfaceControl));
        includingJankData = includingJankData || callbackId.includeJankData;
    }

    // If no registered callback is requesting jank data, but there is a jank listener registered
    // on the new surface control, add a synthetic callback that requests the jank data.
    if (!includingJankData && mJankListeners.count(surfaceControl->getLayerId()) != 0) {
        CallbackId callbackId =
                addCallbackFunctionLocked(&emptyCallback, callbackInfo.surfaceControls,
                                          CallbackId::Type::ON_COMPLETE);
        callbackInfo.callbackIds.emplace(callbackId);
    }
}

void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
    std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> callbacksMap;
    std::multimap<int32_t, sp<JankDataListener>> jankListenersMap;
    {
        std::lock_guard<std::mutex> lock(mMutex);

@@ -366,7 +484,6 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener
         * sp<SurfaceControl> that could possibly exist for the callbacks.
         */
        callbacksMap = mCallbacks;
        jankListenersMap = mJankListeners;
        for (const auto& transactionStats : listenerStats.transactionStats) {
            for (auto& callbackId : transactionStats.callbackIds) {
                mCallbacks.erase(callbackId);
@@ -486,12 +603,6 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener
                        transactionStats.presentFence, surfaceStats);
                }
            }

            if (surfaceStats.jankData.empty()) continue;
            auto jankRange = jankListenersMap.equal_range(layerId);
            for (auto it = jankRange.first; it != jankRange.second; it++) {
                it->second->onJankDataAvailable(surfaceStats.jankData);
            }
        }
    }
}
@@ -1004,8 +1115,9 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Tr

        // register all surface controls for all callbackIds for this listener that is merging
        for (const auto& surfaceControl : currentProcessCallbackInfo.surfaceControls) {
            mTransactionCompletedListener->addSurfaceControlToCallbacks(currentProcessCallbackInfo,
                                                                        surfaceControl);
            mTransactionCompletedListener
                    ->addSurfaceControlToCallbacks(surfaceControl,
                                                   currentProcessCallbackInfo.callbackIds);
        }
    }

@@ -1361,7 +1473,7 @@ void SurfaceComposerClient::Transaction::registerSurfaceControlForCallback(
    auto& callbackInfo = mListenerCallbacks[TransactionCompletedListener::getIInstance()];
    callbackInfo.surfaceControls.insert(sc);

    mTransactionCompletedListener->addSurfaceControlToCallbacks(callbackInfo, sc);
    mTransactionCompletedListener->addSurfaceControlToCallbacks(sc, callbackInfo.callbackIds);
}

SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setPosition(
+29 −0
Original line number Diff line number Diff line
/*
 * Copyright 2024 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.
 */

package android.gui;

import android.gui.JankData;

/** @hide */
interface IJankListener {

  /**
   * Callback reporting jank data of the most recent frames.
   * @See {@link ISurfaceComposer#addJankListener(IBinder, IJankListener)}
   */
  void onJankData(in JankData[] data);
}
+19 −0
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@ import android.gui.ISurfaceComposerClient;
import android.gui.ITunnelModeEnabledListener;
import android.gui.IWindowInfosListener;
import android.gui.IWindowInfosPublisher;
import android.gui.IJankListener;
import android.gui.LayerCaptureArgs;
import android.gui.OverlayProperties;
import android.gui.PullAtomData;
@@ -580,4 +581,22 @@ interface ISurfaceComposer {
     * This method should not block the ShutdownThread therefore it's handled asynchronously.
     */
    oneway void notifyShutdown();

    /**
     * Registers the jank listener on the given layer to receive jank data of future frames.
     */
    void addJankListener(IBinder layer, IJankListener listener);

    /**
     * Flushes any pending jank data on the given layer to any registered listeners on that layer.
     */
    oneway void flushJankData(int layerId);

    /**
     * Schedules the removal of the jank listener from the given layer after the VSync with the
     * specified ID. Use a value <= 0 for afterVsync to remove the listener immediately. The given
     * listener will not be removed before the given VSync, but may still receive data for frames
     * past the provided VSync.
     */
    oneway void removeJankListener(int layerId, IJankListener listener, long afterVsync);
}
+35 −0
Original line number Diff line number Diff line
/*
 * Copyright 2024 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.
 */

 package android.gui;

 /** @hide */
parcelable JankData {
  /**
   * Identifier for the frame submitted with Transaction.setFrameTimelineVsyncId
   */
  long frameVsyncId;

  /**
   * Bitmask of jank types that occurred.
   */
  int jankType;

  /**
   * Expected duration in nanoseconds of this frame.
   */
  long frameIntervalNs;
}
Loading