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

Commit 31d41415 authored by Leon Scroggins III's avatar Leon Scroggins III
Browse files

Create a VsyncSchedule per display

In order to determine the vsync offsets between displays, keep track of
a VsyncSchedule for each display.

Store the VsyncSchedules in a SmallMap. Update getVsyncSchedule with a
parameter to choose the display. The default parameter uses the leader's
display, which is what current external callers want.

Update VsyncDispatches when the leader changes, so that they are always
listening to the leader.

Enable and disable vsync callbacks per display. Earlier attempts to turn
them on and off together could leave a secondary display on a bad
schedule. Move state and logic for enabling/disabling the callbacks into
VsyncSchedule. Add a method for resyncing all displays at once.

Use std::shared_ptrs for VsyncDispatches. This prevents lifetime issues
if a VsyncSchedule gets removed while its VsyncDispatch is still in use.
Same for VsyncTracker, which is referenced by VsyncDispatch.

When the leader VsyncSchedule changes, call cancel on
VsyncCallbackRegistrations and replace them with new ones using the new
VsyncDispatches. If a callback was scheduled, schedule a new one.

Update VsyncSchedule's members' traces so that there is a separate track
for each display.

Move SF's record of the last HWC Vsync states into VsyncSchedule, so it
sits with other related logic. Remove the pending HWC Vsync state, which
did not affect behavior.

For refresh rate changes, modulate vsync config based on the leader
display. When switching leaders, force a period transition to ensure
that a potential refresh rate change is completed.

Bug: 255601557
Bug: 256196556
Bug: 241285473
Bug: 241286146
Test: libsurfaceflinger_unittest
Test: manual (look at perfetto traces)
Change-Id: If60218e85292c786b9fa70ecb33ee374d3a385e0
parent 1d104209
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -93,7 +93,7 @@ public:
    MOCK_METHOD2(onHotplug,
                 std::optional<DisplayIdentificationInfo>(hal::HWDisplayId, hal::Connection));
    MOCK_CONST_METHOD0(updatesDeviceProductInfoOnHotplugReconnect, bool());
    MOCK_METHOD2(onVsync, bool(hal::HWDisplayId, int64_t));
    MOCK_METHOD(std::optional<PhysicalDisplayId>, onVsync, (hal::HWDisplayId, int64_t));
    MOCK_METHOD2(setVsyncEnabled, void(PhysicalDisplayId, hal::Vsync));
    MOCK_CONST_METHOD1(isConnected, bool(PhysicalDisplayId));
    MOCK_CONST_METHOD1(getModes, std::vector<HWComposer::HWCDisplayMode>(PhysicalDisplayId));
+12 −10
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@
#include <compositionengine/Output.h>
#include <compositionengine/OutputLayer.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <ftl/concat.h>
#include <log/log.h>
#include <ui/DebugUtils.h>
#include <ui/GraphicBuffer.h>
@@ -148,16 +149,17 @@ bool HWComposer::updatesDeviceProductInfoOnHotplugReconnect() const {
    return mUpdateDeviceProductInfoOnHotplugReconnect;
}

bool HWComposer::onVsync(hal::HWDisplayId hwcDisplayId, nsecs_t timestamp) {
    const auto displayId = toPhysicalDisplayId(hwcDisplayId);
    if (!displayId) {
std::optional<PhysicalDisplayId> HWComposer::onVsync(hal::HWDisplayId hwcDisplayId,
                                                     nsecs_t timestamp) {
    const auto displayIdOpt = toPhysicalDisplayId(hwcDisplayId);
    if (!displayIdOpt) {
        LOG_HWC_DISPLAY_ERROR(hwcDisplayId, "Invalid HWC display");
        return false;
        return {};
    }

    RETURN_IF_INVALID_DISPLAY(*displayId, false);
    RETURN_IF_INVALID_DISPLAY(*displayIdOpt, {});

    auto& displayData = mDisplayData[*displayId];
    auto& displayData = mDisplayData[*displayIdOpt];

    {
        // There have been reports of HWCs that signal several vsync events
@@ -166,18 +168,18 @@ bool HWComposer::onVsync(hal::HWDisplayId hwcDisplayId, nsecs_t timestamp) {
        // out here so they don't cause havoc downstream.
        if (timestamp == displayData.lastPresentTimestamp) {
            ALOGW("Ignoring duplicate VSYNC event from HWC for display %s (t=%" PRId64 ")",
                  to_string(*displayId).c_str(), timestamp);
            return false;
                  to_string(*displayIdOpt).c_str(), timestamp);
            return {};
        }

        displayData.lastPresentTimestamp = timestamp;
    }

    const auto tag = "HW_VSYNC_" + to_string(*displayId);
    const ftl::Concat tag("HW_VSYNC_", displayIdOpt->value);
    ATRACE_INT(tag.c_str(), displayData.vsyncTraceToggle);
    displayData.vsyncTraceToggle = !displayData.vsyncTraceToggle;

    return true;
    return displayIdOpt;
}

size_t HWComposer::getMaxVirtualDisplayCount() const {
+5 −2
Original line number Diff line number Diff line
@@ -221,7 +221,10 @@ public:
    // TODO(b/157555476): Remove when the framework has proper support for headless mode
    virtual bool updatesDeviceProductInfoOnHotplugReconnect() const = 0;

    virtual bool onVsync(hal::HWDisplayId, nsecs_t timestamp) = 0;
    // Called when a vsync happens. If the vsync is valid, returns the
    // corresponding PhysicalDisplayId. Otherwise returns nullopt.
    virtual std::optional<PhysicalDisplayId> onVsync(hal::HWDisplayId, nsecs_t timestamp) = 0;

    virtual void setVsyncEnabled(PhysicalDisplayId, hal::Vsync enabled) = 0;

    virtual bool isConnected(PhysicalDisplayId) const = 0;
@@ -402,7 +405,7 @@ public:

    bool updatesDeviceProductInfoOnHotplugReconnect() const override;

    bool onVsync(hal::HWDisplayId, nsecs_t timestamp) override;
    std::optional<PhysicalDisplayId> onVsync(hal::HWDisplayId, nsecs_t timestamp) override;
    void setVsyncEnabled(PhysicalDisplayId, hal::Vsync enabled) override;

    bool isConnected(PhysicalDisplayId) const override;
+40 −26
Original line number Diff line number Diff line
@@ -238,29 +238,19 @@ EventThread::~EventThread() = default;

namespace impl {

EventThread::EventThread(const char* name, scheduler::VsyncSchedule& vsyncSchedule,
EventThread::EventThread(const char* name, std::shared_ptr<scheduler::VsyncSchedule> vsyncSchedule,
                         IEventThreadCallback& eventThreadCallback,
                         android::frametimeline::TokenManager* tokenManager,
                         ThrottleVsyncCallback throttleVsyncCallback,
                         GetVsyncPeriodFunction getVsyncPeriodFunction,
                         std::chrono::nanoseconds workDuration,
                         std::chrono::nanoseconds readyDuration)
      : mThreadName(name),
        mVsyncTracer(base::StringPrintf("VSYNC-%s", name), 0),
        mWorkDuration(base::StringPrintf("VsyncWorkDuration-%s", name), workDuration),
        mReadyDuration(readyDuration),
        mVsyncSchedule(vsyncSchedule),
        mVsyncRegistration(
                vsyncSchedule.getDispatch(),
                [this](nsecs_t vsyncTime, nsecs_t wakeupTime, nsecs_t readyTime) {
                    onVsync(vsyncTime, wakeupTime, readyTime);
                },
                name),
        mVsyncSchedule(std::move(vsyncSchedule)),
        mVsyncRegistration(mVsyncSchedule->getDispatch(), createDispatchCallback(), name),
        mTokenManager(tokenManager),
        mThrottleVsyncCallback(std::move(throttleVsyncCallback)),
        mGetVsyncPeriodFunction(std::move(getVsyncPeriodFunction)) {
    LOG_ALWAYS_FATAL_IF(getVsyncPeriodFunction == nullptr,
            "getVsyncPeriodFunction must not be null");

        mEventThreadCallback(eventThreadCallback) {
    mThread = std::thread([this]() NO_THREAD_SAFETY_ANALYSIS {
        std::unique_lock<std::mutex> lock(mMutex);
        threadMain(lock);
@@ -371,16 +361,16 @@ VsyncEventData EventThread::getLatestVsyncEventData(
    }

    VsyncEventData vsyncEventData;
    nsecs_t frameInterval = mGetVsyncPeriodFunction(connection->mOwnerUid);
    vsyncEventData.frameInterval = frameInterval;
    const Fps frameInterval = mEventThreadCallback.getLeaderRenderFrameRate(connection->mOwnerUid);
    vsyncEventData.frameInterval = frameInterval.getPeriodNsecs();
    const auto [presentTime, deadline] = [&]() -> std::pair<nsecs_t, nsecs_t> {
        std::lock_guard<std::mutex> lock(mMutex);
        const auto vsyncTime = mVsyncSchedule.getTracker().nextAnticipatedVSyncTimeFrom(
        const auto vsyncTime = mVsyncSchedule->getTracker().nextAnticipatedVSyncTimeFrom(
                systemTime() + mWorkDuration.get().count() + mReadyDuration.count());
        return {vsyncTime, vsyncTime - mReadyDuration.count()};
    }();
    generateFrameTimeline(vsyncEventData, frameInterval, systemTime(SYSTEM_TIME_MONOTONIC),
                          presentTime, deadline);
    generateFrameTimeline(vsyncEventData, frameInterval.getPeriodNsecs(),
                          systemTime(SYSTEM_TIME_MONOTONIC), presentTime, deadline);
    return vsyncEventData;
}

@@ -541,8 +531,10 @@ void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {
bool EventThread::shouldConsumeEvent(const DisplayEventReceiver::Event& event,
                                     const sp<EventThreadConnection>& connection) const {
    const auto throttleVsync = [&] {
        return mThrottleVsyncCallback &&
                mThrottleVsyncCallback(event.vsync.vsyncData.preferredExpectedPresentationTime(),
        const auto& vsyncData = event.vsync.vsyncData;
        const auto expectedPresentTime =
                TimePoint::fromNs(vsyncData.preferredExpectedPresentationTime());
        return !mEventThreadCallback.isVsyncTargetForUid(expectedPresentTime,
                                                         connection->mOwnerUid);
    };

@@ -631,9 +623,11 @@ void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event,
    for (const auto& consumer : consumers) {
        DisplayEventReceiver::Event copy = event;
        if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
            const int64_t frameInterval = mGetVsyncPeriodFunction(consumer->mOwnerUid);
            copy.vsync.vsyncData.frameInterval = frameInterval;
            generateFrameTimeline(copy.vsync.vsyncData, frameInterval, copy.header.timestamp,
            const Fps frameInterval =
                    mEventThreadCallback.getLeaderRenderFrameRate(consumer->mOwnerUid);
            copy.vsync.vsyncData.frameInterval = frameInterval.getPeriodNsecs();
            generateFrameTimeline(copy.vsync.vsyncData, frameInterval.getPeriodNsecs(),
                                  copy.header.timestamp,
                                  event.vsync.vsyncData.preferredExpectedPresentationTime(),
                                  event.vsync.vsyncData.preferredDeadlineTimestamp());
        }
@@ -699,6 +693,26 @@ const char* EventThread::toCString(State state) {
    }
}

void EventThread::onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule> schedule) {
    std::lock_guard<std::mutex> lock(mMutex);
    const bool reschedule = mVsyncRegistration.cancel() == scheduler::CancelResult::Cancelled;
    mVsyncSchedule = std::move(schedule);
    mVsyncRegistration =
            scheduler::VSyncCallbackRegistration(mVsyncSchedule->getDispatch(),
                                                 createDispatchCallback(), mThreadName);
    if (reschedule) {
        mVsyncRegistration.schedule({.workDuration = mWorkDuration.get().count(),
                                     .readyDuration = mReadyDuration.count(),
                                     .earliestVsync = mLastVsyncCallbackTime.ns()});
    }
}

scheduler::VSyncDispatch::Callback EventThread::createDispatchCallback() {
    return [this](nsecs_t vsyncTime, nsecs_t wakeupTime, nsecs_t readyTime) {
        onVsync(vsyncTime, wakeupTime, readyTime);
    };
}

} // namespace impl
} // namespace android

+22 −9
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@
#include <sys/types.h>
#include <utils/Errors.h>

#include <scheduler/Fps.h>
#include <scheduler/FrameRateMode.h>
#include <condition_variable>
#include <cstdint>
@@ -67,6 +68,15 @@ enum class VSyncRequest {
    // Subsequent values are periods.
};

class IEventThreadCallback {
public:
    virtual ~IEventThreadCallback() = default;

    virtual bool isVsyncTargetForUid(TimePoint expectedVsyncTime, uid_t uid) const = 0;

    virtual Fps getLeaderRenderFrameRate(uid_t uid) const = 0;
};

class EventThreadConnection : public gui::BnDisplayEventConnection {
public:
    EventThreadConnection(EventThread*, uid_t callingUid, ResyncCallback,
@@ -133,18 +143,17 @@ public:

    // Retrieves the number of event connections tracked by this EventThread.
    virtual size_t getEventThreadConnectionCount() = 0;

    virtual void onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule>) = 0;
};

namespace impl {

class EventThread : public android::EventThread {
public:
    using ThrottleVsyncCallback = std::function<bool(nsecs_t, uid_t)>;
    using GetVsyncPeriodFunction = std::function<nsecs_t(uid_t)>;

    EventThread(const char* name, scheduler::VsyncSchedule&, frametimeline::TokenManager*,
                ThrottleVsyncCallback, GetVsyncPeriodFunction,
                std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration);
    EventThread(const char* name, std::shared_ptr<scheduler::VsyncSchedule>, IEventThreadCallback&,
                frametimeline::TokenManager*, std::chrono::nanoseconds workDuration,
                std::chrono::nanoseconds readyDuration);
    ~EventThread();

    sp<EventThreadConnection> createEventConnection(
@@ -176,6 +185,8 @@ public:

    size_t getEventThreadConnectionCount() override;

    void onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule>) override;

private:
    friend EventThreadTest;

@@ -199,17 +210,19 @@ private:
                               nsecs_t timestamp, nsecs_t preferredExpectedPresentationTime,
                               nsecs_t preferredDeadlineTimestamp) const;

    scheduler::VSyncDispatch::Callback createDispatchCallback();

    const char* const mThreadName;
    TracedOrdinal<int> mVsyncTracer;
    TracedOrdinal<std::chrono::nanoseconds> mWorkDuration GUARDED_BY(mMutex);
    std::chrono::nanoseconds mReadyDuration GUARDED_BY(mMutex);
    scheduler::VsyncSchedule& mVsyncSchedule;
    std::shared_ptr<scheduler::VsyncSchedule> mVsyncSchedule;
    TimePoint mLastVsyncCallbackTime GUARDED_BY(mMutex) = TimePoint::now();
    scheduler::VSyncCallbackRegistration mVsyncRegistration GUARDED_BY(mMutex);
    frametimeline::TokenManager* const mTokenManager;

    const ThrottleVsyncCallback mThrottleVsyncCallback;
    const GetVsyncPeriodFunction mGetVsyncPeriodFunction;
    // mEventThreadCallback will outlive the EventThread.
    IEventThreadCallback& mEventThreadCallback;

    std::thread mThread;
    mutable std::mutex mMutex;
Loading