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

Commit f3e02515 authored by Pascal Mütschard's avatar Pascal Mütschard Committed by Android (Google) Code Review
Browse files

Merge "Jank callback API refactor." into main

parents 6da010d8 d56514e9
Loading
Loading
Loading
Loading
+2 −46
Original line number Original line Diff line number Diff line
@@ -43,12 +43,6 @@ enum class Tag : uint32_t {


} // Anonymous namespace
} // Anonymous namespace


namespace { // Anonymous

constexpr int32_t kSerializedCallbackTypeOnCompelteWithJankData = 2;

} // Anonymous namespace

status_t FrameEventHistoryStats::writeToParcel(Parcel* output) const {
status_t FrameEventHistoryStats::writeToParcel(Parcel* output) const {
    status_t err = output->writeUint64(frameNumber);
    status_t err = output->writeUint64(frameNumber);
    if (err != NO_ERROR) return err;
    if (err != NO_ERROR) return err;
@@ -119,23 +113,6 @@ status_t FrameEventHistoryStats::readFromParcel(const Parcel* input) {
    return err;
    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 {
status_t SurfaceStats::writeToParcel(Parcel* output) const {
    SAFE_PARCEL(output->writeStrongBinder, surfaceControl);
    SAFE_PARCEL(output->writeStrongBinder, surfaceControl);
    if (const auto* acquireFence = std::get_if<sp<Fence>>(&acquireTimeOrFence)) {
    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->writeUint32, currentMaxAcquiredBufferCount);
    SAFE_PARCEL(output->writeParcelable, eventStats);
    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);
    SAFE_PARCEL(output->writeParcelable, previousReleaseCallbackId);
    return NO_ERROR;
    return NO_ERROR;
}
}
@@ -200,13 +173,6 @@ status_t SurfaceStats::readFromParcel(const Parcel* input) {
    SAFE_PARCEL(input->readUint32, &currentMaxAcquiredBufferCount);
    SAFE_PARCEL(input->readUint32, &currentMaxAcquiredBufferCount);
    SAFE_PARCEL(input->readParcelable, &eventStats);
    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);
    SAFE_PARCEL(input->readParcelable, &previousReleaseCallbackId);
    return NO_ERROR;
    return NO_ERROR;
}
}
@@ -371,11 +337,7 @@ ListenerCallbacks ListenerCallbacks::filter(CallbackId::Type type) const {


status_t CallbackId::writeToParcel(Parcel* output) const {
status_t CallbackId::writeToParcel(Parcel* output) const {
    SAFE_PARCEL(output->writeInt64, id);
    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));
    SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(type));
    }
    return NO_ERROR;
    return NO_ERROR;
}
}


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


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


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

} // namespace
} // namespace


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


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

JankDataListener::~JankDataListener() {
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
// TransactionCompletedListener does not use ANDROID_SINGLETON_STATIC_INSTANCE because it needs
@@ -256,14 +416,6 @@ CallbackId TransactionCompletedListener::addCallbackFunction(
                surfaceControls,
                surfaceControls,
        CallbackId::Type callbackType) {
        CallbackId::Type callbackType) {
    std::lock_guard<std::mutex> lock(mMutex);
    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();
    startListeningLocked();


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


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

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


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


void TransactionCompletedListener::addSurfaceControlToCallbacks(
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);
    std::lock_guard<std::mutex> lock(mMutex);


    bool includingJankData = false;
    for (auto callbackId : callbackIds) {
    for (auto callbackId : callbackInfo.callbackIds) {
        mCallbacks[callbackId].surfaceControls.emplace(std::piecewise_construct,
        mCallbacks[callbackId].surfaceControls.emplace(std::piecewise_construct,
                                                       std::forward_as_tuple(
                                                       std::forward_as_tuple(
                                                               surfaceControl->getHandle()),
                                                               surfaceControl->getHandle()),
                                                       std::forward_as_tuple(surfaceControl));
                                                       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) {
void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
    std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> callbacksMap;
    std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> callbacksMap;
    std::multimap<int32_t, sp<JankDataListener>> jankListenersMap;
    {
    {
        std::lock_guard<std::mutex> lock(mMutex);
        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.
         * sp<SurfaceControl> that could possibly exist for the callbacks.
         */
         */
        callbacksMap = mCallbacks;
        callbacksMap = mCallbacks;
        jankListenersMap = mJankListeners;
        for (const auto& transactionStats : listenerStats.transactionStats) {
        for (const auto& transactionStats : listenerStats.transactionStats) {
            for (auto& callbackId : transactionStats.callbackIds) {
            for (auto& callbackId : transactionStats.callbackIds) {
                mCallbacks.erase(callbackId);
                mCallbacks.erase(callbackId);
@@ -486,12 +603,6 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener
                        transactionStats.presentFence, surfaceStats);
                        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
        // register all surface controls for all callbackIds for this listener that is merging
        for (const auto& surfaceControl : currentProcessCallbackInfo.surfaceControls) {
        for (const auto& surfaceControl : currentProcessCallbackInfo.surfaceControls) {
            mTransactionCompletedListener->addSurfaceControlToCallbacks(currentProcessCallbackInfo,
            mTransactionCompletedListener
                                                                        surfaceControl);
                    ->addSurfaceControlToCallbacks(surfaceControl,
                                                   currentProcessCallbackInfo.callbackIds);
        }
        }
    }
    }


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


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


SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setPosition(
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setPosition(
+29 −0
Original line number Original line 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 Original line Diff line number Diff line
@@ -42,6 +42,7 @@ import android.gui.ISurfaceComposerClient;
import android.gui.ITunnelModeEnabledListener;
import android.gui.ITunnelModeEnabledListener;
import android.gui.IWindowInfosListener;
import android.gui.IWindowInfosListener;
import android.gui.IWindowInfosPublisher;
import android.gui.IWindowInfosPublisher;
import android.gui.IJankListener;
import android.gui.LayerCaptureArgs;
import android.gui.LayerCaptureArgs;
import android.gui.OverlayProperties;
import android.gui.OverlayProperties;
import android.gui.PullAtomData;
import android.gui.PullAtomData;
@@ -580,4 +581,22 @@ interface ISurfaceComposer {
     * This method should not block the ShutdownThread therefore it's handled asynchronously.
     * This method should not block the ShutdownThread therefore it's handled asynchronously.
     */
     */
    oneway void notifyShutdown();
    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 Original line 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