Loading services/surfaceflinger/Scheduler/DispSyncSource.cpp +2 −8 Original line number Diff line number Diff line Loading @@ -27,16 +27,14 @@ namespace android { DispSyncSource::DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, nsecs_t offsetThresholdForNextVsync, bool traceVsync, DispSyncSource::DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync, const char* name) : mName(name), mValue(base::StringPrintf("VSYNC-%s", name), 0), mTraceVsync(traceVsync), mVsyncOnLabel(base::StringPrintf("VsyncOn-%s", name)), mDispSync(dispSync), mPhaseOffset(base::StringPrintf("VsyncOffset-%s", name), phaseOffset), mOffsetThresholdForNextVsync(offsetThresholdForNextVsync) {} mPhaseOffset(base::StringPrintf("VsyncOffset-%s", name), phaseOffset) {} void DispSyncSource::setVSyncEnabled(bool enable) { std::lock_guard lock(mVsyncMutex); Loading Loading @@ -67,10 +65,6 @@ void DispSyncSource::setCallback(VSyncSource::Callback* callback) { void DispSyncSource::setPhaseOffset(nsecs_t phaseOffset) { std::lock_guard lock(mVsyncMutex); const nsecs_t period = mDispSync->getPeriod(); // Check if offset should be handled as negative if (phaseOffset >= mOffsetThresholdForNextVsync) { phaseOffset -= period; } // Normalize phaseOffset to [-period, period) const int numPeriods = phaseOffset / period; Loading services/surfaceflinger/Scheduler/DispSyncSource.h +1 −3 Original line number Diff line number Diff line Loading @@ -26,8 +26,7 @@ namespace android { class DispSyncSource final : public VSyncSource, private DispSync::Callback { public: DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, nsecs_t offsetThresholdForNextVsync, bool traceVsync, const char* name); DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync, const char* name); ~DispSyncSource() override = default; Loading Loading @@ -55,7 +54,6 @@ private: std::mutex mVsyncMutex; TracedOrdinal<nsecs_t> mPhaseOffset GUARDED_BY(mVsyncMutex); const nsecs_t mOffsetThresholdForNextVsync; bool mEnabled GUARDED_BY(mVsyncMutex) = false; }; Loading services/surfaceflinger/Scheduler/InjectVSyncSource.h +0 −1 Original line number Diff line number Diff line Loading @@ -45,7 +45,6 @@ public: const char* getName() const override { return "inject"; } void setVSyncEnabled(bool) override {} void setPhaseOffset(nsecs_t) override {} void pauseVsyncCallback(bool) {} private: std::mutex mCallbackMutex; Loading services/surfaceflinger/Scheduler/PhaseOffsets.cpp +236 −40 Original line number Diff line number Diff line Loading @@ -35,45 +35,48 @@ std::optional<nsecs_t> getProperty(const char* name) { namespace android::scheduler { PhaseOffsets::~PhaseOffsets() = default; PhaseConfiguration::~PhaseConfiguration() = default; namespace impl { PhaseOffsets::PhaseOffsets() { // Below defines the threshold when an offset is considered to be negative, i.e. targeting PhaseOffsets::PhaseOffsets(const scheduler::RefreshRateConfigs& refreshRateConfigs) : // Below defines the threshold when an offset is considered to be negative, i.e. targeting // for the N+2 vsync instead of N+1. This means that: // For offset < threshold, SF wake up (vsync_duration - offset) before HW vsync. // For offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW vsync. const nsecs_t thresholdForNextVsync = getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns") .value_or(std::numeric_limits<nsecs_t>::max()); mDefaultOffsets = getDefaultOffsets(thresholdForNextVsync); mHighFpsOffsets = getHighFpsOffsets(thresholdForNextVsync); } PhaseOffsets::Offsets PhaseOffsets::getOffsetsForRefreshRate(float fps) const { // TODO(145561086): Once offsets are common for all refresh rates we can remove the magic // number for refresh rate if (fps > 65.0f) { return mHighFpsOffsets; } else { return mDefaultOffsets; } } mThresholdForNextVsync(getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns") .value_or(std::numeric_limits<nsecs_t>::max())), mOffsets(initializeOffsets(refreshRateConfigs)), mRefreshRateFps(refreshRateConfigs.getCurrentRefreshRate().fps) {} void PhaseOffsets::dump(std::string& result) const { const auto [early, earlyGl, late, threshold] = getCurrentOffsets(); const auto [early, earlyGl, late] = getCurrentOffsets(); using base::StringAppendF; StringAppendF(&result, " app phase: %9" PRId64 " ns\t SF phase: %9" PRId64 " ns\n" " early app phase: %9" PRId64 " ns\t early SF phase: %9" PRId64 " ns\n" " GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64 " ns\n" "next VSYNC threshold: %9" PRId64 " ns\n", late.app, late.sf, early.app, early.sf, earlyGl.app, earlyGl.sf, threshold); late.app, late.sf, early.app, early.sf, earlyGl.app, earlyGl.sf, mThresholdForNextVsync); } PhaseOffsets::Offsets PhaseOffsets::getDefaultOffsets(nsecs_t thresholdForNextVsync) { std::unordered_map<float, PhaseDurations::Offsets> PhaseOffsets::initializeOffsets( const scheduler::RefreshRateConfigs& refreshRateConfigs) const { std::unordered_map<float, PhaseDurations::Offsets> offsets; for (const auto& [ignored, refreshRate] : refreshRateConfigs.getAllRefreshRates()) { const nsecs_t vsyncDuration = static_cast<nsecs_t>(1e9f / refreshRate.fps); if (refreshRate.fps > 65.0f) { offsets.emplace(refreshRate.fps, getHighFpsOffsets(vsyncDuration)); } else { offsets.emplace(refreshRate.fps, getDefaultOffsets(vsyncDuration)); } } return offsets; } PhaseOffsets::Offsets PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const { const int64_t vsyncPhaseOffsetNs = sysprop::vsync_event_phase_offset_ns(1000000); const int64_t sfVsyncPhaseOffsetNs = sysprop::vsync_sf_event_phase_offset_ns(1000000); Loading @@ -82,19 +85,32 @@ PhaseOffsets::Offsets PhaseOffsets::getDefaultOffsets(nsecs_t thresholdForNextVs const auto earlyAppOffsetNs = getProperty("debug.sf.early_app_phase_offset_ns"); const auto earlyGlAppOffsetNs = getProperty("debug.sf.early_gl_app_phase_offset_ns"); return {{earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs), earlyAppOffsetNs.value_or(vsyncPhaseOffsetNs)}, return { { earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs) < mThresholdForNextVsync ? earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs) : earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs) - vsyncDuration, {earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs), earlyGlAppOffsetNs.value_or(vsyncPhaseOffsetNs)}, earlyAppOffsetNs.value_or(vsyncPhaseOffsetNs), }, { earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs) < mThresholdForNextVsync ? earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs) : earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs) - vsyncDuration, {sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs}, earlyGlAppOffsetNs.value_or(vsyncPhaseOffsetNs), }, { sfVsyncPhaseOffsetNs < mThresholdForNextVsync ? sfVsyncPhaseOffsetNs : sfVsyncPhaseOffsetNs - vsyncDuration, thresholdForNextVsync}; vsyncPhaseOffsetNs, }, }; } PhaseOffsets::Offsets PhaseOffsets::getHighFpsOffsets(nsecs_t thresholdForNextVsync) { // TODO(b/122905996): Define these in device.mk. PhaseOffsets::Offsets PhaseOffsets::getHighFpsOffsets(nsecs_t vsyncDuration) const { const int highFpsLateAppOffsetNs = getProperty("debug.sf.high_fps_late_app_phase_offset_ns").value_or(2000000); const int highFpsLateSfOffsetNs = Loading @@ -106,15 +122,195 @@ PhaseOffsets::Offsets PhaseOffsets::getHighFpsOffsets(nsecs_t thresholdForNextVs const auto highFpsEarlyGlAppOffsetNs = getProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns"); return {{highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs), highFpsEarlyAppOffsetNs.value_or(highFpsLateAppOffsetNs)}, return { { highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs) < mThresholdForNextVsync ? highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs) : highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs) - vsyncDuration, highFpsEarlyAppOffsetNs.value_or(highFpsLateAppOffsetNs), }, { highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) < mThresholdForNextVsync ? highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) : highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) - vsyncDuration, highFpsEarlyGlAppOffsetNs.value_or(highFpsLateAppOffsetNs), }, { highFpsLateSfOffsetNs < mThresholdForNextVsync ? highFpsLateSfOffsetNs : highFpsLateSfOffsetNs - vsyncDuration, highFpsLateAppOffsetNs, }, }; } static void validateSysprops() { const auto validatePropertyBool = [](const char* prop) { LOG_ALWAYS_FATAL_IF(!property_get_bool(prop, false), "%s is false", prop); }; validatePropertyBool("debug.sf.use_phase_offsets_as_durations"); LOG_ALWAYS_FATAL_IF(sysprop::vsync_event_phase_offset_ns(-1) != -1, "ro.surface_flinger.vsync_event_phase_offset_ns is set but expecting " "duration"); LOG_ALWAYS_FATAL_IF(sysprop::vsync_sf_event_phase_offset_ns(-1) != -1, "ro.surface_flinger.vsync_sf_event_phase_offset_ns is set but expecting " "duration"); const auto validateProperty = [](const char* prop) { LOG_ALWAYS_FATAL_IF(getProperty(prop).has_value(), "%s is set to %" PRId64 " but expecting duration", prop, getProperty(prop).value_or(-1)); }; validateProperty("debug.sf.early_phase_offset_ns"); validateProperty("debug.sf.early_gl_phase_offset_ns"); validateProperty("debug.sf.early_app_phase_offset_ns"); validateProperty("debug.sf.early_gl_app_phase_offset_ns"); validateProperty("debug.sf.high_fps_late_app_phase_offset_ns"); validateProperty("debug.sf.high_fps_late_sf_phase_offset_ns"); validateProperty("debug.sf.high_fps_early_phase_offset_ns"); validateProperty("debug.sf.high_fps_early_gl_phase_offset_ns"); validateProperty("debug.sf.high_fps_early_app_phase_offset_ns"); validateProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns"); } static nsecs_t sfDurationToOffset(nsecs_t sfDuration, nsecs_t vsyncDuration) { return sfDuration == -1 ? 1'000'000 : vsyncDuration - sfDuration % vsyncDuration; } static nsecs_t appDurationToOffset(nsecs_t appDuration, nsecs_t sfDuration, nsecs_t vsyncDuration) { return sfDuration == -1 ? 1'000'000 : vsyncDuration - (appDuration + sfDuration) % vsyncDuration; } static std::vector<float> getRefreshRatesFromConfigs( const android::scheduler::RefreshRateConfigs& refreshRateConfigs) { const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates(); std::vector<float> refreshRates; refreshRates.reserve(allRefreshRates.size()); for (const auto& [ignored, refreshRate] : allRefreshRates) { refreshRates.emplace_back(refreshRate.fps); } return refreshRates; } std::unordered_map<float, PhaseDurations::Offsets> PhaseDurations::initializeOffsets( const std::vector<float>& refreshRates) const { std::unordered_map<float, PhaseDurations::Offsets> offsets; for (const auto fps : refreshRates) { const nsecs_t vsyncDuration = static_cast<nsecs_t>(1e9f / fps); offsets.emplace(fps, Offsets{ { mSfEarlyDuration < vsyncDuration ? sfDurationToOffset(mSfEarlyDuration, vsyncDuration) : sfDurationToOffset(mSfEarlyDuration, vsyncDuration) - vsyncDuration, appDurationToOffset(mAppEarlyDuration, mSfEarlyDuration, vsyncDuration), }, { mSfEarlyGlDuration < vsyncDuration ? sfDurationToOffset(mSfEarlyGlDuration, vsyncDuration) : sfDurationToOffset(mSfEarlyGlDuration, vsyncDuration) - vsyncDuration, appDurationToOffset(mAppEarlyGlDuration, mSfEarlyGlDuration, vsyncDuration), }, { mSfDuration < vsyncDuration ? sfDurationToOffset(mSfDuration, vsyncDuration) : sfDurationToOffset(mSfDuration, vsyncDuration) - vsyncDuration, appDurationToOffset(mAppDuration, mSfDuration, vsyncDuration), }, }); } return offsets; } PhaseDurations::PhaseDurations(const scheduler::RefreshRateConfigs& refreshRateConfigs) : PhaseDurations(getRefreshRatesFromConfigs(refreshRateConfigs), refreshRateConfigs.getCurrentRefreshRate().fps, getProperty("debug.sf.late.sf.duration").value_or(-1), getProperty("debug.sf.late.app.duration").value_or(-1), getProperty("debug.sf.early.sf.duration").value_or(mSfDuration), getProperty("debug.sf.early.app.duration").value_or(mAppDuration), getProperty("debug.sf.earlyGl.sf.duration").value_or(mSfDuration), getProperty("debug.sf.earlyGl.app.duration").value_or(mAppDuration)) { validateSysprops(); } PhaseDurations::PhaseDurations(const std::vector<float>& refreshRates, float currentFps, nsecs_t sfDuration, nsecs_t appDuration, nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration, nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration) : mSfDuration(sfDuration), mAppDuration(appDuration), mSfEarlyDuration(sfEarlyDuration), mAppEarlyDuration(appEarlyDuration), mSfEarlyGlDuration(sfEarlyGlDuration), mAppEarlyGlDuration(appEarlyGlDuration), mOffsets(initializeOffsets(refreshRates)), mRefreshRateFps(currentFps) {} PhaseOffsets::Offsets PhaseDurations::getOffsetsForRefreshRate(float fps) const { const auto iter = mOffsets.find(fps); LOG_ALWAYS_FATAL_IF(iter == mOffsets.end()); return iter->second; } void PhaseDurations::dump(std::string& result) const { const auto [early, earlyGl, late] = getCurrentOffsets(); using base::StringAppendF; StringAppendF(&result, " app phase: %9" PRId64 " ns\t SF phase: %9" PRId64 " ns\n" " app duration: %9" PRId64 " ns\t SF duration: %9" PRId64 " ns\n" " early app phase: %9" PRId64 " ns\t early SF phase: %9" PRId64 " ns\n" " early app duration: %9" PRId64 " ns\t early SF duration: %9" PRId64 " ns\n" " GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64 " ns\n" " GL early app duration: %9" PRId64 " ns\tGL early SF duration: %9" PRId64 " ns\n", late.app, late.sf, mAppDuration, mSfDuration, early.app, early.sf, mAppEarlyDuration, mSfEarlyDuration, {highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs), highFpsEarlyGlAppOffsetNs.value_or(highFpsLateAppOffsetNs)}, earlyGl.app, {highFpsLateSfOffsetNs, highFpsLateAppOffsetNs}, earlyGl.sf, thresholdForNextVsync}; mAppEarlyGlDuration, mSfEarlyGlDuration); } } // namespace impl Loading services/surfaceflinger/Scheduler/PhaseOffsets.h +65 −15 Original line number Diff line number Diff line Loading @@ -29,17 +29,11 @@ namespace android::scheduler { * different offsets will help us with latency. This class keeps track of * which mode the device is on, and returns approprate offsets when needed. */ class PhaseOffsets { class PhaseConfiguration { public: using Offsets = VSyncModulator::OffsetsConfig; virtual ~PhaseOffsets(); nsecs_t getCurrentAppOffset() const { return getCurrentOffsets().late.app; } nsecs_t getCurrentSfOffset() const { return getCurrentOffsets().late.sf; } nsecs_t getOffsetThresholdForNextVsync() const { return getCurrentOffsets().thresholdForNextVsync; } virtual ~PhaseConfiguration(); virtual Offsets getCurrentOffsets() const = 0; virtual Offsets getOffsetsForRefreshRate(float fps) const = 0; Loading @@ -51,9 +45,51 @@ public: namespace impl { class PhaseOffsets : public scheduler::PhaseOffsets { /* * This is the old implementation of phase offsets and considered as deprecated. * PhaseDurations is the new implementation. */ class PhaseOffsets : public scheduler::PhaseConfiguration { public: PhaseOffsets(const scheduler::RefreshRateConfigs&); // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate. Offsets getOffsetsForRefreshRate(float fps) const override { const auto iter = mOffsets.find(fps); LOG_ALWAYS_FATAL_IF(iter == mOffsets.end()); return iter->second; } // Returns early, early GL, and late offsets for Apps and SF. Offsets getCurrentOffsets() const override { return getOffsetsForRefreshRate(mRefreshRateFps); } // This function should be called when the device is switching between different // refresh rates, to properly update the offsets. void setRefreshRateFps(float fps) override { mRefreshRateFps = fps; } // Returns current offsets in human friendly format. void dump(std::string& result) const override; private: std::unordered_map<float, PhaseOffsets::Offsets> initializeOffsets( const scheduler::RefreshRateConfigs&) const; Offsets getDefaultOffsets(nsecs_t vsyncDuration) const; Offsets getHighFpsOffsets(nsecs_t vsyncDuration) const; const nsecs_t mThresholdForNextVsync; const std::unordered_map<float, Offsets> mOffsets; std::atomic<float> mRefreshRateFps; }; /* * Class that encapsulates the phase offsets for SurfaceFlinger and App. * The offsets are calculated from durations for each one of the (late, early, earlyGL) * offset types. */ class PhaseDurations : public scheduler::PhaseConfiguration { public: PhaseOffsets(); PhaseDurations(const scheduler::RefreshRateConfigs&); // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate. Offsets getOffsetsForRefreshRate(float fps) const override; Loading @@ -68,14 +104,28 @@ public: // Returns current offsets in human friendly format. void dump(std::string& result) const override; protected: // Used for unit tests PhaseDurations(const std::vector<float>& refreshRates, float currentFps, nsecs_t sfDuration, nsecs_t appDuration, nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration, nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration); private: static Offsets getDefaultOffsets(nsecs_t thresholdForNextVsync); static Offsets getHighFpsOffsets(nsecs_t thresholdForNextVsync); std::unordered_map<float, PhaseDurations::Offsets> initializeOffsets( const std::vector<float>&) const; const nsecs_t mSfDuration; const nsecs_t mAppDuration; const nsecs_t mSfEarlyDuration; const nsecs_t mAppEarlyDuration; const nsecs_t mSfEarlyGlDuration; const nsecs_t mAppEarlyGlDuration; std::atomic<float> mRefreshRateFps = 0; const std::unordered_map<float, Offsets> mOffsets; Offsets mDefaultOffsets; Offsets mHighFpsOffsets; std::atomic<float> mRefreshRateFps; }; } // namespace impl Loading Loading
services/surfaceflinger/Scheduler/DispSyncSource.cpp +2 −8 Original line number Diff line number Diff line Loading @@ -27,16 +27,14 @@ namespace android { DispSyncSource::DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, nsecs_t offsetThresholdForNextVsync, bool traceVsync, DispSyncSource::DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync, const char* name) : mName(name), mValue(base::StringPrintf("VSYNC-%s", name), 0), mTraceVsync(traceVsync), mVsyncOnLabel(base::StringPrintf("VsyncOn-%s", name)), mDispSync(dispSync), mPhaseOffset(base::StringPrintf("VsyncOffset-%s", name), phaseOffset), mOffsetThresholdForNextVsync(offsetThresholdForNextVsync) {} mPhaseOffset(base::StringPrintf("VsyncOffset-%s", name), phaseOffset) {} void DispSyncSource::setVSyncEnabled(bool enable) { std::lock_guard lock(mVsyncMutex); Loading Loading @@ -67,10 +65,6 @@ void DispSyncSource::setCallback(VSyncSource::Callback* callback) { void DispSyncSource::setPhaseOffset(nsecs_t phaseOffset) { std::lock_guard lock(mVsyncMutex); const nsecs_t period = mDispSync->getPeriod(); // Check if offset should be handled as negative if (phaseOffset >= mOffsetThresholdForNextVsync) { phaseOffset -= period; } // Normalize phaseOffset to [-period, period) const int numPeriods = phaseOffset / period; Loading
services/surfaceflinger/Scheduler/DispSyncSource.h +1 −3 Original line number Diff line number Diff line Loading @@ -26,8 +26,7 @@ namespace android { class DispSyncSource final : public VSyncSource, private DispSync::Callback { public: DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, nsecs_t offsetThresholdForNextVsync, bool traceVsync, const char* name); DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync, const char* name); ~DispSyncSource() override = default; Loading Loading @@ -55,7 +54,6 @@ private: std::mutex mVsyncMutex; TracedOrdinal<nsecs_t> mPhaseOffset GUARDED_BY(mVsyncMutex); const nsecs_t mOffsetThresholdForNextVsync; bool mEnabled GUARDED_BY(mVsyncMutex) = false; }; Loading
services/surfaceflinger/Scheduler/InjectVSyncSource.h +0 −1 Original line number Diff line number Diff line Loading @@ -45,7 +45,6 @@ public: const char* getName() const override { return "inject"; } void setVSyncEnabled(bool) override {} void setPhaseOffset(nsecs_t) override {} void pauseVsyncCallback(bool) {} private: std::mutex mCallbackMutex; Loading
services/surfaceflinger/Scheduler/PhaseOffsets.cpp +236 −40 Original line number Diff line number Diff line Loading @@ -35,45 +35,48 @@ std::optional<nsecs_t> getProperty(const char* name) { namespace android::scheduler { PhaseOffsets::~PhaseOffsets() = default; PhaseConfiguration::~PhaseConfiguration() = default; namespace impl { PhaseOffsets::PhaseOffsets() { // Below defines the threshold when an offset is considered to be negative, i.e. targeting PhaseOffsets::PhaseOffsets(const scheduler::RefreshRateConfigs& refreshRateConfigs) : // Below defines the threshold when an offset is considered to be negative, i.e. targeting // for the N+2 vsync instead of N+1. This means that: // For offset < threshold, SF wake up (vsync_duration - offset) before HW vsync. // For offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW vsync. const nsecs_t thresholdForNextVsync = getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns") .value_or(std::numeric_limits<nsecs_t>::max()); mDefaultOffsets = getDefaultOffsets(thresholdForNextVsync); mHighFpsOffsets = getHighFpsOffsets(thresholdForNextVsync); } PhaseOffsets::Offsets PhaseOffsets::getOffsetsForRefreshRate(float fps) const { // TODO(145561086): Once offsets are common for all refresh rates we can remove the magic // number for refresh rate if (fps > 65.0f) { return mHighFpsOffsets; } else { return mDefaultOffsets; } } mThresholdForNextVsync(getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns") .value_or(std::numeric_limits<nsecs_t>::max())), mOffsets(initializeOffsets(refreshRateConfigs)), mRefreshRateFps(refreshRateConfigs.getCurrentRefreshRate().fps) {} void PhaseOffsets::dump(std::string& result) const { const auto [early, earlyGl, late, threshold] = getCurrentOffsets(); const auto [early, earlyGl, late] = getCurrentOffsets(); using base::StringAppendF; StringAppendF(&result, " app phase: %9" PRId64 " ns\t SF phase: %9" PRId64 " ns\n" " early app phase: %9" PRId64 " ns\t early SF phase: %9" PRId64 " ns\n" " GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64 " ns\n" "next VSYNC threshold: %9" PRId64 " ns\n", late.app, late.sf, early.app, early.sf, earlyGl.app, earlyGl.sf, threshold); late.app, late.sf, early.app, early.sf, earlyGl.app, earlyGl.sf, mThresholdForNextVsync); } PhaseOffsets::Offsets PhaseOffsets::getDefaultOffsets(nsecs_t thresholdForNextVsync) { std::unordered_map<float, PhaseDurations::Offsets> PhaseOffsets::initializeOffsets( const scheduler::RefreshRateConfigs& refreshRateConfigs) const { std::unordered_map<float, PhaseDurations::Offsets> offsets; for (const auto& [ignored, refreshRate] : refreshRateConfigs.getAllRefreshRates()) { const nsecs_t vsyncDuration = static_cast<nsecs_t>(1e9f / refreshRate.fps); if (refreshRate.fps > 65.0f) { offsets.emplace(refreshRate.fps, getHighFpsOffsets(vsyncDuration)); } else { offsets.emplace(refreshRate.fps, getDefaultOffsets(vsyncDuration)); } } return offsets; } PhaseOffsets::Offsets PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const { const int64_t vsyncPhaseOffsetNs = sysprop::vsync_event_phase_offset_ns(1000000); const int64_t sfVsyncPhaseOffsetNs = sysprop::vsync_sf_event_phase_offset_ns(1000000); Loading @@ -82,19 +85,32 @@ PhaseOffsets::Offsets PhaseOffsets::getDefaultOffsets(nsecs_t thresholdForNextVs const auto earlyAppOffsetNs = getProperty("debug.sf.early_app_phase_offset_ns"); const auto earlyGlAppOffsetNs = getProperty("debug.sf.early_gl_app_phase_offset_ns"); return {{earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs), earlyAppOffsetNs.value_or(vsyncPhaseOffsetNs)}, return { { earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs) < mThresholdForNextVsync ? earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs) : earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs) - vsyncDuration, {earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs), earlyGlAppOffsetNs.value_or(vsyncPhaseOffsetNs)}, earlyAppOffsetNs.value_or(vsyncPhaseOffsetNs), }, { earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs) < mThresholdForNextVsync ? earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs) : earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs) - vsyncDuration, {sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs}, earlyGlAppOffsetNs.value_or(vsyncPhaseOffsetNs), }, { sfVsyncPhaseOffsetNs < mThresholdForNextVsync ? sfVsyncPhaseOffsetNs : sfVsyncPhaseOffsetNs - vsyncDuration, thresholdForNextVsync}; vsyncPhaseOffsetNs, }, }; } PhaseOffsets::Offsets PhaseOffsets::getHighFpsOffsets(nsecs_t thresholdForNextVsync) { // TODO(b/122905996): Define these in device.mk. PhaseOffsets::Offsets PhaseOffsets::getHighFpsOffsets(nsecs_t vsyncDuration) const { const int highFpsLateAppOffsetNs = getProperty("debug.sf.high_fps_late_app_phase_offset_ns").value_or(2000000); const int highFpsLateSfOffsetNs = Loading @@ -106,15 +122,195 @@ PhaseOffsets::Offsets PhaseOffsets::getHighFpsOffsets(nsecs_t thresholdForNextVs const auto highFpsEarlyGlAppOffsetNs = getProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns"); return {{highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs), highFpsEarlyAppOffsetNs.value_or(highFpsLateAppOffsetNs)}, return { { highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs) < mThresholdForNextVsync ? highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs) : highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs) - vsyncDuration, highFpsEarlyAppOffsetNs.value_or(highFpsLateAppOffsetNs), }, { highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) < mThresholdForNextVsync ? highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) : highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) - vsyncDuration, highFpsEarlyGlAppOffsetNs.value_or(highFpsLateAppOffsetNs), }, { highFpsLateSfOffsetNs < mThresholdForNextVsync ? highFpsLateSfOffsetNs : highFpsLateSfOffsetNs - vsyncDuration, highFpsLateAppOffsetNs, }, }; } static void validateSysprops() { const auto validatePropertyBool = [](const char* prop) { LOG_ALWAYS_FATAL_IF(!property_get_bool(prop, false), "%s is false", prop); }; validatePropertyBool("debug.sf.use_phase_offsets_as_durations"); LOG_ALWAYS_FATAL_IF(sysprop::vsync_event_phase_offset_ns(-1) != -1, "ro.surface_flinger.vsync_event_phase_offset_ns is set but expecting " "duration"); LOG_ALWAYS_FATAL_IF(sysprop::vsync_sf_event_phase_offset_ns(-1) != -1, "ro.surface_flinger.vsync_sf_event_phase_offset_ns is set but expecting " "duration"); const auto validateProperty = [](const char* prop) { LOG_ALWAYS_FATAL_IF(getProperty(prop).has_value(), "%s is set to %" PRId64 " but expecting duration", prop, getProperty(prop).value_or(-1)); }; validateProperty("debug.sf.early_phase_offset_ns"); validateProperty("debug.sf.early_gl_phase_offset_ns"); validateProperty("debug.sf.early_app_phase_offset_ns"); validateProperty("debug.sf.early_gl_app_phase_offset_ns"); validateProperty("debug.sf.high_fps_late_app_phase_offset_ns"); validateProperty("debug.sf.high_fps_late_sf_phase_offset_ns"); validateProperty("debug.sf.high_fps_early_phase_offset_ns"); validateProperty("debug.sf.high_fps_early_gl_phase_offset_ns"); validateProperty("debug.sf.high_fps_early_app_phase_offset_ns"); validateProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns"); } static nsecs_t sfDurationToOffset(nsecs_t sfDuration, nsecs_t vsyncDuration) { return sfDuration == -1 ? 1'000'000 : vsyncDuration - sfDuration % vsyncDuration; } static nsecs_t appDurationToOffset(nsecs_t appDuration, nsecs_t sfDuration, nsecs_t vsyncDuration) { return sfDuration == -1 ? 1'000'000 : vsyncDuration - (appDuration + sfDuration) % vsyncDuration; } static std::vector<float> getRefreshRatesFromConfigs( const android::scheduler::RefreshRateConfigs& refreshRateConfigs) { const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates(); std::vector<float> refreshRates; refreshRates.reserve(allRefreshRates.size()); for (const auto& [ignored, refreshRate] : allRefreshRates) { refreshRates.emplace_back(refreshRate.fps); } return refreshRates; } std::unordered_map<float, PhaseDurations::Offsets> PhaseDurations::initializeOffsets( const std::vector<float>& refreshRates) const { std::unordered_map<float, PhaseDurations::Offsets> offsets; for (const auto fps : refreshRates) { const nsecs_t vsyncDuration = static_cast<nsecs_t>(1e9f / fps); offsets.emplace(fps, Offsets{ { mSfEarlyDuration < vsyncDuration ? sfDurationToOffset(mSfEarlyDuration, vsyncDuration) : sfDurationToOffset(mSfEarlyDuration, vsyncDuration) - vsyncDuration, appDurationToOffset(mAppEarlyDuration, mSfEarlyDuration, vsyncDuration), }, { mSfEarlyGlDuration < vsyncDuration ? sfDurationToOffset(mSfEarlyGlDuration, vsyncDuration) : sfDurationToOffset(mSfEarlyGlDuration, vsyncDuration) - vsyncDuration, appDurationToOffset(mAppEarlyGlDuration, mSfEarlyGlDuration, vsyncDuration), }, { mSfDuration < vsyncDuration ? sfDurationToOffset(mSfDuration, vsyncDuration) : sfDurationToOffset(mSfDuration, vsyncDuration) - vsyncDuration, appDurationToOffset(mAppDuration, mSfDuration, vsyncDuration), }, }); } return offsets; } PhaseDurations::PhaseDurations(const scheduler::RefreshRateConfigs& refreshRateConfigs) : PhaseDurations(getRefreshRatesFromConfigs(refreshRateConfigs), refreshRateConfigs.getCurrentRefreshRate().fps, getProperty("debug.sf.late.sf.duration").value_or(-1), getProperty("debug.sf.late.app.duration").value_or(-1), getProperty("debug.sf.early.sf.duration").value_or(mSfDuration), getProperty("debug.sf.early.app.duration").value_or(mAppDuration), getProperty("debug.sf.earlyGl.sf.duration").value_or(mSfDuration), getProperty("debug.sf.earlyGl.app.duration").value_or(mAppDuration)) { validateSysprops(); } PhaseDurations::PhaseDurations(const std::vector<float>& refreshRates, float currentFps, nsecs_t sfDuration, nsecs_t appDuration, nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration, nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration) : mSfDuration(sfDuration), mAppDuration(appDuration), mSfEarlyDuration(sfEarlyDuration), mAppEarlyDuration(appEarlyDuration), mSfEarlyGlDuration(sfEarlyGlDuration), mAppEarlyGlDuration(appEarlyGlDuration), mOffsets(initializeOffsets(refreshRates)), mRefreshRateFps(currentFps) {} PhaseOffsets::Offsets PhaseDurations::getOffsetsForRefreshRate(float fps) const { const auto iter = mOffsets.find(fps); LOG_ALWAYS_FATAL_IF(iter == mOffsets.end()); return iter->second; } void PhaseDurations::dump(std::string& result) const { const auto [early, earlyGl, late] = getCurrentOffsets(); using base::StringAppendF; StringAppendF(&result, " app phase: %9" PRId64 " ns\t SF phase: %9" PRId64 " ns\n" " app duration: %9" PRId64 " ns\t SF duration: %9" PRId64 " ns\n" " early app phase: %9" PRId64 " ns\t early SF phase: %9" PRId64 " ns\n" " early app duration: %9" PRId64 " ns\t early SF duration: %9" PRId64 " ns\n" " GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64 " ns\n" " GL early app duration: %9" PRId64 " ns\tGL early SF duration: %9" PRId64 " ns\n", late.app, late.sf, mAppDuration, mSfDuration, early.app, early.sf, mAppEarlyDuration, mSfEarlyDuration, {highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs), highFpsEarlyGlAppOffsetNs.value_or(highFpsLateAppOffsetNs)}, earlyGl.app, {highFpsLateSfOffsetNs, highFpsLateAppOffsetNs}, earlyGl.sf, thresholdForNextVsync}; mAppEarlyGlDuration, mSfEarlyGlDuration); } } // namespace impl Loading
services/surfaceflinger/Scheduler/PhaseOffsets.h +65 −15 Original line number Diff line number Diff line Loading @@ -29,17 +29,11 @@ namespace android::scheduler { * different offsets will help us with latency. This class keeps track of * which mode the device is on, and returns approprate offsets when needed. */ class PhaseOffsets { class PhaseConfiguration { public: using Offsets = VSyncModulator::OffsetsConfig; virtual ~PhaseOffsets(); nsecs_t getCurrentAppOffset() const { return getCurrentOffsets().late.app; } nsecs_t getCurrentSfOffset() const { return getCurrentOffsets().late.sf; } nsecs_t getOffsetThresholdForNextVsync() const { return getCurrentOffsets().thresholdForNextVsync; } virtual ~PhaseConfiguration(); virtual Offsets getCurrentOffsets() const = 0; virtual Offsets getOffsetsForRefreshRate(float fps) const = 0; Loading @@ -51,9 +45,51 @@ public: namespace impl { class PhaseOffsets : public scheduler::PhaseOffsets { /* * This is the old implementation of phase offsets and considered as deprecated. * PhaseDurations is the new implementation. */ class PhaseOffsets : public scheduler::PhaseConfiguration { public: PhaseOffsets(const scheduler::RefreshRateConfigs&); // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate. Offsets getOffsetsForRefreshRate(float fps) const override { const auto iter = mOffsets.find(fps); LOG_ALWAYS_FATAL_IF(iter == mOffsets.end()); return iter->second; } // Returns early, early GL, and late offsets for Apps and SF. Offsets getCurrentOffsets() const override { return getOffsetsForRefreshRate(mRefreshRateFps); } // This function should be called when the device is switching between different // refresh rates, to properly update the offsets. void setRefreshRateFps(float fps) override { mRefreshRateFps = fps; } // Returns current offsets in human friendly format. void dump(std::string& result) const override; private: std::unordered_map<float, PhaseOffsets::Offsets> initializeOffsets( const scheduler::RefreshRateConfigs&) const; Offsets getDefaultOffsets(nsecs_t vsyncDuration) const; Offsets getHighFpsOffsets(nsecs_t vsyncDuration) const; const nsecs_t mThresholdForNextVsync; const std::unordered_map<float, Offsets> mOffsets; std::atomic<float> mRefreshRateFps; }; /* * Class that encapsulates the phase offsets for SurfaceFlinger and App. * The offsets are calculated from durations for each one of the (late, early, earlyGL) * offset types. */ class PhaseDurations : public scheduler::PhaseConfiguration { public: PhaseOffsets(); PhaseDurations(const scheduler::RefreshRateConfigs&); // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate. Offsets getOffsetsForRefreshRate(float fps) const override; Loading @@ -68,14 +104,28 @@ public: // Returns current offsets in human friendly format. void dump(std::string& result) const override; protected: // Used for unit tests PhaseDurations(const std::vector<float>& refreshRates, float currentFps, nsecs_t sfDuration, nsecs_t appDuration, nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration, nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration); private: static Offsets getDefaultOffsets(nsecs_t thresholdForNextVsync); static Offsets getHighFpsOffsets(nsecs_t thresholdForNextVsync); std::unordered_map<float, PhaseDurations::Offsets> initializeOffsets( const std::vector<float>&) const; const nsecs_t mSfDuration; const nsecs_t mAppDuration; const nsecs_t mSfEarlyDuration; const nsecs_t mAppEarlyDuration; const nsecs_t mSfEarlyGlDuration; const nsecs_t mAppEarlyGlDuration; std::atomic<float> mRefreshRateFps = 0; const std::unordered_map<float, Offsets> mOffsets; Offsets mDefaultOffsets; Offsets mHighFpsOffsets; std::atomic<float> mRefreshRateFps; }; } // namespace impl Loading