| /* |
| * Copyright 2024 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. |
| */ |
| |
| #undef LOG_TAG |
| #define LOG_TAG "DisplayModeController" |
| #define ATRACE_TAG ATRACE_TAG_GRAPHICS |
| |
| #include "Display/DisplayModeController.h" |
| #include "Display/DisplaySnapshot.h" |
| #include "DisplayHardware/HWComposer.h" |
| |
| #include <android-base/properties.h> |
| #include <common/FlagManager.h> |
| #include <common/trace.h> |
| #include <ftl/concat.h> |
| #include <ftl/expected.h> |
| #include <log/log.h> |
| #include <utils/Errors.h> |
| |
| namespace android::display { |
| |
| template <size_t N> |
| inline std::string DisplayModeController::Display::concatId(const char (&str)[N]) const { |
| return std::string(ftl::Concat(str, ' ', snapshot.get().displayId().value).str()); |
| } |
| |
| DisplayModeController::Display::Display(DisplaySnapshotRef snapshot, |
| RefreshRateSelectorPtr selectorPtr) |
| : snapshot(snapshot), |
| selectorPtr(std::move(selectorPtr)), |
| pendingModeFpsTrace(concatId("PendingModeFps")), |
| activeModeFpsTrace(concatId("ActiveModeFps")), |
| renderRateFpsTrace(concatId("RenderRateFps")), |
| hasDesiredModeTrace(concatId("HasDesiredMode"), false) {} |
| |
| void DisplayModeController::registerDisplay(PhysicalDisplayId displayId, |
| DisplaySnapshotRef snapshotRef, |
| RefreshRateSelectorPtr selectorPtr) { |
| std::lock_guard lock(mDisplayLock); |
| mDisplays.emplace_or_replace(displayId, std::make_unique<Display>(snapshotRef, selectorPtr)); |
| } |
| |
| void DisplayModeController::registerDisplay(DisplaySnapshotRef snapshotRef, |
| DisplayModeId activeModeId, |
| scheduler::RefreshRateSelector::Config config) { |
| const auto& snapshot = snapshotRef.get(); |
| const auto displayId = snapshot.displayId(); |
| |
| std::lock_guard lock(mDisplayLock); |
| mDisplays.emplace_or_replace(displayId, |
| std::make_unique<Display>(snapshotRef, snapshot.displayModes(), |
| activeModeId, config)); |
| } |
| |
| void DisplayModeController::unregisterDisplay(PhysicalDisplayId displayId) { |
| std::lock_guard lock(mDisplayLock); |
| const bool ok = mDisplays.erase(displayId); |
| ALOGE_IF(!ok, "%s: Unknown display %s", __func__, to_string(displayId).c_str()); |
| } |
| |
| auto DisplayModeController::selectorPtrFor(PhysicalDisplayId displayId) const |
| -> RefreshRateSelectorPtr { |
| std::lock_guard lock(mDisplayLock); |
| return mDisplays.get(displayId) |
| .transform([](const DisplayPtr& displayPtr) { return displayPtr->selectorPtr; }) |
| .value_or(nullptr); |
| } |
| |
| auto DisplayModeController::setDesiredMode(PhysicalDisplayId displayId, |
| DisplayModeRequest&& desiredMode) -> DesiredModeAction { |
| std::lock_guard lock(mDisplayLock); |
| const auto& displayPtr = |
| FTL_EXPECT(mDisplays.get(displayId).ok_or(DesiredModeAction::None)).get(); |
| |
| { |
| SFTRACE_NAME(displayPtr->concatId(__func__).c_str()); |
| ALOGD("%s %s", displayPtr->concatId(__func__).c_str(), to_string(desiredMode).c_str()); |
| |
| std::scoped_lock lock(displayPtr->desiredModeLock); |
| |
| if (auto& desiredModeOpt = displayPtr->desiredModeOpt) { |
| // A mode transition was already scheduled, so just override the desired mode. |
| const bool emitEvent = desiredModeOpt->emitEvent; |
| const bool force = desiredModeOpt->force; |
| desiredModeOpt = std::move(desiredMode); |
| desiredModeOpt->emitEvent |= emitEvent; |
| if (FlagManager::getInstance().connected_display()) { |
| desiredModeOpt->force |= force; |
| } |
| return DesiredModeAction::None; |
| } |
| |
| // If the desired mode is already active... |
| const auto activeMode = displayPtr->selectorPtr->getActiveMode(); |
| if (const auto& desiredModePtr = desiredMode.mode.modePtr; |
| !desiredMode.force && activeMode.modePtr->getId() == desiredModePtr->getId()) { |
| if (activeMode == desiredMode.mode) { |
| return DesiredModeAction::None; |
| } |
| |
| // ...but the render rate changed: |
| setActiveModeLocked(displayId, desiredModePtr->getId(), desiredModePtr->getVsyncRate(), |
| desiredMode.mode.fps); |
| return DesiredModeAction::InitiateRenderRateSwitch; |
| } |
| |
| // Restore peak render rate to schedule the next frame as soon as possible. |
| setActiveModeLocked(displayId, activeMode.modePtr->getId(), |
| activeMode.modePtr->getVsyncRate(), activeMode.modePtr->getPeakFps()); |
| |
| // Initiate a mode change. |
| displayPtr->desiredModeOpt = std::move(desiredMode); |
| displayPtr->hasDesiredModeTrace = true; |
| } |
| |
| return DesiredModeAction::InitiateDisplayModeSwitch; |
| } |
| |
| auto DisplayModeController::getDesiredMode(PhysicalDisplayId displayId) const |
| -> DisplayModeRequestOpt { |
| std::lock_guard lock(mDisplayLock); |
| const auto& displayPtr = |
| FTL_EXPECT(mDisplays.get(displayId).ok_or(DisplayModeRequestOpt())).get(); |
| |
| { |
| std::scoped_lock lock(displayPtr->desiredModeLock); |
| return displayPtr->desiredModeOpt; |
| } |
| } |
| |
| auto DisplayModeController::getPendingMode(PhysicalDisplayId displayId) const |
| -> DisplayModeRequestOpt { |
| std::lock_guard lock(mDisplayLock); |
| const auto& displayPtr = |
| FTL_EXPECT(mDisplays.get(displayId).ok_or(DisplayModeRequestOpt())).get(); |
| |
| { |
| std::scoped_lock lock(displayPtr->desiredModeLock); |
| return displayPtr->pendingModeOpt; |
| } |
| } |
| |
| bool DisplayModeController::isModeSetPending(PhysicalDisplayId displayId) const { |
| std::lock_guard lock(mDisplayLock); |
| const auto& displayPtr = FTL_EXPECT(mDisplays.get(displayId).ok_or(false)).get(); |
| |
| { |
| std::scoped_lock lock(displayPtr->desiredModeLock); |
| return displayPtr->isModeSetPending; |
| } |
| } |
| |
| scheduler::FrameRateMode DisplayModeController::getActiveMode(PhysicalDisplayId displayId) const { |
| return selectorPtrFor(displayId)->getActiveMode(); |
| } |
| |
| void DisplayModeController::clearDesiredMode(PhysicalDisplayId displayId) { |
| std::lock_guard lock(mDisplayLock); |
| const auto& displayPtr = FTL_TRY(mDisplays.get(displayId).ok_or(ftl::Unit())).get(); |
| |
| { |
| std::scoped_lock lock(displayPtr->desiredModeLock); |
| displayPtr->desiredModeOpt.reset(); |
| displayPtr->hasDesiredModeTrace = false; |
| } |
| } |
| |
| auto DisplayModeController::initiateModeChange( |
| PhysicalDisplayId displayId, DisplayModeRequest&& desiredMode, |
| const hal::VsyncPeriodChangeConstraints& constraints, |
| hal::VsyncPeriodChangeTimeline& outTimeline) -> ModeChangeResult { |
| std::lock_guard lock(mDisplayLock); |
| const auto& displayPtr = |
| FTL_EXPECT(mDisplays.get(displayId).ok_or(ModeChangeResult::Aborted)).get(); |
| |
| // TODO: b/255635711 - Flow the DisplayModeRequest through the desired/pending/active states. |
| // For now, `desiredMode` and `desiredModeOpt` are one and the same, but the latter is not |
| // cleared until the next `SF::initiateDisplayModeChanges`. However, the desired mode has been |
| // consumed at this point, so clear the `force` flag to prevent an endless loop of |
| // `initiateModeChange`. |
| if (FlagManager::getInstance().connected_display()) { |
| std::scoped_lock lock(displayPtr->desiredModeLock); |
| if (displayPtr->desiredModeOpt) { |
| displayPtr->desiredModeOpt->force = false; |
| } |
| } |
| |
| displayPtr->pendingModeOpt = std::move(desiredMode); |
| displayPtr->isModeSetPending = true; |
| |
| const auto& mode = *displayPtr->pendingModeOpt->mode.modePtr; |
| |
| const auto error = mComposerPtr->setActiveModeWithConstraints(displayId, mode.getHwcId(), |
| constraints, &outTimeline); |
| switch (error) { |
| case FAILED_TRANSACTION: |
| return ModeChangeResult::Rejected; |
| case OK: |
| SFTRACE_INT(displayPtr->pendingModeFpsTrace.c_str(), mode.getVsyncRate().getIntValue()); |
| return ModeChangeResult::Changed; |
| default: |
| return ModeChangeResult::Aborted; |
| } |
| } |
| |
| void DisplayModeController::finalizeModeChange(PhysicalDisplayId displayId, DisplayModeId modeId, |
| Fps vsyncRate, Fps renderFps) { |
| std::lock_guard lock(mDisplayLock); |
| setActiveModeLocked(displayId, modeId, vsyncRate, renderFps); |
| |
| const auto& displayPtr = FTL_TRY(mDisplays.get(displayId).ok_or(ftl::Unit())).get(); |
| displayPtr->isModeSetPending = false; |
| } |
| |
| void DisplayModeController::setActiveMode(PhysicalDisplayId displayId, DisplayModeId modeId, |
| Fps vsyncRate, Fps renderFps) { |
| std::lock_guard lock(mDisplayLock); |
| setActiveModeLocked(displayId, modeId, vsyncRate, renderFps); |
| } |
| |
| void DisplayModeController::setActiveModeLocked(PhysicalDisplayId displayId, DisplayModeId modeId, |
| Fps vsyncRate, Fps renderFps) { |
| const auto& displayPtr = FTL_TRY(mDisplays.get(displayId).ok_or(ftl::Unit())).get(); |
| |
| SFTRACE_INT(displayPtr->activeModeFpsTrace.c_str(), vsyncRate.getIntValue()); |
| SFTRACE_INT(displayPtr->renderRateFpsTrace.c_str(), renderFps.getIntValue()); |
| |
| displayPtr->selectorPtr->setActiveMode(modeId, renderFps); |
| |
| if (mActiveModeListener) { |
| mActiveModeListener(displayId, vsyncRate, renderFps); |
| } |
| } |
| |
| void DisplayModeController::updateKernelIdleTimer(PhysicalDisplayId displayId) { |
| std::lock_guard lock(mDisplayLock); |
| const auto& displayPtr = FTL_TRY(mDisplays.get(displayId).ok_or(ftl::Unit())).get(); |
| |
| const auto controllerOpt = displayPtr->selectorPtr->kernelIdleTimerController(); |
| if (!controllerOpt) return; |
| |
| using KernelIdleTimerAction = scheduler::RefreshRateSelector::KernelIdleTimerAction; |
| |
| switch (displayPtr->selectorPtr->getIdleTimerAction()) { |
| case KernelIdleTimerAction::TurnOff: |
| if (displayPtr->isKernelIdleTimerEnabled) { |
| SFTRACE_INT("KernelIdleTimer", 0); |
| updateKernelIdleTimer(displayId, std::chrono::milliseconds::zero(), *controllerOpt); |
| displayPtr->isKernelIdleTimerEnabled = false; |
| } |
| break; |
| case KernelIdleTimerAction::TurnOn: |
| if (!displayPtr->isKernelIdleTimerEnabled) { |
| SFTRACE_INT("KernelIdleTimer", 1); |
| const auto timeout = displayPtr->selectorPtr->getIdleTimerTimeout(); |
| updateKernelIdleTimer(displayId, timeout, *controllerOpt); |
| displayPtr->isKernelIdleTimerEnabled = true; |
| } |
| break; |
| } |
| } |
| |
| void DisplayModeController::updateKernelIdleTimer(PhysicalDisplayId displayId, |
| std::chrono::milliseconds timeout, |
| KernelIdleTimerController controller) { |
| switch (controller) { |
| case KernelIdleTimerController::HwcApi: |
| mComposerPtr->setIdleTimerEnabled(displayId, timeout); |
| break; |
| |
| case KernelIdleTimerController::Sysprop: |
| using namespace std::string_literals; |
| base::SetProperty("graphics.display.kernel_idle_timer.enabled"s, |
| timeout > std::chrono::milliseconds::zero() ? "true"s : "false"s); |
| break; |
| } |
| } |
| |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Wunused-value" // b/369277774 |
| auto DisplayModeController::getKernelIdleTimerState(PhysicalDisplayId displayId) const |
| -> KernelIdleTimerState { |
| std::lock_guard lock(mDisplayLock); |
| const auto& displayPtr = |
| FTL_EXPECT(mDisplays.get(displayId).ok_or(KernelIdleTimerState())).get(); |
| |
| const auto desiredModeIdOpt = |
| (std::scoped_lock(displayPtr->desiredModeLock), displayPtr->desiredModeOpt) |
| .transform([](const display::DisplayModeRequest& request) { |
| return request.mode.modePtr->getId(); |
| }); |
| |
| return {desiredModeIdOpt, displayPtr->isKernelIdleTimerEnabled}; |
| } |
| |
| #pragma clang diagnostic pop |
| } // namespace android::display |