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

Commit ec0eac2d authored by Dominik Laskowski's avatar Dominik Laskowski
Browse files

SF: Report missed frames per display

Create a FrameTargeter per display, each with its own present fences and
metrics about missed frames. Label the traces for missed frames with the
DisplayId. Track CompositionCoverage per display.

Fixes: 262269033
Bug: 241285475
Test: Perfetto
Test: dumpsys SurfaceFlinger --scheduler
Change-Id: I0e599c602b9fd9ae4446dd076dea4b8a75652bd4
parent 2fc656d7
Loading
Loading
Loading
Loading
+63 −16
Original line number Original line Diff line number Diff line
@@ -25,6 +25,7 @@
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
#include <configstore/Utils.h>
#include <configstore/Utils.h>
#include <ftl/concat.h>
#include <ftl/enum.h>
#include <ftl/enum.h>
#include <ftl/fake_guard.h>
#include <ftl/fake_guard.h>
#include <ftl/small_map.h>
#include <ftl/small_map.h>
@@ -130,8 +131,8 @@ void Scheduler::registerDisplayInternal(PhysicalDisplayId displayId,
    auto [pacesetterVsyncSchedule, isNew] = [&]() FTL_FAKE_GUARD(kMainThreadContext) {
    auto [pacesetterVsyncSchedule, isNew] = [&]() FTL_FAKE_GUARD(kMainThreadContext) {
        std::scoped_lock lock(mDisplayLock);
        std::scoped_lock lock(mDisplayLock);
        const bool isNew = mDisplays
        const bool isNew = mDisplays
                                   .emplace_or_replace(displayId, std::move(selectorPtr),
                                   .emplace_or_replace(displayId, displayId, std::move(selectorPtr),
                                                       std::move(schedulePtr))
                                                       std::move(schedulePtr), mFeatures)
                                   .second;
                                   .second;


        return std::make_pair(promotePacesetterDisplayLocked(), isNew);
        return std::make_pair(promotePacesetterDisplayLocked(), isNew);
@@ -171,21 +172,43 @@ void Scheduler::run() {


void Scheduler::onFrameSignal(ICompositor& compositor, VsyncId vsyncId,
void Scheduler::onFrameSignal(ICompositor& compositor, VsyncId vsyncId,
                              TimePoint expectedVsyncTime) {
                              TimePoint expectedVsyncTime) {
    mPacesetterFrameTargeter.beginFrame({.frameBeginTime = SchedulerClock::now(),
    const FrameTargeter::BeginFrameArgs beginFrameArgs =
            {.frameBeginTime = SchedulerClock::now(),
             .vsyncId = vsyncId,
             .vsyncId = vsyncId,
             // TODO(b/255601557): Calculate per display.
             .expectedVsyncTime = expectedVsyncTime,
             .expectedVsyncTime = expectedVsyncTime,
                                         .sfWorkDuration =
             .sfWorkDuration = mVsyncModulator->getVsyncConfig().sfWorkDuration};
                                                 mVsyncModulator->getVsyncConfig().sfWorkDuration},
                                        *getVsyncSchedule());


    if (!compositor.commit(mPacesetterFrameTargeter.target())) {
    LOG_ALWAYS_FATAL_IF(!mPacesetterDisplayId);
        return;
    const auto pacesetterId = *mPacesetterDisplayId;
    const auto pacesetterOpt = mDisplays.get(pacesetterId);

    FrameTargeter& pacesetterTargeter = *pacesetterOpt->get().targeterPtr;
    pacesetterTargeter.beginFrame(beginFrameArgs, *pacesetterOpt->get().schedulePtr);

    if (!compositor.commit(pacesetterTargeter.target())) return;

    // TODO(b/256196556): Choose the frontrunner display.
    FrameTargeters targeters;
    targeters.try_emplace(pacesetterId, &pacesetterTargeter);

    for (auto& [id, display] : mDisplays) {
        if (id == pacesetterId) continue;

        FrameTargeter& targeter = *display.targeterPtr;
        targeter.beginFrame(beginFrameArgs, *display.schedulePtr);

        targeters.try_emplace(id, &targeter);
    }
    }


    const auto compositeResult = compositor.composite(mPacesetterFrameTargeter);
    const auto resultsPerDisplay = compositor.composite(pacesetterId, targeters);
    compositor.sample();
    compositor.sample();


    mPacesetterFrameTargeter.endFrame(compositeResult);
    for (const auto& [id, targeter] : targeters) {
        const auto resultOpt = resultsPerDisplay.get(id);
        LOG_ALWAYS_FATAL_IF(!resultOpt);
        targeter->endFrame(*resultOpt);
    }
}
}


std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const {
std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const {
@@ -539,8 +562,16 @@ bool Scheduler::addResyncSample(PhysicalDisplayId id, nsecs_t timestamp,
}
}


void Scheduler::addPresentFence(PhysicalDisplayId id, std::shared_ptr<FenceTime> fence) {
void Scheduler::addPresentFence(PhysicalDisplayId id, std::shared_ptr<FenceTime> fence) {
    auto schedule = getVsyncSchedule(id);
    const auto scheduleOpt =
    LOG_ALWAYS_FATAL_IF(!schedule);
            (ftl::FakeGuard(mDisplayLock), mDisplays.get(id)).and_then([](const Display& display) {
                return display.powerMode == hal::PowerMode::OFF
                        ? std::nullopt
                        : std::make_optional(display.schedulePtr);
            });

    if (!scheduleOpt) return;
    const auto& schedule = scheduleOpt->get();

    if (const bool needMoreSignals = schedule->getController().addPresentFence(std::move(fence))) {
    if (const bool needMoreSignals = schedule->getController().addPresentFence(std::move(fence))) {
        schedule->enableHardwareVsync();
        schedule->enableHardwareVsync();
    } else {
    } else {
@@ -750,7 +781,23 @@ void Scheduler::dump(utils::Dumper& dumper) const {
    mFrameRateOverrideMappings.dump(dumper);
    mFrameRateOverrideMappings.dump(dumper);
    dumper.eol();
    dumper.eol();


    mPacesetterFrameTargeter.dump(dumper);
    {
        utils::Dumper::Section section(dumper, "Frame Targeting"sv);

        std::scoped_lock lock(mDisplayLock);
        ftl::FakeGuard guard(kMainThreadContext);

        for (const auto& [id, display] : mDisplays) {
            utils::Dumper::Section
                    section(dumper,
                            id == mPacesetterDisplayId
                                    ? ftl::Concat("Pacesetter Display ", id.value).c_str()
                                    : ftl::Concat("Follower Display ", id.value).c_str());

            display.targeterPtr->dump(dumper);
            dumper.eol();
        }
    }
}
}


void Scheduler::dumpVsync(std::string& out) const {
void Scheduler::dumpVsync(std::string& out) const {
+24 −7
Original line number Original line Diff line number Diff line
@@ -222,7 +222,7 @@ public:
    // otherwise.
    // otherwise.
    bool addResyncSample(PhysicalDisplayId, nsecs_t timestamp,
    bool addResyncSample(PhysicalDisplayId, nsecs_t timestamp,
                         std::optional<nsecs_t> hwcVsyncPeriod);
                         std::optional<nsecs_t> hwcVsyncPeriod);
    void addPresentFence(PhysicalDisplayId, std::shared_ptr<FenceTime>) EXCLUDES(mDisplayLock)
    void addPresentFence(PhysicalDisplayId, std::shared_ptr<FenceTime>)
            REQUIRES(kMainThreadContext);
            REQUIRES(kMainThreadContext);


    // Layers are registered on creation, and unregistered when the weak reference expires.
    // Layers are registered on creation, and unregistered when the weak reference expires.
@@ -254,7 +254,14 @@ public:
        return std::const_pointer_cast<VsyncSchedule>(std::as_const(*this).getVsyncSchedule(idOpt));
        return std::const_pointer_cast<VsyncSchedule>(std::as_const(*this).getVsyncSchedule(idOpt));
    }
    }


    const FrameTarget& pacesetterFrameTarget() { return mPacesetterFrameTargeter.target(); }
    TimePoint expectedPresentTimeForPacesetter() const EXCLUDES(mDisplayLock) {
        std::scoped_lock lock(mDisplayLock);
        return pacesetterDisplayLocked()
                .transform([](const Display& display) {
                    return display.targeterPtr->target().expectedPresentTime();
                })
                .value_or(TimePoint());
    }


    // Returns true if a given vsync timestamp is considered valid vsync
    // Returns true if a given vsync timestamp is considered valid vsync
    // for a given uid
    // for a given uid
@@ -308,7 +315,8 @@ private:
    enum class TouchState { Inactive, Active };
    enum class TouchState { Inactive, Active };


    // impl::MessageQueue overrides:
    // impl::MessageQueue overrides:
    void onFrameSignal(ICompositor&, VsyncId, TimePoint expectedVsyncTime) override;
    void onFrameSignal(ICompositor&, VsyncId, TimePoint expectedVsyncTime) override
            REQUIRES(kMainThreadContext, mDisplayLock);


    // Create a connection on the given EventThread.
    // Create a connection on the given EventThread.
    ConnectionHandle createConnection(std::unique_ptr<EventThread>);
    ConnectionHandle createConnection(std::unique_ptr<EventThread>);
@@ -434,13 +442,24 @@ private:
    // must lock for writes but not reads. See also mPolicyLock for locking order.
    // must lock for writes but not reads. See also mPolicyLock for locking order.
    mutable std::mutex mDisplayLock;
    mutable std::mutex mDisplayLock;


    using FrameTargeterPtr = std::unique_ptr<FrameTargeter>;

    struct Display {
    struct Display {
        Display(RefreshRateSelectorPtr selectorPtr, VsyncSchedulePtr schedulePtr)
        Display(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr,
              : selectorPtr(std::move(selectorPtr)), schedulePtr(std::move(schedulePtr)) {}
                VsyncSchedulePtr schedulePtr, FeatureFlags features)
              : displayId(displayId),
                selectorPtr(std::move(selectorPtr)),
                schedulePtr(std::move(schedulePtr)),
                targeterPtr(std::make_unique<
                            FrameTargeter>(displayId,
                                           features.test(Feature::kBackpressureGpuComposition))) {}

        const PhysicalDisplayId displayId;


        // Effectively const except in move constructor.
        // Effectively const except in move constructor.
        RefreshRateSelectorPtr selectorPtr;
        RefreshRateSelectorPtr selectorPtr;
        VsyncSchedulePtr schedulePtr;
        VsyncSchedulePtr schedulePtr;
        FrameTargeterPtr targeterPtr;


        hal::PowerMode powerMode = hal::PowerMode::OFF;
        hal::PowerMode powerMode = hal::PowerMode::OFF;
    };
    };
@@ -454,8 +473,6 @@ private:
    ftl::Optional<PhysicalDisplayId> mPacesetterDisplayId GUARDED_BY(mDisplayLock)
    ftl::Optional<PhysicalDisplayId> mPacesetterDisplayId GUARDED_BY(mDisplayLock)
            GUARDED_BY(kMainThreadContext);
            GUARDED_BY(kMainThreadContext);


    FrameTargeter mPacesetterFrameTargeter{mFeatures.test(Feature::kBackpressureGpuComposition)};

    ftl::Optional<DisplayRef> pacesetterDisplayLocked() REQUIRES(mDisplayLock) {
    ftl::Optional<DisplayRef> pacesetterDisplayLocked() REQUIRES(mDisplayLock) {
        return static_cast<const Scheduler*>(this)->pacesetterDisplayLocked().transform(
        return static_cast<const Scheduler*>(this)->pacesetterDisplayLocked().transform(
                [](const Display& display) { return std::ref(const_cast<Display&>(display)); });
                [](const Display& display) { return std::ref(const_cast<Display&>(display)); });
+9 −6
Original line number Original line Diff line number Diff line
@@ -20,6 +20,7 @@
#include <atomic>
#include <atomic>
#include <memory>
#include <memory>


#include <ui/DisplayId.h>
#include <ui/Fence.h>
#include <ui/Fence.h>
#include <ui/FenceTime.h>
#include <ui/FenceTime.h>


@@ -75,16 +76,17 @@ public:
    bool didMissHwcFrame() const { return mHwcFrameMissed && !mGpuFrameMissed; }
    bool didMissHwcFrame() const { return mHwcFrameMissed && !mGpuFrameMissed; }


protected:
protected:
    explicit FrameTarget(const std::string& displayLabel);
    ~FrameTarget() = default;
    ~FrameTarget() = default;


    VsyncId mVsyncId;
    VsyncId mVsyncId;
    TimePoint mFrameBeginTime;
    TimePoint mFrameBeginTime;
    TimePoint mExpectedPresentTime;
    TimePoint mExpectedPresentTime;


    TracedOrdinal<bool> mFramePending{"PrevFramePending", false};
    TracedOrdinal<bool> mFramePending;
    TracedOrdinal<bool> mFrameMissed{"PrevFrameMissed", false};
    TracedOrdinal<bool> mFrameMissed;
    TracedOrdinal<bool> mHwcFrameMissed{"PrevHwcFrameMissed", false};
    TracedOrdinal<bool> mHwcFrameMissed;
    TracedOrdinal<bool> mGpuFrameMissed{"PrevGpuFrameMissed", false};
    TracedOrdinal<bool> mGpuFrameMissed;


    struct FenceWithFenceTime {
    struct FenceWithFenceTime {
        sp<Fence> fence = Fence::NO_FENCE;
        sp<Fence> fence = Fence::NO_FENCE;
@@ -103,8 +105,9 @@ private:
// Computes a display's per-frame metrics about past/upcoming targeting of present deadlines.
// Computes a display's per-frame metrics about past/upcoming targeting of present deadlines.
class FrameTargeter final : private FrameTarget {
class FrameTargeter final : private FrameTarget {
public:
public:
    explicit FrameTargeter(bool backpressureGpuComposition)
    FrameTargeter(PhysicalDisplayId displayId, bool backpressureGpuComposition)
          : mBackpressureGpuComposition(backpressureGpuComposition) {}
          : FrameTarget(to_string(displayId)),
            mBackpressureGpuComposition(backpressureGpuComposition) {}


    const FrameTarget& target() const { return *this; }
    const FrameTarget& target() const { return *this; }


+5 −0
Original line number Original line Diff line number Diff line
@@ -16,6 +16,9 @@


#pragma once
#pragma once


#include <ui/DisplayId.h>
#include <ui/DisplayMap.h>

#include <scheduler/interface/CompositionCoverage.h>
#include <scheduler/interface/CompositionCoverage.h>


namespace android {
namespace android {
@@ -24,4 +27,6 @@ struct CompositeResult {
    CompositionCoverageFlags compositionCoverage;
    CompositionCoverageFlags compositionCoverage;
};
};


using CompositeResultsPerDisplay = ui::PhysicalDisplayMap<PhysicalDisplayId, CompositeResult>;

} // namespace android
} // namespace android
+12 −0
Original line number Original line Diff line number Diff line
@@ -19,6 +19,8 @@
#include <cstdint>
#include <cstdint>


#include <ftl/flags.h>
#include <ftl/flags.h>
#include <ui/DisplayId.h>
#include <ui/DisplayMap.h>


namespace android {
namespace android {


@@ -34,4 +36,14 @@ enum class CompositionCoverage : std::uint8_t {


using CompositionCoverageFlags = ftl::Flags<CompositionCoverage>;
using CompositionCoverageFlags = ftl::Flags<CompositionCoverage>;


using CompositionCoveragePerDisplay = ui::DisplayMap<DisplayId, CompositionCoverageFlags>;

inline CompositionCoverageFlags multiDisplayUnion(const CompositionCoveragePerDisplay& displays) {
    CompositionCoverageFlags coverage;
    for (const auto& [id, flags] : displays) {
        coverage |= flags;
    }
    return coverage;
}

} // namespace android
} // namespace android
Loading