blob: ff0ea2e5249642ccc9d45116d13c3f8b41d0c99a [file]
/*
* Copyright (C) 2023 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.
*/
#define ATRACE_TAG (ATRACE_TAG_GRAPHICS | ATRACE_TAG_HAL)
#include "VariableRefreshRateController.h"
#include <android-base/logging.h>
#include <processgroup/sched_policy.h>
#include <sync/sync.h>
#include <utils/Trace.h>
#include "ExynosHWCHelper.h"
#include "drmmode.h"
#include <chrono>
#include <tuple>
#include "RefreshRateCalculator/RefreshRateCalculatorFactory.h"
#include "display/DisplayContextProviderFactory.h"
#include "interface/Panel_def.h"
namespace android::hardware::graphics::composer {
namespace {
using android::hardware::graphics::composer::VrrControllerEventType;
}
static OperationSpeedMode getOperationSpeedModeWrapper(void* host) {
VariableRefreshRateController* controller =
reinterpret_cast<VariableRefreshRateController*>(host);
return controller->getOperationSpeedMode();
}
static BrightnessMode getBrightnessModeWrapper(void* host) {
VariableRefreshRateController* controller =
reinterpret_cast<VariableRefreshRateController*>(host);
return controller->getBrightnessMode();
}
static int getBrightnessNitsWrapper(void* host) {
VariableRefreshRateController* controller =
reinterpret_cast<VariableRefreshRateController*>(host);
return controller->getBrightnessNits();
}
static const char* getDisplayFileNodePathWrapper(void* host) {
VariableRefreshRateController* controller =
reinterpret_cast<VariableRefreshRateController*>(host);
return controller->getDisplayFileNodePath();
}
static int getEstimateVideoFrameRateWrapper(void* host) {
VariableRefreshRateController* controller =
reinterpret_cast<VariableRefreshRateController*>(host);
return controller->getEstimatedVideoFrameRate();
}
static int getAmbientLightSensorOutputWrapper(void* host) {
VariableRefreshRateController* controller =
reinterpret_cast<VariableRefreshRateController*>(host);
return controller->getAmbientLightSensorOutput();
}
static bool isProximityThrottlingEnabledWrapper(void* host) {
VariableRefreshRateController* controller =
reinterpret_cast<VariableRefreshRateController*>(host);
return controller->isProximityThrottlingEnabled();
}
auto VariableRefreshRateController::CreateInstance(ExynosDisplay* display,
const std::string& panelName)
-> std::shared_ptr<VariableRefreshRateController> {
if (!display) {
LOG(ERROR)
<< "VrrController: create VariableRefreshRateController without display handler.";
return nullptr;
}
auto controller = std::shared_ptr<VariableRefreshRateController>(
new VariableRefreshRateController(display, panelName));
std::thread thread = std::thread(&VariableRefreshRateController::threadBody, controller.get());
std::string threadName = "VrrCtrl_";
threadName += display->mIndex == 0 ? "Primary" : "Second";
int error = pthread_setname_np(thread.native_handle(), threadName.c_str());
if (error != 0) {
LOG(WARNING) << "VrrController: Unable to set thread name, error = " << strerror(error);
}
thread.detach();
return controller;
}
VariableRefreshRateController::VariableRefreshRateController(ExynosDisplay* display,
const std::string& panelName)
: mDisplay(display), mPanelName(panelName) {
mState = VrrControllerState::kDisable;
std::string displayFileNodePath = mDisplay->getPanelSysfsPath();
if (displayFileNodePath.empty()) {
LOG(WARNING) << "VrrController: Cannot find file node of display: "
<< mDisplay->mDisplayName;
} else {
auto& fileNodeManager =
android::hardware::graphics::composer::FileNodeManager::getInstance();
mFileNode = fileNodeManager.getFileNode(displayFileNodePath);
auto content = mFileNode->readString(kRefreshControlNodeName);
if (!(content.has_value()) ||
(content.value().compare(0, kRefreshControlNodeEnabled.length(),
kRefreshControlNodeEnabled))) {
LOG(ERROR) << "VrrController: RefreshControlNode is not enabled";
}
}
// Initialize DisplayContextProviderInterface.
mDisplayContextProviderInterface.getOperationSpeedMode = (&getOperationSpeedModeWrapper);
mDisplayContextProviderInterface.getBrightnessMode = (&getBrightnessModeWrapper);
mDisplayContextProviderInterface.getBrightnessNits = (&getBrightnessNitsWrapper);
mDisplayContextProviderInterface.getDisplayFileNodePath = (&getDisplayFileNodePathWrapper);
mDisplayContextProviderInterface.getEstimatedVideoFrameRate =
(&getEstimateVideoFrameRateWrapper);
mDisplayContextProviderInterface.getAmbientLightSensorOutput =
(&getAmbientLightSensorOutputWrapper);
mDisplayContextProviderInterface.isProximityThrottlingEnabled =
(&isProximityThrottlingEnabledWrapper);
// Flow to build refresh rate calculator.
RefreshRateCalculatorFactory refreshRateCalculatorFactory;
std::vector<std::shared_ptr<RefreshRateCalculator>> Calculators;
Calculators.emplace_back(std::move(
refreshRateCalculatorFactory
.BuildRefreshRateCalculator(&mEventQueue, RefreshRateCalculatorType::kAod)));
Calculators.emplace_back(
std::move(refreshRateCalculatorFactory
.BuildRefreshRateCalculator(&mEventQueue,
RefreshRateCalculatorType::kExitIdle)));
// videoFrameRateCalculator will be shared with display context provider.
auto videoFrameRateCalculator =
refreshRateCalculatorFactory
.BuildRefreshRateCalculator(&mEventQueue,
RefreshRateCalculatorType::kVideoPlayback);
Calculators.emplace_back(videoFrameRateCalculator);
PeriodRefreshRateCalculatorParameters peridParams;
peridParams.mConfidencePercentage = 0;
Calculators.emplace_back(std::move(
refreshRateCalculatorFactory.BuildRefreshRateCalculator(&mEventQueue, peridParams)));
mRefreshRateCalculator =
refreshRateCalculatorFactory.BuildRefreshRateCalculator(std::move(Calculators));
mRefreshRateCalculator->registerRefreshRateChangeCallback(
std::bind(&VariableRefreshRateController::onRefreshRateChanged, this,
std::placeholders::_1));
mPowerModeListeners.push_back(mRefreshRateCalculator.get());
DisplayContextProviderFactory displayContextProviderFactory(mDisplay, this, &mEventQueue);
mDisplayContextProvider =
displayContextProviderFactory
.buildDisplayContextProvider(DisplayContextProviderType::kExynos,
std::move(videoFrameRateCalculator));
mPresentTimeoutEventHandlerLoader.reset(
new ExternalEventHandlerLoader(std::string(kVendorDisplayPanelLibrary).c_str(),
&mDisplayContextProviderInterface, this,
mPanelName.c_str()));
mPresentTimeoutEventHandler = mPresentTimeoutEventHandlerLoader->getEventHandler();
mVariableRefreshRateStatistic =
std::make_shared<VariableRefreshRateStatistic>(mDisplayContextProvider.get(),
&mEventQueue, kMaxFrameRate,
kMaxTefrequency,
(1 * std::nano::den /*1 second*/));
mPowerModeListeners.push_back(mVariableRefreshRateStatistic.get());
mResidencyWatcher =
ndk::SharedRefBase::make<DisplayStateResidencyWatcher>(mDisplayContextProvider,
mVariableRefreshRateStatistic);
}
VariableRefreshRateController::~VariableRefreshRateController() {
stopThread(true);
const std::lock_guard<std::mutex> lock(mMutex);
if (mLastPresentFence.has_value()) {
if (close(mLastPresentFence.value())) {
LOG(ERROR) << "VrrController: close fence file failed, errno = " << errno;
}
mLastPresentFence = std::nullopt;
}
};
int VariableRefreshRateController::notifyExpectedPresent(int64_t timestamp,
int32_t frameIntervalNs) {
ATRACE_CALL();
{
const std::lock_guard<std::mutex> lock(mMutex);
mRecord.mNextExpectedPresentTime = {mVrrActiveConfig, timestamp, frameIntervalNs};
// Post kNotifyExpectedPresentConfig event.
postEvent(VrrControllerEventType::kNotifyExpectedPresentConfig, getSteadyClockTimeNs());
}
mCondition.notify_all();
return 0;
}
void VariableRefreshRateController::reset() {
ATRACE_CALL();
const std::lock_guard<std::mutex> lock(mMutex);
mEventQueue.mPriorityQueue = std::priority_queue<VrrControllerEvent>();
mRecord.clear();
dropEventLocked();
if (mLastPresentFence.has_value()) {
if (close(mLastPresentFence.value())) {
LOG(ERROR) << "VrrController: close fence file failed, errno = " << errno;
}
mLastPresentFence = std::nullopt;
}
}
void VariableRefreshRateController::setActiveVrrConfiguration(hwc2_config_t config) {
LOG(INFO) << "VrrController: Set active Vrr configuration = " << config
<< ", power mode = " << mPowerMode;
ATRACE_CALL();
{
const std::lock_guard<std::mutex> lock(mMutex);
if (mVrrConfigs.count(config) == 0) {
LOG(ERROR) << "VrrController: Set an undefined active configuration";
return;
}
// If the minimum refresh rate is active and the maximum refresh rate timeout is set,
// also we are stay at the maximum refresh rate, any change in the active configuration
// needs to reconfigure the maximum refresh rate according to the newly activated
// configuration.
if (mMinimumRefreshRatePresentStates >= kAtMaximumRefreshRate) {
if (isMinimumRefreshRateActive() && (mMaximumRefreshRateTimeoutNs > 0)) {
uint32_t command = getCurrentRefreshControlStateLocked();
auto newMaxFrameRate = durationNsToFreq(mVrrConfigs[config].minFrameIntervalNs);
setBitField(command, newMaxFrameRate, kPanelRefreshCtrlMinimumRefreshRateOffset,
kPanelRefreshCtrlMinimumRefreshRateMask);
if (!mFileNode->WriteUint32(composer::kRefreshControlNodeName, command)) {
LOG(WARNING) << "VrrController: write file node error, command = " << command;
}
onRefreshRateChangedInternal(newMaxFrameRate);
const auto& oldMaxFrameRate =
durationNsToFreq(mVrrConfigs[mVrrActiveConfig].minFrameIntervalNs);
LOG(INFO) << "VrrController: update maximum refresh rate from " << oldMaxFrameRate
<< " to " << newMaxFrameRate;
} else {
LOG(ERROR) << "VrrController: MinimumRefreshRatePresentState cannot be "
<< mMinimumRefreshRatePresentStates
<< " when minimum refresh rate = " << mMinimumRefreshRate
<< " , mMaximumRefreshRateTimeoutNs = " << mMaximumRefreshRateTimeoutNs;
}
}
mVrrActiveConfig = config;
if (mVariableRefreshRateStatistic) {
mVariableRefreshRateStatistic
->setActiveVrrConfiguration(config,
durationNsToFreq(mVrrConfigs[mVrrActiveConfig]
.vsyncPeriodNs));
}
reportRefreshRateIndicator();
if (mState == VrrControllerState::kDisable) {
return;
}
mState = VrrControllerState::kRendering;
dropEventLocked(VrrControllerEventType::kSystemRenderingTimeout);
if (mVrrConfigs[mVrrActiveConfig].isFullySupported) {
postEvent(VrrControllerEventType::kSystemRenderingTimeout,
getSteadyClockTimeNs() +
mVrrConfigs[mVrrActiveConfig].notifyExpectedPresentConfig->TimeoutNs);
}
if (mRefreshRateCalculator) {
mRefreshRateCalculator
->setVrrConfigAttributes(mVrrConfigs[mVrrActiveConfig].vsyncPeriodNs,
mVrrConfigs[mVrrActiveConfig].minFrameIntervalNs);
}
}
mCondition.notify_all();
}
void VariableRefreshRateController::setEnable(bool isEnabled) {
ATRACE_CALL();
{
const std::lock_guard<std::mutex> lock(mMutex);
if (mEnabled == isEnabled) {
return;
}
mEnabled = isEnabled;
if (mEnabled == false) {
dropEventLocked();
}
}
mCondition.notify_all();
}
void VariableRefreshRateController::preSetPowerMode(int32_t powerMode) {
ATRACE_CALL();
LOG(INFO) << "VrrController: preSet power mode to " << powerMode << ", from " << mPowerMode;
{
const std::lock_guard<std::mutex> lock(mMutex);
if (mPowerMode == powerMode) {
return;
}
switch (powerMode) {
case HWC_POWER_MODE_DOZE:
case HWC_POWER_MODE_DOZE_SUSPEND: {
uint32_t command = getCurrentRefreshControlStateLocked();
setBit(command, kPanelRefreshCtrlFrameInsertionAutoModeOffset);
if (!mFileNode->WriteUint32(kRefreshControlNodeName, command)) {
LOG(ERROR) << "VrrController: write file node error, command = " << command;
}
dropEventLocked(VrrControllerEventType::kVendorRenderingTimeout);
return;
}
case HWC_POWER_MODE_OFF:
case HWC_POWER_MODE_NORMAL: {
return;
}
default: {
LOG(ERROR) << "VrrController: Unknown power mode = " << powerMode;
return;
}
}
}
}
void VariableRefreshRateController::postSetPowerMode(int32_t powerMode) {
ATRACE_CALL();
LOG(INFO) << "VrrController: postSet power mode to " << powerMode << ", from " << mPowerMode;
{
const std::lock_guard<std::mutex> lock(mMutex);
if (mPowerMode == powerMode) {
return;
}
switch (powerMode) {
case HWC_POWER_MODE_OFF:
case HWC_POWER_MODE_DOZE:
case HWC_POWER_MODE_DOZE_SUSPEND: {
mState = VrrControllerState::kDisable;
dropEventLocked(VrrControllerEventType::kGeneralEventMask);
break;
}
case HWC_POWER_MODE_NORMAL: {
// We should transition from either HWC_POWER_MODE_OFF, HWC_POWER_MODE_DOZE, or
// HWC_POWER_MODE_DOZE_SUSPEND. At this point, there should be no pending events
// posted.
if (!mEventQueue.mPriorityQueue.empty()) {
LOG(WARNING) << "VrrController: there should be no pending event when resume "
"from power mode = "
<< mPowerMode << " to power mode = " << powerMode;
LOG(INFO) << dumpEventQueueLocked();
}
mState = VrrControllerState::kRendering;
const auto& vrrConfig = mVrrConfigs[mVrrActiveConfig];
if (vrrConfig.isFullySupported) {
postEvent(VrrControllerEventType::kSystemRenderingTimeout,
getSteadyClockTimeNs() +
vrrConfig.notifyExpectedPresentConfig->TimeoutNs);
}
break;
}
default: {
LOG(ERROR) << "VrrController: Unknown power mode = " << powerMode;
return;
}
}
if (!mPowerModeListeners.empty()) {
for (const auto& listener : mPowerModeListeners) {
listener->onPowerStateChange(mPowerMode, powerMode);
}
}
mPowerMode = powerMode;
}
mCondition.notify_all();
}
void VariableRefreshRateController::setVrrConfigurations(
std::unordered_map<hwc2_config_t, VrrConfig_t> configs) {
ATRACE_CALL();
std::unordered_map<hwc2_config_t, std::vector<int>> validRefreshRates;
for (const auto& [id, config] : configs) {
LOG(INFO) << "VrrController: set Vrr configuration id = " << id;
if (config.isFullySupported) {
if (!config.notifyExpectedPresentConfig.has_value()) {
LOG(ERROR) << "VrrController: full vrr config should have "
"notifyExpectedPresentConfig.";
return;
}
}
validRefreshRates[id] = generateValidRefreshRates(config);
}
const std::lock_guard<std::mutex> lock(mMutex);
mVrrConfigs = std::move(configs);
mValidRefreshRates = std::move(validRefreshRates);
}
int VariableRefreshRateController::getAmbientLightSensorOutput() const {
return mDisplayContextProvider->getAmbientLightSensorOutput();
}
BrightnessMode VariableRefreshRateController::getBrightnessMode() const {
return mDisplayContextProvider->getBrightnessMode();
}
int VariableRefreshRateController::getBrightnessNits() const {
return mDisplayContextProvider->getBrightnessNits();
}
const char* VariableRefreshRateController::getDisplayFileNodePath() const {
return mDisplayContextProvider->getDisplayFileNodePath();
}
int VariableRefreshRateController::getEstimatedVideoFrameRate() const {
return mDisplayContextProvider->getEstimatedVideoFrameRate();
}
OperationSpeedMode VariableRefreshRateController::getOperationSpeedMode() const {
return mDisplayContextProvider->getOperationSpeedMode();
}
bool VariableRefreshRateController::isProximityThrottlingEnabled() const {
return mDisplayContextProvider->isProximityThrottlingEnabled();
}
void VariableRefreshRateController::setPresentTimeoutParameters(
int timeoutNs, const std::vector<std::pair<uint32_t, uint32_t>>& settings) {
const std::lock_guard<std::mutex> lock(mMutex);
if (!mPresentTimeoutEventHandler) {
return;
}
if ((timeoutNs >= 0) && (!settings.empty())) {
auto functor = mPresentTimeoutEventHandler->getHandleFunction();
mVendorPresentTimeoutOverride = std::make_optional<PresentTimeoutSettings>();
mVendorPresentTimeoutOverride.value().mTimeoutNs = timeoutNs;
mVendorPresentTimeoutOverride.value().mFunctor = std::move(functor);
for (const auto& setting : settings) {
mVendorPresentTimeoutOverride.value().mSchedule.emplace_back(setting);
}
} else {
mVendorPresentTimeoutOverride = std::nullopt;
}
}
void VariableRefreshRateController::setPresentTimeoutController(uint32_t controllerType) {
const std::lock_guard<std::mutex> lock(mMutex);
PresentTimeoutControllerType newControllerType =
static_cast<PresentTimeoutControllerType>(controllerType);
if (newControllerType != mPresentTimeoutController) {
if (mPresentTimeoutController == PresentTimeoutControllerType::kSoftware) {
dropEventLocked(VrrControllerEventType::kVendorRenderingTimeout);
}
mPresentTimeoutController = newControllerType;
uint32_t command = getCurrentRefreshControlStateLocked();
if (newControllerType == PresentTimeoutControllerType::kHardware) {
setBit(command, kPanelRefreshCtrlFrameInsertionAutoModeOffset);
} else {
clearBit(command, kPanelRefreshCtrlFrameInsertionAutoModeOffset);
}
if (!mFileNode->WriteUint32(composer::kRefreshControlNodeName, command)) {
LOG(ERROR) << "VrrController: write file node error, command = " << command;
}
}
}
int VariableRefreshRateController::setFixedRefreshRateRange(
uint32_t minimumRefreshRate, uint64_t minLockTimeForPeakRefreshRate) {
const std::lock_guard<std::mutex> lock(mMutex);
// If the new setting is equivalent to the old setting.
if ((minimumRefreshRate) <= 1 && (mMinimumRefreshRate <= 1)) {
// When |mMinimumRefreshRate| is 0 or 1, it is normal mode; there's no need to compare
// |mMaximumRefreshRateTimeoutNs|.
return NO_ERROR;
} else {
if ((minimumRefreshRate == mMinimumRefreshRate) &&
(mMaximumRefreshRateTimeoutNs == minLockTimeForPeakRefreshRate)) {
return NO_ERROR;
}
}
uint32_t command = getCurrentRefreshControlStateLocked();
mMinimumRefreshRate = minimumRefreshRate;
mMaximumRefreshRateTimeoutNs = minLockTimeForPeakRefreshRate;
dropEventLocked(VrrControllerEventType::kMinLockTimeForPeakRefreshRate);
if (isMinimumRefreshRateActive()) {
dropEventLocked(VrrControllerEventType::kVendorRenderingTimeout);
// Delegate timeout management to hardware.
setBit(command, kPanelRefreshCtrlFrameInsertionAutoModeOffset);
// Configure panel to maintain the minimum refresh rate.
setBitField(command, minimumRefreshRate, kPanelRefreshCtrlMinimumRefreshRateOffset,
kPanelRefreshCtrlMinimumRefreshRateMask);
// TODO(b/333204544): ensure the correct refresh rate is set when calling
// setFixedRefreshRate().
// Inform Statistics to stay at the minimum refresh rate change.
if (mVariableRefreshRateStatistic) {
mVariableRefreshRateStatistic->setFixedRefreshRate(mMinimumRefreshRate);
}
mMinimumRefreshRatePresentStates = kAtMinimumRefreshRate;
if (mMaximumRefreshRateTimeoutNs > 0) {
// Set up peak refresh rate timeout event accordingly.
mMinimumRefreshRateTimeoutEvent =
std::make_optional<TimedEvent>("MinimumRefreshRateTimeout");
mMinimumRefreshRateTimeoutEvent->mFunctor = [this]() -> int {
if (mMinimumRefreshRatePresentStates == kAtMaximumRefreshRate) {
mMinimumRefreshRatePresentStates = kTransitionToMinimumRefreshRate;
mMinimumRefreshRateTimeoutEvent->mIsRelativeTime = false;
auto delayNs =
(std::nano::den / mMinimumRefreshRate) + kMillisecondToNanoSecond;
mMinimumRefreshRateTimeoutEvent->mWhenNs = getSteadyClockTimeNs() + delayNs;
postEvent(VrrControllerEventType::kMinLockTimeForPeakRefreshRate,
mMinimumRefreshRateTimeoutEvent.value());
return 1;
} else {
if (mMinimumRefreshRatePresentStates != kTransitionToMinimumRefreshRate) {
LOG(ERROR) << "VrrController: expect mMinimumRefreshRatePresentStates is "
"kTransitionToMinimumRefreshRate, but it is "
<< mMinimumRefreshRatePresentStates;
return -1;
}
mMinimumRefreshRatePresentStates = kAtMinimumRefreshRate;
// TODO(b/333204544): ensure the correct refresh rate is set when calling
// setFixedRefreshRate().
if (mVariableRefreshRateStatistic) {
mVariableRefreshRateStatistic->setFixedRefreshRate(mMinimumRefreshRate);
}
uint32_t command = getCurrentRefreshControlStateLocked();
setBit(command, kPanelRefreshCtrlFrameInsertionAutoModeOffset);
setBitField(command, mMinimumRefreshRate,
kPanelRefreshCtrlMinimumRefreshRateOffset,
kPanelRefreshCtrlMinimumRefreshRateMask);
onRefreshRateChangedInternal(mMinimumRefreshRate);
return mFileNode->WriteUint32(composer::kRefreshControlNodeName, command);
}
};
}
if (!mFileNode->WriteUint32(composer::kRefreshControlNodeName, command)) {
return -1;
}
// Report refresh rate change.
onRefreshRateChangedInternal(mMinimumRefreshRate);
} else {
clearBit(command, kPanelRefreshCtrlFrameInsertionAutoModeOffset);
// Configure panel with the minimum refresh rate = 1.
setBitField(command, 1, kPanelRefreshCtrlMinimumRefreshRateOffset,
kPanelRefreshCtrlMinimumRefreshRateMask);
// Inform Statistics about the minimum refresh rate change.
if (!mFileNode->WriteUint32(composer::kRefreshControlNodeName, command)) {
return -1;
}
// TODO(b/333204544): ensure the correct refresh rate is set when calling
// setFixedRefreshRate().
if (mVariableRefreshRateStatistic) {
mVariableRefreshRateStatistic->setFixedRefreshRate(0);
}
mMaximumRefreshRateTimeoutNs = 0;
onRefreshRateChangedInternal(1);
mMinimumRefreshRateTimeoutEvent = std::nullopt;
mMinimumRefreshRatePresentStates = kMinRefreshRateUnset;
}
return 1;
}
void VariableRefreshRateController::stopThread(bool exit) {
ATRACE_CALL();
{
const std::lock_guard<std::mutex> lock(mMutex);
mThreadExit = exit;
mEnabled = false;
mState = VrrControllerState::kDisable;
}
mCondition.notify_all();
}
void VariableRefreshRateController::onPresent(int fence) {
if (fence < 0) {
return;
}
ATRACE_CALL();
{
const std::lock_guard<std::mutex> lock(mMutex);
if (!mRecord.mPendingCurrentPresentTime.has_value()) {
LOG(WARNING) << "VrrController: VrrController: Present without expected present time "
"information";
return;
} else {
if (mRefreshRateCalculator) {
mRefreshRateCalculator->onPresent(mRecord.mPendingCurrentPresentTime.value().mTime,
getPresentFrameFlag());
}
if (mVariableRefreshRateStatistic) {
mVariableRefreshRateStatistic
->onPresent(mRecord.mPendingCurrentPresentTime.value().mTime,
getPresentFrameFlag());
}
mRecord.mPresentHistory.next() = mRecord.mPendingCurrentPresentTime.value();
}
if (mState == VrrControllerState::kDisable) {
return;
} else if (mState == VrrControllerState::kHibernate) {
LOG(WARNING) << "VrrController: Present during hibernation without prior notification "
"via notifyExpectedPresent.";
mState = VrrControllerState::kRendering;
dropEventLocked(VrrControllerEventType::kHibernateTimeout);
}
if ((mMaximumRefreshRateTimeoutNs > 0) && (mMinimumRefreshRate > 1)) {
auto maxFrameRate = durationNsToFreq(mVrrConfigs[mVrrActiveConfig].minFrameIntervalNs);
// If the target minimum refresh rate equals the maxFrameRate, there's no need to
// promote the refresh rate to maxFrameRate during presentation.
// E.g. in low-light conditions, with |maxFrameRate| and |mMinimumRefreshRate| both at
// 120, no refresh rate promotion is needed.
if (maxFrameRate != mMinimumRefreshRate) {
if (mMinimumRefreshRatePresentStates == kAtMinimumRefreshRate) {
uint32_t command = getCurrentRefreshControlStateLocked();
// Delegate timeout management to hardware.
setBit(command, kPanelRefreshCtrlFrameInsertionAutoModeOffset);
// Configure panel to maintain the minimum refresh rate.
setBitField(command, maxFrameRate, kPanelRefreshCtrlMinimumRefreshRateOffset,
kPanelRefreshCtrlMinimumRefreshRateMask);
if (!mFileNode->WriteUint32(composer::kRefreshControlNodeName, command)) {
LOG(WARNING)
<< "VrrController: write file node error, command = " << command;
return;
}
mMinimumRefreshRatePresentStates = kAtMaximumRefreshRate;
onRefreshRateChangedInternal(maxFrameRate);
mMinimumRefreshRateTimeoutEvent->mIsRelativeTime = false;
mMinimumRefreshRateTimeoutEvent->mWhenNs =
mRecord.mPendingCurrentPresentTime.value().mTime +
mMaximumRefreshRateTimeoutNs;
postEvent(VrrControllerEventType::kMinLockTimeForPeakRefreshRate,
mMinimumRefreshRateTimeoutEvent.value());
} else if (mMinimumRefreshRatePresentStates == kTransitionToMinimumRefreshRate) {
dropEventLocked(VrrControllerEventType::kMinLockTimeForPeakRefreshRate);
mMinimumRefreshRateTimeoutEvent->mIsRelativeTime = false;
auto delayNs =
(std::nano::den / mMinimumRefreshRate) + kMillisecondToNanoSecond;
mMinimumRefreshRateTimeoutEvent->mWhenNs =
mRecord.mPendingCurrentPresentTime.value().mTime + delayNs;
postEvent(VrrControllerEventType::kMinLockTimeForPeakRefreshRate,
mMinimumRefreshRateTimeoutEvent.value());
} else {
if (mMinimumRefreshRatePresentStates != kAtMaximumRefreshRate) {
LOG(ERROR) << "VrrController: wrong state when setting min refresh rate: "
<< mMinimumRefreshRatePresentStates;
}
}
}
return;
}
}
// Prior to pushing the most recent fence update, verify the release timestamps of all preceding
// fences.
// TODO(b/309873055): delegate the task of executing updateVsyncHistory to the Vrr controller's
// loop thread in order to reduce the workload of calling thread.
updateVsyncHistory();
int dupFence = dup(fence);
if (dupFence < 0) {
LOG(ERROR) << "VrrController: duplicate fence file failed." << errno;
}
{
const std::lock_guard<std::mutex> lock(mMutex);
if (mLastPresentFence.has_value()) {
LOG(WARNING) << "VrrController: last present fence remains open.";
}
mLastPresentFence = dupFence;
// Drop the out of date timeout.
dropEventLocked(VrrControllerEventType::kSystemRenderingTimeout);
cancelPresentTimeoutHandlingLocked();
// Post next rendering timeout.
if (mVrrConfigs[mVrrActiveConfig].isFullySupported) {
postEvent(VrrControllerEventType::kSystemRenderingTimeout,
getSteadyClockTimeNs() +
mVrrConfigs[mVrrActiveConfig].notifyExpectedPresentConfig->TimeoutNs);
}
if (shouldHandleVendorRenderingTimeout()) {
auto presentTimeoutNs = mVendorPresentTimeoutOverride
? mVendorPresentTimeoutOverride.value().mTimeoutNs
: mPresentTimeoutEventHandler->getPresentTimeoutNs();
// If |presentTimeoutNs| == 0, we don't need to handle the present timeout. Otherwise,
// post the next frame insertion event
if (presentTimeoutNs) {
// Convert the relative time clock from now to the absolute steady time clock.
presentTimeoutNs = getSteadyClockTimeNs() + presentTimeoutNs;
postEvent(VrrControllerEventType::kVendorRenderingTimeout, presentTimeoutNs);
}
}
mRecord.mPendingCurrentPresentTime = std::nullopt;
}
mCondition.notify_all();
}
void VariableRefreshRateController::setExpectedPresentTime(int64_t timestampNanos,
int frameIntervalNs) {
ATRACE_CALL();
const std::lock_guard<std::mutex> lock(mMutex);
mRecord.mPendingCurrentPresentTime = {mVrrActiveConfig, timestampNanos, frameIntervalNs};
}
void VariableRefreshRateController::onVsync(int64_t timestampNanos,
int32_t __unused vsyncPeriodNanos) {
const std::lock_guard<std::mutex> lock(mMutex);
mRecord.mVsyncHistory
.next() = {.mType = VariableRefreshRateController::VsyncEvent::Type::kVblank,
.mTime = timestampNanos};
}
void VariableRefreshRateController::cancelPresentTimeoutHandlingLocked() {
dropEventLocked(VrrControllerEventType::kVendorRenderingTimeout);
dropEventLocked(VrrControllerEventType::kHandleVendorRenderingTimeout);
}
void VariableRefreshRateController::dropEventLocked() {
mEventQueue.mPriorityQueue = std::priority_queue<VrrControllerEvent>();
}
void VariableRefreshRateController::dropEventLocked(VrrControllerEventType eventType) {
std::priority_queue<VrrControllerEvent> q;
auto target = static_cast<int>(eventType);
while (!mEventQueue.mPriorityQueue.empty()) {
const auto& it = mEventQueue.mPriorityQueue.top();
if ((static_cast<int>(it.mEventType) & target) != target) {
q.push(it);
}
mEventQueue.mPriorityQueue.pop();
}
mEventQueue.mPriorityQueue = std::move(q);
}
std::string VariableRefreshRateController::dumpEventQueueLocked() {
std::string content;
if (mEventQueue.mPriorityQueue.empty()) {
return content;
}
std::priority_queue<VrrControllerEvent> q;
while (!mEventQueue.mPriorityQueue.empty()) {
const auto& it = mEventQueue.mPriorityQueue.top();
content += "VrrController: event = ";
content += it.toString();
content += "\n";
q.push(it);
mEventQueue.mPriorityQueue.pop();
}
mEventQueue.mPriorityQueue = std::move(q);
return content;
}
uint32_t VariableRefreshRateController::getCurrentRefreshControlStateLocked() const {
return (mFileNode->getLastWrittenValue(kRefreshControlNodeName) &
kPanelRefreshCtrlStateBitsMask);
}
int64_t VariableRefreshRateController::getLastFenceSignalTimeUnlocked(int fd) {
if (fd == -1) {
return SIGNAL_TIME_INVALID;
}
struct sync_file_info* finfo = sync_file_info(fd);
if (finfo == nullptr) {
LOG(ERROR) << "VrrController: sync_file_info returned NULL for fd " << fd;
return SIGNAL_TIME_INVALID;
}
if (finfo->status != 1) {
const auto status = finfo->status;
if (status < 0) {
LOG(ERROR) << "VrrController: sync_file_info contains an error: " << status;
}
sync_file_info_free(finfo);
return status < 0 ? SIGNAL_TIME_INVALID : SIGNAL_TIME_PENDING;
}
uint64_t timestamp = 0;
struct sync_fence_info* pinfo = sync_get_fence_info(finfo);
if (finfo->num_fences != 1) {
LOG(WARNING) << "VrrController:: there is more than one fence in the file descriptor = "
<< fd;
}
for (size_t i = 0; i < finfo->num_fences; i++) {
if (pinfo[i].timestamp_ns > timestamp) {
timestamp = pinfo[i].timestamp_ns;
}
}
sync_file_info_free(finfo);
return timestamp;
}
int64_t VariableRefreshRateController::getNextEventTimeLocked() const {
if (mEventQueue.mPriorityQueue.empty()) {
LOG(WARNING) << "VrrController: event queue should NOT be empty.";
return -1;
}
const auto& event = mEventQueue.mPriorityQueue.top();
return event.mWhenNs;
}
std::string VariableRefreshRateController::getStateName(VrrControllerState state) const {
switch (state) {
case VrrControllerState::kDisable:
return "Disable";
case VrrControllerState::kRendering:
return "Rendering";
case VrrControllerState::kHibernate:
return "Hibernate";
default:
return "Unknown";
}
}
void VariableRefreshRateController::handleCadenceChange() {
ATRACE_CALL();
if (!mRecord.mNextExpectedPresentTime.has_value()) {
LOG(WARNING) << "VrrController: cadence change occurs without the expected present timing "
"information.";
return;
}
// TODO(b/305311056): handle frame rate change.
mRecord.mNextExpectedPresentTime = std::nullopt;
}
void VariableRefreshRateController::handleResume() {
ATRACE_CALL();
if (!mRecord.mNextExpectedPresentTime.has_value()) {
LOG(WARNING)
<< "VrrController: resume occurs without the expected present timing information.";
return;
}
// TODO(b/305311281): handle panel resume.
mRecord.mNextExpectedPresentTime = std::nullopt;
}
void VariableRefreshRateController::handleHibernate() {
ATRACE_CALL();
// TODO(b/305311206): handle entering panel hibernate.
postEvent(VrrControllerEventType::kHibernateTimeout,
getSteadyClockTimeNs() + kDefaultWakeUpTimeInPowerSaving);
}
void VariableRefreshRateController::handleStayHibernate() {
ATRACE_CALL();
// TODO(b/305311698): handle keeping panel hibernate.
postEvent(VrrControllerEventType::kHibernateTimeout,
getSteadyClockTimeNs() + kDefaultWakeUpTimeInPowerSaving);
}
void VariableRefreshRateController::handlePresentTimeout(const VrrControllerEvent& event) {
ATRACE_CALL();
if (mState == VrrControllerState::kDisable) {
cancelPresentTimeoutHandlingLocked();
return;
}
uint32_t command = mFileNode->getLastWrittenValue(composer::kRefreshControlNodeName);
clearBit(command, kPanelRefreshCtrlFrameInsertionAutoModeOffset);
setBitField(command, 1, kPanelRefreshCtrlFrameInsertionFrameCountOffset,
kPanelRefreshCtrlFrameInsertionFrameCountMask);
mFileNode->WriteUint32(composer::kRefreshControlNodeName, command);
}
void VariableRefreshRateController::onRefreshRateChanged(int refreshRate) {
if (mMinimumRefreshRate > 1) {
// If the minimum refresh rate has been set, the refresh rate remains fixed at a specific
// value.
return;
}
onRefreshRateChangedInternal(refreshRate);
}
void VariableRefreshRateController::onRefreshRateChangedInternal(int refreshRate) {
if (!(mDisplay) || !(mDisplay->mDevice)) {
LOG(ERROR) << "VrrController: absence of a device or display.";
return;
}
refreshRate =
refreshRate == kDefaultInvalidRefreshRate ? kDefaultMinimumRefreshRate : refreshRate;
refreshRate = convertToValidRefreshRate(refreshRate);
if (mLastRefreshRate == refreshRate) {
return;
}
mLastRefreshRate = refreshRate;
for (const auto& listener : mRefreshRateChangeListeners) {
if (listener) listener->onRefreshRateChange(refreshRate);
}
reportRefreshRateIndicator();
}
void VariableRefreshRateController::reportRefreshRateIndicator() {
if (mRefreshRateCalculatorEnabled) {
if (!mDisplay->mDevice->isVrrApiSupported()) {
// For legacy API, vsyncPeriodNanos is utilized to denote the refresh rate,
// refreshPeriodNanos is disregarded.
mDisplay->mDevice->onRefreshRateChangedDebug(mDisplay->mDisplayId,
freqToDurationNs(mLastRefreshRate));
} else {
mDisplay->mDevice
->onRefreshRateChangedDebug(mDisplay->mDisplayId,
mVrrConfigs[mVrrActiveConfig].vsyncPeriodNs,
freqToDurationNs(mLastRefreshRate));
}
}
}
std::vector<int> VariableRefreshRateController::generateValidRefreshRates(
const VrrConfig_t& config) const {
std::vector<int> refreshRates;
int teFrequency = durationNsToFreq(config.vsyncPeriodNs);
int minVsyncNum = roundDivide(config.minFrameIntervalNs, config.vsyncPeriodNs);
for (int vsyncNum = minVsyncNum; vsyncNum <= teFrequency; vsyncNum++) {
refreshRates.push_back(roundDivide(teFrequency, vsyncNum));
}
std::set<int> uniqueRefreshRates(refreshRates.begin(), refreshRates.end());
refreshRates.assign(uniqueRefreshRates.begin(), uniqueRefreshRates.end());
return refreshRates;
}
int VariableRefreshRateController::convertToValidRefreshRate(int refreshRate) {
const auto& validRefreshRates = mValidRefreshRates[mVrrActiveConfig];
auto it = std::lower_bound(validRefreshRates.begin(), validRefreshRates.end(), refreshRate);
if (it != validRefreshRates.end()) {
return *it;
}
LOG(ERROR) << "Could not match to any valid refresh rate: " << refreshRate;
return durationNsToFreq(mVrrConfigs[mVrrActiveConfig].minFrameIntervalNs);
}
bool VariableRefreshRateController::shouldHandleVendorRenderingTimeout() const {
return (mPresentTimeoutController == PresentTimeoutControllerType::kSoftware) &&
((!mVendorPresentTimeoutOverride) ||
(mVendorPresentTimeoutOverride.value().mSchedule.size() > 0)) &&
(mPowerMode == HWC_POWER_MODE_NORMAL);
}
void VariableRefreshRateController::threadBody() {
struct sched_param param = {.sched_priority = sched_get_priority_max(SCHED_FIFO)};
if (sched_setscheduler(0, SCHED_FIFO, &param) != 0) {
LOG(ERROR) << "VrrController: fail to set scheduler to SCHED_FIFO.";
return;
}
for (;;) {
bool stateChanged = false;
{
std::unique_lock<std::mutex> lock(mMutex);
if (mThreadExit) break;
if (!mEnabled) mCondition.wait(lock);
if (!mEnabled) continue;
if (mEventQueue.mPriorityQueue.empty()) {
mCondition.wait(lock);
}
int64_t whenNs = getNextEventTimeLocked();
int64_t nowNs = getSteadyClockTimeNs();
if (whenNs > nowNs) {
int64_t delayNs = whenNs - nowNs;
auto res = mCondition.wait_for(lock, std::chrono::nanoseconds(delayNs));
if (res != std::cv_status::timeout) {
continue;
}
}
if (mEventQueue.mPriorityQueue.empty()) {
continue;
}
auto event = mEventQueue.mPriorityQueue.top();
if (event.mWhenNs > getSteadyClockTimeNs()) {
continue;
}
mEventQueue.mPriorityQueue.pop();
if (static_cast<int>(event.mEventType) &
static_cast<int>(VrrControllerEventType::kCallbackEventMask)) {
handleCallbackEventLocked(event);
continue;
}
if (mState == VrrControllerState::kRendering) {
if (event.mEventType == VrrControllerEventType::kHibernateTimeout) {
LOG(ERROR) << "VrrController: receiving a hibernate timeout event while in the "
"rendering state.";
}
switch (event.mEventType) {
case VrrControllerEventType::kSystemRenderingTimeout: {
handleHibernate();
mState = VrrControllerState::kHibernate;
stateChanged = true;
break;
}
case VrrControllerEventType::kNotifyExpectedPresentConfig: {
handleCadenceChange();
break;
}
case VrrControllerEventType::kVendorRenderingTimeout: {
if (mPresentTimeoutEventHandler) {
// Verify whether a present timeout override exists, and if so, execute
// it first.
if (mVendorPresentTimeoutOverride) {
const auto& params = mVendorPresentTimeoutOverride.value();
TimedEvent timedEvent("VendorPresentTimeoutOverride");
timedEvent.mIsRelativeTime = true;
timedEvent.mFunctor = params.mFunctor;
int64_t whenFromNowNs = 0;
for (int i = 0; i < params.mSchedule.size(); ++i) {
uint32_t intervalNs = params.mSchedule[i].second;
for (int j = 0; j < params.mSchedule[i].first; ++j) {
timedEvent.mWhenNs = whenFromNowNs;
postEvent(VrrControllerEventType::
kHandleVendorRenderingTimeout,
timedEvent);
whenFromNowNs += intervalNs;
}
}
} else {
auto handleEvents = mPresentTimeoutEventHandler->getHandleEvents();
for (auto& event : handleEvents) {
postEvent(VrrControllerEventType::kHandleVendorRenderingTimeout,
event);
}
}
}
break;
}
case VrrControllerEventType::kHandleVendorRenderingTimeout: {
handlePresentTimeout(event);
break;
}
default: {
break;
}
}
} else {
if (event.mEventType == VrrControllerEventType::kSystemRenderingTimeout) {
LOG(ERROR) << "VrrController: receiving a rendering timeout event while in the "
"hibernate state.";
}
if (mState != VrrControllerState::kHibernate) {
LOG(ERROR) << "VrrController: expecting to be in hibernate, but instead in "
"state = "
<< getStateName(mState);
}
switch (event.mEventType) {
case VrrControllerEventType::kHibernateTimeout: {
handleStayHibernate();
break;
}
case VrrControllerEventType::kNotifyExpectedPresentConfig: {
handleResume();
mState = VrrControllerState::kRendering;
stateChanged = true;
break;
}
default: {
break;
}
}
}
}
// TODO(b/309873055): implement a handler to serialize all outer function calls to the same
// thread owned by the VRR controller.
if (stateChanged) {
updateVsyncHistory();
}
}
}
void VariableRefreshRateController::postEvent(VrrControllerEventType type, int64_t when) {
VrrControllerEvent event;
event.mEventType = type;
event.mWhenNs = when;
mEventQueue.mPriorityQueue.emplace(event);
}
void VariableRefreshRateController::postEvent(VrrControllerEventType type, TimedEvent& timedEvent) {
VrrControllerEvent event;
event.mEventType = type;
event.mWhenNs = timedEvent.mIsRelativeTime ? (getSteadyClockTimeNs() + timedEvent.mWhenNs)
: timedEvent.mWhenNs;
event.mFunctor = std::move(timedEvent.mFunctor);
mEventQueue.mPriorityQueue.emplace(event);
}
void VariableRefreshRateController::updateVsyncHistory() {
int fence = -1;
{
const std::lock_guard<std::mutex> lock(mMutex);
if (!mLastPresentFence.has_value()) {
return;
}
fence = mLastPresentFence.value();
mLastPresentFence = std::nullopt;
}
// Execute the following logic unlocked to enhance performance.
int64_t lastSignalTime = getLastFenceSignalTimeUnlocked(fence);
if (close(fence)) {
LOG(ERROR) << "VrrController: close fence file failed, errno = " << errno;
return;
} else if (lastSignalTime == SIGNAL_TIME_PENDING || lastSignalTime == SIGNAL_TIME_INVALID) {
return;
}
{
// Acquire the mutex again to store the vsync record.
const std::lock_guard<std::mutex> lock(mMutex);
mRecord.mVsyncHistory
.next() = {.mType = VariableRefreshRateController::VsyncEvent::Type::kReleaseFence,
.mTime = lastSignalTime};
}
}
} // namespace android::hardware::graphics::composer