You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
381 lines
18 KiB
381 lines
18 KiB
/*
|
|
* 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 <chrono>
|
|
#include <cinttypes>
|
|
#include <optional>
|
|
|
|
#include <cutils/properties.h>
|
|
#include <log/log.h>
|
|
|
|
#include "SurfaceFlingerProperties.h"
|
|
|
|
namespace {
|
|
|
|
using namespace std::chrono_literals;
|
|
|
|
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;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace android::scheduler::impl {
|
|
|
|
VsyncConfiguration::VsyncConfiguration(Fps currentFps) : mRefreshRateFps(currentFps) {}
|
|
|
|
PhaseOffsets::VsyncConfigSet VsyncConfiguration::getConfigsForRefreshRate(Fps fps) const {
|
|
std::lock_guard lock(mLock);
|
|
return getConfigsForRefreshRateLocked(fps);
|
|
}
|
|
|
|
PhaseOffsets::VsyncConfigSet VsyncConfiguration::getConfigsForRefreshRateLocked(Fps fps) const {
|
|
const auto iter = mOffsetsCache.find(fps);
|
|
if (iter != mOffsetsCache.end()) {
|
|
return iter->second;
|
|
}
|
|
|
|
const auto offset = constructOffsets(fps.getPeriodNsecs());
|
|
mOffsetsCache[fps] = offset;
|
|
return offset;
|
|
}
|
|
|
|
void VsyncConfiguration::dump(std::string& result) const {
|
|
const auto [early, earlyGpu, late, hwcMinWorkDuration] = 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"
|
|
" HWC min 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(),
|
|
|
|
hwcMinWorkDuration.count());
|
|
}
|
|
|
|
PhaseOffsets::PhaseOffsets(Fps currentRefreshRate)
|
|
: PhaseOffsets(currentRefreshRate, 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()),
|
|
getProperty("debug.sf.hwc.min.duration").value_or(0)) {}
|
|
|
|
PhaseOffsets::PhaseOffsets(Fps 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, nsecs_t hwcMinWorkDuration)
|
|
: 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),
|
|
mHwcMinWorkDuration(hwcMinWorkDuration) {}
|
|
|
|
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)},
|
|
.hwcMinWorkDuration = std::chrono::nanoseconds(mHwcMinWorkDuration),
|
|
};
|
|
}
|
|
|
|
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),
|
|
},
|
|
.hwcMinWorkDuration = std::chrono::nanoseconds(mHwcMinWorkDuration),
|
|
};
|
|
}
|
|
|
|
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,
|
|
},
|
|
.hwcMinWorkDuration = std::chrono::nanoseconds(mHwcMinWorkDuration),
|
|
};
|
|
}
|
|
|
|
WorkDuration::WorkDuration(Fps currentRefreshRate)
|
|
: WorkDuration(currentRefreshRate, 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),
|
|
getProperty("debug.sf.hwc.min.duration").value_or(0)) {
|
|
validateSysprops();
|
|
}
|
|
|
|
WorkDuration::WorkDuration(Fps currentRefreshRate, nsecs_t sfDuration, nsecs_t appDuration,
|
|
nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
|
|
nsecs_t sfEarlyGpuDuration, nsecs_t appEarlyGpuDuration,
|
|
nsecs_t hwcMinWorkDuration)
|
|
: VsyncConfiguration(currentRefreshRate),
|
|
mSfDuration(sfDuration),
|
|
mAppDuration(appDuration),
|
|
mSfEarlyDuration(sfEarlyDuration),
|
|
mAppEarlyDuration(appEarlyDuration),
|
|
mSfEarlyGpuDuration(sfEarlyGpuDuration),
|
|
mAppEarlyGpuDuration(appEarlyGpuDuration),
|
|
mHwcMinWorkDuration(hwcMinWorkDuration) {}
|
|
|
|
} // namespace android::scheduler::impl
|