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
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 08667bf..eeae932 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 {
@@ -539,8 +562,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 {
@@ -750,7 +781,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 {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 0ffa2d2..8f51e5d 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -222,7 +222,7 @@
// otherwise.
bool addResyncSample(PhysicalDisplayId, nsecs_t timestamp,
std::optional<nsecs_t> hwcVsyncPeriod);
- void addPresentFence(PhysicalDisplayId, std::shared_ptr<FenceTime>) EXCLUDES(mDisplayLock)
+ void addPresentFence(PhysicalDisplayId, std::shared_ptr<FenceTime>)
REQUIRES(kMainThreadContext);
// Layers are registered on creation, and unregistered when the weak reference expires.
@@ -254,7 +254,14 @@
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
// for a given uid
@@ -308,7 +315,8 @@
enum class TouchState { Inactive, Active };
// 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.
ConnectionHandle createConnection(std::unique_ptr<EventThread>);
@@ -434,13 +442,24 @@
// must lock for writes but not reads. See also mPolicyLock for locking order.
mutable std::mutex mDisplayLock;
+ using FrameTargeterPtr = std::unique_ptr<FrameTargeter>;
+
struct Display {
- Display(RefreshRateSelectorPtr selectorPtr, VsyncSchedulePtr schedulePtr)
- : selectorPtr(std::move(selectorPtr)), schedulePtr(std::move(schedulePtr)) {}
+ Display(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr,
+ 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.
RefreshRateSelectorPtr selectorPtr;
VsyncSchedulePtr schedulePtr;
+ FrameTargeterPtr targeterPtr;
hal::PowerMode powerMode = hal::PowerMode::OFF;
};
@@ -454,8 +473,6 @@
ftl::Optional<PhysicalDisplayId> mPacesetterDisplayId GUARDED_BY(mDisplayLock)
GUARDED_BY(kMainThreadContext);
- FrameTargeter mPacesetterFrameTargeter{mFeatures.test(Feature::kBackpressureGpuComposition)};
-
ftl::Optional<DisplayRef> pacesetterDisplayLocked() REQUIRES(mDisplayLock) {
return static_cast<const Scheduler*>(this)->pacesetterDisplayLocked().transform(
[](const Display& display) { return std::ref(const_cast<Display&>(display)); });
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
index 85f2e64..ae74205 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
@@ -20,6 +20,7 @@
#include <atomic>
#include <memory>
+#include <ui/DisplayId.h>
#include <ui/Fence.h>
#include <ui/FenceTime.h>
@@ -75,16 +76,17 @@
bool didMissHwcFrame() const { return mHwcFrameMissed && !mGpuFrameMissed; }
protected:
+ explicit FrameTarget(const std::string& displayLabel);
~FrameTarget() = default;
VsyncId mVsyncId;
TimePoint mFrameBeginTime;
TimePoint mExpectedPresentTime;
- TracedOrdinal<bool> mFramePending{"PrevFramePending", false};
- TracedOrdinal<bool> mFrameMissed{"PrevFrameMissed", false};
- TracedOrdinal<bool> mHwcFrameMissed{"PrevHwcFrameMissed", false};
- TracedOrdinal<bool> mGpuFrameMissed{"PrevGpuFrameMissed", false};
+ TracedOrdinal<bool> mFramePending;
+ TracedOrdinal<bool> mFrameMissed;
+ TracedOrdinal<bool> mHwcFrameMissed;
+ TracedOrdinal<bool> mGpuFrameMissed;
struct FenceWithFenceTime {
sp<Fence> fence = Fence::NO_FENCE;
@@ -103,8 +105,9 @@
// Computes a display's per-frame metrics about past/upcoming targeting of present deadlines.
class FrameTargeter final : private FrameTarget {
public:
- explicit FrameTargeter(bool backpressureGpuComposition)
- : mBackpressureGpuComposition(backpressureGpuComposition) {}
+ FrameTargeter(PhysicalDisplayId displayId, bool backpressureGpuComposition)
+ : FrameTarget(to_string(displayId)),
+ mBackpressureGpuComposition(backpressureGpuComposition) {}
const FrameTarget& target() const { return *this; }
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositeResult.h b/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositeResult.h
index f795f1f..87c704e 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositeResult.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositeResult.h
@@ -16,6 +16,9 @@
#pragma once
+#include <ui/DisplayId.h>
+#include <ui/DisplayMap.h>
+
#include <scheduler/interface/CompositionCoverage.h>
namespace android {
@@ -24,4 +27,6 @@
CompositionCoverageFlags compositionCoverage;
};
+using CompositeResultsPerDisplay = ui::PhysicalDisplayMap<PhysicalDisplayId, CompositeResult>;
+
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositionCoverage.h b/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositionCoverage.h
index 3d0f1a9..767462d 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositionCoverage.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositionCoverage.h
@@ -19,6 +19,8 @@
#include <cstdint>
#include <ftl/flags.h>
+#include <ui/DisplayId.h>
+#include <ui/DisplayMap.h>
namespace android {
@@ -34,4 +36,14 @@
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
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h b/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h
index 2696076..6fe813a 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h
@@ -16,6 +16,9 @@
#pragma once
+#include <ui/DisplayId.h>
+#include <ui/DisplayMap.h>
+
#include <scheduler/Time.h>
#include <scheduler/VsyncId.h>
#include <scheduler/interface/CompositeResult.h>
@@ -26,6 +29,8 @@
class FrameTarget;
class FrameTargeter;
+using FrameTargeters = ui::PhysicalDisplayMap<PhysicalDisplayId, scheduler::FrameTargeter*>;
+
} // namespace scheduler
struct ICompositor {
@@ -38,7 +43,8 @@
// Composites a frame for each display. CompositionEngine performs GPU and/or HAL composition
// via RenderEngine and the Composer HAL, respectively.
- virtual CompositeResult composite(scheduler::FrameTargeter&) = 0;
+ virtual CompositeResultsPerDisplay composite(PhysicalDisplayId pacesetterId,
+ const scheduler::FrameTargeters&) = 0;
// Samples the composited frame via RegionSamplingThread.
virtual void sample() = 0;
diff --git a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
index 7138afd..7a18654 100644
--- a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
+++ b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
@@ -21,6 +21,12 @@
namespace android::scheduler {
+FrameTarget::FrameTarget(const std::string& displayLabel)
+ : mFramePending("PrevFramePending " + displayLabel, false),
+ mFrameMissed("PrevFrameMissed " + displayLabel, false),
+ mHwcFrameMissed("PrevHwcFrameMissed " + displayLabel, false),
+ mGpuFrameMissed("PrevGpuFrameMissed " + displayLabel, false) {}
+
TimePoint FrameTarget::pastVsyncTime(Period vsyncPeriod) const {
// TODO(b/267315508): Generalize to N VSYNCs.
const int shift = static_cast<int>(targetsVsyncsAhead<2>(vsyncPeriod));
@@ -130,10 +136,6 @@
}
void FrameTargeter::dump(utils::Dumper& dumper) const {
- using namespace std::string_view_literals;
-
- utils::Dumper::Section section(dumper, "Frame Targeting"sv);
-
// There are scripts and tests that expect this (rather than "name=value") format.
dumper.dump({}, "Total missed frame count: " + std::to_string(mFrameMissedCount));
dumper.dump({}, "HWC missed frame count: " + std::to_string(mHwcFrameMissedCount));
diff --git a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
index 908f214..1e038d1 100644
--- a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
+++ b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
@@ -96,7 +96,7 @@
FenceToFenceTimeMap mFenceMap;
static constexpr bool kBackpressureGpuComposition = true;
- FrameTargeter mTargeter{kBackpressureGpuComposition};
+ FrameTargeter mTargeter{PhysicalDisplayId::fromPort(13), kBackpressureGpuComposition};
};
TEST_F(FrameTargeterTest, targetsFrames) {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index a9a1d80..711bea5 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2181,7 +2181,7 @@
}
}
-void SurfaceFlinger::configure() FTL_FAKE_GUARD(kMainThreadContext) {
+void SurfaceFlinger::configure() {
Mutex::Autolock lock(mStateLock);
if (configureLocked()) {
setTransactionFlags(eDisplayTransactionNeeded);
@@ -2401,8 +2401,7 @@
return mustComposite;
}
-bool SurfaceFlinger::commit(const scheduler::FrameTarget& pacesetterFrameTarget)
- FTL_FAKE_GUARD(kMainThreadContext) {
+bool SurfaceFlinger::commit(const scheduler::FrameTarget& pacesetterFrameTarget) {
const VsyncId vsyncId = pacesetterFrameTarget.vsyncId();
ATRACE_NAME(ftl::Concat(__func__, ' ', ftl::to_underlying(vsyncId)).c_str());
@@ -2538,31 +2537,42 @@
return mustComposite && CC_LIKELY(mBootStage != BootStage::BOOTLOADER);
}
-CompositeResult SurfaceFlinger::composite(scheduler::FrameTargeter& pacesetterFrameTargeter)
- FTL_FAKE_GUARD(kMainThreadContext) {
- const scheduler::FrameTarget& pacesetterFrameTarget = pacesetterFrameTargeter.target();
+CompositeResultsPerDisplay SurfaceFlinger::composite(
+ PhysicalDisplayId pacesetterId, const scheduler::FrameTargeters& frameTargeters) {
+ const scheduler::FrameTarget& pacesetterTarget =
+ frameTargeters.get(pacesetterId)->get()->target();
- const VsyncId vsyncId = pacesetterFrameTarget.vsyncId();
+ const VsyncId vsyncId = pacesetterTarget.vsyncId();
ATRACE_NAME(ftl::Concat(__func__, ' ', ftl::to_underlying(vsyncId)).c_str());
compositionengine::CompositionRefreshArgs refreshArgs;
refreshArgs.powerCallback = this;
const auto& displays = FTL_FAKE_GUARD(mStateLock, mDisplays);
refreshArgs.outputs.reserve(displays.size());
+
+ // Add outputs for physical displays.
+ for (const auto& [id, targeter] : frameTargeters) {
+ ftl::FakeGuard guard(mStateLock);
+
+ if (const auto display = getCompositionDisplayLocked(id)) {
+ refreshArgs.outputs.push_back(display);
+ }
+ }
+
std::vector<DisplayId> displayIds;
for (const auto& [_, display] : displays) {
displayIds.push_back(display->getId());
display->tracePowerMode();
+ // Add outputs for virtual displays.
if (display->isVirtual()) {
const Fps refreshRate = display->getAdjustedRefreshRate();
- if (refreshRate.isValid() &&
- !mScheduler->isVsyncInPhase(pacesetterFrameTarget.frameBeginTime(), refreshRate)) {
- continue;
+
+ if (!refreshRate.isValid() ||
+ mScheduler->isVsyncInPhase(pacesetterTarget.frameBeginTime(), refreshRate)) {
+ refreshArgs.outputs.push_back(display->getCompositionDisplay());
}
}
-
- refreshArgs.outputs.push_back(display->getCompositionDisplay());
}
mPowerAdvisor->setDisplays(displayIds);
@@ -2622,15 +2632,16 @@
if (!getHwComposer().getComposer()->isSupported(
Hwc2::Composer::OptionalFeature::ExpectedPresentTime) &&
- pacesetterFrameTarget.wouldPresentEarly(vsyncPeriod)) {
+ pacesetterTarget.wouldPresentEarly(vsyncPeriod)) {
const auto hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration;
+ // TODO(b/255601557): Calculate and pass per-display values for each FrameTarget.
refreshArgs.earliestPresentTime =
- pacesetterFrameTarget.previousFrameVsyncTime(vsyncPeriod) - hwcMinWorkDuration;
+ pacesetterTarget.previousFrameVsyncTime(vsyncPeriod) - hwcMinWorkDuration;
}
refreshArgs.scheduledFrameTime = mScheduler->getScheduledFrameTime();
- refreshArgs.expectedPresentTime = pacesetterFrameTarget.expectedPresentTime().ns();
+ refreshArgs.expectedPresentTime = pacesetterTarget.expectedPresentTime().ns();
refreshArgs.hasTrustedPresentationListener = mNumTrustedPresentationListeners > 0;
// Store the present time just before calling to the composition engine so we could notify
@@ -2656,14 +2667,14 @@
}
}
- mTimeStats->recordFrameDuration(pacesetterFrameTarget.frameBeginTime().ns(), systemTime());
+ mTimeStats->recordFrameDuration(pacesetterTarget.frameBeginTime().ns(), systemTime());
// Send a power hint after presentation is finished.
if (mPowerHintSessionEnabled) {
// Now that the current frame has been presented above, PowerAdvisor needs the present time
// of the previous frame (whose fence is signaled by now) to determine how long the HWC had
// waited on that fence to retire before presenting.
- const auto& previousPresentFence = pacesetterFrameTarget.presentFenceForPreviousFrame();
+ const auto& previousPresentFence = pacesetterTarget.presentFenceForPreviousFrame();
mPowerAdvisor->setSfPresentTiming(TimePoint::fromNs(previousPresentFence->getSignalTime()),
TimePoint::now());
@@ -2674,23 +2685,27 @@
scheduleComposite(FrameHint::kNone);
}
- postComposition(pacesetterFrameTargeter, presentTime);
+ postComposition(pacesetterId, frameTargeters, presentTime);
- const bool hadGpuComposited = mCompositionCoverage.test(CompositionCoverage::Gpu);
+ const bool hadGpuComposited =
+ multiDisplayUnion(mCompositionCoverage).test(CompositionCoverage::Gpu);
mCompositionCoverage.clear();
TimeStats::ClientCompositionRecord clientCompositionRecord;
+
for (const auto& [_, display] : displays) {
const auto& state = display->getCompositionDisplay()->getState();
+ CompositionCoverageFlags& flags =
+ mCompositionCoverage.try_emplace(display->getId()).first->second;
if (state.usesDeviceComposition) {
- mCompositionCoverage |= CompositionCoverage::Hwc;
+ flags |= CompositionCoverage::Hwc;
}
if (state.reusedClientComposition) {
- mCompositionCoverage |= CompositionCoverage::GpuReuse;
+ flags |= CompositionCoverage::GpuReuse;
} else if (state.usesClientComposition) {
- mCompositionCoverage |= CompositionCoverage::Gpu;
+ flags |= CompositionCoverage::Gpu;
}
clientCompositionRecord.predicted |=
@@ -2699,10 +2714,11 @@
(state.strategyPrediction == CompositionStrategyPredictionState::SUCCESS);
}
- const bool hasGpuComposited = mCompositionCoverage.test(CompositionCoverage::Gpu);
+ const auto coverage = multiDisplayUnion(mCompositionCoverage);
+ const bool hasGpuComposited = coverage.test(CompositionCoverage::Gpu);
clientCompositionRecord.hadClientComposition = hasGpuComposited;
- clientCompositionRecord.reused = mCompositionCoverage.test(CompositionCoverage::GpuReuse);
+ clientCompositionRecord.reused = coverage.test(CompositionCoverage::GpuReuse);
clientCompositionRecord.changed = hadGpuComposited != hasGpuComposited;
mTimeStats->pushCompositionStrategyState(clientCompositionRecord);
@@ -2711,16 +2727,16 @@
// TODO(b/160583065): Enable skip validation when SF caches all client composition layers.
const bool hasGpuUseOrReuse =
- mCompositionCoverage.any(CompositionCoverage::Gpu | CompositionCoverage::GpuReuse);
+ coverage.any(CompositionCoverage::Gpu | CompositionCoverage::GpuReuse);
mScheduler->modulateVsync({}, &VsyncModulator::onDisplayRefresh, hasGpuUseOrReuse);
mLayersWithQueuedFrames.clear();
if (mLayerTracingEnabled && mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
// This will block and should only be used for debugging.
- addToLayerTracing(mVisibleRegionsDirty, pacesetterFrameTarget.frameBeginTime(), vsyncId);
+ addToLayerTracing(mVisibleRegionsDirty, pacesetterTarget.frameBeginTime(), vsyncId);
}
- updateInputFlinger(vsyncId, pacesetterFrameTarget.frameBeginTime());
+ updateInputFlinger(vsyncId, pacesetterTarget.frameBeginTime());
if (mVisibleRegionsDirty) mHdrLayerInfoChanged = true;
mVisibleRegionsDirty = false;
@@ -2733,7 +2749,16 @@
mPowerAdvisor->setCompositeEnd(TimePoint::now());
}
- return {mCompositionCoverage};
+ CompositeResultsPerDisplay resultsPerDisplay;
+
+ // Filter out virtual displays.
+ for (const auto& [id, coverage] : mCompositionCoverage) {
+ if (const auto idOpt = PhysicalDisplayId::tryCast(id)) {
+ resultsPerDisplay.try_emplace(*idOpt, CompositeResult{coverage});
+ }
+ }
+
+ return resultsPerDisplay;
}
void SurfaceFlinger::updateLayerGeometry() {
@@ -2817,35 +2842,56 @@
return ui::ROTATION_0;
}
-void SurfaceFlinger::postComposition(scheduler::FrameTargeter& pacesetterFrameTargeter,
+void SurfaceFlinger::postComposition(PhysicalDisplayId pacesetterId,
+ const scheduler::FrameTargeters& frameTargeters,
nsecs_t presentStartTime) {
ATRACE_CALL();
ALOGV(__func__);
- const auto* defaultDisplay = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()).get();
+ ui::PhysicalDisplayMap<PhysicalDisplayId, std::shared_ptr<FenceTime>> presentFences;
+ ui::PhysicalDisplayMap<PhysicalDisplayId, const sp<Fence>> gpuCompositionDoneFences;
- std::shared_ptr<FenceTime> glCompositionDoneFenceTime;
- if (defaultDisplay &&
- defaultDisplay->getCompositionDisplay()->getState().usesClientComposition) {
- glCompositionDoneFenceTime =
- std::make_shared<FenceTime>(defaultDisplay->getCompositionDisplay()
- ->getRenderSurface()
- ->getClientTargetAcquireFence());
- } else {
- glCompositionDoneFenceTime = FenceTime::NO_FENCE;
+ for (const auto& [id, targeter] : frameTargeters) {
+ auto presentFence = getHwComposer().getPresentFence(id);
+
+ if (id == pacesetterId) {
+ mTransactionCallbackInvoker.addPresentFence(presentFence);
+ }
+
+ if (auto fenceTime = targeter->setPresentFence(std::move(presentFence));
+ fenceTime->isValid()) {
+ presentFences.try_emplace(id, std::move(fenceTime));
+ }
+
+ ftl::FakeGuard guard(mStateLock);
+ if (const auto display = getCompositionDisplayLocked(id);
+ display && display->getState().usesClientComposition) {
+ gpuCompositionDoneFences
+ .try_emplace(id, display->getRenderSurface()->getClientTargetAcquireFence());
+ }
}
- auto presentFence = defaultDisplay
- ? getHwComposer().getPresentFence(defaultDisplay->getPhysicalId())
- : Fence::NO_FENCE;
+ const auto pacesetterDisplay = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(pacesetterId));
- auto presentFenceTime = pacesetterFrameTargeter.setPresentFence(presentFence);
+ std::shared_ptr<FenceTime> pacesetterPresentFenceTime =
+ presentFences.get(pacesetterId)
+ .transform([](const FenceTimePtr& ptr) { return ptr; })
+ .value_or(FenceTime::NO_FENCE);
+
+ std::shared_ptr<FenceTime> pacesetterGpuCompositionDoneFenceTime =
+ gpuCompositionDoneFences.get(pacesetterId)
+ .transform([](sp<Fence> fence) {
+ return std::make_shared<FenceTime>(std::move(fence));
+ })
+ .value_or(FenceTime::NO_FENCE);
+
const TimePoint presentTime = TimePoint::now();
// Set presentation information before calling Layer::releasePendingBuffer, such that jank
// information from previous' frame classification is already available when sending jank info
// to clients, so they get jank classification as early as possible.
- mFrameTimeline->setSfPresent(presentTime.ns(), presentFenceTime, glCompositionDoneFenceTime);
+ mFrameTimeline->setSfPresent(presentTime.ns(), pacesetterPresentFenceTime,
+ pacesetterGpuCompositionDoneFenceTime);
// We use the CompositionEngine::getLastFrameRefreshTimestamp() which might
// be sampled a little later than when we started doing work for this frame,
@@ -2853,9 +2899,9 @@
const TimePoint compositeTime =
TimePoint::fromNs(mCompositionEngine->getLastFrameRefreshTimestamp());
const Duration presentLatency =
- !getHwComposer().hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE)
- ? mPresentLatencyTracker.trackPendingFrame(compositeTime, presentFenceTime)
- : Duration::zero();
+ getHwComposer().hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE)
+ ? Duration::zero()
+ : mPresentLatencyTracker.trackPendingFrame(compositeTime, pacesetterPresentFenceTime);
const auto schedule = mScheduler->getVsyncSchedule();
const TimePoint vsyncDeadline = schedule->vsyncDeadlineAfter(presentTime);
@@ -2892,8 +2938,8 @@
mLayersWithBuffersRemoved.clear();
for (const auto& layer: mLayersWithQueuedFrames) {
- layer->onPostComposition(defaultDisplay, glCompositionDoneFenceTime, presentFenceTime,
- compositorTiming);
+ layer->onPostComposition(pacesetterDisplay.get(), pacesetterGpuCompositionDoneFenceTime,
+ pacesetterPresentFenceTime, compositorTiming);
layer->releasePendingBuffer(presentTime.ns());
}
@@ -2980,38 +3026,32 @@
mHdrLayerInfoChanged = false;
- mTransactionCallbackInvoker.addPresentFence(std::move(presentFence));
mTransactionCallbackInvoker.sendCallbacks(false /* onCommitOnly */);
mTransactionCallbackInvoker.clearCompletedTransactions();
mTimeStats->incrementTotalFrames();
- mTimeStats->setPresentFenceGlobal(presentFenceTime);
+ mTimeStats->setPresentFenceGlobal(pacesetterPresentFenceTime);
- {
+ for (auto&& [id, presentFence] : presentFences) {
ftl::FakeGuard guard(mStateLock);
- for (const auto& [id, physicalDisplay] : mPhysicalDisplays) {
- if (auto displayDevice = getDisplayDeviceLocked(id);
- displayDevice && displayDevice->isPoweredOn() && physicalDisplay.isInternal()) {
- auto presentFenceTimeI = defaultDisplay && defaultDisplay->getPhysicalId() == id
- ? std::move(presentFenceTime)
- : std::make_shared<FenceTime>(getHwComposer().getPresentFence(id));
- if (presentFenceTimeI->isValid()) {
- mScheduler->addPresentFence(id, std::move(presentFenceTimeI));
- }
- }
+ const bool isInternalDisplay =
+ mPhysicalDisplays.get(id).transform(&PhysicalDisplay::isInternal).value_or(false);
+
+ if (isInternalDisplay) {
+ mScheduler->addPresentFence(id, std::move(presentFence));
}
}
- const bool isDisplayConnected =
- defaultDisplay && getHwComposer().isConnected(defaultDisplay->getPhysicalId());
+ const bool hasPacesetterDisplay =
+ pacesetterDisplay && getHwComposer().isConnected(pacesetterId);
if (!hasSyncFramework) {
- if (isDisplayConnected && defaultDisplay->isPoweredOn()) {
- mScheduler->enableHardwareVsync(defaultDisplay->getPhysicalId());
+ if (hasPacesetterDisplay && pacesetterDisplay->isPoweredOn()) {
+ mScheduler->enableHardwareVsync(pacesetterId);
}
}
- if (isDisplayConnected && !defaultDisplay->isPoweredOn()) {
+ if (hasPacesetterDisplay && !pacesetterDisplay->isPoweredOn()) {
getRenderEngine().cleanupPostRender();
return;
}
@@ -4298,7 +4338,7 @@
const auto& transaction = *flushState.transaction;
const TimePoint desiredPresentTime = TimePoint::fromNs(transaction.desiredPresentTime);
- const TimePoint expectedPresentTime = mScheduler->pacesetterFrameTarget().expectedPresentTime();
+ const TimePoint expectedPresentTime = mScheduler->expectedPresentTimeForPacesetter();
using TransactionReadiness = TransactionHandler::TransactionReadiness;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index aeaeb47..3d46239 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -636,9 +636,12 @@
void onRefreshRateChangedDebug(const RefreshRateChangedDebugData&) override;
// ICompositor overrides:
- void configure() override;
- bool commit(const scheduler::FrameTarget&) override;
- CompositeResult composite(scheduler::FrameTargeter&) override;
+ void configure() override REQUIRES(kMainThreadContext);
+ bool commit(const scheduler::FrameTarget&) override REQUIRES(kMainThreadContext);
+ CompositeResultsPerDisplay composite(PhysicalDisplayId pacesetterId,
+ const scheduler::FrameTargeters&) override
+ REQUIRES(kMainThreadContext);
+
void sample() override;
// ISchedulerCallback overrides:
@@ -891,6 +894,14 @@
return findDisplay([id](const auto& display) { return display.getId() == id; });
}
+ std::shared_ptr<compositionengine::Display> getCompositionDisplayLocked(DisplayId id) const
+ REQUIRES(mStateLock) {
+ if (const auto display = getDisplayDeviceLocked(id)) {
+ return display->getCompositionDisplay();
+ }
+ return nullptr;
+ }
+
// Returns the primary display or (for foldables) the active display, assuming that the inner
// and outer displays have mutually exclusive power states.
sp<const DisplayDevice> getDefaultDisplayDeviceLocked() const REQUIRES(mStateLock) {
@@ -964,8 +975,8 @@
/*
* Compositing
*/
- void postComposition(scheduler::FrameTargeter&, nsecs_t presentStartTime)
- REQUIRES(kMainThreadContext);
+ void postComposition(PhysicalDisplayId pacesetterId, const scheduler::FrameTargeters&,
+ nsecs_t presentStartTime) REQUIRES(kMainThreadContext);
/*
* Display management
@@ -1298,7 +1309,7 @@
std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine;
- CompositionCoverageFlags mCompositionCoverage;
+ CompositionCoveragePerDisplay mCompositionCoverage;
// mMaxRenderTargetSize is only set once in init() so it doesn't need to be protected by
// any mutex.
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 00e92a1..28ac664 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -287,7 +287,10 @@
// ICompositor overrides:
void configure() override {}
bool commit(const scheduler::FrameTarget&) override { return false; }
- CompositeResult composite(scheduler::FrameTargeter&) override { return {}; }
+ CompositeResultsPerDisplay composite(PhysicalDisplayId,
+ const scheduler::FrameTargeters&) override {
+ return {};
+ }
void sample() override {}
// MessageQueue overrides:
@@ -474,25 +477,25 @@
&outWideColorGamutPixelFormat);
}
- void overrideHdrTypes(sp<IBinder> &display, FuzzedDataProvider *fdp) {
+ void overrideHdrTypes(const sp<IBinder>& display, FuzzedDataProvider* fdp) {
std::vector<ui::Hdr> hdrTypes;
hdrTypes.push_back(fdp->PickValueInArray(kHdrTypes));
mFlinger->overrideHdrTypes(display, hdrTypes);
}
- void getDisplayedContentSample(sp<IBinder> &display, FuzzedDataProvider *fdp) {
+ void getDisplayedContentSample(const sp<IBinder>& display, FuzzedDataProvider* fdp) {
DisplayedFrameStats outDisplayedFrameStats;
mFlinger->getDisplayedContentSample(display, fdp->ConsumeIntegral<uint64_t>(),
fdp->ConsumeIntegral<uint64_t>(),
&outDisplayedFrameStats);
}
- void getDisplayStats(sp<IBinder> &display) {
+ void getDisplayStats(const sp<IBinder>& display) {
android::DisplayStatInfo stats;
mFlinger->getDisplayStats(display, &stats);
}
- void getDisplayState(sp<IBinder> &display) {
+ void getDisplayState(const sp<IBinder>& display) {
ui::DisplayState displayState;
mFlinger->getDisplayState(display, &displayState);
}
@@ -506,12 +509,12 @@
android::ui::DynamicDisplayInfo dynamicDisplayInfo;
mFlinger->getDynamicDisplayInfoFromId(displayId, &dynamicDisplayInfo);
}
- void getDisplayNativePrimaries(sp<IBinder> &display) {
+ void getDisplayNativePrimaries(const sp<IBinder>& display) {
android::ui::DisplayPrimaries displayPrimaries;
mFlinger->getDisplayNativePrimaries(display, displayPrimaries);
}
- void getDesiredDisplayModeSpecs(sp<IBinder> &display) {
+ void getDesiredDisplayModeSpecs(const sp<IBinder>& display) {
gui::DisplayModeSpecs _;
mFlinger->getDesiredDisplayModeSpecs(display, &_);
}
@@ -523,7 +526,7 @@
return ids.front();
}
- std::pair<sp<IBinder>, int64_t> fuzzBoot(FuzzedDataProvider *fdp) {
+ std::pair<sp<IBinder>, PhysicalDisplayId> fuzzBoot(FuzzedDataProvider* fdp) {
mFlinger->callingThreadHasUnscopedSurfaceFlingerAccess(fdp->ConsumeBool());
const sp<Client> client = sp<Client>::make(mFlinger);
@@ -550,13 +553,13 @@
mFlinger->bootFinished();
- return {display, physicalDisplayId.value};
+ return {display, physicalDisplayId};
}
void fuzzSurfaceFlinger(const uint8_t *data, size_t size) {
FuzzedDataProvider mFdp(data, size);
- auto [display, displayId] = fuzzBoot(&mFdp);
+ const auto [display, displayId] = fuzzBoot(&mFdp);
sp<IGraphicBufferProducer> bufferProducer = sp<mock::GraphicBufferProducer>::make();
@@ -564,8 +567,8 @@
getDisplayStats(display);
getDisplayState(display);
- getStaticDisplayInfo(displayId);
- getDynamicDisplayInfo(displayId);
+ getStaticDisplayInfo(displayId.value);
+ getDynamicDisplayInfo(displayId.value);
getDisplayNativePrimaries(display);
mFlinger->setAutoLowLatencyMode(display, mFdp.ConsumeBool());
@@ -605,8 +608,9 @@
mFlinger->commitTransactions();
mFlinger->flushTransactionQueues(getFuzzedVsyncId(mFdp));
- scheduler::FrameTargeter frameTargeter(mFdp.ConsumeBool());
- mFlinger->postComposition(frameTargeter, mFdp.ConsumeIntegral<nsecs_t>());
+ scheduler::FrameTargeter frameTargeter(displayId, mFdp.ConsumeBool());
+ mFlinger->postComposition(displayId, ftl::init::map(displayId, &frameTargeter),
+ mFdp.ConsumeIntegral<nsecs_t>());
}
mFlinger->setTransactionFlags(mFdp.ConsumeIntegral<uint32_t>());
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index b1fd06f..4d1a5ff 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -50,7 +50,7 @@
constexpr uint16_t kRandomStringLength = 256;
constexpr std::chrono::duration kSyncPeriod(16ms);
-constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u);
+constexpr PhysicalDisplayId kDisplayId = PhysicalDisplayId::fromPort(42u);
template <typename T>
void dump(T* component, FuzzedDataProvider* fdp) {
@@ -177,9 +177,8 @@
uint16_t now = mFdp.ConsumeIntegral<uint16_t>();
uint16_t historySize = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX);
uint16_t minimumSamplesForPrediction = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX);
- scheduler::VSyncPredictor tracker{DEFAULT_DISPLAY_ID,
- mFdp.ConsumeIntegral<uint16_t>() /*period*/, historySize,
- minimumSamplesForPrediction,
+ scheduler::VSyncPredictor tracker{kDisplayId, mFdp.ConsumeIntegral<uint16_t>() /*period*/,
+ historySize, minimumSamplesForPrediction,
mFdp.ConsumeIntegral<uint32_t>() /*outlierTolerancePercent*/};
uint16_t period = mFdp.ConsumeIntegral<uint16_t>();
tracker.setPeriod(period);
@@ -251,7 +250,7 @@
void SchedulerFuzzer::fuzzVSyncReactor() {
std::shared_ptr<FuzzImplVSyncTracker> vSyncTracker = std::make_shared<FuzzImplVSyncTracker>();
- scheduler::VSyncReactor reactor(DEFAULT_DISPLAY_ID,
+ scheduler::VSyncReactor reactor(kDisplayId,
std::make_unique<ClockWrapper>(
std::make_shared<FuzzImplClock>()),
*vSyncTracker, mFdp.ConsumeIntegral<uint8_t>() /*pendingLimit*/,
@@ -408,7 +407,7 @@
}
void SchedulerFuzzer::fuzzFrameTargeter() {
- scheduler::FrameTargeter frameTargeter(mFdp.ConsumeBool());
+ scheduler::FrameTargeter frameTargeter(kDisplayId, mFdp.ConsumeBool());
const struct VsyncSource final : scheduler::IVsyncSource {
explicit VsyncSource(FuzzedDataProvider& fuzzer) : fuzzer(fuzzer) {}
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index 359e2ab..1dcf222 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -36,7 +36,10 @@
struct NoOpCompositor final : ICompositor {
void configure() override {}
bool commit(const scheduler::FrameTarget&) override { return false; }
- CompositeResult composite(scheduler::FrameTargeter&) override { return {}; }
+ CompositeResultsPerDisplay composite(PhysicalDisplayId,
+ const scheduler::FrameTargeters&) override {
+ return {};
+ }
void sample() override {}
} gNoOpCompositor;
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index fa7a947..f3c9d0d 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -181,7 +181,10 @@
// ICompositor overrides:
void configure() override {}
bool commit(const scheduler::FrameTarget&) override { return false; }
- CompositeResult composite(scheduler::FrameTargeter&) override { return {}; }
+ CompositeResultsPerDisplay composite(PhysicalDisplayId,
+ const scheduler::FrameTargeters&) override {
+ return {};
+ }
void sample() override {}
};
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 909b8b8..9b3a893 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -359,7 +359,10 @@
* Forwarding for functions being tested
*/
- void configure() { mFlinger->configure(); }
+ void configure() {
+ ftl::FakeGuard guard(kMainThreadContext);
+ mFlinger->configure();
+ }
void configureAndCommit() {
configure();
@@ -368,8 +371,14 @@
void commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime,
bool composite = false) {
+ ftl::FakeGuard guard(kMainThreadContext);
+
+ const auto displayIdOpt = mScheduler->pacesetterDisplayId();
+ LOG_ALWAYS_FATAL_IF(!displayIdOpt);
+ const auto displayId = *displayIdOpt;
+
constexpr bool kBackpressureGpuComposition = true;
- scheduler::FrameTargeter frameTargeter(kBackpressureGpuComposition);
+ scheduler::FrameTargeter frameTargeter(displayId, kBackpressureGpuComposition);
frameTargeter.beginFrame({.frameBeginTime = frameTime,
.vsyncId = vsyncId,
@@ -380,7 +389,7 @@
mFlinger->commit(frameTargeter.target());
if (composite) {
- mFlinger->composite(frameTargeter);
+ mFlinger->composite(displayId, ftl::init::map(displayId, &frameTargeter));
}
}