SurfaceFlinger: introduce PhaseOffsetsAsDurations
Currently we define phase offset for each refresh rate. This works for
<= 2 refresh rates, but doesn't scale well. This change is introducing
a new way to calculate phase offsets, which is based on duration. Then,
based on the duration and refresh rate, a phase offset is calculated.
The calculation is captured here: https://docs.google.com/spreadsheets/d/1a_5cVNY3LUAkeg-yL56rYQNwved6Hy-dvEcKSxp6f8k/edit#gid=0
Bug: 145561086
Bug: 141329414
Test: jank tests
Test: adb shell /data/nativetest64/libsurfaceflinger_unittest/libsurfaceflinger_unittest
Change-Id: I16aaf7437d30c4b12f955bdaac36582dd100519f
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
index 571c9ca..bd4b0ec 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -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);
@@ -67,10 +65,6 @@
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;
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.h b/services/surfaceflinger/Scheduler/DispSyncSource.h
index 536464e..328b8c1 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.h
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.h
@@ -26,8 +26,7 @@
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;
@@ -55,7 +54,6 @@
std::mutex mVsyncMutex;
TracedOrdinal<nsecs_t> mPhaseOffset GUARDED_BY(mVsyncMutex);
- const nsecs_t mOffsetThresholdForNextVsync;
bool mEnabled GUARDED_BY(mVsyncMutex) = false;
};
diff --git a/services/surfaceflinger/Scheduler/InjectVSyncSource.h b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
index 6c502e6..fa46e6f 100644
--- a/services/surfaceflinger/Scheduler/InjectVSyncSource.h
+++ b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
@@ -45,7 +45,6 @@
const char* getName() const override { return "inject"; }
void setVSyncEnabled(bool) override {}
void setPhaseOffset(nsecs_t) override {}
- void pauseVsyncCallback(bool) {}
private:
std::mutex mCallbackMutex;
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
index 12832a6..4330742 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -35,45 +35,48 @@
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
- // 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;
- }
-}
+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.
+ 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);
@@ -82,19 +85,32 @@
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 =
@@ -106,15 +122,195 @@
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,
- {highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs),
- highFpsEarlyGlAppOffsetNs.value_or(highFpsLateAppOffsetNs)},
+ highFpsEarlyAppOffsetNs.value_or(highFpsLateAppOffsetNs),
+ },
+ {
+ highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) <
+ mThresholdForNextVsync
+ ? highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs)
+ : highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) -
+ vsyncDuration,
- {highFpsLateSfOffsetNs, highFpsLateAppOffsetNs},
+ highFpsEarlyGlAppOffsetNs.value_or(highFpsLateAppOffsetNs),
+ },
+ {
+ highFpsLateSfOffsetNs < mThresholdForNextVsync
+ ? highFpsLateSfOffsetNs
+ : highFpsLateSfOffsetNs - vsyncDuration,
- thresholdForNextVsync};
+ 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,
+
+ earlyGl.app,
+
+ earlyGl.sf,
+
+ mAppEarlyGlDuration, mSfEarlyGlDuration);
}
} // namespace impl
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h
index 7747f0c..c10efde 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.h
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.h
@@ -29,17 +29,11 @@
* 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;
@@ -51,9 +45,51 @@
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();
+ 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:
+ 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;
@@ -68,14 +104,28 @@
// 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;
- std::atomic<float> mRefreshRateFps = 0;
+ const nsecs_t mSfDuration;
+ const nsecs_t mAppDuration;
- Offsets mDefaultOffsets;
- Offsets mHighFpsOffsets;
+ const nsecs_t mSfEarlyDuration;
+ const nsecs_t mAppEarlyDuration;
+
+ const nsecs_t mSfEarlyGlDuration;
+ const nsecs_t mAppEarlyGlDuration;
+
+ const std::unordered_map<float, Offsets> mOffsets;
+
+ std::atomic<float> mRefreshRateFps;
};
} // namespace impl
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 71a6a2f..71ebfc3 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -125,18 +125,16 @@
return *mPrimaryDispSync;
}
-std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource(
- const char* name, nsecs_t phaseOffsetNs, nsecs_t offsetThresholdForNextVsync) {
+std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource(const char* name,
+ nsecs_t phaseOffsetNs) {
return std::make_unique<DispSyncSource>(mPrimaryDispSync.get(), phaseOffsetNs,
- offsetThresholdForNextVsync, true /* traceVsync */,
- name);
+ true /* traceVsync */, name);
}
Scheduler::ConnectionHandle Scheduler::createConnection(
- const char* connectionName, nsecs_t phaseOffsetNs, nsecs_t offsetThresholdForNextVsync,
+ const char* connectionName, nsecs_t phaseOffsetNs,
impl::EventThread::InterceptVSyncsCallback interceptCallback) {
- auto vsyncSource =
- makePrimaryDispSyncSource(connectionName, phaseOffsetNs, offsetThresholdForNextVsync);
+ auto vsyncSource = makePrimaryDispSyncSource(connectionName, phaseOffsetNs);
auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource),
std::move(interceptCallback));
return createConnection(std::move(eventThread));
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 15277ce..2fa8b3f 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -66,7 +66,6 @@
using ConnectionHandle = scheduler::ConnectionHandle;
ConnectionHandle createConnection(const char* connectionName, nsecs_t phaseOffsetNs,
- nsecs_t offsetThresholdForNextVsync,
impl::EventThread::InterceptVSyncsCallback);
sp<IDisplayEventConnection> createDisplayEventConnection(ConnectionHandle,
@@ -149,8 +148,7 @@
Scheduler(std::unique_ptr<DispSync>, std::unique_ptr<EventControlThread>,
const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback);
- std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name, nsecs_t phaseOffsetNs,
- nsecs_t offsetThresholdForNextVsync);
+ std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name, nsecs_t phaseOffsetNs);
// Create a connection on the given EventThread.
ConnectionHandle createConnection(std::unique_ptr<EventThread>);
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h
index 63c0feb..704a5d5 100644
--- a/services/surfaceflinger/Scheduler/VSyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.h
@@ -43,6 +43,10 @@
struct Offsets {
nsecs_t sf;
nsecs_t app;
+
+ bool operator==(const Offsets& other) const { return sf == other.sf && app == other.app; }
+
+ bool operator!=(const Offsets& other) const { return !(*this == other); }
};
struct OffsetsConfig {
@@ -50,7 +54,11 @@
Offsets earlyGl; // As above but while compositing with GL.
Offsets late; // Default.
- nsecs_t thresholdForNextVsync;
+ bool operator==(const OffsetsConfig& other) const {
+ return early == other.early && earlyGl == other.earlyGl && late == other.late;
+ }
+
+ bool operator!=(const OffsetsConfig& other) const { return !(*this == other); }
};
VSyncModulator(Scheduler&, ConnectionHandle appConnectionHandle,
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 3403b28..dddd552 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -260,7 +260,6 @@
mFrameTracer(std::make_unique<FrameTracer>()),
mEventQueue(mFactory.createMessageQueue()),
mCompositionEngine(mFactory.createCompositionEngine()),
- mPhaseOffsets(mFactory.createPhaseOffsets()),
mPendingSyncInputWindows(false) {}
SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipInitialization) {
@@ -582,8 +581,6 @@
void SurfaceFlinger::init() {
ALOGI( "SurfaceFlinger's main thread ready to run. "
"Initializing graphics H/W...");
- ALOGI("Phase offset: %" PRId64 " ns", mPhaseOffsets->getCurrentAppOffset());
-
Mutex::Autolock _l(mStateLock);
// Get a RenderEngine for the given display / config (can't fail)
@@ -816,7 +813,7 @@
info.ydpi = ydpi;
info.fps = 1e9 / hwConfig->getVsyncPeriod();
- const auto offset = mPhaseOffsets->getOffsetsForRefreshRate(info.fps);
+ const auto offset = mPhaseConfiguration->getOffsetsForRefreshRate(info.fps);
info.appVsyncOffset = offset.late.app;
// This is how far in advance a buffer must be queued for
@@ -896,8 +893,8 @@
// DispSync model is locked.
mVSyncModulator->onRefreshRateChangeInitiated();
- mPhaseOffsets->setRefreshRateFps(refreshRate.fps);
- mVSyncModulator->setPhaseOffsets(mPhaseOffsets->getCurrentOffsets());
+ mPhaseConfiguration->setRefreshRateFps(refreshRate.fps);
+ mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
}
mDesiredActiveConfigChanged = true;
@@ -930,8 +927,8 @@
auto refreshRate =
mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId);
- mPhaseOffsets->setRefreshRateFps(refreshRate.fps);
- mVSyncModulator->setPhaseOffsets(mPhaseOffsets->getCurrentOffsets());
+ mPhaseConfiguration->setRefreshRateFps(refreshRate.fps);
+ mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
ATRACE_INT("ActiveConfigFPS", refreshRate.fps);
if (mUpcomingActiveConfig.event != Scheduler::ConfigEvent::None) {
@@ -948,8 +945,8 @@
mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
auto refreshRate =
mRefreshRateConfigs->getRefreshRateFromConfigId(mDesiredActiveConfig.configId);
- mPhaseOffsets->setRefreshRateFps(refreshRate.fps);
- mVSyncModulator->setPhaseOffsets(mPhaseOffsets->getCurrentOffsets());
+ mPhaseConfiguration->setRefreshRateFps(refreshRate.fps);
+ mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
}
bool SurfaceFlinger::performSetActiveConfig() {
@@ -1597,10 +1594,8 @@
// We are storing the last 2 present fences. If sf's phase offset is to be
// woken up before the actual vsync but targeting the next vsync, we need to check
// fence N-2
- const sp<Fence>& fence =
- mVSyncModulator->getOffsets().sf < mPhaseOffsets->getOffsetThresholdForNextVsync()
- ? mPreviousPresentFences[0]
- : mPreviousPresentFences[1];
+ const sp<Fence>& fence = mVSyncModulator->getOffsets().sf > 0 ? mPreviousPresentFences[0]
+ : mPreviousPresentFences[1];
if (fence == Fence::NO_FENCE) {
return false;
@@ -1618,10 +1613,8 @@
mScheduler->getDisplayStatInfo(&stats);
const nsecs_t presentTime = mScheduler->getDispSyncExpectedPresentTime();
// Inflate the expected present time if we're targetting the next vsync.
- mExpectedPresentTime.store(mVSyncModulator->getOffsets().sf <
- mPhaseOffsets->getOffsetThresholdForNextVsync()
- ? presentTime
- : presentTime + stats.vsyncPeriod);
+ mExpectedPresentTime.store(
+ mVSyncModulator->getOffsets().sf > 0 ? presentTime : presentTime + stats.vsyncPeriod);
}
void SurfaceFlinger::onMessageReceived(int32_t what) NO_THREAD_SAFETY_ANALYSIS {
@@ -1865,11 +1858,12 @@
nsecs_t compositeToPresentLatency) {
// Integer division and modulo round toward 0 not -inf, so we need to
// treat negative and positive offsets differently.
- nsecs_t idealLatency = (mPhaseOffsets->getCurrentSfOffset() > 0)
- ? (stats.vsyncPeriod - (mPhaseOffsets->getCurrentSfOffset() % stats.vsyncPeriod))
- : ((-mPhaseOffsets->getCurrentSfOffset()) % stats.vsyncPeriod);
+ nsecs_t idealLatency = (mPhaseConfiguration->getCurrentOffsets().late.sf > 0)
+ ? (stats.vsyncPeriod -
+ (mPhaseConfiguration->getCurrentOffsets().late.sf % stats.vsyncPeriod))
+ : ((-mPhaseConfiguration->getCurrentOffsets().late.sf) % stats.vsyncPeriod);
- // Just in case mPhaseOffsets->getCurrentSfOffset() == -vsyncInterval.
+ // Just in case mPhaseConfiguration->getCurrentOffsets().late.sf == -vsyncInterval.
if (idealLatency <= 0) {
idealLatency = stats.vsyncPeriod;
}
@@ -1878,8 +1872,8 @@
// composition and present times, which often have >1ms of jitter.
// Reducing jitter is important if an app attempts to extrapolate
// something (such as user input) to an accurate diasplay time.
- // Snapping also allows an app to precisely calculate mPhaseOffsets->getCurrentSfOffset()
- // with (presentLatency % interval).
+ // Snapping also allows an app to precisely calculate
+ // mPhaseConfiguration->getCurrentOffsets().late.sf with (presentLatency % interval).
nsecs_t bias = stats.vsyncPeriod / 2;
int64_t extraVsyncs = (compositeToPresentLatency - idealLatency + bias) / stats.vsyncPeriod;
nsecs_t snappedCompositeToPresentLatency =
@@ -2570,24 +2564,24 @@
currentConfig, HWC_POWER_MODE_OFF);
mRefreshRateStats->setConfigMode(currentConfig);
+ mPhaseConfiguration = getFactory().createPhaseConfiguration(*mRefreshRateConfigs);
+
// start the EventThread
mScheduler =
getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); },
*mRefreshRateConfigs, *this);
mAppConnectionHandle =
- mScheduler->createConnection("app", mPhaseOffsets->getCurrentAppOffset(),
- mPhaseOffsets->getOffsetThresholdForNextVsync(),
+ mScheduler->createConnection("app", mPhaseConfiguration->getCurrentOffsets().late.app,
impl::EventThread::InterceptVSyncsCallback());
mSfConnectionHandle =
- mScheduler->createConnection("sf", mPhaseOffsets->getCurrentSfOffset(),
- mPhaseOffsets->getOffsetThresholdForNextVsync(),
+ mScheduler->createConnection("sf", mPhaseConfiguration->getCurrentOffsets().late.sf,
[this](nsecs_t timestamp) {
mInterceptor->saveVSyncEvent(timestamp);
});
mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle));
mVSyncModulator.emplace(*mScheduler, mAppConnectionHandle, mSfConnectionHandle,
- mPhaseOffsets->getCurrentOffsets());
+ mPhaseConfiguration->getCurrentOffsets());
mRegionSamplingThread =
new RegionSamplingThread(*this, *mScheduler,
@@ -3985,7 +3979,7 @@
mRefreshRateStats->dump(result);
result.append("\n");
- mPhaseOffsets->dump(result);
+ mPhaseConfiguration->dump(result);
StringAppendF(&result,
" present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 " ns\n\n",
dispSyncPresentTimeOffset, getVsyncPeriod());
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index b5bc5c5..a470924 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -1106,7 +1106,7 @@
scheduler::ConnectionHandle mSfConnectionHandle;
// Stores phase offsets configured per refresh rate.
- const std::unique_ptr<scheduler::PhaseOffsets> mPhaseOffsets;
+ std::unique_ptr<scheduler::PhaseConfiguration> mPhaseConfiguration;
// Optional to defer construction until scheduler connections are created.
std::optional<scheduler::VSyncModulator> mVSyncModulator;
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index bd4cdba..375730b 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -15,6 +15,7 @@
*/
#include <compositionengine/impl/CompositionEngine.h>
+#include <cutils/properties.h>
#include <ui/GraphicBuffer.h>
#include "BufferLayerConsumer.h"
@@ -59,8 +60,13 @@
return std::make_unique<android::impl::MessageQueue>();
}
-std::unique_ptr<scheduler::PhaseOffsets> DefaultFactory::createPhaseOffsets() {
- return std::make_unique<scheduler::impl::PhaseOffsets>();
+std::unique_ptr<scheduler::PhaseConfiguration> DefaultFactory::createPhaseConfiguration(
+ const scheduler::RefreshRateConfigs& refreshRateConfigs) {
+ if (property_get_bool("debug.sf.use_phase_offsets_as_durations", false)) {
+ return std::make_unique<scheduler::impl::PhaseDurations>(refreshRateConfigs);
+ } else {
+ return std::make_unique<scheduler::impl::PhaseOffsets>(refreshRateConfigs);
+ }
}
std::unique_ptr<Scheduler> DefaultFactory::createScheduler(
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index 1a24448..36fae21 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -30,7 +30,8 @@
std::unique_ptr<EventControlThread> createEventControlThread(SetVSyncEnabled) override;
std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override;
std::unique_ptr<MessageQueue> createMessageQueue() override;
- std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() override;
+ std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration(
+ const scheduler::RefreshRateConfigs&) override;
std::unique_ptr<Scheduler> createScheduler(SetVSyncEnabled,
const scheduler::RefreshRateConfigs&,
ISchedulerCallback&) override;
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index 0db941d..951bd09 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -56,7 +56,7 @@
} // namespace compositionengine
namespace scheduler {
-class PhaseOffsets;
+class PhaseConfiguration;
class RefreshRateConfigs;
} // namespace scheduler
@@ -74,7 +74,8 @@
virtual std::unique_ptr<EventControlThread> createEventControlThread(SetVSyncEnabled) = 0;
virtual std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) = 0;
virtual std::unique_ptr<MessageQueue> createMessageQueue() = 0;
- virtual std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() = 0;
+ virtual std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration(
+ const scheduler::RefreshRateConfigs&) = 0;
virtual std::unique_ptr<Scheduler> createScheduler(SetVSyncEnabled,
const scheduler::RefreshRateConfigs&,
ISchedulerCallback&) = 0;
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 0c4a752..a950c76 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -45,6 +45,7 @@
"OneShotTimerTest.cpp",
"LayerHistoryTest.cpp",
"LayerMetadataTest.cpp",
+ "PhaseOffsetsTest.cpp",
"SchedulerTest.cpp",
"SchedulerUtilsTest.cpp",
"RefreshRateConfigsTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
index 0aa8cf5..2e705da 100644
--- a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
@@ -51,7 +51,6 @@
AsyncCallRecorder<void (*)(nsecs_t)> mVSyncEventCallRecorder;
static constexpr std::chrono::nanoseconds mPhaseOffset = 6ms;
- static constexpr std::chrono::nanoseconds mOffsetThresholdForNextVsync = 16ms;
static constexpr int mIterations = 100;
};
@@ -79,8 +78,7 @@
void DispSyncSourceTest::createDispSyncSource() {
createDispSync();
- mDispSyncSource = std::make_unique<DispSyncSource>(mDispSync.get(), mPhaseOffset.count(),
- mOffsetThresholdForNextVsync.count(), true,
+ mDispSyncSource = std::make_unique<DispSyncSource>(mDispSync.get(), mPhaseOffset.count(), true,
"DispSyncSourceTest");
mDispSyncSource->setCallback(this);
}
diff --git a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
index da4eea0..b50ddf5 100644
--- a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
+++ b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
@@ -22,7 +22,7 @@
namespace android::scheduler {
-struct FakePhaseOffsets : PhaseOffsets {
+struct FakePhaseOffsets : PhaseConfiguration {
static constexpr nsecs_t FAKE_PHASE_OFFSET_NS = 0;
Offsets getOffsetsForRefreshRate(float) const override { return getCurrentOffsets(); }
@@ -30,8 +30,7 @@
Offsets getCurrentOffsets() const override {
return {{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
- {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
- FAKE_PHASE_OFFSET_NS};
+ {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}};
}
void setRefreshRateFps(float) override {}
diff --git a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
new file mode 100644
index 0000000..6360ec1
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2019 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "SchedulerUnittests"
+
+#include <gmock/gmock.h>
+#include <log/log.h>
+#include <thread>
+
+#include "Scheduler/PhaseOffsets.h"
+
+using namespace testing;
+
+namespace android {
+namespace scheduler {
+
+class TestablePhaseOffsetsAsDurations : public impl::PhaseDurations {
+public:
+ TestablePhaseOffsetsAsDurations(float currentFps, nsecs_t sfDuration, nsecs_t appDuration,
+ nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
+ nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration)
+ : impl::PhaseDurations({60.0f, 90.0f}, currentFps, sfDuration, appDuration,
+ sfEarlyDuration, appEarlyDuration, sfEarlyGlDuration,
+ appEarlyGlDuration) {}
+};
+
+class PhaseOffsetsTest : public testing::Test {
+protected:
+ PhaseOffsetsTest()
+ : mPhaseOffsets(60.0f, 10'500'000, 20'500'000, 16'000'000, 33'500'000, 13'500'000,
+ 38'000'000) {}
+
+ ~PhaseOffsetsTest() = default;
+
+ TestablePhaseOffsetsAsDurations mPhaseOffsets;
+};
+
+namespace {
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_60Hz) {
+ mPhaseOffsets.setRefreshRateFps(60.0f);
+ auto currentOffsets = mPhaseOffsets.getCurrentOffsets();
+ auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(60.0f);
+
+ EXPECT_EQ(currentOffsets, offsets);
+ EXPECT_EQ(offsets.late.sf, 6'166'667);
+
+ EXPECT_EQ(offsets.late.app, 2'333'334);
+
+ EXPECT_EQ(offsets.early.sf, 666'667);
+
+ EXPECT_EQ(offsets.early.app, 500'001);
+
+ EXPECT_EQ(offsets.earlyGl.sf, 3'166'667);
+
+ EXPECT_EQ(offsets.earlyGl.app, 15'166'668);
+}
+
+TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_90Hz) {
+ mPhaseOffsets.setRefreshRateFps(90.0f);
+ auto currentOffsets = mPhaseOffsets.getCurrentOffsets();
+ auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(90.0f);
+
+ EXPECT_EQ(currentOffsets, offsets);
+ EXPECT_EQ(offsets.late.sf, 611'111);
+
+ EXPECT_EQ(offsets.late.app, 2'333'333);
+
+ EXPECT_EQ(offsets.early.sf, -4'888'889);
+
+ EXPECT_EQ(offsets.early.app, 6'055'555);
+
+ EXPECT_EQ(offsets.earlyGl.sf, -2'388'889);
+
+ EXPECT_EQ(offsets.earlyGl.app, 4'055'555);
+}
+
+TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_DefaultOffsets) {
+ TestablePhaseOffsetsAsDurations phaseOffsetsWithDefaultValues(60.0f, -1, -1, -1, -1, -1, -1);
+
+ auto validateOffsets = [](auto& offsets) {
+ EXPECT_EQ(offsets.late.sf, 1'000'000);
+
+ EXPECT_EQ(offsets.late.app, 1'000'000);
+
+ EXPECT_EQ(offsets.early.sf, 1'000'000);
+
+ EXPECT_EQ(offsets.early.app, 1'000'000);
+
+ EXPECT_EQ(offsets.earlyGl.sf, 1'000'000);
+
+ EXPECT_EQ(offsets.earlyGl.app, 1'000'000);
+ };
+
+ phaseOffsetsWithDefaultValues.setRefreshRateFps(90.0f);
+ auto currentOffsets = phaseOffsetsWithDefaultValues.getCurrentOffsets();
+ auto offsets = phaseOffsetsWithDefaultValues.getOffsetsForRefreshRate(90.0f);
+ EXPECT_EQ(currentOffsets, offsets);
+ validateOffsets(offsets);
+
+ phaseOffsetsWithDefaultValues.setRefreshRateFps(60.0f);
+ currentOffsets = phaseOffsetsWithDefaultValues.getCurrentOffsets();
+ offsets = phaseOffsetsWithDefaultValues.getOffsetsForRefreshRate(90.0f);
+ EXPECT_EQ(currentOffsets, offsets);
+ validateOffsets(offsets);
+}
+
+} // namespace
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 8a7c132..c6c3147 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -79,7 +79,8 @@
return std::make_unique<android::impl::MessageQueue>();
}
- std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() override {
+ std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration(
+ const scheduler::RefreshRateConfigs& /*refreshRateConfigs*/) override {
return std::make_unique<scheduler::FakePhaseOffsets>();
}
@@ -203,6 +204,8 @@
scheduler::RefreshRateStats>(*mFlinger->mRefreshRateConfigs, *mFlinger->mTimeStats,
/*currentConfig=*/HwcConfigIndexType(0),
/*powerMode=*/HWC_POWER_MODE_OFF);
+ mFlinger->mPhaseConfiguration =
+ mFactory.createPhaseConfiguration(*mFlinger->mRefreshRateConfigs);
mScheduler =
new TestableScheduler(std::move(primaryDispSync), std::move(eventControlThread),
@@ -214,7 +217,7 @@
mFlinger->mVSyncModulator.emplace(*mScheduler, mFlinger->mAppConnectionHandle,
mFlinger->mSfConnectionHandle,
- mFlinger->mPhaseOffsets->getCurrentOffsets());
+ mFlinger->mPhaseConfiguration->getCurrentOffsets());
}
void resetScheduler(Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); }