diff options
20 files changed, 886 insertions, 254 deletions
diff --git a/libs/ui/include/ui/FenceTime.h b/libs/ui/include/ui/FenceTime.h index ac75f431a0..334106f0cf 100644 --- a/libs/ui/include/ui/FenceTime.h +++ b/libs/ui/include/ui/FenceTime.h @@ -142,6 +142,8 @@ private: std::atomic<nsecs_t> mSignalTime{Fence::SIGNAL_TIME_INVALID}; }; +using FenceTimePtr = std::shared_ptr<FenceTime>; + // A queue of FenceTimes that are expected to signal in FIFO order. // Only maintains a queue of weak pointers so it doesn't keep references // to Fences on its own. @@ -190,8 +192,15 @@ private: // before the new one is added. class FenceToFenceTimeMap { public: - // Create a new FenceTime with that wraps the provided Fence. - std::shared_ptr<FenceTime> createFenceTimeForTest(const sp<Fence>& fence); + using FencePair = std::pair<sp<Fence>, FenceTimePtr>; + + FencePair makePendingFenceForTest() { + const auto fence = sp<Fence>::make(); + return {fence, createFenceTimeForTest(fence)}; + } + + // Create a new FenceTime that wraps the provided Fence. + FenceTimePtr createFenceTimeForTest(const sp<Fence>&); // Signals all FenceTimes created through this class that are wrappers // around |fence|. @@ -205,7 +214,6 @@ private: std::unordered_map<Fence*, std::vector<std::weak_ptr<FenceTime>>> mMap; }; - -}; // namespace android +} // namespace android #endif // ANDROID_FENCE_TIME_H diff --git a/services/surfaceflinger/Scheduler/Android.bp b/services/surfaceflinger/Scheduler/Android.bp index d5d868839f..6d2586ae9c 100644 --- a/services/surfaceflinger/Scheduler/Android.bp +++ b/services/surfaceflinger/Scheduler/Android.bp @@ -40,6 +40,7 @@ cc_library_static { name: "libscheduler", defaults: ["libscheduler_defaults"], srcs: [ + "src/FrameTargeter.cpp", "src/PresentLatencyTracker.cpp", "src/Timer.cpp", ], @@ -52,6 +53,7 @@ cc_test { test_suites: ["device-tests"], defaults: ["libscheduler_defaults"], srcs: [ + "tests/FrameTargeterTest.cpp", "tests/PresentLatencyTrackerTest.cpp", "tests/TimerTest.cpp", ], diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 918d401059..41639b6c67 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -171,14 +171,21 @@ void Scheduler::run() { void Scheduler::onFrameSignal(ICompositor& compositor, VsyncId vsyncId, TimePoint expectedVsyncTime) { - const TimePoint frameTime = SchedulerClock::now(); - - if (!compositor.commit(frameTime, vsyncId, expectedVsyncTime)) { + mPacesetterFrameTargeter.beginFrame({.frameBeginTime = SchedulerClock::now(), + .vsyncId = vsyncId, + .expectedVsyncTime = expectedVsyncTime, + .sfWorkDuration = + mVsyncModulator->getVsyncConfig().sfWorkDuration}, + *getVsyncSchedule()); + + if (!compositor.commit(mPacesetterFrameTargeter.target())) { return; } - compositor.composite(frameTime, vsyncId); + const auto compositeResult = compositor.composite(mPacesetterFrameTargeter); compositor.sample(); + + mPacesetterFrameTargeter.endFrame(compositeResult); } std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const { @@ -188,23 +195,23 @@ std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const { .getFrameRateOverrideForUid(uid, supportsFrameRateOverrideByContent); } -bool Scheduler::isVsyncValid(TimePoint expectedVsyncTimestamp, uid_t uid) const { +bool Scheduler::isVsyncValid(TimePoint expectedVsyncTime, uid_t uid) const { const auto frameRate = getFrameRateOverride(uid); if (!frameRate.has_value()) { return true; } ATRACE_FORMAT("%s uid: %d frameRate: %s", __func__, uid, to_string(*frameRate).c_str()); - return getVsyncSchedule()->getTracker().isVSyncInPhase(expectedVsyncTimestamp.ns(), *frameRate); + return getVsyncSchedule()->getTracker().isVSyncInPhase(expectedVsyncTime.ns(), *frameRate); } -bool Scheduler::isVsyncInPhase(TimePoint timePoint, const Fps frameRate) const { - return getVsyncSchedule()->getTracker().isVSyncInPhase(timePoint.ns(), frameRate); +bool Scheduler::isVsyncInPhase(TimePoint expectedVsyncTime, Fps frameRate) const { + return getVsyncSchedule()->getTracker().isVSyncInPhase(expectedVsyncTime.ns(), frameRate); } impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const { - return [this](nsecs_t expectedVsyncTimestamp, uid_t uid) { - return !isVsyncValid(TimePoint::fromNs(expectedVsyncTimestamp), uid); + return [this](nsecs_t expectedVsyncTime, uid_t uid) { + return !isVsyncValid(TimePoint::fromNs(expectedVsyncTime), uid); }; } @@ -716,6 +723,8 @@ void Scheduler::dump(utils::Dumper& dumper) const { mFrameRateOverrideMappings.dump(dumper); dumper.eol(); + + mPacesetterFrameTargeter.dump(dumper); } void Scheduler::dumpVsync(std::string& out) const { diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index a1354fa6f7..17e9ceaf66 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -35,6 +35,7 @@ #include <ftl/fake_guard.h> #include <ftl/optional.h> #include <scheduler/Features.h> +#include <scheduler/FrameTargeter.h> #include <scheduler/Time.h> #include <scheduler/VsyncConfig.h> #include <ui/DisplayId.h> @@ -249,9 +250,11 @@ public: return std::const_pointer_cast<VsyncSchedule>(std::as_const(*this).getVsyncSchedule(idOpt)); } + const FrameTarget& pacesetterFrameTarget() { return mPacesetterFrameTargeter.target(); } + // Returns true if a given vsync timestamp is considered valid vsync // for a given uid - bool isVsyncValid(TimePoint expectedVsyncTimestamp, uid_t uid) const; + bool isVsyncValid(TimePoint expectedVsyncTime, uid_t uid) const; bool isVsyncInPhase(TimePoint expectedVsyncTime, Fps frameRate) const; @@ -446,6 +449,8 @@ private: 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/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h index dcf92cc8fd..1b1396d6ab 100644 --- a/services/surfaceflinger/Scheduler/VsyncSchedule.h +++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h @@ -20,14 +20,17 @@ #include <memory> #include <string> -#include <ThreadContext.h> #include <android-base/thread_annotations.h> #include <ThreadContext.h> #include <ftl/enum.h> #include <ftl/optional.h> +#include <ui/DisplayId.h> + #include <scheduler/Features.h> +#include <scheduler/IVsyncSource.h> #include <scheduler/Time.h> -#include <ui/DisplayId.h> + +#include "ThreadContext.h" namespace android { class EventThreadTest; @@ -49,15 +52,16 @@ using VsyncDispatch = VSyncDispatch; using VsyncTracker = VSyncTracker; // Schedule that synchronizes to hardware VSYNC of a physical display. -class VsyncSchedule { +class VsyncSchedule final : public IVsyncSource { public: using RequestHardwareVsync = std::function<void(PhysicalDisplayId, bool enabled)>; VsyncSchedule(PhysicalDisplayId, FeatureFlags, RequestHardwareVsync); ~VsyncSchedule(); - Period period() const; - TimePoint vsyncDeadlineAfter(TimePoint) const; + // IVsyncSource overrides: + Period period() const override; + TimePoint vsyncDeadlineAfter(TimePoint) const override; // Inform the schedule that the period is changing and the schedule needs to recalibrate // itself. The schedule will end the period transition internally. This will diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Features.h b/services/surfaceflinger/Scheduler/include/scheduler/Features.h index b3a6a606c3..200407d1a6 100644 --- a/services/surfaceflinger/Scheduler/include/scheduler/Features.h +++ b/services/surfaceflinger/Scheduler/include/scheduler/Features.h @@ -23,10 +23,11 @@ namespace android::scheduler { enum class Feature : std::uint8_t { - kPresentFences = 0b1, - kKernelIdleTimer = 0b10, - kContentDetection = 0b100, - kTracePredictedVsync = 0b1000, + kPresentFences = 1 << 0, + kKernelIdleTimer = 1 << 1, + kContentDetection = 1 << 2, + kTracePredictedVsync = 1 << 3, + kBackpressureGpuComposition = 1 << 4, }; using FeatureFlags = ftl::Flags<Feature>; diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h new file mode 100644 index 0000000000..85f2e64faa --- /dev/null +++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h @@ -0,0 +1,147 @@ +/* + * Copyright 2023 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. + */ + +#pragma once + +#include <array> +#include <atomic> +#include <memory> + +#include <ui/Fence.h> +#include <ui/FenceTime.h> + +#include <scheduler/Time.h> +#include <scheduler/VsyncId.h> +#include <scheduler/interface/CompositeResult.h> + +// TODO(b/185536303): Pull to FTL. +#include "../../../TracedOrdinal.h" +#include "../../../Utils/Dumper.h" + +namespace android::scheduler { + +struct IVsyncSource; + +// Read-only interface to the metrics computed by FrameTargeter for the latest frame. +class FrameTarget { +public: + VsyncId vsyncId() const { return mVsyncId; } + + // The time when the frame actually began, as opposed to when it had been scheduled to begin. + TimePoint frameBeginTime() const { return mFrameBeginTime; } + + // Relative to when the frame actually began, as opposed to when it had been scheduled to begin. + Duration expectedFrameDuration() const { return mExpectedPresentTime - mFrameBeginTime; } + + TimePoint expectedPresentTime() const { return mExpectedPresentTime; } + + // The time of the VSYNC that preceded this frame. See `presentFenceForPastVsync` for details. + TimePoint pastVsyncTime(Period vsyncPeriod) const; + + // Equivalent to `pastVsyncTime` unless running N VSYNCs ahead. + TimePoint previousFrameVsyncTime(Period vsyncPeriod) const { + return mExpectedPresentTime - vsyncPeriod; + } + + // The present fence for the frame that had targeted the most recent VSYNC before this frame. + // If the target VSYNC for any given frame is more than `vsyncPeriod` in the future, then the + // VSYNC of at least one previous frame has not yet passed. In other words, this is NOT the + // `presentFenceForPreviousFrame` if running N VSYNCs ahead, but the one that should have been + // signaled by now (unless that frame missed). + const FenceTimePtr& presentFenceForPastVsync(Period vsyncPeriod) const; + + // Equivalent to `presentFenceForPastVsync` unless running N VSYNCs ahead. + const FenceTimePtr& presentFenceForPreviousFrame() const { + return mPresentFences.front().fenceTime; + } + + bool wouldPresentEarly(Period vsyncPeriod) const; + + bool isFramePending() const { return mFramePending; } + bool didMissFrame() const { return mFrameMissed; } + bool didMissHwcFrame() const { return mHwcFrameMissed && !mGpuFrameMissed; } + +protected: + ~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}; + + struct FenceWithFenceTime { + sp<Fence> fence = Fence::NO_FENCE; + FenceTimePtr fenceTime = FenceTime::NO_FENCE; + }; + std::array<FenceWithFenceTime, 2> mPresentFences; + +private: + template <int N> + inline bool targetsVsyncsAhead(Period vsyncPeriod) const { + static_assert(N > 1); + return expectedFrameDuration() > (N - 1) * vsyncPeriod; + } +}; + +// 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) {} + + const FrameTarget& target() const { return *this; } + + struct BeginFrameArgs { + TimePoint frameBeginTime; + VsyncId vsyncId; + TimePoint expectedVsyncTime; + Duration sfWorkDuration; + }; + + void beginFrame(const BeginFrameArgs&, const IVsyncSource&); + + // TODO(b/241285191): Merge with FrameTargeter::endFrame. + FenceTimePtr setPresentFence(sp<Fence>); + + void endFrame(const CompositeResult&); + + void dump(utils::Dumper&) const; + +private: + friend class FrameTargeterTest; + + // For tests. + using IsFencePendingFuncPtr = bool (*)(const FenceTimePtr&, int graceTimeMs); + void beginFrame(const BeginFrameArgs&, const IVsyncSource&, IsFencePendingFuncPtr); + FenceTimePtr setPresentFence(sp<Fence>, FenceTimePtr); + + static bool isFencePending(const FenceTimePtr&, int graceTimeMs); + + const bool mBackpressureGpuComposition; + + TimePoint mScheduledPresentTime; + CompositionCoverageFlags mCompositionCoverage; + + std::atomic_uint mFrameMissedCount = 0; + std::atomic_uint mHwcFrameMissedCount = 0; + std::atomic_uint mGpuFrameMissedCount = 0; +}; + +} // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h b/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h new file mode 100644 index 0000000000..bb2de75ac4 --- /dev/null +++ b/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h @@ -0,0 +1,31 @@ +/* + * Copyright 2023 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. + */ + +#pragma once + +#include <scheduler/Time.h> + +namespace android::scheduler { + +struct IVsyncSource { + virtual Period period() const = 0; + virtual TimePoint vsyncDeadlineAfter(TimePoint) const = 0; + +protected: + ~IVsyncSource() = default; +}; + +} // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositeResult.h b/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositeResult.h new file mode 100644 index 0000000000..f795f1f09d --- /dev/null +++ b/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositeResult.h @@ -0,0 +1,27 @@ +/* + * Copyright 2023 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. + */ + +#pragma once + +#include <scheduler/interface/CompositionCoverage.h> + +namespace android { + +struct CompositeResult { + CompositionCoverageFlags compositionCoverage; +}; + +} // namespace android diff --git a/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h b/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h index cc419259ef..26960766e5 100644 --- a/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h +++ b/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h @@ -18,8 +18,15 @@ #include <scheduler/Time.h> #include <scheduler/VsyncId.h> +#include <scheduler/interface/CompositeResult.h> namespace android { +namespace scheduler { + +class FrameTarget; +class FrameTargeter; + +} // namespace scheduler struct ICompositor { // Configures physical displays, processing hotplug and/or mode setting via the Composer HAL. @@ -27,11 +34,11 @@ struct ICompositor { // Commits transactions for layers and displays. Returns whether any state has been invalidated, // i.e. whether a frame should be composited for each display. - virtual bool commit(TimePoint frameTime, VsyncId, TimePoint expectedVsyncTime) = 0; + virtual bool commit(const scheduler::FrameTarget&) = 0; // Composites a frame for each display. CompositionEngine performs GPU and/or HAL composition // via RenderEngine and the Composer HAL, respectively. - virtual void composite(TimePoint frameTime, VsyncId) = 0; + virtual CompositeResult composite(scheduler::FrameTargeter&) = 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 new file mode 100644 index 0000000000..7138afdf8b --- /dev/null +++ b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp @@ -0,0 +1,152 @@ +/* + * Copyright 2023 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. + */ + +#include <gui/TraceUtils.h> + +#include <scheduler/FrameTargeter.h> +#include <scheduler/IVsyncSource.h> + +namespace android::scheduler { + +TimePoint FrameTarget::pastVsyncTime(Period vsyncPeriod) const { + // TODO(b/267315508): Generalize to N VSYNCs. + const int shift = static_cast<int>(targetsVsyncsAhead<2>(vsyncPeriod)); + return mExpectedPresentTime - Period::fromNs(vsyncPeriod.ns() << shift); +} + +const FenceTimePtr& FrameTarget::presentFenceForPastVsync(Period vsyncPeriod) const { + // TODO(b/267315508): Generalize to N VSYNCs. + const size_t i = static_cast<size_t>(targetsVsyncsAhead<2>(vsyncPeriod)); + return mPresentFences[i].fenceTime; +} + +bool FrameTarget::wouldPresentEarly(Period vsyncPeriod) const { + // TODO(b/241285475): Since this is called during `composite`, the calls to `targetsVsyncsAhead` + // should use `TimePoint::now()` in case of delays since `mFrameBeginTime`. + + // TODO(b/267315508): Generalize to N VSYNCs. + if (targetsVsyncsAhead<3>(vsyncPeriod)) { + return true; + } + + const auto fence = presentFenceForPastVsync(vsyncPeriod); + return fence->isValid() && fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING; +} + +void FrameTargeter::beginFrame(const BeginFrameArgs& args, const IVsyncSource& vsyncSource) { + return beginFrame(args, vsyncSource, &FrameTargeter::isFencePending); +} + +void FrameTargeter::beginFrame(const BeginFrameArgs& args, const IVsyncSource& vsyncSource, + IsFencePendingFuncPtr isFencePendingFuncPtr) { + mVsyncId = args.vsyncId; + mFrameBeginTime = args.frameBeginTime; + + // The `expectedVsyncTime`, which was predicted when this frame was scheduled, is normally in + // the future relative to `frameBeginTime`, but may not be for delayed frames. Adjust + // `mExpectedPresentTime` accordingly, but not `mScheduledPresentTime`. + const TimePoint lastScheduledPresentTime = mScheduledPresentTime; + mScheduledPresentTime = args.expectedVsyncTime; + + const Period vsyncPeriod = vsyncSource.period(); + + // Calculate the expected present time once and use the cached value throughout this frame to + // make sure all layers are seeing this same value. + if (args.expectedVsyncTime >= args.frameBeginTime) { + mExpectedPresentTime = args.expectedVsyncTime; + } else { + mExpectedPresentTime = vsyncSource.vsyncDeadlineAfter(args.frameBeginTime); + if (args.sfWorkDuration > vsyncPeriod) { + // Inflate the expected present time if we're targeting the next VSYNC. + mExpectedPresentTime += vsyncPeriod; + } + } + + ATRACE_FORMAT("%s %" PRId64 " vsyncIn %.2fms%s", __func__, ftl::to_underlying(args.vsyncId), + ticks<std::milli, float>(mExpectedPresentTime - TimePoint::now()), + mExpectedPresentTime == args.expectedVsyncTime ? "" : " (adjusted)"); + + const FenceTimePtr& pastPresentFence = presentFenceForPastVsync(vsyncPeriod); + + // In cases where the present fence is about to fire, give it a small grace period instead of + // giving up on the frame. + // + // TODO(b/280667110): The grace period should depend on `sfWorkDuration` and `vsyncPeriod` being + // approximately equal, not whether backpressure propagation is enabled. + const int graceTimeForPresentFenceMs = static_cast<int>( + mBackpressureGpuComposition || !mCompositionCoverage.test(CompositionCoverage::Gpu)); + + // Pending frames may trigger backpressure propagation. + const auto& isFencePending = *isFencePendingFuncPtr; + mFramePending = pastPresentFence != FenceTime::NO_FENCE && + isFencePending(pastPresentFence, graceTimeForPresentFenceMs); + + // A frame is missed if the prior frame is still pending. If no longer pending, then we still + // count the frame as missed if the predicted present time was further in the past than when the + // fence actually fired. Add some slop to correct for drift. This should generally be smaller + // than a typical frame duration, but should not be so small that it reports reasonable drift as + // a missed frame. + mFrameMissed = mFramePending || [&] { + const nsecs_t pastPresentTime = pastPresentFence->getSignalTime(); + if (pastPresentTime < 0) return false; + const nsecs_t frameMissedSlop = vsyncPeriod.ns() / 2; + return lastScheduledPresentTime.ns() < pastPresentTime - frameMissedSlop; + }(); + + mHwcFrameMissed = mFrameMissed && mCompositionCoverage.test(CompositionCoverage::Hwc); + mGpuFrameMissed = mFrameMissed && mCompositionCoverage.test(CompositionCoverage::Gpu); + + if (mFrameMissed) mFrameMissedCount++; + if (mHwcFrameMissed) mHwcFrameMissedCount++; + if (mGpuFrameMissed) mGpuFrameMissedCount++; +} + +void FrameTargeter::endFrame(const CompositeResult& result) { + mCompositionCoverage = result.compositionCoverage; +} + +FenceTimePtr FrameTargeter::setPresentFence(sp<Fence> presentFence) { + auto presentFenceTime = std::make_shared<FenceTime>(presentFence); + return setPresentFence(std::move(presentFence), std::move(presentFenceTime)); +} + +FenceTimePtr FrameTargeter::setPresentFence(sp<Fence> presentFence, FenceTimePtr presentFenceTime) { + mPresentFences[1] = mPresentFences[0]; + mPresentFences[0] = {std::move(presentFence), presentFenceTime}; + return presentFenceTime; +} + +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)); + dumper.dump({}, "GPU missed frame count: " + std::to_string(mGpuFrameMissedCount)); +} + +bool FrameTargeter::isFencePending(const FenceTimePtr& fence, int graceTimeMs) { + ATRACE_CALL(); + const status_t status = fence->wait(graceTimeMs); + + // This is the same as Fence::Status::Unsignaled, but it saves a call to getStatus, + // which calls wait(0) again internally. + return status == -ETIME; +} + +} // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp new file mode 100644 index 0000000000..908f214e85 --- /dev/null +++ b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp @@ -0,0 +1,301 @@ +/* + * Copyright 2023 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. + */ + +#include <ftl/optional.h> +#include <gtest/gtest.h> + +#include <scheduler/Fps.h> +#include <scheduler/FrameTargeter.h> +#include <scheduler/IVsyncSource.h> + +using namespace std::chrono_literals; + +namespace android::scheduler { +namespace { + +struct VsyncSource final : IVsyncSource { + VsyncSource(Period period, TimePoint deadline) : vsyncPeriod(period), vsyncDeadline(deadline) {} + + const Period vsyncPeriod; + const TimePoint vsyncDeadline; + + Period period() const override { return vsyncPeriod; } + TimePoint vsyncDeadlineAfter(TimePoint) const override { return vsyncDeadline; } +}; + +} // namespace + +class FrameTargeterTest : public testing::Test { +public: + const auto& target() const { return mTargeter.target(); } + + struct Frame { + Frame(FrameTargeterTest* testPtr, VsyncId vsyncId, TimePoint& frameBeginTime, + Duration frameDuration, Fps refreshRate, + FrameTargeter::IsFencePendingFuncPtr isFencePendingFuncPtr = Frame::fenceSignaled, + const ftl::Optional<VsyncSource>& vsyncSourceOpt = std::nullopt) + : testPtr(testPtr), frameBeginTime(frameBeginTime), period(refreshRate.getPeriod()) { + const FrameTargeter::BeginFrameArgs args{.frameBeginTime = frameBeginTime, + .vsyncId = vsyncId, + .expectedVsyncTime = + frameBeginTime + frameDuration, + .sfWorkDuration = 10ms}; + + testPtr->mTargeter.beginFrame(args, + vsyncSourceOpt + .or_else([&] { + return std::make_optional( + VsyncSource(period, + args.expectedVsyncTime)); + }) + .value(), + isFencePendingFuncPtr); + } + + FenceTimePtr end(CompositionCoverage coverage = CompositionCoverage::Hwc) { + if (ended) return nullptr; + ended = true; + + auto [fence, fenceTime] = testPtr->mFenceMap.makePendingFenceForTest(); + testPtr->mTargeter.setPresentFence(std::move(fence), fenceTime); + + testPtr->mTargeter.endFrame({.compositionCoverage = coverage}); + return fenceTime; + } + + ~Frame() { + end(); + frameBeginTime += period; + } + + static bool fencePending(const FenceTimePtr&, int) { return true; } + static bool fenceSignaled(const FenceTimePtr&, int) { return false; } + + FrameTargeterTest* const testPtr; + + TimePoint& frameBeginTime; + const Period period; + + bool ended = false; + }; + +private: + FenceToFenceTimeMap mFenceMap; + + static constexpr bool kBackpressureGpuComposition = true; + FrameTargeter mTargeter{kBackpressureGpuComposition}; +}; + +TEST_F(FrameTargeterTest, targetsFrames) { + VsyncId vsyncId{42}; + { + TimePoint frameBeginTime(989ms); + const Frame frame(this, vsyncId++, frameBeginTime, 10ms, 60_Hz); + + EXPECT_EQ(target().vsyncId(), VsyncId{42}); + EXPECT_EQ(target().frameBeginTime(), TimePoint(989ms)); + EXPECT_EQ(target().expectedPresentTime(), TimePoint(999ms)); + EXPECT_EQ(target().expectedFrameDuration(), 10ms); + } + { + TimePoint frameBeginTime(1100ms); + const Frame frame(this, vsyncId++, frameBeginTime, 11ms, 60_Hz); + + EXPECT_EQ(target().vsyncId(), VsyncId{43}); + EXPECT_EQ(target().frameBeginTime(), TimePoint(1100ms)); + EXPECT_EQ(target().expectedPresentTime(), TimePoint(1111ms)); + EXPECT_EQ(target().expectedFrameDuration(), 11ms); + } +} + +TEST_F(FrameTargeterTest, inflatesExpectedPresentTime) { + // Negative such that `expectedVsyncTime` is in the past. + constexpr Duration kFrameDuration = -3ms; + TimePoint frameBeginTime(777ms); + + constexpr Fps kRefreshRate = 120_Hz; + const VsyncSource vsyncSource(kRefreshRate.getPeriod(), frameBeginTime + 5ms); + const Frame frame(this, VsyncId{123}, frameBeginTime, kFrameDuration, kRefreshRate, + Frame::fenceSignaled, vsyncSource); + + EXPECT_EQ(target().expectedPresentTime(), vsyncSource.vsyncDeadline + vsyncSource.vsyncPeriod); +} + +TEST_F(FrameTargeterTest, recallsPastVsync) { + VsyncId vsyncId{111}; + TimePoint frameBeginTime(1000ms); + constexpr Fps kRefreshRate = 60_Hz; + constexpr Period kPeriod = kRefreshRate.getPeriod(); + constexpr Duration kFrameDuration = 13ms; + + for (int n = 5; n-- > 0;) { + Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate); + const auto fence = frame.end(); + + EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - kPeriod); + EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), fence); + } +} + +TEST_F(FrameTargeterTest, recallsPastVsyncTwoVsyncsAhead) { + VsyncId vsyncId{222}; + TimePoint frameBeginTime(2000ms); + constexpr Fps kRefreshRate = 120_Hz; + constexpr Period kPeriod = kRefreshRate.getPeriod(); + constexpr Duration kFrameDuration = 10ms; + + FenceTimePtr previousFence = FenceTime::NO_FENCE; + + for (int n = 5; n-- > 0;) { + Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate); + const auto fence = frame.end(); + + EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - 2 * kPeriod); + EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), previousFence); + + previousFence = fence; + } +} + +TEST_F(FrameTargeterTest, doesNotDetectEarlyPresentIfNoFence) { + constexpr Period kPeriod = (60_Hz).getPeriod(); + EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), FenceTime::NO_FENCE); + EXPECT_FALSE(target().wouldPresentEarly(kPeriod)); +} + +TEST_F(FrameTargeterTest, detectsEarlyPresent) { + VsyncId vsyncId{333}; + TimePoint frameBeginTime(3000ms); + constexpr Fps kRefreshRate = 60_Hz; + constexpr Period kPeriod = kRefreshRate.getPeriod(); + + // The target is not early while past present fences are pending. + for (int n = 3; n-- > 0;) { + const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate); + EXPECT_FALSE(target().wouldPresentEarly(kPeriod)); + } + + // The target is early if the past present fence was signaled. + Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate); + const auto fence = frame.end(); + fence->signalForTest(frameBeginTime.ns()); + + EXPECT_TRUE(target().wouldPresentEarly(kPeriod)); +} + +TEST_F(FrameTargeterTest, detectsEarlyPresentTwoVsyncsAhead) { + VsyncId vsyncId{444}; + TimePoint frameBeginTime(4000ms); + constexpr Fps kRefreshRate = 120_Hz; + constexpr Period kPeriod = kRefreshRate.getPeriod(); + + // The target is not early while past present fences are pending. + for (int n = 3; n-- > 0;) { + const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate); + EXPECT_FALSE(target().wouldPresentEarly(kPeriod)); + } + + Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate); + const auto fence = frame.end(); + fence->signalForTest(frameBeginTime.ns()); + + // The target is two VSYNCs ahead, so the past present fence is still pending. + EXPECT_FALSE(target().wouldPresentEarly(kPeriod)); + + { const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate); } + + // The target is early if the past present fence was signaled. + EXPECT_TRUE(target().wouldPresentEarly(kPeriod)); +} + +TEST_F(FrameTargeterTest, detectsEarlyPresentThreeVsyncsAhead) { + TimePoint frameBeginTime(5000ms); + constexpr Fps kRefreshRate = 144_Hz; + constexpr Period kPeriod = kRefreshRate.getPeriod(); + + const Frame frame(this, VsyncId{555}, frameBeginTime, 16ms, kRefreshRate); + + // The target is more than two VSYNCs ahead, but present fences are not tracked that far back. + EXPECT_TRUE(target().wouldPresentEarly(kPeriod)); +} + +TEST_F(FrameTargeterTest, detectsMissedFrames) { + VsyncId vsyncId{555}; + TimePoint frameBeginTime(5000ms); + constexpr Fps kRefreshRate = 60_Hz; + constexpr Period kPeriod = kRefreshRate.getPeriod(); + + EXPECT_FALSE(target().isFramePending()); + EXPECT_FALSE(target().didMissFrame()); + EXPECT_FALSE(target().didMissHwcFrame()); + + { + const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate); + EXPECT_FALSE(target().isFramePending()); + + // The frame did not miss if the past present fence is invalid. + EXPECT_FALSE(target().didMissFrame()); + EXPECT_FALSE(target().didMissHwcFrame()); + } + { + Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, Frame::fencePending); + EXPECT_TRUE(target().isFramePending()); + + // The frame missed if the past present fence is pending. + EXPECT_TRUE(target().didMissFrame()); + EXPECT_TRUE(target().didMissHwcFrame()); + + frame.end(CompositionCoverage::Gpu); + } + { + const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, Frame::fencePending); + EXPECT_TRUE(target().isFramePending()); + + // The GPU frame missed if the past present fence is pending. + EXPECT_TRUE(target().didMissFrame()); + EXPECT_FALSE(target().didMissHwcFrame()); + } + { + Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate); + EXPECT_FALSE(target().isFramePending()); + + const auto fence = frame.end(); + const auto expectedPresentTime = target().expectedPresentTime(); + fence->signalForTest(expectedPresentTime.ns() + kPeriod.ns() / 2 + 1); + } + { + Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate); + EXPECT_FALSE(target().isFramePending()); + + const auto fence = frame.end(); + const auto expectedPresentTime = target().expectedPresentTime(); + fence->signalForTest(expectedPresentTime.ns() + kPeriod.ns() / 2); + + // The frame missed if the past present fence was signaled but not within slop. + EXPECT_TRUE(target().didMissFrame()); + EXPECT_TRUE(target().didMissHwcFrame()); + } + { + Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate); + EXPECT_FALSE(target().isFramePending()); + + // The frame did not miss if the past present fence was signaled within slop. + EXPECT_FALSE(target().didMissFrame()); + EXPECT_FALSE(target().didMissHwcFrame()); + } +} + +} // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/tests/PresentLatencyTrackerTest.cpp b/services/surfaceflinger/Scheduler/tests/PresentLatencyTrackerTest.cpp index 8952ca99ab..df2ea83112 100644 --- a/services/surfaceflinger/Scheduler/tests/PresentLatencyTrackerTest.cpp +++ b/services/surfaceflinger/Scheduler/tests/PresentLatencyTrackerTest.cpp @@ -23,16 +23,6 @@ #include <ui/FenceTime.h> namespace android::scheduler { -namespace { - -using FencePair = std::pair<sp<Fence>, std::shared_ptr<FenceTime>>; - -FencePair makePendingFence(FenceToFenceTimeMap& fenceMap) { - const auto fence = sp<Fence>::make(); - return {fence, fenceMap.createFenceTimeForTest(fence)}; -} - -} // namespace TEST(PresentLatencyTrackerTest, skipsInvalidFences) { PresentLatencyTracker tracker; @@ -43,7 +33,7 @@ TEST(PresentLatencyTrackerTest, skipsInvalidFences) { EXPECT_EQ(tracker.trackPendingFrame(kCompositeTime, FenceTime::NO_FENCE), Duration::zero()); FenceToFenceTimeMap fenceMap; - const auto [fence, fenceTime] = makePendingFence(fenceMap); + const auto [fence, fenceTime] = fenceMap.makePendingFenceForTest(); EXPECT_EQ(tracker.trackPendingFrame(kCompositeTime, fenceTime), Duration::zero()); fenceTime->signalForTest(9999); @@ -56,8 +46,9 @@ TEST(PresentLatencyTrackerTest, tracksPendingFrames) { PresentLatencyTracker tracker; FenceToFenceTimeMap fenceMap; - std::array<FencePair, PresentLatencyTracker::kMaxPendingFrames> fences; - std::generate(fences.begin(), fences.end(), [&fenceMap] { return makePendingFence(fenceMap); }); + std::array<FenceToFenceTimeMap::FencePair, PresentLatencyTracker::kMaxPendingFrames> fences; + std::generate(fences.begin(), fences.end(), + [&fenceMap] { return fenceMap.makePendingFenceForTest(); }); // The present latency is 0 if all fences are pending. const TimePoint kCompositeTime = TimePoint::fromNs(1234); @@ -71,7 +62,7 @@ TEST(PresentLatencyTrackerTest, tracksPendingFrames) { fences[i].second->signalForTest(kCompositeTime.ns() + static_cast<nsecs_t>(i)); } - const auto fence = makePendingFence(fenceMap); + const auto fence = fenceMap.makePendingFenceForTest(); // ...then the present latency is measured using the latest frame. constexpr Duration kPresentLatency = Duration::fromNs(static_cast<nsecs_t>(kPresentCount) - 1); diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 6113a09f2b..975b54a27d 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -77,6 +77,7 @@ #include <processgroup/processgroup.h> #include <renderengine/RenderEngine.h> #include <renderengine/impl/ExternalTexture.h> +#include <scheduler/FrameTargeter.h> #include <sys/types.h> #include <ui/ColorSpace.h> #include <ui/DataspaceUtils.h> @@ -2167,44 +2168,6 @@ void SurfaceFlinger::onRefreshRateChangedDebug(const RefreshRateChangedDebugData } } -bool SurfaceFlinger::wouldPresentEarly(TimePoint frameTime, Period vsyncPeriod) const { - const bool isThreeVsyncsAhead = mExpectedPresentTime - frameTime > 2 * vsyncPeriod; - return isThreeVsyncsAhead || - getPreviousPresentFence(frameTime, vsyncPeriod)->getSignalTime() != - Fence::SIGNAL_TIME_PENDING; -} - -auto SurfaceFlinger::getPreviousPresentFence(TimePoint frameTime, Period vsyncPeriod) const - -> const FenceTimePtr& { - const bool isTwoVsyncsAhead = mExpectedPresentTime - frameTime > vsyncPeriod; - const size_t i = static_cast<size_t>(isTwoVsyncsAhead); - return mPreviousPresentFences[i].fenceTime; -} - -bool SurfaceFlinger::isFencePending(const FenceTimePtr& fence, int graceTimeMs) { - ATRACE_CALL(); - if (fence == FenceTime::NO_FENCE) { - return false; - } - - const status_t status = fence->wait(graceTimeMs); - // This is the same as Fence::Status::Unsignaled, but it saves a getStatus() call, - // which calls wait(0) again internally - return status == -ETIME; -} - -TimePoint SurfaceFlinger::calculateExpectedPresentTime(TimePoint frameTime) const { - const auto& schedule = mScheduler->getVsyncSchedule(); - - const TimePoint vsyncDeadline = schedule->vsyncDeadlineAfter(frameTime); - if (mScheduler->vsyncModulator().getVsyncConfig().sfOffset > 0) { - return vsyncDeadline; - } - - // Inflate the expected present time if we're targeting the next vsync. - return vsyncDeadline + schedule->period(); -} - void SurfaceFlinger::configure() FTL_FAKE_GUARD(kMainThreadContext) { Mutex::Autolock lock(mStateLock); if (configureLocked()) { @@ -2378,75 +2341,15 @@ bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, frontend::Update& upd return mustComposite; } -bool SurfaceFlinger::commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime) +bool SurfaceFlinger::commit(const scheduler::FrameTarget& pacesetterFrameTarget) FTL_FAKE_GUARD(kMainThreadContext) { - // The expectedVsyncTime, which was predicted when this frame was scheduled, is normally in the - // future relative to frameTime, but may not be for delayed frames. Adjust mExpectedPresentTime - // accordingly, but not mScheduledPresentTime. - const TimePoint lastScheduledPresentTime = mScheduledPresentTime; - mScheduledPresentTime = expectedVsyncTime; - - // Calculate the expected present time once and use the cached value throughout this frame to - // make sure all layers are seeing this same value. - mExpectedPresentTime = expectedVsyncTime >= frameTime ? expectedVsyncTime - : calculateExpectedPresentTime(frameTime); - - ATRACE_FORMAT("%s %" PRId64 " vsyncIn %.2fms%s", __func__, ftl::to_underlying(vsyncId), - ticks<std::milli, float>(mExpectedPresentTime - TimePoint::now()), - mExpectedPresentTime == expectedVsyncTime ? "" : " (adjusted)"); + const VsyncId vsyncId = pacesetterFrameTarget.vsyncId(); + ATRACE_NAME(ftl::Concat(__func__, ' ', ftl::to_underlying(vsyncId)).c_str()); - const Period vsyncPeriod = mScheduler->getVsyncSchedule()->period(); - const FenceTimePtr& previousPresentFence = getPreviousPresentFence(frameTime, vsyncPeriod); - - // When backpressure propagation is enabled, we want to give a small grace period of 1ms - // for the present fence to fire instead of just giving up on this frame to handle cases - // where present fence is just about to get signaled. - const int graceTimeForPresentFenceMs = static_cast<int>( - mBackpressureGpuComposition || !mCompositionCoverage.test(CompositionCoverage::Gpu)); - - // Pending frames may trigger backpressure propagation. - const TracedOrdinal<bool> framePending = {"PrevFramePending", - isFencePending(previousPresentFence, - graceTimeForPresentFenceMs)}; - - // Frame missed counts for metrics tracking. - // A frame is missed if the prior frame is still pending. If no longer pending, - // then we still count the frame as missed if the predicted present time - // was further in the past than when the fence actually fired. - - // Add some slop to correct for drift. This should generally be - // smaller than a typical frame duration, but should not be so small - // that it reports reasonable drift as a missed frame. - const nsecs_t frameMissedSlop = vsyncPeriod.ns() / 2; - const nsecs_t previousPresentTime = previousPresentFence->getSignalTime(); - const TracedOrdinal<bool> frameMissed = {"PrevFrameMissed", - framePending || - (previousPresentTime >= 0 && - (lastScheduledPresentTime.ns() < - previousPresentTime - frameMissedSlop))}; - const TracedOrdinal<bool> hwcFrameMissed = {"PrevHwcFrameMissed", - frameMissed && - mCompositionCoverage.test( - CompositionCoverage::Hwc)}; - - const TracedOrdinal<bool> gpuFrameMissed = {"PrevGpuFrameMissed", - frameMissed && - mCompositionCoverage.test( - CompositionCoverage::Gpu)}; - - if (frameMissed) { - mFrameMissedCount++; + if (pacesetterFrameTarget.didMissFrame()) { mTimeStats->incrementMissedFrames(); } - if (hwcFrameMissed) { - mHwcFrameMissedCount++; - } - - if (gpuFrameMissed) { - mGpuFrameMissedCount++; - } - if (mTracingEnabledChanged) { mLayerTracingEnabled = mLayerTracing.isEnabled(); mTracingEnabledChanged = false; @@ -2455,7 +2358,7 @@ bool SurfaceFlinger::commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expe // If we are in the middle of a mode change and the fence hasn't // fired yet just wait for the next commit. if (mSetActiveModePending) { - if (framePending) { + if (pacesetterFrameTarget.isFramePending()) { mScheduler->scheduleFrame(); return false; } @@ -2469,26 +2372,29 @@ bool SurfaceFlinger::commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expe } } - if (framePending) { - if (mBackpressureGpuComposition || (hwcFrameMissed && !gpuFrameMissed)) { + if (pacesetterFrameTarget.isFramePending()) { + if (mBackpressureGpuComposition || pacesetterFrameTarget.didMissHwcFrame()) { scheduleCommit(FrameHint::kNone); return false; } } + const Period vsyncPeriod = mScheduler->getVsyncSchedule()->period(); + // Save this once per commit + composite to ensure consistency // TODO (b/240619471): consider removing active display check once AOD is fixed const auto activeDisplay = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(mActiveDisplayId)); mPowerHintSessionEnabled = mPowerAdvisor->usePowerHintSession() && activeDisplay && activeDisplay->getPowerMode() == hal::PowerMode::ON; if (mPowerHintSessionEnabled) { - mPowerAdvisor->setCommitStart(frameTime); - mPowerAdvisor->setExpectedPresentTime(mExpectedPresentTime); + mPowerAdvisor->setCommitStart(pacesetterFrameTarget.frameBeginTime()); + mPowerAdvisor->setExpectedPresentTime(pacesetterFrameTarget.expectedPresentTime()); // Frame delay is how long we should have minus how long we actually have. const Duration idealSfWorkDuration = mScheduler->vsyncModulator().getVsyncConfig().sfWorkDuration; - const Duration frameDelay = idealSfWorkDuration - (mExpectedPresentTime - frameTime); + const Duration frameDelay = + idealSfWorkDuration - pacesetterFrameTarget.expectedFrameDuration(); mPowerAdvisor->setFrameDelay(frameDelay); mPowerAdvisor->setTotalFrameTargetWorkDuration(idealSfWorkDuration); @@ -2508,7 +2414,8 @@ bool SurfaceFlinger::commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expe // Composite if transactions were committed, or if requested by HWC. bool mustComposite = mMustComposite.exchange(false); { - mFrameTimeline->setSfWakeUp(ftl::to_underlying(vsyncId), frameTime.ns(), + mFrameTimeline->setSfWakeUp(ftl::to_underlying(vsyncId), + pacesetterFrameTarget.frameBeginTime().ns(), Fps::fromPeriodNsecs(vsyncPeriod.ns())); const bool flushTransactions = clearTransactionFlags(eTransactionFlushNeeded); @@ -2516,10 +2423,11 @@ bool SurfaceFlinger::commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expe if (flushTransactions) { updates = flushLifecycleUpdates(); if (mTransactionTracing) { - mTransactionTracing->addCommittedTransactions(ftl::to_underlying(vsyncId), - frameTime.ns(), updates, - mFrontEndDisplayInfos, - mFrontEndDisplayInfosChanged); + mTransactionTracing + ->addCommittedTransactions(ftl::to_underlying(vsyncId), + pacesetterFrameTarget.frameBeginTime().ns(), + updates, mFrontEndDisplayInfos, + mFrontEndDisplayInfosChanged); } } bool transactionsAreEmpty; @@ -2558,11 +2466,11 @@ bool SurfaceFlinger::commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expe } updateCursorAsync(); - updateInputFlinger(vsyncId, frameTime); + updateInputFlinger(vsyncId, pacesetterFrameTarget.frameBeginTime()); if (mLayerTracingEnabled && !mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) { // This will block and tracing should only be enabled for debugging. - addToLayerTracing(mVisibleRegionsDirty, frameTime, vsyncId); + addToLayerTracing(mVisibleRegionsDirty, pacesetterFrameTarget.frameBeginTime(), vsyncId); } mLastCommittedVsyncId = vsyncId; @@ -2571,8 +2479,11 @@ bool SurfaceFlinger::commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expe return mustComposite && CC_LIKELY(mBootStage != BootStage::BOOTLOADER); } -void SurfaceFlinger::composite(TimePoint frameTime, VsyncId vsyncId) +CompositeResult SurfaceFlinger::composite(scheduler::FrameTargeter& pacesetterFrameTargeter) FTL_FAKE_GUARD(kMainThreadContext) { + const scheduler::FrameTarget& pacesetterFrameTarget = pacesetterFrameTargeter.target(); + + const VsyncId vsyncId = pacesetterFrameTarget.vsyncId(); ATRACE_NAME(ftl::Concat(__func__, ' ', ftl::to_underlying(vsyncId)).c_str()); compositionengine::CompositionRefreshArgs refreshArgs; @@ -2580,17 +2491,18 @@ void SurfaceFlinger::composite(TimePoint frameTime, VsyncId vsyncId) refreshArgs.outputs.reserve(displays.size()); std::vector<DisplayId> displayIds; for (const auto& [_, display] : displays) { - bool dropFrame = false; + displayIds.push_back(display->getId()); + display->tracePowerMode(); + if (display->isVirtual()) { - Fps refreshRate = display->getAdjustedRefreshRate(); - using fps_approx_ops::operator>; - dropFrame = (refreshRate > 0_Hz) && !mScheduler->isVsyncInPhase(frameTime, refreshRate); - } - if (!dropFrame) { - refreshArgs.outputs.push_back(display->getCompositionDisplay()); + const Fps refreshRate = display->getAdjustedRefreshRate(); + if (refreshRate.isValid() && + !mScheduler->isVsyncInPhase(pacesetterFrameTarget.frameBeginTime(), refreshRate)) { + continue; + } } - display->tracePowerMode(); - displayIds.push_back(display->getId()); + + refreshArgs.outputs.push_back(display->getCompositionDisplay()); } mPowerAdvisor->setDisplays(displayIds); @@ -2650,15 +2562,15 @@ void SurfaceFlinger::composite(TimePoint frameTime, VsyncId vsyncId) if (!getHwComposer().getComposer()->isSupported( Hwc2::Composer::OptionalFeature::ExpectedPresentTime) && - wouldPresentEarly(frameTime, vsyncPeriod)) { - const auto prevVsyncTime = mExpectedPresentTime - vsyncPeriod; + pacesetterFrameTarget.wouldPresentEarly(vsyncPeriod)) { const auto hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration; - refreshArgs.earliestPresentTime = prevVsyncTime - hwcMinWorkDuration; + refreshArgs.earliestPresentTime = + pacesetterFrameTarget.previousFrameVsyncTime(vsyncPeriod) - hwcMinWorkDuration; } refreshArgs.scheduledFrameTime = mScheduler->getScheduledFrameTime(); - refreshArgs.expectedPresentTime = mExpectedPresentTime.ns(); + refreshArgs.expectedPresentTime = pacesetterFrameTarget.expectedPresentTime().ns(); refreshArgs.hasTrustedPresentationListener = mNumTrustedPresentationListeners > 0; // Store the present time just before calling to the composition engine so we could notify @@ -2684,14 +2596,14 @@ void SurfaceFlinger::composite(TimePoint frameTime, VsyncId vsyncId) } } - mTimeStats->recordFrameDuration(frameTime.ns(), systemTime()); + mTimeStats->recordFrameDuration(pacesetterFrameTarget.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 = mPreviousPresentFences[0].fenceTime; + const auto& previousPresentFence = pacesetterFrameTarget.presentFenceForPreviousFrame(); mPowerAdvisor->setSfPresentTiming(TimePoint::fromNs(previousPresentFence->getSignalTime()), TimePoint::now()); @@ -2702,7 +2614,7 @@ void SurfaceFlinger::composite(TimePoint frameTime, VsyncId vsyncId) scheduleComposite(FrameHint::kNone); } - postComposition(presentTime); + postComposition(pacesetterFrameTargeter, presentTime); const bool hadGpuComposited = mCompositionCoverage.test(CompositionCoverage::Gpu); mCompositionCoverage.clear(); @@ -2745,7 +2657,7 @@ void SurfaceFlinger::composite(TimePoint frameTime, VsyncId vsyncId) mLayersWithQueuedFrames.clear(); if (mLayerTracingEnabled && mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) { // This will block and should only be used for debugging. - addToLayerTracing(mVisibleRegionsDirty, frameTime, vsyncId); + addToLayerTracing(mVisibleRegionsDirty, pacesetterFrameTarget.frameBeginTime(), vsyncId); } if (mVisibleRegionsDirty) mHdrLayerInfoChanged = true; @@ -2758,6 +2670,8 @@ void SurfaceFlinger::composite(TimePoint frameTime, VsyncId vsyncId) if (mPowerHintSessionEnabled) { mPowerAdvisor->setCompositeEnd(TimePoint::now()); } + + return {mCompositionCoverage}; } void SurfaceFlinger::updateLayerGeometry() { @@ -2841,7 +2755,8 @@ ui::Rotation SurfaceFlinger::getPhysicalDisplayOrientation(DisplayId displayId, return ui::ROTATION_0; } -void SurfaceFlinger::postComposition(nsecs_t callTime) { +void SurfaceFlinger::postComposition(scheduler::FrameTargeter& pacesetterFrameTargeter, + nsecs_t presentStartTime) { ATRACE_CALL(); ALOGV(__func__); @@ -2858,15 +2773,11 @@ void SurfaceFlinger::postComposition(nsecs_t callTime) { glCompositionDoneFenceTime = FenceTime::NO_FENCE; } - mPreviousPresentFences[1] = mPreviousPresentFences[0]; - auto presentFence = defaultDisplay ? getHwComposer().getPresentFence(defaultDisplay->getPhysicalId()) : Fence::NO_FENCE; - auto presentFenceTime = std::make_shared<FenceTime>(presentFence); - mPreviousPresentFences[0] = {presentFence, presentFenceTime}; - + auto presentFenceTime = pacesetterFrameTargeter.setPresentFence(presentFence); const TimePoint presentTime = TimePoint::now(); // Set presentation information before calling Layer::releasePendingBuffer, such that jank @@ -3049,7 +2960,7 @@ void SurfaceFlinger::postComposition(nsecs_t callTime) { if (!layer->hasTrustedPresentationListener()) { return; } - const frontend::LayerSnapshot* snapshot = (mLayerLifecycleManagerEnabled) + const frontend::LayerSnapshot* snapshot = mLayerLifecycleManagerEnabled ? mLayerSnapshotBuilder.getSnapshot(layer->sequence) : layer->getLayerSnapshot(); std::optional<const DisplayDevice*> displayOpt = std::nullopt; @@ -3058,7 +2969,8 @@ void SurfaceFlinger::postComposition(nsecs_t callTime) { } const DisplayDevice* display = displayOpt.value_or(nullptr); layer->updateTrustedPresentationState(display, snapshot, - nanoseconds_to_milliseconds(callTime), false); + nanoseconds_to_milliseconds(presentStartTime), + false); }); } @@ -3969,6 +3881,9 @@ void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { if (display->refreshRateSelector().kernelIdleTimerController()) { features |= Feature::kKernelIdleTimer; } + if (mBackpressureGpuComposition) { + features |= Feature::kBackpressureGpuComposition; + } auto modulatorPtr = sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs()); @@ -4286,33 +4201,38 @@ void SurfaceFlinger::setTransactionFlags(uint32_t mask, TransactionSchedule sche TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyTimelineCheck( const TransactionHandler::TransactionFlushState& flushState) { - using TransactionReadiness = TransactionHandler::TransactionReadiness; const auto& transaction = *flushState.transaction; - TimePoint desiredPresentTime = TimePoint::fromNs(transaction.desiredPresentTime); + + const TimePoint desiredPresentTime = TimePoint::fromNs(transaction.desiredPresentTime); + const TimePoint expectedPresentTime = mScheduler->pacesetterFrameTarget().expectedPresentTime(); + + using TransactionReadiness = TransactionHandler::TransactionReadiness; + // Do not present if the desiredPresentTime has not passed unless it is more than // one second in the future. We ignore timestamps more than 1 second in the future // for stability reasons. - if (!transaction.isAutoTimestamp && desiredPresentTime >= mExpectedPresentTime && - desiredPresentTime < mExpectedPresentTime + 1s) { + if (!transaction.isAutoTimestamp && desiredPresentTime >= expectedPresentTime && + desiredPresentTime < expectedPresentTime + 1s) { ATRACE_FORMAT("not current desiredPresentTime: %" PRId64 " expectedPresentTime: %" PRId64, - desiredPresentTime, mExpectedPresentTime); + desiredPresentTime, expectedPresentTime); return TransactionReadiness::NotReady; } - if (!mScheduler->isVsyncValid(mExpectedPresentTime, transaction.originUid)) { - ATRACE_FORMAT("!isVsyncValid expectedPresentTime: %" PRId64 " uid: %d", - mExpectedPresentTime, transaction.originUid); + if (!mScheduler->isVsyncValid(expectedPresentTime, transaction.originUid)) { + ATRACE_FORMAT("!isVsyncValid expectedPresentTime: %" PRId64 " uid: %d", expectedPresentTime, + transaction.originUid); return TransactionReadiness::NotReady; } // If the client didn't specify desiredPresentTime, use the vsyncId to determine the // expected present time of this transaction. if (transaction.isAutoTimestamp && - frameIsEarly(mExpectedPresentTime, VsyncId{transaction.frameTimelineInfo.vsyncId})) { + frameIsEarly(expectedPresentTime, VsyncId{transaction.frameTimelineInfo.vsyncId})) { ATRACE_FORMAT("frameIsEarly vsyncId: %" PRId64 " expectedPresentTime: %" PRId64, - transaction.frameTimelineInfo.vsyncId, mExpectedPresentTime); + transaction.frameTimelineInfo.vsyncId, expectedPresentTime); return TransactionReadiness::NotReady; } + return TransactionReadiness::Ready; } @@ -6052,10 +5972,6 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, const std::string& comp dumpVsync(result); result.append("\n"); - StringAppendF(&result, "Total missed frame count: %u\n", mFrameMissedCount.load()); - StringAppendF(&result, "HWC missed frame count: %u\n", mHwcFrameMissedCount.load()); - StringAppendF(&result, "GPU missed frame count: %u\n\n", mGpuFrameMissedCount.load()); - /* * Dump the visible layer list */ diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 9187168408..fd050167f7 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -636,8 +636,8 @@ private: // ICompositor overrides: void configure() override; - bool commit(TimePoint frameTime, VsyncId, TimePoint expectedVsyncTime) override; - void composite(TimePoint frameTime, VsyncId) override; + bool commit(const scheduler::FrameTarget&) override; + CompositeResult composite(scheduler::FrameTargeter&) override; void sample() override; // ISchedulerCallback overrides: @@ -956,7 +956,8 @@ private: /* * Compositing */ - void postComposition(nsecs_t callTime) REQUIRES(kMainThreadContext); + void postComposition(scheduler::FrameTargeter&, nsecs_t presentStartTime) + REQUIRES(kMainThreadContext); /* * Display management @@ -997,20 +998,6 @@ private: */ nsecs_t getVsyncPeriodFromHWC() const REQUIRES(mStateLock); - using FenceTimePtr = std::shared_ptr<FenceTime>; - - bool wouldPresentEarly(TimePoint frameTime, Period) const REQUIRES(kMainThreadContext); - - const FenceTimePtr& getPreviousPresentFence(TimePoint frameTime, Period) const - REQUIRES(kMainThreadContext); - - // Blocks the thread waiting for up to graceTimeMs in case the fence is about to signal. - static bool isFencePending(const FenceTimePtr&, int graceTimeMs); - - // Calculates the expected present time for this frame. For negative offsets, performs a - // correction using the predicted vsync for the next frame instead. - TimePoint calculateExpectedPresentTime(TimePoint frameTime) const; - /* * Display identification */ @@ -1256,9 +1243,6 @@ private: // If blurs should be enabled on this device. bool mSupportsBlur = false; - std::atomic<uint32_t> mFrameMissedCount = 0; - std::atomic<uint32_t> mHwcFrameMissedCount = 0; - std::atomic<uint32_t> mGpuFrameMissedCount = 0; TransactionCallbackInvoker mTransactionCallbackInvoker; @@ -1326,15 +1310,6 @@ private: std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats; scheduler::PresentLatencyTracker mPresentLatencyTracker GUARDED_BY(kMainThreadContext); - struct FenceWithFenceTime { - sp<Fence> fence = Fence::NO_FENCE; - FenceTimePtr fenceTime = FenceTime::NO_FENCE; - }; - std::array<FenceWithFenceTime, 2> mPreviousPresentFences; - - TimePoint mScheduledPresentTime GUARDED_BY(kMainThreadContext); - TimePoint mExpectedPresentTime GUARDED_BY(kMainThreadContext); - // below flags are set by main thread only bool mSetActiveModePending = false; diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h index 8e208bc538..0c9a16bee3 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h +++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h @@ -286,8 +286,8 @@ public: private: // ICompositor overrides: void configure() override {} - bool commit(TimePoint, VsyncId, TimePoint) override { return false; } - void composite(TimePoint, VsyncId) override {} + bool commit(const scheduler::FrameTarget&) override { return false; } + CompositeResult composite(scheduler::FrameTargeter&) override { return {}; } void sample() override {} // MessageQueue overrides: @@ -604,7 +604,9 @@ public: mFlinger->commitTransactions(); mFlinger->flushTransactionQueues(getFuzzedVsyncId(mFdp)); - mFlinger->postComposition(systemTime()); + + scheduler::FrameTargeter frameTargeter(mFdp.ConsumeBool()); + mFlinger->postComposition(frameTargeter, mFdp.ConsumeIntegral<nsecs_t>()); } mFlinger->setTransactionFlags(mFdp.ConsumeIntegral<uint32_t>()); @@ -622,8 +624,6 @@ public: mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mFdp.ConsumeIntegral<uid_t>()); - mFlinger->calculateExpectedPresentTime({}); - mFlinger->enableHalVirtualDisplays(mFdp.ConsumeBool()); fuzzDumpsysAndDebug(&mFdp); diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp index f17d2e1cb4..b1fd06f998 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp +++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp @@ -19,6 +19,7 @@ #include <fuzzer/FuzzedDataProvider.h> #include <processgroup/sched_policy.h> +#include <scheduler/IVsyncSource.h> #include <scheduler/PresentLatencyTracker.h> #include "Scheduler/OneShotTimer.h" @@ -42,6 +43,7 @@ constexpr nsecs_t kVsyncPeriods[] = {(30_Hz).getPeriodNsecs(), (60_Hz).getPeriod (120_Hz).getPeriodNsecs()}; constexpr auto kLayerVoteTypes = ftl::enum_range<scheduler::RefreshRateSelector::LayerVoteType>(); +constexpr auto kCompositionCoverage = ftl::enum_range<CompositionCoverage>(); constexpr PowerMode kPowerModes[] = {PowerMode::ON, PowerMode::DOZE, PowerMode::OFF, PowerMode::DOZE_SUSPEND, PowerMode::ON_SUSPEND}; @@ -56,6 +58,10 @@ void dump(T* component, FuzzedDataProvider* fdp) { component->dump(res); } +inline sp<Fence> makeFakeFence() { + return sp<Fence>::make(memfd_create("fd", MFD_ALLOW_SEALING)); +} + class SchedulerFuzzer { public: SchedulerFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){}; @@ -65,6 +71,7 @@ private: void fuzzRefreshRateSelection(); void fuzzRefreshRateSelector(); void fuzzPresentLatencyTracker(); + void fuzzFrameTargeter(); void fuzzVSyncModulator(); void fuzzVSyncPredictor(); void fuzzVSyncReactor(); @@ -256,13 +263,13 @@ void SchedulerFuzzer::fuzzVSyncReactor() { reactor.addHwVsyncTimestamp(0, std::nullopt, &periodFlushed); reactor.addHwVsyncTimestamp(mFdp.ConsumeIntegral<nsecs_t>() /*newPeriod*/, std::nullopt, &periodFlushed); - sp<Fence> fence = sp<Fence>::make(memfd_create("fd", MFD_ALLOW_SEALING)); - std::shared_ptr<FenceTime> ft = std::make_shared<FenceTime>(fence); + + const auto fence = std::make_shared<FenceTime>(makeFakeFence()); vSyncTracker->addVsyncTimestamp(mFdp.ConsumeIntegral<nsecs_t>()); FenceTime::Snapshot snap(mFdp.ConsumeIntegral<nsecs_t>()); - ft->applyTrustedSnapshot(snap); + fence->applyTrustedSnapshot(snap); reactor.setIgnorePresentFences(mFdp.ConsumeBool()); - reactor.addPresentFence(ft); + reactor.addPresentFence(fence); dump<scheduler::VSyncReactor>(&reactor, &mFdp); } @@ -392,14 +399,45 @@ void SchedulerFuzzer::fuzzRefreshRateSelector() { void SchedulerFuzzer::fuzzPresentLatencyTracker() { scheduler::PresentLatencyTracker tracker; - tracker.trackPendingFrame(TimePoint::fromNs(mFdp.ConsumeIntegral<nsecs_t>()), - FenceTime::NO_FENCE); + + int i = 5; + while (i-- > 0) { + tracker.trackPendingFrame(getFuzzedTimePoint(mFdp), + std::make_shared<FenceTime>(makeFakeFence())); + } +} + +void SchedulerFuzzer::fuzzFrameTargeter() { + scheduler::FrameTargeter frameTargeter(mFdp.ConsumeBool()); + + const struct VsyncSource final : scheduler::IVsyncSource { + explicit VsyncSource(FuzzedDataProvider& fuzzer) : fuzzer(fuzzer) {} + FuzzedDataProvider& fuzzer; + + Period period() const { return getFuzzedDuration(fuzzer); } + TimePoint vsyncDeadlineAfter(TimePoint) const { return getFuzzedTimePoint(fuzzer); } + } vsyncSource{mFdp}; + + int i = 10; + while (i-- > 0) { + frameTargeter.beginFrame({.frameBeginTime = getFuzzedTimePoint(mFdp), + .vsyncId = getFuzzedVsyncId(mFdp), + .expectedVsyncTime = getFuzzedTimePoint(mFdp), + .sfWorkDuration = getFuzzedDuration(mFdp)}, + vsyncSource); + + frameTargeter.setPresentFence(makeFakeFence()); + + frameTargeter.endFrame( + {.compositionCoverage = mFdp.PickValueInArray(kCompositionCoverage.values)}); + } } void SchedulerFuzzer::process() { fuzzRefreshRateSelection(); fuzzRefreshRateSelector(); fuzzPresentLatencyTracker(); + fuzzFrameTargeter(); fuzzVSyncModulator(); fuzzVSyncPredictor(); fuzzVSyncReactor(); diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp index 91875ccc6e..359e2ab7fb 100644 --- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp +++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp @@ -20,9 +20,10 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> +#include <scheduler/interface/ICompositor.h> + #include "FrameTimeline.h" #include "Scheduler/MessageQueue.h" -#include "SurfaceFlinger.h" #include "mock/MockVSyncDispatch.h" namespace android { @@ -34,8 +35,8 @@ using CallbackToken = scheduler::VSyncDispatch::CallbackToken; struct NoOpCompositor final : ICompositor { void configure() override {} - bool commit(TimePoint, VsyncId, TimePoint) override { return false; } - void composite(TimePoint, VsyncId) override {} + bool commit(const scheduler::FrameTarget&) override { return false; } + CompositeResult composite(scheduler::FrameTargeter&) override { return {}; } void sample() override {} } gNoOpCompositor; diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h index a30f7e01ef..aac11c01b8 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.h +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h @@ -173,8 +173,8 @@ public: private: // ICompositor overrides: void configure() override {} - bool commit(TimePoint, VsyncId, TimePoint) override { return false; } - void composite(TimePoint, VsyncId) override {} + bool commit(const scheduler::FrameTarget&) override { return false; } + CompositeResult composite(scheduler::FrameTargeter&) override { return {}; } void sample() override {} }; diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index deb0957b97..156c40a721 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -20,6 +20,11 @@ #include <chrono> #include <variant> +#include <ftl/fake_guard.h> +#include <ftl/match.h> +#include <gui/ScreenCaptureResults.h> +#include <ui/DynamicDisplayInfo.h> + #include <compositionengine/Display.h> #include <compositionengine/LayerFECompositionState.h> #include <compositionengine/OutputLayer.h> @@ -27,11 +32,7 @@ #include <compositionengine/impl/Display.h> #include <compositionengine/impl/OutputLayerCompositionState.h> #include <compositionengine/mock/DisplaySurface.h> -#include <ftl/fake_guard.h> -#include <ftl/match.h> -#include <gui/ScreenCaptureResults.h> -#include <ui/DynamicDisplayInfo.h> #include "DisplayDevice.h" #include "FakeVsyncConfiguration.h" #include "FrameTracer/FrameTracer.h" @@ -44,7 +45,6 @@ #include "Scheduler/RefreshRateSelector.h" #include "StartPropertySetThread.h" #include "SurfaceFlinger.h" -#include "SurfaceFlingerDefaultFactory.h" #include "TestableScheduler.h" #include "mock/DisplayHardware/MockComposer.h" #include "mock/DisplayHardware/MockDisplayMode.h" @@ -360,25 +360,42 @@ public: commitTransactionsLocked(eDisplayTransactionNeeded); } - TimePoint commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime) { - mFlinger->commit(frameTime, vsyncId, expectedVsyncTime); - return frameTime; + void commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime, + bool composite = false) { + constexpr bool kBackpressureGpuComposition = true; + scheduler::FrameTargeter frameTargeter(kBackpressureGpuComposition); + + frameTargeter.beginFrame({.frameBeginTime = frameTime, + .vsyncId = vsyncId, + .expectedVsyncTime = expectedVsyncTime, + .sfWorkDuration = 10ms}, + *mScheduler->getVsyncSchedule()); + + mFlinger->commit(frameTargeter.target()); + + if (composite) { + mFlinger->composite(frameTargeter); + } } - TimePoint commit(TimePoint frameTime, VsyncId vsyncId) { - return commit(frameTime, vsyncId, frameTime + Period(10ms)); + void commit(TimePoint frameTime, VsyncId vsyncId, bool composite = false) { + return commit(frameTime, vsyncId, frameTime + Period(10ms), composite); } - TimePoint commit() { + void commit(bool composite = false) { const TimePoint frameTime = scheduler::SchedulerClock::now(); - return commit(frameTime, kVsyncId); + commit(frameTime, kVsyncId, composite); } void commitAndComposite(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime) { - mFlinger->composite(commit(frameTime, vsyncId, expectedVsyncTime), vsyncId); + constexpr bool kComposite = true; + commit(frameTime, vsyncId, expectedVsyncTime, kComposite); } - void commitAndComposite() { mFlinger->composite(commit(), kVsyncId); } + void commitAndComposite() { + constexpr bool kComposite = true; + commit(kComposite); + } auto createDisplay(const String8& displayName, bool secure, float requestedRefreshRate = 0.0f) { return mFlinger->createDisplay(displayName, secure, requestedRefreshRate); |