blob: aac2569044747abe5b65782ad788874422f9e1e2 [file] [log] [blame]
/*
* 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::impl {
VsyncConfiguration::VsyncConfiguration(float currentFps) : mRefreshRateFps(currentFps) {}
PhaseOffsets::VsyncConfigSet VsyncConfiguration::getConfigsForRefreshRate(float fps) const {
const auto iter = std::find_if(mOffsets.begin(), mOffsets.end(),
[&fps](const std::pair<float, VsyncConfigSet>& 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 VsyncConfiguration::initializeOffsets(const std::vector<float>& refreshRates) {
for (const auto fps : refreshRates) {
mOffsets.emplace(fps, constructOffsets(static_cast<nsecs_t>(1e9f / fps)));
}
}
void VsyncConfiguration::dump(std::string& result) const {
const auto [early, earlyGpu, late] = getCurrentConfigs();
using base::StringAppendF;
StringAppendF(&result,
" app phase: %9" PRId64 " ns\t SF phase: %9" PRId64
" ns\n"
" app duration: %9lld ns\t SF duration: %9lld ns\n"
" early app phase: %9" PRId64 " ns\t early SF phase: %9" PRId64
" ns\n"
" early app duration: %9lld ns\t early SF duration: %9lld ns\n"
" GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64
" ns\n"
" GL early app duration: %9lld ns\tGL early SF duration: %9lld ns\n",
late.appOffset, late.sfOffset,
late.appWorkDuration.count(), late.sfWorkDuration.count(),
early.appOffset, early.sfOffset,
early.appWorkDuration.count(), early.sfWorkDuration.count(),
earlyGpu.appOffset, earlyGpu.sfOffset,
earlyGpu.appWorkDuration.count(), earlyGpu.sfWorkDuration.count());
}
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"),
getProperty("debug.sf.high_fps_late_app_phase_offset_ns").value_or(2000000),
getProperty("debug.sf.high_fps_late_sf_phase_offset_ns").value_or(1000000),
getProperty("debug.sf.high_fps_early_phase_offset_ns"),
getProperty("debug.sf.high_fps_early_gl_phase_offset_ns"),
getProperty("debug.sf.high_fps_early_app_phase_offset_ns"),
getProperty("debug.sf.high_fps_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> earlyGpuSfOffsetNs, std::optional<nsecs_t> earlyAppOffsetNs,
std::optional<nsecs_t> earlyGpuAppOffsetNs, nsecs_t highFpsVsyncPhaseOffsetNs,
nsecs_t highFpsSfVSyncPhaseOffsetNs, std::optional<nsecs_t> highFpsEarlySfOffsetNs,
std::optional<nsecs_t> highFpsEarlyGpuSfOffsetNs,
std::optional<nsecs_t> highFpsEarlyAppOffsetNs,
std::optional<nsecs_t> highFpsEarlyGpuAppOffsetNs, nsecs_t thresholdForNextVsync)
: VsyncConfiguration(currentFps),
mVSyncPhaseOffsetNs(vsyncPhaseOffsetNs),
mSfVSyncPhaseOffsetNs(sfVSyncPhaseOffsetNs),
mEarlySfOffsetNs(earlySfOffsetNs),
mEarlyGpuSfOffsetNs(earlyGpuSfOffsetNs),
mEarlyAppOffsetNs(earlyAppOffsetNs),
mEarlyGpuAppOffsetNs(earlyGpuAppOffsetNs),
mHighFpsVSyncPhaseOffsetNs(highFpsVsyncPhaseOffsetNs),
mHighFpsSfVSyncPhaseOffsetNs(highFpsSfVSyncPhaseOffsetNs),
mHighFpsEarlySfOffsetNs(highFpsEarlySfOffsetNs),
mHighFpsEarlyGpuSfOffsetNs(highFpsEarlyGpuSfOffsetNs),
mHighFpsEarlyAppOffsetNs(highFpsEarlyAppOffsetNs),
mHighFpsEarlyGpuAppOffsetNs(highFpsEarlyGpuAppOffsetNs),
mThresholdForNextVsync(thresholdForNextVsync) {
initializeOffsets(refreshRates);
}
PhaseOffsets::VsyncConfigSet PhaseOffsets::constructOffsets(nsecs_t vsyncDuration) const {
if (vsyncDuration < std::chrono::nanoseconds(15ms).count()) {
return getHighFpsOffsets(vsyncDuration);
} else {
return getDefaultOffsets(vsyncDuration);
}
}
namespace {
std::chrono::nanoseconds sfOffsetToDuration(nsecs_t sfOffset, nsecs_t vsyncDuration) {
return std::chrono::nanoseconds(vsyncDuration - sfOffset);
}
std::chrono::nanoseconds appOffsetToDuration(nsecs_t appOffset, nsecs_t sfOffset,
nsecs_t vsyncDuration) {
auto duration = vsyncDuration + (sfOffset - appOffset);
if (duration < vsyncDuration) {
duration += vsyncDuration;
}
return std::chrono::nanoseconds(duration);
}
} // namespace
PhaseOffsets::VsyncConfigSet PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const {
const auto earlySfOffset =
mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
? mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs)
: mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration;
const auto earlyAppOffset = mEarlyAppOffsetNs.value_or(mVSyncPhaseOffsetNs);
const auto earlyGpuSfOffset =
mEarlyGpuSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
? mEarlyGpuSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs)
: mEarlyGpuSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration;
const auto earlyGpuAppOffset = mEarlyGpuAppOffsetNs.value_or(mVSyncPhaseOffsetNs);
const auto lateSfOffset = mSfVSyncPhaseOffsetNs < mThresholdForNextVsync
? mSfVSyncPhaseOffsetNs
: mSfVSyncPhaseOffsetNs - vsyncDuration;
const auto lateAppOffset = mVSyncPhaseOffsetNs;
return {
.early = {.sfOffset = earlySfOffset,
.appOffset = earlyAppOffset,
.sfWorkDuration = sfOffsetToDuration(earlySfOffset, vsyncDuration),
.appWorkDuration =
appOffsetToDuration(earlyAppOffset, earlySfOffset, vsyncDuration)},
.earlyGpu = {.sfOffset = earlyGpuSfOffset,
.appOffset = earlyGpuAppOffset,
.sfWorkDuration = sfOffsetToDuration(earlyGpuSfOffset, vsyncDuration),
.appWorkDuration = appOffsetToDuration(earlyGpuAppOffset, earlyGpuSfOffset,
vsyncDuration)},
.late = {.sfOffset = lateSfOffset,
.appOffset = lateAppOffset,
.sfWorkDuration = sfOffsetToDuration(lateSfOffset, vsyncDuration),
.appWorkDuration =
appOffsetToDuration(lateAppOffset, lateSfOffset, vsyncDuration)},
};
}
PhaseOffsets::VsyncConfigSet PhaseOffsets::getHighFpsOffsets(nsecs_t vsyncDuration) const {
const auto earlySfOffset =
mHighFpsEarlySfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
? mHighFpsEarlySfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs)
: mHighFpsEarlySfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs) - vsyncDuration;
const auto earlyAppOffset = mHighFpsEarlyAppOffsetNs.value_or(mHighFpsVSyncPhaseOffsetNs);
const auto earlyGpuSfOffset = mHighFpsEarlyGpuSfOffsetNs.value_or(
mHighFpsSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
? mHighFpsEarlyGpuSfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs)
: mHighFpsEarlyGpuSfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs) - vsyncDuration;
const auto earlyGpuAppOffset = mHighFpsEarlyGpuAppOffsetNs.value_or(mHighFpsVSyncPhaseOffsetNs);
const auto lateSfOffset = mHighFpsSfVSyncPhaseOffsetNs < mThresholdForNextVsync
? mHighFpsSfVSyncPhaseOffsetNs
: mHighFpsSfVSyncPhaseOffsetNs - vsyncDuration;
const auto lateAppOffset = mHighFpsVSyncPhaseOffsetNs;
return {
.early =
{
.sfOffset = earlySfOffset,
.appOffset = earlyAppOffset,
.sfWorkDuration = sfOffsetToDuration(earlySfOffset, vsyncDuration),
.appWorkDuration = appOffsetToDuration(earlyAppOffset, earlySfOffset,
vsyncDuration),
},
.earlyGpu =
{
.sfOffset = earlyGpuSfOffset,
.appOffset = earlyGpuAppOffset,
.sfWorkDuration = sfOffsetToDuration(earlyGpuSfOffset, vsyncDuration),
.appWorkDuration = appOffsetToDuration(earlyGpuAppOffset,
earlyGpuSfOffset, vsyncDuration),
},
.late =
{
.sfOffset = lateSfOffset,
.appOffset = lateAppOffset,
.sfWorkDuration = sfOffsetToDuration(lateSfOffset, vsyncDuration),
.appWorkDuration =
appOffsetToDuration(lateAppOffset, lateSfOffset, vsyncDuration),
},
};
}
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");
}
namespace {
nsecs_t sfDurationToOffset(std::chrono::nanoseconds sfDuration, nsecs_t vsyncDuration) {
return vsyncDuration - sfDuration.count() % vsyncDuration;
}
nsecs_t appDurationToOffset(std::chrono::nanoseconds appDuration,
std::chrono::nanoseconds sfDuration, nsecs_t vsyncDuration) {
return vsyncDuration - (appDuration + sfDuration).count() % vsyncDuration;
}
} // namespace
WorkDuration::VsyncConfigSet WorkDuration::constructOffsets(nsecs_t vsyncDuration) const {
const auto sfDurationFixup = [vsyncDuration](nsecs_t duration) {
return duration == -1 ? std::chrono::nanoseconds(vsyncDuration) - 1ms
: std::chrono::nanoseconds(duration);
};
const auto appDurationFixup = [vsyncDuration](nsecs_t duration) {
return duration == -1 ? std::chrono::nanoseconds(vsyncDuration)
: std::chrono::nanoseconds(duration);
};
const auto sfEarlyDuration = sfDurationFixup(mSfEarlyDuration);
const auto appEarlyDuration = appDurationFixup(mAppEarlyDuration);
const auto sfEarlyGpuDuration = sfDurationFixup(mSfEarlyGpuDuration);
const auto appEarlyGpuDuration = appDurationFixup(mAppEarlyGpuDuration);
const auto sfDuration = sfDurationFixup(mSfDuration);
const auto appDuration = appDurationFixup(mAppDuration);
return {
.early =
{
.sfOffset = sfEarlyDuration.count() < vsyncDuration
? sfDurationToOffset(sfEarlyDuration, vsyncDuration)
: sfDurationToOffset(sfEarlyDuration, vsyncDuration) -
vsyncDuration,
.appOffset = appDurationToOffset(appEarlyDuration, sfEarlyDuration,
vsyncDuration),
.sfWorkDuration = sfEarlyDuration,
.appWorkDuration = appEarlyDuration,
},
.earlyGpu =
{
.sfOffset = sfEarlyGpuDuration.count() < vsyncDuration
? sfDurationToOffset(sfEarlyGpuDuration, vsyncDuration)
: sfDurationToOffset(sfEarlyGpuDuration, vsyncDuration) -
vsyncDuration,
.appOffset = appDurationToOffset(appEarlyGpuDuration,
sfEarlyGpuDuration, vsyncDuration),
.sfWorkDuration = sfEarlyGpuDuration,
.appWorkDuration = appEarlyGpuDuration,
},
.late =
{
.sfOffset = sfDuration.count() < vsyncDuration
? sfDurationToOffset(sfDuration, vsyncDuration)
: sfDurationToOffset(sfDuration, vsyncDuration) - vsyncDuration,
.appOffset =
appDurationToOffset(appDuration, sfDuration, vsyncDuration),
.sfWorkDuration = sfDuration,
.appWorkDuration = appDuration,
},
};
}
WorkDuration::WorkDuration(const scheduler::RefreshRateConfigs& refreshRateConfigs)
: WorkDuration(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();
}
WorkDuration::WorkDuration(const std::vector<float>& refreshRates, float currentFps,
nsecs_t sfDuration, nsecs_t appDuration, nsecs_t sfEarlyDuration,
nsecs_t appEarlyDuration, nsecs_t sfEarlyGpuDuration,
nsecs_t appEarlyGpuDuration)
: VsyncConfiguration(currentFps),
mSfDuration(sfDuration),
mAppDuration(appDuration),
mSfEarlyDuration(sfEarlyDuration),
mAppEarlyDuration(appEarlyDuration),
mSfEarlyGpuDuration(sfEarlyGpuDuration),
mAppEarlyGpuDuration(appEarlyGpuDuration) {
initializeOffsets(refreshRates);
}
} // namespace android::scheduler::impl