/* 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 #include #include #include #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(std::round(static_cast(ms2ns(1000))/static_cast(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 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 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 lock(mDecoupleModeMutex); updateDurationOffset(mDecoupleDuration); } if (mOverrideModeSwitch) { std::lock_guard 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 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 lock(mExtraVsyncMutex); for (size_t i = 0; i < mRequestedExtraVsyncCounts.size(); i++) { if (mActiveDisplayId == mRequestedExtraVsyncCounts[i].disp_id) { result += static_cast(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 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 lock(mDecoupleModeMutex); decreTransactionFrames(); return getDecoupleModeLocked(getActiveDisplayIndex()); } void MtkDuration::onNewInternalDisplay(uint64_t id) { std::lock_guard 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 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 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 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 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 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 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 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 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