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
Merged-In: I0e599c602b9fd9ae4446dd076dea4b8a75652bd4
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 41639b6..0dca21e 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -25,6 +25,7 @@
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
 #include <configstore/Utils.h>
+#include <ftl/concat.h>
 #include <ftl/enum.h>
 #include <ftl/fake_guard.h>
 #include <ftl/small_map.h>
@@ -130,8 +131,8 @@
     auto [pacesetterVsyncSchedule, isNew] = [&]() FTL_FAKE_GUARD(kMainThreadContext) {
         std::scoped_lock lock(mDisplayLock);
         const bool isNew = mDisplays
-                                   .emplace_or_replace(displayId, std::move(selectorPtr),
-                                                       std::move(schedulePtr))
+                                   .emplace_or_replace(displayId, displayId, std::move(selectorPtr),
+                                                       std::move(schedulePtr), mFeatures)
                                    .second;
 
         return std::make_pair(promotePacesetterDisplayLocked(), isNew);
@@ -171,21 +172,43 @@
 
 void Scheduler::onFrameSignal(ICompositor& compositor, VsyncId vsyncId,
                               TimePoint expectedVsyncTime) {
-    mPacesetterFrameTargeter.beginFrame({.frameBeginTime = SchedulerClock::now(),
-                                         .vsyncId = vsyncId,
-                                         .expectedVsyncTime = expectedVsyncTime,
-                                         .sfWorkDuration =
-                                                 mVsyncModulator->getVsyncConfig().sfWorkDuration},
-                                        *getVsyncSchedule());
+    const FrameTargeter::BeginFrameArgs beginFrameArgs =
+            {.frameBeginTime = SchedulerClock::now(),
+             .vsyncId = vsyncId,
+             // TODO(b/255601557): Calculate per display.
+             .expectedVsyncTime = expectedVsyncTime,
+             .sfWorkDuration = mVsyncModulator->getVsyncConfig().sfWorkDuration};
 
-    if (!compositor.commit(mPacesetterFrameTargeter.target())) {
-        return;
+    LOG_ALWAYS_FATAL_IF(!mPacesetterDisplayId);
+    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();
 
-    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 {
@@ -534,8 +557,16 @@
 }
 
 void Scheduler::addPresentFence(PhysicalDisplayId id, std::shared_ptr<FenceTime> fence) {
-    auto schedule = getVsyncSchedule(id);
-    LOG_ALWAYS_FATAL_IF(!schedule);
+    const auto scheduleOpt =
+            (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))) {
         schedule->enableHardwareVsync();
     } else {
@@ -724,7 +755,23 @@
     mFrameRateOverrideMappings.dump(dumper);
     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 {