Surfaceflinger: rename PhaseOffsets to VsyncConfiguration

Renaming for easier diff with upcoming CLs.

Test: boot
Bug: 162888874
Change-Id: I38b6b3675e3a6ca8ad2a8ac66d8566930779c08e
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
new file mode 100644
index 0000000..5373742
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
@@ -0,0 +1,358 @@
+/*
+ * 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.
+ */
+
+#include "VsyncConfiguration.h"
+
+#include <cutils/properties.h>
+
+#include <optional>
+
+#include "SurfaceFlingerProperties.h"
+
+namespace {
+
+std::optional<nsecs_t> getProperty(const char* name) {
+    char value[PROPERTY_VALUE_MAX];
+    property_get(name, value, "-1");
+    if (const int i = atoi(value); i != -1) return i;
+    return std::nullopt;
+}
+
+bool fpsEqualsWithMargin(float fpsA, float fpsB) {
+    static constexpr float MARGIN = 0.01f;
+    return std::abs(fpsA - fpsB) <= MARGIN;
+}
+
+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->getFps());
+    }
+
+    return refreshRates;
+}
+
+} // namespace
+
+namespace android::scheduler {
+
+PhaseConfiguration::~PhaseConfiguration() = default;
+
+namespace impl {
+
+PhaseOffsets::PhaseOffsets(const scheduler::RefreshRateConfigs& refreshRateConfigs)
+      : PhaseOffsets(getRefreshRatesFromConfigs(refreshRateConfigs),
+                     refreshRateConfigs.getCurrentRefreshRate().getFps(),
+                     sysprop::vsync_event_phase_offset_ns(1000000),
+                     sysprop::vsync_sf_event_phase_offset_ns(1000000),
+                     getProperty("debug.sf.early_phase_offset_ns"),
+                     getProperty("debug.sf.early_gl_phase_offset_ns"),
+                     getProperty("debug.sf.early_app_phase_offset_ns"),
+                     getProperty("debug.sf.early_gl_app_phase_offset_ns"),
+                     // 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.
+                     getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
+                             .value_or(std::numeric_limits<nsecs_t>::max())) {}
+
+PhaseOffsets::PhaseOffsets(const std::vector<float>& refreshRates, float currentFps,
+                           nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
+                           std::optional<nsecs_t> earlySfOffsetNs,
+                           std::optional<nsecs_t> earlyGlSfOffsetNs,
+                           std::optional<nsecs_t> earlyAppOffsetNs,
+                           std::optional<nsecs_t> earlyGlAppOffsetNs, nsecs_t thresholdForNextVsync)
+      : mVSyncPhaseOffsetNs(vsyncPhaseOffsetNs),
+        mSfVSyncPhaseOffsetNs(sfVSyncPhaseOffsetNs),
+        mEarlySfOffsetNs(earlySfOffsetNs),
+        mEarlyGlSfOffsetNs(earlyGlSfOffsetNs),
+        mEarlyAppOffsetNs(earlyAppOffsetNs),
+        mEarlyGlAppOffsetNs(earlyGlAppOffsetNs),
+        mThresholdForNextVsync(thresholdForNextVsync),
+        mOffsets(initializeOffsets(refreshRates)),
+        mRefreshRateFps(currentFps) {}
+
+void PhaseOffsets::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"
+                  "     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,
+                  mThresholdForNextVsync);
+}
+
+std::unordered_map<float, PhaseOffsets::Offsets> PhaseOffsets::initializeOffsets(
+        const std::vector<float>& refreshRates) const {
+    std::unordered_map<float, Offsets> offsets;
+
+    for (const auto& refreshRate : refreshRates) {
+        offsets.emplace(refreshRate,
+                        getPhaseOffsets(refreshRate, static_cast<nsecs_t>(1e9f / refreshRate)));
+    }
+    return offsets;
+}
+
+PhaseOffsets::Offsets PhaseOffsets::getPhaseOffsets(float fps, nsecs_t vsyncPeriod) const {
+    if (fps > 65.0f) {
+        return getHighFpsOffsets(vsyncPeriod);
+    } else {
+        return getDefaultOffsets(vsyncPeriod);
+    }
+}
+
+PhaseOffsets::Offsets PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const {
+    return {
+            {
+                    mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
+                            ? mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs)
+                            : mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration,
+
+                    mEarlyAppOffsetNs.value_or(mVSyncPhaseOffsetNs),
+            },
+            {
+                    mEarlyGlSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
+                            ? mEarlyGlSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs)
+                            : mEarlyGlSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration,
+
+                    mEarlyGlAppOffsetNs.value_or(mVSyncPhaseOffsetNs),
+            },
+            {
+                    mSfVSyncPhaseOffsetNs < mThresholdForNextVsync
+                            ? mSfVSyncPhaseOffsetNs
+                            : mSfVSyncPhaseOffsetNs - vsyncDuration,
+
+                    mVSyncPhaseOffsetNs,
+            },
+    };
+}
+
+PhaseOffsets::Offsets PhaseOffsets::getHighFpsOffsets(nsecs_t vsyncDuration) const {
+    const auto highFpsLateAppOffsetNs =
+            getProperty("debug.sf.high_fps_late_app_phase_offset_ns").value_or(2000000);
+    const auto highFpsLateSfOffsetNs =
+            getProperty("debug.sf.high_fps_late_sf_phase_offset_ns").value_or(1000000);
+
+    const auto highFpsEarlySfOffsetNs = getProperty("debug.sf.high_fps_early_phase_offset_ns");
+    const auto highFpsEarlyGlSfOffsetNs = getProperty("debug.sf.high_fps_early_gl_phase_offset_ns");
+    const auto highFpsEarlyAppOffsetNs = getProperty("debug.sf.high_fps_early_app_phase_offset_ns");
+    const auto highFpsEarlyGlAppOffsetNs =
+            getProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns");
+
+    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,
+            },
+    };
+}
+
+PhaseOffsets::Offsets PhaseOffsets::getOffsetsForRefreshRate(float fps) const {
+    const auto iter = std::find_if(mOffsets.begin(), mOffsets.end(),
+                                   [&fps](const std::pair<float, Offsets>& candidateFps) {
+                                       return fpsEqualsWithMargin(fps, candidateFps.first);
+                                   });
+
+    if (iter != mOffsets.end()) {
+        return iter->second;
+    }
+
+    // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
+    // In this case just construct the offset.
+    ALOGW("Can't find offset for %.2f fps", fps);
+    return getPhaseOffsets(fps, static_cast<nsecs_t>(1e9f / fps));
+}
+
+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;
+}
+
+PhaseDurations::Offsets PhaseDurations::constructOffsets(nsecs_t vsyncDuration) const {
+    return 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),
+            },
+    };
+}
+
+std::unordered_map<float, PhaseDurations::Offsets> PhaseDurations::initializeOffsets(
+        const std::vector<float>& refreshRates) const {
+    std::unordered_map<float, Offsets> offsets;
+
+    for (const auto fps : refreshRates) {
+        offsets.emplace(fps, constructOffsets(static_cast<nsecs_t>(1e9f / fps)));
+    }
+    return offsets;
+}
+
+PhaseDurations::PhaseDurations(const scheduler::RefreshRateConfigs& refreshRateConfigs)
+      : PhaseDurations(getRefreshRatesFromConfigs(refreshRateConfigs),
+                       refreshRateConfigs.getCurrentRefreshRate().getFps(),
+                       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 = std::find_if(mOffsets.begin(), mOffsets.end(), [=](const auto& candidateFps) {
+        return fpsEqualsWithMargin(fps, candidateFps.first);
+    });
+
+    if (iter != mOffsets.end()) {
+        return iter->second;
+    }
+
+    // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
+    // In this case just construct the offset.
+    ALOGW("Can't find offset for %.2f fps", fps);
+    return constructOffsets(static_cast<nsecs_t>(1e9f / fps));
+}
+
+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
+} // namespace android::scheduler