unplugged-system/frameworks/native/services/surfaceflinger/mediatek/MtkDuration.cpp

481 lines
18 KiB
C++

/* Copyright Statement:
*
* This software/firmware and related documentation ("MediaTek Software") are
* protected under relevant copyright laws. The information contained herein is
* confidential and proprietary to MediaTek Inc. and/or its licensors. Without
* the prior written permission of MediaTek inc. and/or its licensors, any
* reproduction, modification, use or disclosure of MediaTek Software, and
* information contained herein, in whole or in part, shall be strictly
* prohibited.
*
* MediaTek Inc. (C) 2022. All rights reserved.
*
* BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
* THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
* RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER
* ON AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL
* WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
* NONINFRINGEMENT. NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH
* RESPECT TO THE SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY,
* INCORPORATED IN, OR SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES
* TO LOOK ONLY TO SUCH THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO.
* RECEIVER EXPRESSLY ACKNOWLEDGES THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO
* OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES CONTAINED IN MEDIATEK
* SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK SOFTWARE
* RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
* STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S
* ENTIRE AND CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE
* RELEASED HEREUNDER WILL BE, AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE
* MEDIATEK SOFTWARE AT ISSUE, OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE
* CHARGE PAID BY RECEIVER TO MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
*
* The following software/firmware and/or related documentation ("MediaTek
* Software") have been modified by MediaTek Inc. All revisions are subject to
* any receiver's applicable license agreements with MediaTek Inc.
*/
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <cinttypes>
#include <gui/TraceUtils.h>
#include <log/log.h>
#include <android-base/properties.h>
#include "MtkDuration.h"
namespace android {
// ---------------------------------------------------------------------------
bool isDynamicDurationEnable() {
static bool read = false, sw = false;
// check switch first
if (!read) {
sw = base::GetBoolProperty("vendor.debug.sf.dynamic_duration.switch", false);
read = true;
}
return sw;
}
nsecs_t loadMtkDuration(const char* name) {
// if switch is off, not allow to get MKT Duration
if (!isDynamicDurationEnable() || name == nullptr) return -1;
return base::GetIntProperty(name, -1);
}
int vsync2hz(nsecs_t vsync) {
return vsync > 0 ?
static_cast<int>(std::round(static_cast<float>(ms2ns(1000))/static_cast<float>(vsync))) : -1;
}
// below code from VsyncConfiguration.cpp
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;
}
// above code from VsyncConfiguration.cpp
MtkDuration::MtkDuration() {
mType = DurationType_Unset;
mVsyncPeriod = 0;
nsecs_t sf_decouple = loadMtkDuration(MTK_DURATION_SF_DECOUPLE);
nsecs_t app_decouple = loadMtkDuration(MTK_DURATION_APP_DECOUPLE);
mDecoupleDuration = {
.sfOffset = 0,
.appOffset = 0,
.sfWorkDuration = std::chrono::nanoseconds(sf_decouple),
.appWorkDuration = std::chrono::nanoseconds(app_decouple),
};
mDecoupleModeSwitch = base::GetBoolProperty("vendor.debug.sf.dynamic_duration.switch.decouple", isDynamicDurationEnable());
mDrawingDecoupleMode = false;
mActiveDisplayId = 0;
mActiveDisplayIndex = 0;
mOverrideModeChange = false;
mOverrideDuration = {
.sfOffset = 0,
.appOffset = 0,
.sfWorkDuration = std::chrono::nanoseconds(0),
.appWorkDuration = std::chrono::nanoseconds(0),
};
mOverrideModeSwitch = base::GetBoolProperty("vendor.debug.sf.dynamic_duration.switch.override", false);
mIsExtraVsyncSupported = false;
mIsClientComposition = false;
}
bool MtkDuration::isEnable() {
return isDynamicDurationEnable();
}
nsecs_t MtkDuration::initMtkDuration(const char* type, nsecs_t vsync, nsecs_t origin) const {
if (type == nullptr || vsync == 0) return origin;
const std::string prop
= type + std::to_string(vsync2hz(vsync));
nsecs_t duration = loadMtkDuration(prop.c_str());
if (duration <= 0) {
// if duration is not set, we try to get default max/min
if (vsync > DEFAULT_MIN_VSYNC) {
const std::string default_prop
= type + std::to_string(vsync2hz(DEFAULT_MIN_VSYNC));
duration = loadMtkDuration(default_prop.c_str());
} else if (vsync < DEFAULT_MAX_VSYNC) {
const std::string default_prop
= type + std::to_string(vsync2hz(DEFAULT_MAX_VSYNC));
duration = loadMtkDuration(default_prop.c_str());
}
}
return duration > 0 ? duration : origin;
}
bool MtkDuration::usingMtkDuration() {
std::lock_guard<std::mutex> lock(mTypeMutex);
if (((mType & DurationType_Override) == DurationType_Override) ||
((mType & DurationType_Decouple) == DurationType_Decouple)) {
return true;
}
return false;
}
scheduler::VsyncConfig MtkDuration::getMtkDuration(scheduler::VsyncConfig &original) {
std::lock_guard<std::mutex> lock(mTypeMutex);
if ((mType & DurationType_Override) == DurationType_Override) {
ATRACE_NAME("DurationType_Override");
scheduler::VsyncConfig config = mOverrideDuration;
bool change = false;
if (config.sfWorkDuration.count() <= 0) {
config.sfWorkDuration = original.sfWorkDuration;
change = true;
}
if (config.appWorkDuration.count() <= 0) {
config.appWorkDuration = original.appWorkDuration;
change = true;
}
if (change) updateDurationOffset(config);
return config;
} else if ((mType & DurationType_Decouple) == DurationType_Decouple) {
ATRACE_NAME("DurationType_Decouple");
return mDecoupleDuration;
} else {
ALOGE("MtkDuration not using mtk duration but someone is trying to get");
return {};
}
}
void MtkDuration::updateDurations() {
if (isDecoupleModeAvailable()) {
std::lock_guard<std::mutex> lock(mDecoupleModeMutex);
updateDurationOffset(mDecoupleDuration);
}
if (mOverrideModeSwitch) {
std::lock_guard<std::mutex> lock(mOverrideModeMutex);
updateDurationOffset(mOverrideDuration);
}
}
void MtkDuration::setVsyncPeriod(nsecs_t vsync) {
if (mVsyncPeriod == vsync) return;
mVsyncPeriod = vsync;
updateDurations();
}
void MtkDuration::updateDurationOffset(scheduler::VsyncConfig& config) {
config.sfOffset = config.sfWorkDuration.count() < mVsyncPeriod
? sfDurationToOffset(config.sfWorkDuration, mVsyncPeriod)
: sfDurationToOffset(config.sfWorkDuration, mVsyncPeriod) - mVsyncPeriod;
config.appOffset = config.appWorkDuration.count() < mVsyncPeriod
? appDurationToOffset(config.appWorkDuration, config.sfWorkDuration, mVsyncPeriod)
: appDurationToOffset(config.appWorkDuration, config.sfWorkDuration, mVsyncPeriod) - mVsyncPeriod;
}
bool MtkDuration::onDisplayRefresh() {
ATRACE_NAME("MtkDuration::onDisplayRefresh");
bool updateNeeded = false;
// Override mode
updateNeeded |= isOverrideModeChange();
// Decouple mode
updateNeeded |= isDecoupleModeChange();
// Default unset
return updateNeeded;
}
std::chrono::nanoseconds MtkDuration::getCurrentMtkPresentLatency(
std::chrono::nanoseconds app, std::chrono::nanoseconds sf) {
if ((mType & DurationType_Override) == DurationType_Override) {
return mOverrideDuration.appWorkDuration + mOverrideDuration.sfWorkDuration;
} else if ((mType & DurationType_Decouple) == DurationType_Decouple) {
return mDecoupleDuration.appWorkDuration + mDecoupleDuration.sfWorkDuration;
} else {
return app + sf;
}
}
std::chrono::nanoseconds MtkDuration::getDecoupleMtkPresentLatency(
std::chrono::nanoseconds app, std::chrono::nanoseconds sf) {
if (isDecoupleModeAvailable()) {
return mDecoupleDuration.appWorkDuration + mDecoupleDuration.sfWorkDuration;
}
return app + sf;
}
void MtkDuration::setIsExtraVsyncSupported(bool support) {
mIsExtraVsyncSupported = support;
}
uint32_t MtkDuration::setExtraVsyncCount(uint64_t disp_id, uint32_t count) {
std::lock_guard<std::mutex> lock(mExtraVsyncMutex);
for (size_t i = 0; i < mRequestedExtraVsyncCounts.size(); i++) {
if (mRequestedExtraVsyncCounts[i].disp_id == disp_id) {
ALOGD("update extraVsyncCount %" PRIu64 ", %" PRIu32 , disp_id, count);
mRequestedExtraVsyncCounts[i].count = count;
return count;
}
}
ALOGV("new extraVsyncCount %" PRIu64 ", %" PRIu32 , disp_id, count);
mRequestedExtraVsyncCounts.emplace_back(disp_id, count);
return count;
}
size_t MtkDuration::getExtraVsyncCount() {
size_t result = 0;
if (mIsExtraVsyncSupported) {
std::lock_guard<std::mutex> lock(mExtraVsyncMutex);
for (size_t i = 0; i < mRequestedExtraVsyncCounts.size(); i++) {
if (mActiveDisplayId == mRequestedExtraVsyncCounts[i].disp_id) {
result += static_cast<size_t>(mRequestedExtraVsyncCounts[i].count);
break;
}
}
if (result == 0 && mIsClientComposition) {
result = 1;
}
ALOGV("getExtraVsyncCount=%zu, mIsClientComposition=%s", result, (mIsClientComposition?"yes":"no"));
ATRACE_FORMAT("EV=%zu C=%s", result, (mIsClientComposition?"1":"0"));
}
return result;
}
void MtkDuration::setIsClientComposition(bool is) {
std::lock_guard<std::mutex> lock(mExtraVsyncMutex);
mIsClientComposition = is;
ALOGV("setIsClientComposition %s", mIsClientComposition ? "yes" : "no");
}
bool MtkDuration::controlGpuBackpressure(bool origin) {
return mIsExtraVsyncSupported || origin;
}
int MtkDuration::getExtraBuffers() {
if (!mIsExtraVsyncSupported) return 0;
static int count = base::GetIntProperty("vendor.debug.sf.dynamic_duration.extra_buffers", 1);
return count;
}
// Decouple mode - START
bool MtkDuration::isDecoupleModeAvailable() {
// If hwc support extra vsync to present, we disable deboule mode.
return mDecoupleModeSwitch && !mIsExtraVsyncSupported;
}
bool MtkDuration::duringTransaction(size_t i) {
return 0 < mDispMML[i].transactionFrames;
}
void MtkDuration::decreTransactionFrames() {
for (size_t i = 0; i < mDispMML.size(); i++) {
if (mDispMML[i].transactionFrames > 0) {
mDispMML[i].transactionFrames--;
}
}
}
bool MtkDuration::getDecoupleModeLocked(size_t i) {
if (duringTransaction(i)) {
// duration transaction
return mDispMML[i].transactionMode && isDecoupleModeAvailable();
}
return mDispMML[i].decoupleMode && isDecoupleModeAvailable();
}
size_t MtkDuration::getActiveDisplayIndex() {
if (mActiveDisplayIndex == INVALID_ACTIVE_DISPLAY_INDEX ||
mDispMML[mActiveDisplayIndex].displayId != mActiveDisplayId) {
for (size_t i = 0; i < mDispMML.size(); i++) {
if (mDispMML[i].displayId == mActiveDisplayId) {
mActiveDisplayIndex = i;
break;
}
}
}
if (mActiveDisplayIndex == INVALID_ACTIVE_DISPLAY_INDEX) {
ALOGW("mActiveDisplayIndex !valid, return 0 instead");
mActiveDisplayIndex = 0;
}
return mActiveDisplayIndex;
}
bool MtkDuration::getDecoupleModeUpdate() {
std::lock_guard<std::mutex> lock(mDecoupleModeMutex);
decreTransactionFrames();
return getDecoupleModeLocked(getActiveDisplayIndex());
}
void MtkDuration::onNewInternalDisplay(uint64_t id) {
std::lock_guard<std::mutex> lock(mDecoupleModeMutex);
for (size_t i = 0; i < mDispMML.size(); i++) {
if (mDispMML[i].displayId == id) {
ALOGW("onNewInternalDisplay, already exist %" PRIu64, id);
return;
}
}
mDispMML.emplace_back(id, false, 0, false);
ALOGD("onNewInternalDisplay, %" PRIu64, id);
}
void MtkDuration::updateActiveDisplayId(uint64_t id) {
std::lock_guard<std::mutex> lock(mDecoupleModeMutex);
mActiveDisplayId = id;
bool find = false;
for (size_t i = 0; i < mDispMML.size(); i++) {
if (mDispMML[i].displayId == mActiveDisplayId) {
find = true;
mActiveDisplayIndex = i;
break;
}
}
if (!find) {
ALOGW("updateActiveDisplayId !find %" PRIu64, id);
} else {
ALOGD("updateActiveDisplayId %" PRIu64, id);
}
}
void MtkDuration::setDecoupleMode(uint64_t id, bool mode) {
std::lock_guard<std::mutex> lock(mDecoupleModeMutex);
bool find = false;
for (size_t i = 0; i < mDispMML.size(); i++) {
if (mDispMML[i].displayId == id) {
find = true;
if (mDispMML[i].decoupleMode != mode && !duringTransaction(i)) {
mDispMML[i].transactionFrames = MIN_TRANSACTION_FRAMES;
mDispMML[i].transactionMode = mode;
}
mDispMML[i].decoupleMode = mode;
break;
}
}
if (find) {
std::string name = "DecoupleMode_" + std::to_string(id);
ATRACE_INT(name.c_str(), (mode?1:0));
}
}
bool MtkDuration::isDecoupleModeChange() {
if (isDecoupleModeAvailable()) {
bool nextMode = getDecoupleModeUpdate();
if (mDrawingDecoupleMode != nextMode) {
mDrawingDecoupleMode = nextMode;
std::lock_guard<std::mutex> lock(mTypeMutex);
if (mDrawingDecoupleMode) {
mType |= DurationType_Decouple;
} else {
mType &= ~(DurationType_Decouple);
}
return true;
}
}
return false;
}
// Decouple mode - END
// Override mode - START
bool MtkDuration::isOverrideModeChange() {
std::lock_guard<std::mutex> lock(mOverrideModeMutex);
if (mOverrideModeSwitch && mOverrideModeChange) {
return true;
}
return false;
}
void MtkDuration::setOverrideDuration(nsecs_t app, nsecs_t sf) {
if (!mOverrideModeSwitch) {
ALOGE("MtkDuration override mode is disabled");
return;
}
bool set = false;
{
std::lock_guard<std::mutex> lock(mOverrideModeMutex);
mOverrideModeChange = true;
if (app <= 0 && sf <= 0) {
mOverrideDuration = {
.sfOffset = 0,
.appOffset = 0,
.sfWorkDuration = std::chrono::nanoseconds(0),
.appWorkDuration = std::chrono::nanoseconds(0),
};
set = false;
} else {
mOverrideDuration = {
.sfOffset = 0,
.appOffset = 0,
.sfWorkDuration = std::chrono::nanoseconds(sf),
.appWorkDuration = std::chrono::nanoseconds(app),
};
updateDurationOffset(mOverrideDuration);
set = true;
}
}
{
std::lock_guard<std::mutex> lock(mTypeMutex);
if (set) {
mType |= DurationType_Override;
} else {
mType &= ~(DurationType_Override);
}
}
}
// Override mode - END
void MtkDuration::dump(std::string& result) {
using base::StringAppendF;
StringAppendF(&result,
"\n MTKDuration: current type: %d\n"
" Decouple mode switch: %s Using decouple mode: %s\n"
" Decouple app phase: %9" PRId64 " ns\tDecouple SF phase: %9" PRId64
" ns\n"
" Decouple app duration: %9lld ns\tDecouple SF duration: %9lld ns\n"
" Override mode switch: %s Using Override mode: %s\n"
" Override app phase: %9" PRId64 " ns\tOverride SF phase: %9" PRId64
" ns\n"
" Override app duration: %9lld ns\tOverride SF duration: %9lld ns\n"
" ExtraVsyncSupported=%s\n\n",
mType,
mDecoupleModeSwitch ? "true" : "false",
mDrawingDecoupleMode ? "true" : "false",
mDecoupleDuration.appOffset, mDecoupleDuration.sfOffset,
mDecoupleDuration.appWorkDuration.count(), mDecoupleDuration.sfWorkDuration.count(),
mOverrideModeSwitch ? "true" : "false",
(mType & DurationType_Override) == DurationType_Override ? "true" : "false",
mOverrideDuration.appOffset, mOverrideDuration.sfOffset,
mOverrideDuration.appWorkDuration.count(), mOverrideDuration.sfWorkDuration.count(),
mIsExtraVsyncSupported ? "yes" : "no");
{
std::lock_guard<std::mutex> lock(mDecoupleModeMutex);
for (size_t i = 0; i < mDispMML.size(); i++) {
StringAppendF(&result, " DispMML[%zu]: dispId=%" PRIu64 ", mode=%s\n",
i, mDispMML[i].displayId, mDispMML[i].decoupleMode?"true":"false");
}
}
{
std::lock_guard<std::mutex> lock(mExtraVsyncMutex);
for (size_t i = 0; i < mRequestedExtraVsyncCounts.size(); i++) {
StringAppendF(&result, " ExtraVsync[%zu]: dispId=%" PRIu64 ", count=%" PRIu32 "\n",
i, mRequestedExtraVsyncCounts[i].disp_id, mRequestedExtraVsyncCounts[i].count);
}
}
result.append("\n");
}
// ---------------------------------------------------------------------------
}; // namespace android