/* * Copyright (C) 2012 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 #include "ExynosDisplay.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "BrightnessController.h" #include "DisplayTe2Manager.h" #include "ExynosExternalDisplay.h" #include "ExynosLayer.h" #include "HistogramController.h" #include "VendorGraphicBuffer.h" #include "exynos_format.h" #include "utils/Timers.h" /** * ExynosDisplay implementation */ using namespace android; using namespace vendor::graphics; using namespace std::chrono_literals; using ::aidl::android::hardware::power::IPower; using ::aidl::google::hardware::power::extension::pixel::IPowerExt; namespace AidlComposer3 = ::aidl::android::hardware::graphics::composer3; namespace AidlCommon = ::aidl::android::hardware::graphics::common; extern struct exynos_hwc_control exynosHWCControl; extern struct update_time_info updateTimeInfo; constexpr const char* kBufferDumpPath = "/data/vendor/log/hwc"; constexpr float kDynamicRecompFpsThreshold = 1.0 / 5.0; // 1 frame update per 5 second constexpr float nsecsPerSec = std::chrono::nanoseconds(1s).count(); constexpr int64_t nsecsIdleHintTimeout = std::chrono::nanoseconds(100ms).count(); ExynosDisplay::PowerHalHintWorker::PowerHalHintWorker(uint32_t displayId, const String8& displayTraceName) : Worker("DisplayHints", HAL_PRIORITY_URGENT_DISPLAY), mNeedUpdateRefreshRateHint(false), mLastRefreshRateHint(0), mIdleHintIsEnabled(false), mForceUpdateIdleHint(false), mIdleHintDeadlineTime(0), mIdleHintSupportIsChecked(false), mIdleHintIsSupported(false), mDisplayTraceName(displayTraceName), mPowerModeState(HWC2_POWER_MODE_OFF), mRefreshRate(kDefaultRefreshRateFrequency), mConnectRetryCount(0), mDeathRecipient(AIBinder_DeathRecipient_new(BinderDiedCallback)), mPowerHalExtAidl(nullptr), mPowerHalAidl(nullptr), mPowerHintSession(nullptr) { if (property_get_bool("vendor.display.powerhal_hint_per_display", false)) { std::string displayIdStr = std::to_string(displayId); mIdleHintStr = "DISPLAY_" + displayIdStr + "_IDLE"; mRefreshRateHintPrefixStr = "DISPLAY_" + displayIdStr + "_"; } else { mIdleHintStr = "DISPLAY_IDLE"; mRefreshRateHintPrefixStr = "REFRESH_"; } } ExynosDisplay::PowerHalHintWorker::~PowerHalHintWorker() { Exit(); } int ExynosDisplay::PowerHalHintWorker::Init() { return InitWorker(); } void ExynosDisplay::PowerHalHintWorker::BinderDiedCallback(void *cookie) { ALOGE("PowerHal is died"); auto powerHint = reinterpret_cast(cookie); powerHint->forceUpdateHints(); } int32_t ExynosDisplay::PowerHalHintWorker::connectPowerHal() { if (mPowerHalAidl && mPowerHalExtAidl) { return NO_ERROR; } const std::string kInstance = std::string(IPower::descriptor) + "/default"; ndk::SpAIBinder pwBinder = ndk::SpAIBinder(AServiceManager_getService(kInstance.c_str())); mPowerHalAidl = IPower::fromBinder(pwBinder); if (!mPowerHalAidl) { ALOGE("failed to connect power HAL (retry %u)", mConnectRetryCount); mConnectRetryCount++; return -EINVAL; } ndk::SpAIBinder pwExtBinder; AIBinder_getExtension(pwBinder.get(), pwExtBinder.getR()); mPowerHalExtAidl = IPowerExt::fromBinder(pwExtBinder); if (!mPowerHalExtAidl) { mPowerHalAidl = nullptr; ALOGE("failed to connect power HAL extension (retry %u)", mConnectRetryCount); mConnectRetryCount++; return -EINVAL; } mConnectRetryCount = 0; AIBinder_linkToDeath(pwExtBinder.get(), mDeathRecipient.get(), reinterpret_cast(this)); // ensure the hint session is recreated every time powerhal is recreated mPowerHintSession = nullptr; forceUpdateHints(); ALOGI("connected power HAL successfully"); return NO_ERROR; } int32_t ExynosDisplay::PowerHalHintWorker::checkPowerHalExtHintSupport(const std::string &mode) { if (mode.empty() || connectPowerHal() != NO_ERROR) { return -EINVAL; } bool isSupported = false; auto ret = mPowerHalExtAidl->isModeSupported(mode.c_str(), &isSupported); if (!ret.isOk()) { ALOGE("failed to check power HAL extension hint: mode=%s", mode.c_str()); if (ret.getExceptionCode() == EX_TRANSACTION_FAILED) { /* * PowerHAL service may crash due to some reasons, this could end up * binder transaction failure. Set nullptr here to trigger re-connection. */ ALOGE("binder transaction failed for power HAL extension hint"); mPowerHalExtAidl = nullptr; return -ENOTCONN; } return -EINVAL; } if (!isSupported) { ALOGW("power HAL extension hint is not supported: mode=%s", mode.c_str()); return -EOPNOTSUPP; } ALOGI("power HAL extension hint is supported: mode=%s", mode.c_str()); return NO_ERROR; } int32_t ExynosDisplay::PowerHalHintWorker::sendPowerHalExtHint(const std::string &mode, bool enabled) { if (mode.empty() || connectPowerHal() != NO_ERROR) { return -EINVAL; } auto ret = mPowerHalExtAidl->setMode(mode.c_str(), enabled); if (!ret.isOk()) { ALOGE("failed to send power HAL extension hint: mode=%s, enabled=%d", mode.c_str(), enabled); if (ret.getExceptionCode() == EX_TRANSACTION_FAILED) { /* * PowerHAL service may crash due to some reasons, this could end up * binder transaction failure. Set nullptr here to trigger re-connection. */ ALOGE("binder transaction failed for power HAL extension hint"); mPowerHalExtAidl = nullptr; return -ENOTCONN; } return -EINVAL; } return NO_ERROR; } int32_t ExynosDisplay::PowerHalHintWorker::checkRefreshRateHintSupport(const int32_t refreshRate) { int32_t ret = NO_ERROR; if (!isPowerHalExist()) { return -EOPNOTSUPP; } const auto its = mRefreshRateHintSupportMap.find(refreshRate); if (its == mRefreshRateHintSupportMap.end()) { /* check new hint */ std::string refreshRateHintStr = mRefreshRateHintPrefixStr + std::to_string(refreshRate) + "FPS"; ret = checkPowerHalExtHintSupport(refreshRateHintStr); if (ret == NO_ERROR || ret == -EOPNOTSUPP) { mRefreshRateHintSupportMap[refreshRate] = (ret == NO_ERROR); ALOGI("cache refresh rate hint %s: %d", refreshRateHintStr.c_str(), !ret); } else { ALOGE("failed to check the support of refresh rate hint, ret %d", ret); } } else { /* check existing hint */ if (!its->second) { ret = -EOPNOTSUPP; } } return ret; } int32_t ExynosDisplay::PowerHalHintWorker::sendRefreshRateHint(const int32_t refreshRate, bool enabled) { std::string hintStr = mRefreshRateHintPrefixStr + std::to_string(refreshRate) + "FPS"; int32_t ret = sendPowerHalExtHint(hintStr, enabled); if (ret == -ENOTCONN) { /* Reset the hints when binder failure occurs */ mLastRefreshRateHint = 0; } return ret; } int32_t ExynosDisplay::PowerHalHintWorker::updateRefreshRateHintInternal( const hwc2_power_mode_t powerMode, const int32_t refreshRate) { int32_t ret = NO_ERROR; /* TODO: add refresh rate buckets, tracked in b/181100731 */ // skip sending unnecessary hint if it's still the same. if (mLastRefreshRateHint == refreshRate && powerMode == HWC2_POWER_MODE_ON) { return NO_ERROR; } if (mLastRefreshRateHint) { ret = sendRefreshRateHint(mLastRefreshRateHint, false); if (ret == NO_ERROR) { mLastRefreshRateHint = 0; } else { return ret; } } // disable all refresh rate hints if power mode is not ON. if (powerMode != HWC2_POWER_MODE_ON) { return ret; } ret = checkRefreshRateHintSupport(refreshRate); if (ret != NO_ERROR) { return ret; } ret = sendRefreshRateHint(refreshRate, true); if (ret != NO_ERROR) { return ret; } mLastRefreshRateHint = refreshRate; return ret; } int32_t ExynosDisplay::PowerHalHintWorker::checkIdleHintSupport(void) { int32_t ret = NO_ERROR; if (!isPowerHalExist()) { return -EOPNOTSUPP; } Lock(); if (mIdleHintSupportIsChecked) { ret = mIdleHintIsSupported ? NO_ERROR : -EOPNOTSUPP; Unlock(); return ret; } Unlock(); ret = checkPowerHalExtHintSupport(mIdleHintStr); Lock(); if (ret == NO_ERROR) { mIdleHintIsSupported = true; mIdleHintSupportIsChecked = true; ALOGI("display idle hint is supported"); } else if (ret == -EOPNOTSUPP) { mIdleHintSupportIsChecked = true; ALOGI("display idle hint is unsupported"); } else { ALOGW("failed to check the support of display idle hint, ret %d", ret); } Unlock(); return ret; } int32_t ExynosDisplay::PowerHalHintWorker::checkPowerHintSessionSupport() { std::scoped_lock lock(sSharedDisplayMutex); if (sSharedDisplayData.hintSessionSupported.has_value()) { mHintSessionSupportChecked = true; return *(sSharedDisplayData.hintSessionSupported); } if (!isPowerHalExist()) { return -EOPNOTSUPP; } if (connectPowerHal() != NO_ERROR) { ALOGW("Error connecting to the PowerHAL"); return -EINVAL; } int64_t rate; // Try to get preferred rate to determine if it's supported auto ret = mPowerHalAidl->getHintSessionPreferredRate(&rate); int32_t out; if (ret.isOk()) { ALOGV("Power hint session is supported"); out = NO_ERROR; } else if (ret.getExceptionCode() == EX_UNSUPPORTED_OPERATION) { ALOGW("Power hint session unsupported"); out = -EOPNOTSUPP; } else { ALOGW("Error checking power hint status"); out = -EINVAL; } mHintSessionSupportChecked = true; sSharedDisplayData.hintSessionSupported = out; return out; } int32_t ExynosDisplay::PowerHalHintWorker::updateIdleHint(const int64_t deadlineTime, const bool forceUpdate) { int32_t ret = checkIdleHintSupport(); if (ret != NO_ERROR) { return ret; } bool enableIdleHint = (deadlineTime < systemTime(SYSTEM_TIME_MONOTONIC) && CC_LIKELY(deadlineTime > 0)); DISPLAY_ATRACE_INT("HWCIdleHintTimer", enableIdleHint); if (mIdleHintIsEnabled != enableIdleHint || forceUpdate) { ret = sendPowerHalExtHint(mIdleHintStr, enableIdleHint); if (ret == NO_ERROR) { mIdleHintIsEnabled = enableIdleHint; } } return ret; } void ExynosDisplay::PowerHalHintWorker::forceUpdateHints(void) { Lock(); mLastRefreshRateHint = 0; mNeedUpdateRefreshRateHint = true; mLastErrorSent = std::nullopt; if (mIdleHintSupportIsChecked && mIdleHintIsSupported) { mForceUpdateIdleHint = true; } Unlock(); Signal(); } int32_t ExynosDisplay::PowerHalHintWorker::sendActualWorkDuration() { Lock(); if (mPowerHintSession == nullptr) { Unlock(); return -EINVAL; } if (!needSendActualWorkDurationLocked()) { Unlock(); return NO_ERROR; } if (mActualWorkDuration.has_value()) { mLastErrorSent = *mActualWorkDuration - mTargetWorkDuration; } std::vector hintQueue(std::move(mPowerHintQueue)); mPowerHintQueue.clear(); Unlock(); ALOGV("Sending hint update batch"); mLastActualReportTimestamp = systemTime(SYSTEM_TIME_MONOTONIC); auto ret = mPowerHintSession->reportActualWorkDuration(hintQueue); if (!ret.isOk()) { ALOGW("Failed to report power hint session timing: %s %s", ret.getMessage(), ret.getDescription().c_str()); if (ret.getExceptionCode() == EX_TRANSACTION_FAILED) { Lock(); mPowerHalExtAidl = nullptr; Unlock(); } } return ret.isOk() ? NO_ERROR : -EINVAL; } int32_t ExynosDisplay::PowerHalHintWorker::updateTargetWorkDuration() { if (sNormalizeTarget) { return NO_ERROR; } if (mPowerHintSession == nullptr) { return -EINVAL; } Lock(); if (!needUpdateTargetWorkDurationLocked()) { Unlock(); return NO_ERROR; } nsecs_t targetWorkDuration = mTargetWorkDuration; mLastTargetDurationReported = targetWorkDuration; Unlock(); ALOGV("Sending target time: %lld ns", static_cast(targetWorkDuration)); auto ret = mPowerHintSession->updateTargetWorkDuration(targetWorkDuration); if (!ret.isOk()) { ALOGW("Failed to send power hint session target: %s %s", ret.getMessage(), ret.getDescription().c_str()); if (ret.getExceptionCode() == EX_TRANSACTION_FAILED) { Lock(); mPowerHalExtAidl = nullptr; Unlock(); } } return ret.isOk() ? NO_ERROR : -EINVAL; } void ExynosDisplay::PowerHalHintWorker::signalActualWorkDuration(nsecs_t actualDurationNanos) { ATRACE_CALL(); if (!usePowerHintSession()) { return; } Lock(); nsecs_t reportedDurationNs = actualDurationNanos; if (sNormalizeTarget) { reportedDurationNs += mLastTargetDurationReported - mTargetWorkDuration; } else { if (mLastTargetDurationReported != kDefaultTarget.count() && mTargetWorkDuration != 0) { reportedDurationNs = static_cast(static_cast(mLastTargetDurationReported) / mTargetWorkDuration * actualDurationNanos); } } mActualWorkDuration = reportedDurationNs; WorkDuration duration = {.timeStampNanos = systemTime(), .durationNanos = reportedDurationNs}; if (sTraceHintSessionData) { DISPLAY_ATRACE_INT64("Measured duration", actualDurationNanos); DISPLAY_ATRACE_INT64("Target error term", mTargetWorkDuration - actualDurationNanos); DISPLAY_ATRACE_INT64("Reported duration", reportedDurationNs); DISPLAY_ATRACE_INT64("Reported target", mLastTargetDurationReported); DISPLAY_ATRACE_INT64("Reported target error term", mLastTargetDurationReported - reportedDurationNs); } ALOGV("Sending actual work duration of: %" PRId64 " on reported target: %" PRId64 " with error: %" PRId64, reportedDurationNs, mLastTargetDurationReported, mLastTargetDurationReported - reportedDurationNs); mPowerHintQueue.push_back(duration); bool shouldSignal = needSendActualWorkDurationLocked(); Unlock(); if (shouldSignal) { Signal(); } } void ExynosDisplay::PowerHalHintWorker::signalTargetWorkDuration(nsecs_t targetDurationNanos) { ATRACE_CALL(); if (!usePowerHintSession()) { return; } Lock(); mTargetWorkDuration = targetDurationNanos - kTargetSafetyMargin.count(); if (sTraceHintSessionData) DISPLAY_ATRACE_INT64("Time target", mTargetWorkDuration); bool shouldSignal = false; if (!sNormalizeTarget) { shouldSignal = needUpdateTargetWorkDurationLocked(); if (shouldSignal && mActualWorkDuration.has_value() && sTraceHintSessionData) { DISPLAY_ATRACE_INT64("Target error term", *mActualWorkDuration - mTargetWorkDuration); } } Unlock(); if (shouldSignal) { Signal(); } } void ExynosDisplay::PowerHalHintWorker::signalRefreshRate(hwc2_power_mode_t powerMode, int32_t refreshRate) { Lock(); mPowerModeState = powerMode; mRefreshRate = refreshRate; mNeedUpdateRefreshRateHint = true; Unlock(); Signal(); } void ExynosDisplay::PowerHalHintWorker::signalNonIdle() { ATRACE_CALL(); Lock(); if (mIdleHintSupportIsChecked && !mIdleHintIsSupported) { Unlock(); return; } mIdleHintDeadlineTime = systemTime(SYSTEM_TIME_MONOTONIC) + nsecsIdleHintTimeout; Unlock(); Signal(); } bool ExynosDisplay::PowerHalHintWorker::needUpdateIdleHintLocked(int64_t &timeout) { if (!mIdleHintIsSupported) { return false; } int64_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); bool shouldEnableIdleHint = (mIdleHintDeadlineTime < currentTime) && CC_LIKELY(mIdleHintDeadlineTime > 0); if (mIdleHintIsEnabled != shouldEnableIdleHint || mForceUpdateIdleHint) { return true; } timeout = mIdleHintDeadlineTime - currentTime; return false; } void ExynosDisplay::PowerHalHintWorker::Routine() { Lock(); bool useHintSession = usePowerHintSession(); // if the tids have updated, we restart the session if (mTidsUpdated && useHintSession) mPowerHintSession = nullptr; bool needStartHintSession = (mPowerHintSession == nullptr) && useHintSession && !mBinderTids.empty(); int ret = 0; int64_t timeout = -1; if (!mNeedUpdateRefreshRateHint && !needUpdateIdleHintLocked(timeout) && !needSendActualWorkDurationLocked() && !needStartHintSession && !needUpdateTargetWorkDurationLocked()) { ret = WaitForSignalOrExitLocked(timeout); } // exit() signal received if (ret == -EINTR) { Unlock(); return; } // store internal values so they are consistent after Unlock() // some defined earlier also might have changed during the wait useHintSession = usePowerHintSession(); needStartHintSession = (mPowerHintSession == nullptr) && useHintSession && !mBinderTids.empty(); bool needUpdateRefreshRateHint = mNeedUpdateRefreshRateHint; int64_t deadlineTime = mIdleHintDeadlineTime; hwc2_power_mode_t powerMode = mPowerModeState; /* * Clear the flags here instead of clearing them after calling the hint * update functions. The flags may be set by signals after Unlock() and * before the hint update functions are done. Thus we may miss the newest * hints if we clear the flags after the hint update functions work without * errors. */ mTidsUpdated = false; mNeedUpdateRefreshRateHint = false; bool forceUpdateIdleHint = mForceUpdateIdleHint; mForceUpdateIdleHint = false; Unlock(); if (!mHintSessionSupportChecked) { checkPowerHintSessionSupport(); } updateIdleHint(deadlineTime, forceUpdateIdleHint); if (needUpdateRefreshRateHint) { int32_t rc = updateRefreshRateHintInternal(powerMode, mRefreshRate); if (rc != NO_ERROR && rc != -EOPNOTSUPP) { Lock(); if (mPowerModeState == HWC2_POWER_MODE_ON) { /* Set the flag to trigger update again for next loop */ mNeedUpdateRefreshRateHint = true; } Unlock(); } } if (useHintSession) { if (needStartHintSession) { startHintSession(); } sendActualWorkDuration(); updateTargetWorkDuration(); } } void ExynosDisplay::PowerHalHintWorker::addBinderTid(pid_t tid) { Lock(); if (mBinderTids.count(tid) != 0) { Unlock(); return; } mTidsUpdated = true; mBinderTids.emplace(tid); Unlock(); Signal(); } void ExynosDisplay::PowerHalHintWorker::removeBinderTid(pid_t tid) { Lock(); if (mBinderTids.erase(tid) == 0) { Unlock(); return; } mTidsUpdated = true; Unlock(); Signal(); } int32_t ExynosDisplay::PowerHalHintWorker::startHintSession() { Lock(); std::vector tids(mBinderTids.begin(), mBinderTids.end()); nsecs_t targetWorkDuration = sNormalizeTarget ? mLastTargetDurationReported : mTargetWorkDuration; // we want to stay locked during this one since it assigns "mPowerHintSession" auto ret = mPowerHalAidl->createHintSession(getpid(), static_cast(getuid()), tids, targetWorkDuration, &mPowerHintSession); if (!ret.isOk()) { ALOGW("Failed to start power hal hint session with error %s %s", ret.getMessage(), ret.getDescription().c_str()); if (ret.getExceptionCode() == EX_TRANSACTION_FAILED) { mPowerHalExtAidl = nullptr; } Unlock(); return -EINVAL; } else { mLastTargetDurationReported = targetWorkDuration; } Unlock(); return NO_ERROR; } bool ExynosDisplay::PowerHalHintWorker::checkPowerHintSessionReady() { static constexpr const std::chrono::milliseconds maxFlagWaitTime = 20s; static const std::string propName = "persist.device_config.surface_flinger_native_boot.AdpfFeature__adpf_cpu_hint"; static std::once_flag hintSessionFlag; // wait once for 20 seconds in another thread for the value to become available, or give up std::call_once(hintSessionFlag, [&] { std::thread hintSessionChecker([&] { std::optional flagValue = waitForPropertyValue(propName, maxFlagWaitTime.count()); bool enabled = flagValue.has_value() && (base::ParseBool(flagValue->c_str()) == base::ParseBoolResult::kTrue); std::scoped_lock lock(sSharedDisplayMutex); sSharedDisplayData.hintSessionEnabled = enabled; }); hintSessionChecker.detach(); }); std::scoped_lock lock(sSharedDisplayMutex); return sSharedDisplayData.hintSessionEnabled.has_value() && sSharedDisplayData.hintSessionSupported.has_value(); } bool ExynosDisplay::PowerHalHintWorker::usePowerHintSession() { std::optional useSessionCached{mUsePowerHintSession.load()}; if (useSessionCached.has_value()) { return *useSessionCached; } if (!checkPowerHintSessionReady()) return false; std::scoped_lock lock(sSharedDisplayMutex); bool out = *(sSharedDisplayData.hintSessionEnabled) && (*(sSharedDisplayData.hintSessionSupported) == NO_ERROR); mUsePowerHintSession.store(out); return out; } bool ExynosDisplay::PowerHalHintWorker::needUpdateTargetWorkDurationLocked() { if (!usePowerHintSession() || sNormalizeTarget) return false; // to disable the rate limiter we just use a max deviation of 1 nsecs_t maxDeviation = sUseRateLimiter ? kAllowedDeviation.count() : 1; // report if the change in target from our last submission to now exceeds the threshold return abs(mTargetWorkDuration - mLastTargetDurationReported) >= maxDeviation; } bool ExynosDisplay::PowerHalHintWorker::needSendActualWorkDurationLocked() { if (!usePowerHintSession() || mPowerHintQueue.size() == 0 || !mActualWorkDuration.has_value()) { return false; } if (!mLastErrorSent.has_value() || (systemTime(SYSTEM_TIME_MONOTONIC) - mLastActualReportTimestamp) > kStaleTimeout.count()) { return true; } // to effectively disable the rate limiter we just use a max deviation of 1 nsecs_t maxDeviation = sUseRateLimiter ? kAllowedDeviation.count() : 1; // report if the change in error term from our last submission to now exceeds the threshold return abs((*mActualWorkDuration - mTargetWorkDuration) - *mLastErrorSent) >= maxDeviation; } // track the tid of any thread that calls in and remove it on thread death void ExynosDisplay::PowerHalHintWorker::trackThisThread() { thread_local struct TidTracker { TidTracker(PowerHalHintWorker *worker) : mWorker(worker) { mTid = gettid(); mWorker->addBinderTid(mTid); } ~TidTracker() { mWorker->removeBinderTid(mTid); } pid_t mTid; PowerHalHintWorker *mWorker; } tracker(this); } const bool ExynosDisplay::PowerHalHintWorker::sTraceHintSessionData = base::GetBoolProperty(std::string("debug.hwc.trace_hint_sessions"), false); const bool ExynosDisplay::PowerHalHintWorker::sNormalizeTarget = base::GetBoolProperty(std::string("debug.hwc.normalize_hint_session_durations"), false); const bool ExynosDisplay::PowerHalHintWorker::sUseRateLimiter = base::GetBoolProperty(std::string("debug.hwc.use_rate_limiter"), true); ExynosDisplay::PowerHalHintWorker::SharedDisplayData ExynosDisplay::PowerHalHintWorker::sSharedDisplayData; std::mutex ExynosDisplay::PowerHalHintWorker::sSharedDisplayMutex; int ExynosSortedLayer::compare(void const *lhs, void const *rhs) { ExynosLayer *left = *((ExynosLayer**)(lhs)); ExynosLayer *right = *((ExynosLayer**)(rhs)); return left->mZOrder > right->mZOrder; } ssize_t ExynosSortedLayer::remove(const ExynosLayer *item) { for (size_t i = 0; i < size(); i++) { if (array()[i] == item) { removeAt(i); return i; } } return -1; } status_t ExynosSortedLayer::vector_sort() { int (*cmp)(ExynosLayer *const *, ExynosLayer *const *); cmp = (int (*)(ExynosLayer *const *, ExynosLayer *const *)) &compare; return sort(cmp); } ExynosLowFpsLayerInfo::ExynosLowFpsLayerInfo() : mHasLowFpsLayer(false), mFirstIndex(-1), mLastIndex(-1) { } void ExynosLowFpsLayerInfo::initializeInfos() { mHasLowFpsLayer = false; mFirstIndex = -1; mLastIndex = -1; } int32_t ExynosLowFpsLayerInfo::addLowFpsLayer(uint32_t layerIndex) { if (mHasLowFpsLayer == false) { mFirstIndex = layerIndex; mLastIndex = layerIndex; mHasLowFpsLayer = true; } else { mFirstIndex = min(mFirstIndex, (int32_t)layerIndex); mLastIndex = max(mLastIndex, (int32_t)layerIndex); } return NO_ERROR; } ExynosCompositionInfo::ExynosCompositionInfo(uint32_t type) : ExynosMPPSource(MPP_SOURCE_COMPOSITION_TARGET, this), mType(type), mHasCompositionLayer(false), mFirstIndex(-1), mLastIndex(-1), mTargetBuffer(NULL), mDataSpace(HAL_DATASPACE_UNKNOWN), mAcquireFence(-1), mReleaseFence(-1), mEnableSkipStatic(false), mSkipStaticInitFlag(false), mSkipFlag(false), mWindowIndex(-1) { /* If AFBC compression of mTargetBuffer is changed, */ /* mCompressionInfo should be set properly before resource assigning */ char value[256]; int afbc_prop; property_get("ro.vendor.ddk.set.afbc", value, "0"); afbc_prop = atoi(value); if (afbc_prop == 0) mCompressionInfo.type = COMP_TYPE_NONE; else mCompressionInfo.type = COMP_TYPE_AFBC; memset(&mSkipSrcInfo, 0, sizeof(mSkipSrcInfo)); for (int i = 0; i < NUM_SKIP_STATIC_LAYER; i++) { mSkipSrcInfo.srcInfo[i].acquireFenceFd = -1; mSkipSrcInfo.srcInfo[i].releaseFenceFd = -1; mSkipSrcInfo.dstInfo[i].acquireFenceFd = -1; mSkipSrcInfo.dstInfo[i].releaseFenceFd = -1; } if(type == COMPOSITION_CLIENT) mEnableSkipStatic = true; memset(&mLastWinConfigData, 0x0, sizeof(mLastWinConfigData)); mLastWinConfigData.acq_fence = -1; mLastWinConfigData.rel_fence = -1; } void ExynosCompositionInfo::initializeInfosComplete(ExynosDisplay *display) { mTargetBuffer = NULL; mDataSpace = HAL_DATASPACE_UNKNOWN; if (mAcquireFence >= 0) { ALOGD("ExynosCompositionInfo(%d):: mAcquire is not initialized(%d)", mType, mAcquireFence); if (display != NULL) fence_close(mAcquireFence, display, FENCE_TYPE_UNDEFINED, FENCE_IP_UNDEFINED); } mAcquireFence = -1; initializeInfos(display); } void ExynosCompositionInfo::initializeInfos(ExynosDisplay *display) { mHasCompositionLayer = false; mFirstIndex = -1; mLastIndex = -1; if (mType != COMPOSITION_CLIENT) { mTargetBuffer = NULL; mDataSpace = HAL_DATASPACE_UNKNOWN; if (mAcquireFence >= 0) { ALOGD("ExynosCompositionInfo(%d):: mAcquire is not initialized(%d)", mType, mAcquireFence); if (display != NULL) fence_close(mAcquireFence, display, FENCE_TYPE_UNDEFINED, FENCE_IP_UNDEFINED); } mAcquireFence = -1; } if (mReleaseFence >= 0) { ALOGD("ExynosCompositionInfo(%d):: mReleaseFence is not initialized(%d)", mType, mReleaseFence); if (display!= NULL) fence_close(mReleaseFence, display, FENCE_TYPE_UNDEFINED, FENCE_IP_UNDEFINED); } mReleaseFence = -1; mWindowIndex = -1; mOtfMPP = NULL; mM2mMPP = NULL; if ((display != NULL) && (display->mType == HWC_DISPLAY_VIRTUAL) && (mType == COMPOSITION_EXYNOS)) { mM2mMPP = display->mResourceManager->getExynosMPP(MPP_LOGICAL_G2D_COMBO); } } void ExynosCompositionInfo::setTargetBuffer(ExynosDisplay *display, buffer_handle_t handle, int32_t acquireFence, android_dataspace dataspace) { mTargetBuffer = handle; if (mType == COMPOSITION_CLIENT) { if (display != NULL) { if (mAcquireFence >= 0) { mAcquireFence = fence_close(mAcquireFence, display, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_FB); } mAcquireFence = hwcCheckFenceDebug(display, FENCE_TYPE_DST_ACQUIRE, FENCE_IP_FB, acquireFence); } } else { if (display != NULL) { if (mAcquireFence >= 0) { mAcquireFence = fence_close(mAcquireFence, display, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_G2D); } mAcquireFence = hwcCheckFenceDebug(display, FENCE_TYPE_DST_ACQUIRE, FENCE_IP_G2D, acquireFence); } } if ((display != NULL) && (mDataSpace != dataspace)) display->setGeometryChanged(GEOMETRY_DISPLAY_DATASPACE_CHANGED); mDataSpace = dataspace; } void ExynosCompositionInfo::setCompressionType(uint32_t compressionType) { mCompressionInfo.type = compressionType; } void ExynosCompositionInfo::dump(String8& result) const { result.appendFormat("CompositionInfo (%d)\n", mType); result.appendFormat("mHasCompositionLayer(%d)\n", mHasCompositionLayer); if (mHasCompositionLayer) { result.appendFormat("\tfirstIndex: %d, lastIndex: %d, dataSpace: 0x%8x, compression: %s, " "windowIndex: %d\n", mFirstIndex, mLastIndex, mDataSpace, getCompressionStr(mCompressionInfo).c_str(), mWindowIndex); result.appendFormat("\thandle: %p, acquireFence: %d, releaseFence: %d, skipFlag: %d", mTargetBuffer, mAcquireFence, mReleaseFence, mSkipFlag); if ((mOtfMPP == NULL) && (mM2mMPP == NULL)) result.appendFormat("\tresource is not assigned\n"); if (mOtfMPP != NULL) result.appendFormat("\tassignedMPP: %s\n", mOtfMPP->mName.c_str()); if (mM2mMPP != NULL) result.appendFormat("\t%s\n", mM2mMPP->mName.c_str()); } if (mTargetBuffer != NULL) { uint64_t internal_format = 0; internal_format = VendorGraphicBufferMeta::get_internal_format(mTargetBuffer); result.appendFormat("\tinternal_format: 0x%" PRIx64 ", afbc: %d\n", internal_format, isAFBCCompressed(mTargetBuffer)); } uint32_t assignedSrcNum = 0; if ((mM2mMPP != NULL) && ((assignedSrcNum = mM2mMPP->mAssignedSources.size()) > 0)) { result.appendFormat("\tAssigned source num: %d\n", assignedSrcNum); result.append("\t"); for (uint32_t i = 0; i < assignedSrcNum; i++) { if (mM2mMPP->mAssignedSources[i]->mSourceType == MPP_SOURCE_LAYER) { ExynosLayer* layer = (ExynosLayer*)(mM2mMPP->mAssignedSources[i]); result.appendFormat("[%d]layer_%p ", i, layer->mLayerBuffer); } else { result.appendFormat("[%d]sourceType_%d ", i, mM2mMPP->mAssignedSources[i]->mSourceType); } } result.append("\n"); } result.append("\n"); } String8 ExynosCompositionInfo::getTypeStr() { switch(mType) { case COMPOSITION_NONE: return String8("COMPOSITION_NONE"); case COMPOSITION_CLIENT: return String8("COMPOSITION_CLIENT"); case COMPOSITION_EXYNOS: return String8("COMPOSITION_EXYNOS"); default: return String8("InvalidType"); } } ExynosDisplay::ExynosDisplay(uint32_t type, uint32_t index, ExynosDevice* device, const std::string& displayName) : mDisplayId(getDisplayId(type, index)), mType(type), mIndex(index), mDeconNodeName(""), mXres(1440), mYres(2960), mXdpi(25400), mYdpi(25400), mVsyncPeriod(kDefaultVsyncPeriodNanoSecond), mBtsFrameScanoutPeriod(kDefaultVsyncPeriodNanoSecond), mBtsPendingOperationRatePeriod(0), mDevice(device), mDisplayName(displayName.c_str()), mDisplayTraceName(String8::format("%s(%d)", displayName.c_str(), mDisplayId)), mPlugState(false), mHasSingleBuffer(false), mResourceManager(NULL), mClientCompositionInfo(COMPOSITION_CLIENT), mExynosCompositionInfo(COMPOSITION_EXYNOS), mGeometryChanged(0x0), mBufferUpdates(0), mRenderingState(RENDERING_STATE_NONE), mHWCRenderingState(RENDERING_STATE_NONE), mDisplayBW(0), mDynamicReCompMode(CLIENT_2_DEVICE), mDREnable(false), mDRDefault(false), mLastFpsTime(0), mFrameCount(0), mLastFrameCount(0), mErrorFrameCount(0), mUpdateEventCnt(0), mUpdateCallCnt(0), mDefaultDMA(MAX_DECON_DMA_TYPE), mLastRetireFence(-1), mWindowNumUsed(0), mBaseWindowIndex(0), mNumMaxPriorityAllowed(1), mCursorIndex(-1), mColorTransformHint(HAL_COLOR_TRANSFORM_IDENTITY), mMaxLuminance(0), mMaxAverageLuminance(0), mMinLuminance(0), mDisplayTe2Manager(nullptr), mHWC1LayerList(NULL), /* Support DDI scalser */ mOldScalerMode(0), mNewScaledWidth(0), mNewScaledHeight(0), mDeviceXres(0), mDeviceYres(0), mColorMode(HAL_COLOR_MODE_NATIVE), mSkipFrame(false), mVsyncPeriodChangeConstraints{systemTime(SYSTEM_TIME_MONOTONIC), 0}, mVsyncAppliedTimeLine{false, 0, systemTime(SYSTEM_TIME_MONOTONIC)}, mConfigRequestState(hwc_request_state_t::SET_CONFIG_STATE_DONE), mPowerHalHint(mDisplayId, mDisplayTraceName), mErrLogFileWriter(2, ERR_LOG_SIZE), mDebugDumpFileWriter(10, 1, ".dump"), mFenceFileWriter(2, FENCE_ERR_LOG_SIZE), mOperationRateManager(nullptr) { mDisplayControl.enableCompositionCrop = true; mDisplayControl.enableExynosCompositionOptimization = true; mDisplayControl.enableClientCompositionOptimization = true; mDisplayControl.useMaxG2DSrc = false; mDisplayControl.handleLowFpsLayers = false; mDisplayControl.earlyStartMPP = true; mDisplayControl.adjustDisplayFrame = false; mDisplayControl.cursorSupport = false; mDisplayConfigs.clear(); mPowerModeState = std::nullopt; mVsyncState = HWC2_VSYNC_DISABLE; /* TODO : Exception handling here */ if (device == NULL) { ALOGE("Display creation failed!"); return; } mResourceManager = device->mResourceManager; /* The number of window is same with the number of otfMPP */ mMaxWindowNum = mResourceManager->getOtfMPPs().size(); mDpuData.init(mMaxWindowNum, 0); mLastDpuData.init(mMaxWindowNum, 0); ALOGI("window configs size(%zu)", mDpuData.configs.size()); mLowFpsLayerInfo.initializeInfos(); mPowerHalHint.Init(); mUseDpu = true; mHpdStatus = false; return; } ExynosDisplay::~ExynosDisplay() { } /** * Member function for Dynamic AFBC Control solution. */ bool ExynosDisplay::comparePreferedLayers() { return false; } int ExynosDisplay::getId() { return mDisplayId; } void ExynosDisplay::initDisplay() { mClientCompositionInfo.initializeInfos(this); mClientCompositionInfo.mEnableSkipStatic = true; mClientCompositionInfo.mSkipStaticInitFlag = false; mClientCompositionInfo.mSkipFlag = false; memset(&mClientCompositionInfo.mSkipSrcInfo, 0x0, sizeof(mClientCompositionInfo.mSkipSrcInfo)); for (int i = 0; i < NUM_SKIP_STATIC_LAYER; i++) { mClientCompositionInfo.mSkipSrcInfo.srcInfo[i].acquireFenceFd = -1; mClientCompositionInfo.mSkipSrcInfo.srcInfo[i].releaseFenceFd = -1; mClientCompositionInfo.mSkipSrcInfo.dstInfo[i].acquireFenceFd = -1; mClientCompositionInfo.mSkipSrcInfo.dstInfo[i].releaseFenceFd = -1; } memset(&mClientCompositionInfo.mLastWinConfigData, 0x0, sizeof(mClientCompositionInfo.mLastWinConfigData)); mClientCompositionInfo.mLastWinConfigData.acq_fence = -1; mClientCompositionInfo.mLastWinConfigData.rel_fence = -1; mExynosCompositionInfo.initializeInfos(this); mExynosCompositionInfo.mEnableSkipStatic = false; mExynosCompositionInfo.mSkipStaticInitFlag = false; mExynosCompositionInfo.mSkipFlag = false; memset(&mExynosCompositionInfo.mSkipSrcInfo, 0x0, sizeof(mExynosCompositionInfo.mSkipSrcInfo)); for (int i = 0; i < NUM_SKIP_STATIC_LAYER; i++) { mExynosCompositionInfo.mSkipSrcInfo.srcInfo[i].acquireFenceFd = -1; mExynosCompositionInfo.mSkipSrcInfo.srcInfo[i].releaseFenceFd = -1; mExynosCompositionInfo.mSkipSrcInfo.dstInfo[i].acquireFenceFd = -1; mExynosCompositionInfo.mSkipSrcInfo.dstInfo[i].releaseFenceFd = -1; } memset(&mExynosCompositionInfo.mLastWinConfigData, 0x0, sizeof(mExynosCompositionInfo.mLastWinConfigData)); mExynosCompositionInfo.mLastWinConfigData.acq_fence = -1; mExynosCompositionInfo.mLastWinConfigData.rel_fence = -1; mGeometryChanged = 0x0; mRenderingState = RENDERING_STATE_NONE; mDisplayBW = 0; mDynamicReCompMode = CLIENT_2_DEVICE; mCursorIndex = -1; mDpuData.reset(); mLastDpuData.reset(); if (mDisplayControl.earlyStartMPP == true) { for (size_t i = 0; i < mLayers.size(); i++) { exynos_image outImage; ExynosMPP* m2mMPP = mLayers[i]->mM2mMPP; /* Close release fence of dst buffer of last frame */ if ((mLayers[i]->mValidateCompositionType == HWC2_COMPOSITION_DEVICE) && (m2mMPP != NULL) && (m2mMPP->mAssignedDisplay == this) && (m2mMPP->getDstImageInfo(&outImage) == NO_ERROR)) { if (m2mMPP->mPhysicalType == MPP_MSC) { fence_close(outImage.releaseFenceFd, this, FENCE_TYPE_DST_RELEASE, FENCE_IP_MSC); } else if (m2mMPP->mPhysicalType == MPP_G2D) { fence_close(outImage.releaseFenceFd, this, FENCE_TYPE_DST_RELEASE, FENCE_IP_G2D); } else { DISPLAY_LOGE("[%zu] layer has invalid mppType(%d)", i, m2mMPP->mPhysicalType); fence_close(outImage.releaseFenceFd, this, FENCE_TYPE_DST_RELEASE, FENCE_IP_ALL); } m2mMPP->resetDstReleaseFence(); } } } } /** * @param outLayer * @return int32_t */ int32_t ExynosDisplay::destroyLayer(hwc2_layer_t outLayer) { Mutex::Autolock lock(mDRMutex); ExynosLayer *layer = (ExynosLayer *)outLayer; if (layer == nullptr) { return HWC2_ERROR_BAD_LAYER; } if (mLayers.remove(layer) < 0) { auto it = std::find(mIgnoreLayers.begin(), mIgnoreLayers.end(), layer); if (it == mIgnoreLayers.end()) { ALOGE("%s:: There is no layer", __func__); } else { mIgnoreLayers.erase(it); } } else { setGeometryChanged(GEOMETRY_DISPLAY_LAYER_REMOVED); } mDisplayInterface->destroyLayer(layer); layer->resetAssignedResource(); delete layer; if (mPlugState == false) { DISPLAY_LOGI("%s : destroyLayer is done. But display is already disconnected", __func__); return HWC2_ERROR_BAD_DISPLAY; } return HWC2_ERROR_NONE; } /** * @return void */ void ExynosDisplay::destroyLayers() { Mutex::Autolock lock(mDRMutex); for (uint32_t index = 0; index < mLayers.size();) { ExynosLayer *layer = mLayers[index]; mLayers.removeAt(index); delete layer; } for (auto it = mIgnoreLayers.begin(); it != mIgnoreLayers.end();) { ExynosLayer *layer = *it; it = mIgnoreLayers.erase(it); delete layer; } } ExynosLayer *ExynosDisplay::checkLayer(hwc2_layer_t addr) { ExynosLayer *temp = (ExynosLayer *)addr; if (!mLayers.isEmpty()) { for (size_t i = 0; i < mLayers.size(); i++) { if (mLayers[i] == temp) return mLayers[i]; } } if (mIgnoreLayers.size()) { auto it = std::find(mIgnoreLayers.begin(), mIgnoreLayers.end(), temp); return (it == mIgnoreLayers.end()) ? NULL : *it; } ALOGE("HWC2 : %s : %d, wrong layer request!", __func__, __LINE__); return NULL; } void ExynosDisplay::checkIgnoreLayers() { Mutex::Autolock lock(mDRMutex); for (auto it = mIgnoreLayers.begin(); it != mIgnoreLayers.end();) { ExynosLayer *layer = *it; if ((layer->mLayerFlag & EXYNOS_HWC_IGNORE_LAYER) == 0) { mLayers.push_back(layer); it = mIgnoreLayers.erase(it); } else { it++; } } for (uint32_t index = 0; index < mLayers.size();) { ExynosLayer *layer = mLayers[index]; if (layer->mLayerFlag & EXYNOS_HWC_IGNORE_LAYER) { layer->resetValidateData(); layer->mValidateCompositionType = HWC2_COMPOSITION_DEVICE; /* * Directly close without counting down * because it was not counted by validate */ if (layer->mAcquireFence > 0) { close(layer->mAcquireFence); } layer->mAcquireFence = -1; layer->mReleaseFence = -1; mIgnoreLayers.push_back(layer); mLayers.removeAt(index); } else { index++; } } } /** * @return void */ void ExynosDisplay::doPreProcessing() { /* Low persistence setting */ int ret = 0; bool hasSingleBuffer = false; bool hasClientLayer = false; for (size_t i=0; i < mLayers.size(); i++) { buffer_handle_t handle = mLayers[i]->mLayerBuffer; VendorGraphicBufferMeta gmeta(handle); if (mLayers[i]->mCompositionType == HWC2_COMPOSITION_CLIENT) { hasClientLayer = true; } exynos_image srcImg; exynos_image dstImg; mLayers[i]->setSrcExynosImage(&srcImg); mLayers[i]->setDstExynosImage(&dstImg); mLayers[i]->setExynosImage(srcImg, dstImg); } /* * Disable skip static layer feature if there is the layer that's * mCompositionType is HWC2_COMPOSITION_CLIENT * HWC should not change compositionType if it is HWC2_COMPOSITION_CLIENT */ if (mType != HWC_DISPLAY_VIRTUAL) mClientCompositionInfo.mEnableSkipStatic = (!hasClientLayer && !hasSingleBuffer); if (mHasSingleBuffer != hasSingleBuffer) { if ((ret = mDisplayInterface->disableSelfRefresh(uint32_t(hasSingleBuffer))) < 0) DISPLAY_LOGE("ioctl S3CFB_LOW_PERSISTENCE failed: %s ret(%d)", strerror(errno), ret); mDisplayControl.skipM2mProcessing = !hasSingleBuffer; mHasSingleBuffer = hasSingleBuffer; setGeometryChanged(GEOMETRY_DISPLAY_SINGLEBUF_CHANGED); } if ((exynosHWCControl.displayMode < DISPLAY_MODE_NUM) && (mDevice->mDisplayMode != exynosHWCControl.displayMode)) setGeometryChanged(GEOMETRY_DEVICE_DISP_MODE_CHAGED); if ((ret = mResourceManager->checkScenario(this)) != NO_ERROR) DISPLAY_LOGE("checkScenario error ret(%d)", ret); if (exynosHWCControl.skipResourceAssign == 0) { /* Set any flag to mGeometryChanged */ setGeometryChanged(GEOMETRY_DEVICE_SCENARIO_CHANGED); } #ifdef HWC_NO_SUPPORT_SKIP_VALIDATE if (mDevice->checkNonInternalConnection()) { /* Set any flag to mGeometryChanged */ mDevice->mGeometryChanged = 0x10; } #endif return; } /** * @return int */ int ExynosDisplay::checkLayerFps() { mLowFpsLayerInfo.initializeInfos(); if (mDisplayControl.handleLowFpsLayers == false) return NO_ERROR; Mutex::Autolock lock(mDRMutex); for (size_t i=0; i < mLayers.size(); i++) { if ((mLayers[i]->mOverlayPriority < ePriorityHigh) && (mLayers[i]->getFps() < LOW_FPS_THRESHOLD)) { mLowFpsLayerInfo.addLowFpsLayer(i); } else if (mLowFpsLayerInfo.mHasLowFpsLayer == true) { break; } } /* There is only one low fps layer, Overlay is better in this case */ if ((mLowFpsLayerInfo.mHasLowFpsLayer == true) && (mLowFpsLayerInfo.mFirstIndex == mLowFpsLayerInfo.mLastIndex)) mLowFpsLayerInfo.initializeInfos(); return NO_ERROR; } int ExynosDisplay::switchDynamicReCompMode(dynamic_recomp_mode mode) { if (mDynamicReCompMode == mode) return NO_MODE_SWITCH; ATRACE_INT("Force client composition by DR", (mode == DEVICE_2_CLIENT)); mDynamicReCompMode = mode; setGeometryChanged(GEOMETRY_DISPLAY_DYNAMIC_RECOMPOSITION); return mode; } /** * @return int */ int ExynosDisplay::checkDynamicReCompMode() { ATRACE_CALL(); Mutex::Autolock lock(mDRMutex); if (!exynosHWCControl.useDynamicRecomp) { mLastModeSwitchTimeStamp = 0; return switchDynamicReCompMode(CLIENT_2_DEVICE); } /* initialize the Timestamps */ if (!mLastModeSwitchTimeStamp) { mLastModeSwitchTimeStamp = mLastUpdateTimeStamp; return switchDynamicReCompMode(CLIENT_2_DEVICE); } /* Avoid to use DEVICE_2_CLIENT if there's a layer with priority >= ePriorityHigh such as: * front buffer, video layer, HDR, DRM layer, etc. */ for (size_t i = 0; i < mLayers.size(); i++) { if ((mLayers[i]->mOverlayPriority >= ePriorityHigh) || mLayers[i]->mPreprocessedInfo.preProcessed) { auto ret = switchDynamicReCompMode(CLIENT_2_DEVICE); if (ret) { mUpdateCallCnt = 0; mLastModeSwitchTimeStamp = mLastUpdateTimeStamp; DISPLAY_LOGD(eDebugDynamicRecomp, "[DYNAMIC_RECOMP] GLES_2_HWC by video layer"); } return ret; } } unsigned int incomingPixels = 0; hwc_rect_t dispRect = {INT_MAX, INT_MAX, 0, 0}; for (size_t i = 0; i < mLayers.size(); i++) { auto& r = mLayers[i]->mPreprocessedInfo.displayFrame; if (r.top < dispRect.top) dispRect.top = r.top; if (r.left < dispRect.left) dispRect.left = r.left; if (r.bottom > dispRect.bottom) dispRect.bottom = r.bottom; if (r.right > dispRect.right) dispRect.right = r.right; auto w = WIDTH(r); auto h = HEIGHT(r); incomingPixels += w * h; } /* Mode Switch is not required if total pixels are not more than the threshold */ unsigned int mergedDisplayFrameSize = WIDTH(dispRect) * HEIGHT(dispRect); if (incomingPixels <= mergedDisplayFrameSize) { auto ret = switchDynamicReCompMode(CLIENT_2_DEVICE); if (ret) { mUpdateCallCnt = 0; mLastModeSwitchTimeStamp = mLastUpdateTimeStamp; DISPLAY_LOGD(eDebugDynamicRecomp, "[DYNAMIC_RECOMP] GLES_2_HWC by BW check"); } return ret; } /* * There will be at least one composition call per one minute (because of time update) * To minimize the analysis overhead, just analyze it once in 5 second */ auto timeStampDiff = systemTime(SYSTEM_TIME_MONOTONIC) - mLastModeSwitchTimeStamp; /* * previous CompModeSwitch was CLIENT_2_DEVICE: check fps after 5s from mLastModeSwitchTimeStamp * previous CompModeSwitch was DEVICE_2_CLIENT: check immediately */ if ((mDynamicReCompMode != DEVICE_2_CLIENT) && (timeStampDiff < kLayerFpsStableTimeNs)) return 0; mLastModeSwitchTimeStamp = mLastUpdateTimeStamp; float updateFps = 0; if ((mUpdateEventCnt != 1) && (mDynamicReCompMode == DEVICE_2_CLIENT) && (mUpdateCallCnt == 1)) { DISPLAY_LOGD(eDebugDynamicRecomp, "[DYNAMIC_RECOMP] first frame after DEVICE_2_CLIENT"); updateFps = kDynamicRecompFpsThreshold + 1; } else { float maxFps = 0; for (uint32_t i = 0; i < mLayers.size(); i++) { float layerFps = mLayers[i]->checkFps(/* increaseCount */ false); if (maxFps < layerFps) maxFps = layerFps; } updateFps = maxFps; } mUpdateCallCnt = 0; /* * FPS estimation. * If FPS is lower than kDynamicRecompFpsThreshold, try to switch the mode to GLES */ if (updateFps < kDynamicRecompFpsThreshold) { auto ret = switchDynamicReCompMode(DEVICE_2_CLIENT); if (ret) { DISPLAY_LOGD(eDebugDynamicRecomp, "[DYNAMIC_RECOMP] DEVICE_2_CLIENT by low FPS(%.2f)", updateFps); } return ret; } else { auto ret = switchDynamicReCompMode(CLIENT_2_DEVICE); if (ret) { DISPLAY_LOGD(eDebugDynamicRecomp, "[DYNAMIC_RECOMP] CLIENT_2_HWC by high FPS((%.2f)", updateFps); } return ret; } return 0; } /** * @return int */ int ExynosDisplay::handleDynamicReCompMode() { return 0; } /** * @param changedBit * @return int */ void ExynosDisplay::setGeometryChanged(uint64_t changedBit) { mGeometryChanged |= changedBit; mDevice->setGeometryChanged(changedBit); } void ExynosDisplay::clearGeometryChanged() { mGeometryChanged = 0; mBufferUpdates = 0; for (size_t i=0; i < mLayers.size(); i++) { mLayers[i]->clearGeometryChanged(); } } int ExynosDisplay::handleStaticLayers(ExynosCompositionInfo& compositionInfo) { if (compositionInfo.mType != COMPOSITION_CLIENT) return -EINVAL; if (mType == HWC_DISPLAY_VIRTUAL) return NO_ERROR; if (compositionInfo.mHasCompositionLayer == false) { DISPLAY_LOGD(eDebugSkipStaicLayer, "there is no client composition"); return NO_ERROR; } if ((compositionInfo.mWindowIndex < 0) || (compositionInfo.mWindowIndex >= (int32_t)mDpuData.configs.size())) { DISPLAY_LOGE("invalid mWindowIndex(%d)", compositionInfo.mWindowIndex); return -EINVAL; } exynos_win_config_data &config = mDpuData.configs[compositionInfo.mWindowIndex]; /* Store configuration of client target configuration */ if (compositionInfo.mSkipFlag == false) { compositionInfo.mLastWinConfigData = config; DISPLAY_LOGD(eDebugSkipStaicLayer, "config[%d] is stored", compositionInfo.mWindowIndex); } else { for (size_t i = (size_t)compositionInfo.mFirstIndex; i <= (size_t)compositionInfo.mLastIndex; i++) { if ((mLayers[i]->mExynosCompositionType == HWC2_COMPOSITION_CLIENT) && (mLayers[i]->mAcquireFence >= 0)) fence_close(mLayers[i]->mAcquireFence, this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_ALL); mLayers[i]->mAcquireFence = -1; mLayers[i]->mReleaseFence = -1; } if (compositionInfo.mTargetBuffer == NULL) { fence_close(config.acq_fence, this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_ALL); config = compositionInfo.mLastWinConfigData; /* Assigned otfMPP for client target can be changed */ config.assignedMPP = compositionInfo.mOtfMPP; /* acq_fence was closed by DPU driver in the previous frame */ config.acq_fence = -1; } else { /* Check target buffer is same with previous frame */ if (!std::equal(config.fd_idma, config.fd_idma+3, compositionInfo.mLastWinConfigData.fd_idma)) { DISPLAY_LOGE("Current config [%d][%d, %d, %d]", compositionInfo.mWindowIndex, config.fd_idma[0], config.fd_idma[1], config.fd_idma[2]); DISPLAY_LOGE("============================= dump last win configs ==================================="); for (size_t i = 0; i < mLastDpuData.configs.size(); i++) { android::String8 result; result.appendFormat("config[%zu]\n", i); dumpConfig(result, mLastDpuData.configs[i]); DISPLAY_LOGE("%s", result.c_str()); } DISPLAY_LOGE("compositionInfo.mLastWinConfigData config [%d, %d, %d]", compositionInfo.mLastWinConfigData.fd_idma[0], compositionInfo.mLastWinConfigData.fd_idma[1], compositionInfo.mLastWinConfigData.fd_idma[2]); return -EINVAL; } } DISPLAY_LOGD(eDebugSkipStaicLayer, "skipStaticLayer config[%d]", compositionInfo.mWindowIndex); dumpConfig(config); } return NO_ERROR; } bool ExynosDisplay::skipStaticLayerChanged(ExynosCompositionInfo& compositionInfo) { if ((int)compositionInfo.mSkipSrcInfo.srcNum != (compositionInfo.mLastIndex - compositionInfo.mFirstIndex + 1)) { DISPLAY_LOGD(eDebugSkipStaicLayer, "Client composition number is changed (%d -> %d)", compositionInfo.mSkipSrcInfo.srcNum, compositionInfo.mLastIndex - compositionInfo.mFirstIndex + 1); return true; } bool isChanged = false; for (size_t i = (size_t)compositionInfo.mFirstIndex; i <= (size_t)compositionInfo.mLastIndex; i++) { ExynosLayer *layer = mLayers[i]; size_t index = i - compositionInfo.mFirstIndex; if ((layer->mLayerBuffer == NULL) || (compositionInfo.mSkipSrcInfo.srcInfo[index].bufferHandle != layer->mLayerBuffer)) { isChanged = true; DISPLAY_LOGD(eDebugSkipStaicLayer, "layer[%zu] handle is changed"\ " handle(%p -> %p), layerFlag(0x%8x)", i, compositionInfo.mSkipSrcInfo.srcInfo[index].bufferHandle, layer->mLayerBuffer, layer->mLayerFlag); break; } else if ((compositionInfo.mSkipSrcInfo.srcInfo[index].x != layer->mSrcImg.x) || (compositionInfo.mSkipSrcInfo.srcInfo[index].y != layer->mSrcImg.y) || (compositionInfo.mSkipSrcInfo.srcInfo[index].w != layer->mSrcImg.w) || (compositionInfo.mSkipSrcInfo.srcInfo[index].h != layer->mSrcImg.h) || (compositionInfo.mSkipSrcInfo.srcInfo[index].dataSpace != layer->mSrcImg.dataSpace) || (compositionInfo.mSkipSrcInfo.srcInfo[index].blending != layer->mSrcImg.blending) || (compositionInfo.mSkipSrcInfo.srcInfo[index].transform != layer->mSrcImg.transform) || (compositionInfo.mSkipSrcInfo.srcInfo[index].planeAlpha != layer->mSrcImg.planeAlpha)) { isChanged = true; DISPLAY_LOGD(eDebugSkipStaicLayer, "layer[%zu] source info is changed, "\ "x(%d->%d), y(%d->%d), w(%d->%d), h(%d->%d), dataSpace(%d->%d), "\ "blending(%d->%d), transform(%d->%d), planeAlpha(%3.1f->%3.1f)", i, compositionInfo.mSkipSrcInfo.srcInfo[index].x, layer->mSrcImg.x, compositionInfo.mSkipSrcInfo.srcInfo[index].y, layer->mSrcImg.y, compositionInfo.mSkipSrcInfo.srcInfo[index].w, layer->mSrcImg.w, compositionInfo.mSkipSrcInfo.srcInfo[index].h, layer->mSrcImg.h, compositionInfo.mSkipSrcInfo.srcInfo[index].dataSpace, layer->mSrcImg.dataSpace, compositionInfo.mSkipSrcInfo.srcInfo[index].blending, layer->mSrcImg.blending, compositionInfo.mSkipSrcInfo.srcInfo[index].transform, layer->mSrcImg.transform, compositionInfo.mSkipSrcInfo.srcInfo[index].planeAlpha, layer->mSrcImg.planeAlpha); break; } else if ((compositionInfo.mSkipSrcInfo.dstInfo[index].x != layer->mDstImg.x) || (compositionInfo.mSkipSrcInfo.dstInfo[index].y != layer->mDstImg.y) || (compositionInfo.mSkipSrcInfo.dstInfo[index].w != layer->mDstImg.w) || (compositionInfo.mSkipSrcInfo.dstInfo[index].h != layer->mDstImg.h)) { isChanged = true; DISPLAY_LOGD(eDebugSkipStaicLayer, "layer[%zu] dst info is changed, "\ "x(%d->%d), y(%d->%d), w(%d->%d), h(%d->%d)", i, compositionInfo.mSkipSrcInfo.dstInfo[index].x, layer->mDstImg.x, compositionInfo.mSkipSrcInfo.dstInfo[index].y, layer->mDstImg.y, compositionInfo.mSkipSrcInfo.dstInfo[index].w, layer->mDstImg.w, compositionInfo.mSkipSrcInfo.dstInfo[index].h, layer->mDstImg.h); break; } } return isChanged; } void ExynosDisplay::requestLhbm(bool on) { mDevice->onRefresh(mDisplayId); if (mBrightnessController) { mBrightnessController->processLocalHbm(on); } } /** * @param compositionType * @return int */ int ExynosDisplay::skipStaticLayers(ExynosCompositionInfo& compositionInfo) { compositionInfo.mSkipFlag = false; if (compositionInfo.mType != COMPOSITION_CLIENT) return -EINVAL; if ((exynosHWCControl.skipStaticLayers == 0) || (compositionInfo.mEnableSkipStatic == false)) { DISPLAY_LOGD(eDebugSkipStaicLayer, "skipStaticLayers(%d), mEnableSkipStatic(%d)", exynosHWCControl.skipStaticLayers, compositionInfo.mEnableSkipStatic); compositionInfo.mSkipStaticInitFlag = false; return NO_ERROR; } if ((compositionInfo.mHasCompositionLayer == false) || (compositionInfo.mFirstIndex < 0) || (compositionInfo.mLastIndex < 0) || ((compositionInfo.mLastIndex - compositionInfo.mFirstIndex + 1) > NUM_SKIP_STATIC_LAYER)) { DISPLAY_LOGD(eDebugSkipStaicLayer, "mHasCompositionLayer(%d), mFirstIndex(%d), mLastIndex(%d)", compositionInfo.mHasCompositionLayer, compositionInfo.mFirstIndex, compositionInfo.mLastIndex); compositionInfo.mSkipStaticInitFlag = false; return NO_ERROR; } if (compositionInfo.mSkipStaticInitFlag) { bool isChanged = skipStaticLayerChanged(compositionInfo); if (isChanged == true) { compositionInfo.mSkipStaticInitFlag = false; return NO_ERROR; } for (size_t i = (size_t)compositionInfo.mFirstIndex; i <= (size_t)compositionInfo.mLastIndex; i++) { ExynosLayer *layer = mLayers[i]; if (layer->mValidateCompositionType == COMPOSITION_CLIENT) { layer->mOverlayInfo |= eSkipStaticLayer; } else { compositionInfo.mSkipStaticInitFlag = false; if (layer->mOverlayPriority < ePriorityHigh) { DISPLAY_LOGE("[%zu] Invalid layer type: %d", i, layer->mValidateCompositionType); return -EINVAL; } else { return NO_ERROR; } } } compositionInfo.mSkipFlag = true; DISPLAY_LOGD(eDebugSkipStaicLayer, "SkipStaicLayer is enabled"); return NO_ERROR; } compositionInfo.mSkipStaticInitFlag = true; memset(&compositionInfo.mSkipSrcInfo, 0, sizeof(compositionInfo.mSkipSrcInfo)); for (int i = 0; i < NUM_SKIP_STATIC_LAYER; i++) { compositionInfo.mSkipSrcInfo.srcInfo[i].acquireFenceFd = -1; compositionInfo.mSkipSrcInfo.srcInfo[i].releaseFenceFd = -1; compositionInfo.mSkipSrcInfo.dstInfo[i].acquireFenceFd = -1; compositionInfo.mSkipSrcInfo.dstInfo[i].releaseFenceFd = -1; } for (size_t i = (size_t)compositionInfo.mFirstIndex; i <= (size_t)compositionInfo.mLastIndex; i++) { ExynosLayer *layer = mLayers[i]; size_t index = i - compositionInfo.mFirstIndex; compositionInfo.mSkipSrcInfo.srcInfo[index] = layer->mSrcImg; compositionInfo.mSkipSrcInfo.dstInfo[index] = layer->mDstImg; DISPLAY_LOGD(eDebugSkipStaicLayer, "mSkipSrcInfo.srcInfo[%zu] is initialized, %p", index, layer->mSrcImg.bufferHandle); } compositionInfo.mSkipSrcInfo.srcNum = (compositionInfo.mLastIndex - compositionInfo.mFirstIndex + 1); return NO_ERROR; } bool ExynosDisplay::shouldSignalNonIdle(void) { // Some cases such that we can skip calling mPowerHalHint.signalNonIdle(): // 1. Updating source crop or buffer for video layer // 2. Updating refresh rate indicator layer uint64_t exclude = GEOMETRY_LAYER_SOURCECROP_CHANGED; if ((mGeometryChanged & ~exclude) != 0) { return true; } for (size_t i = 0; i < mLayers.size(); i++) { // Frame update for refresh rate overlay indicator layer can be ignored if (mLayers[i]->mRequestedCompositionType == HWC2_COMPOSITION_REFRESH_RATE_INDICATOR) continue; // Frame update for video layer can be ignored if (mLayers[i]->isLayerFormatYuv()) continue; if (mLayers[i]->mLastLayerBuffer != mLayers[i]->mLayerBuffer || mLayers[i]->mGeometryChanged != 0) { return true; } } return false; } /** * @return int */ int ExynosDisplay::doPostProcessing() { for (size_t i=0; i < mLayers.size(); i++) { /* Layer handle back-up */ mLayers[i]->mLastLayerBuffer = mLayers[i]->mLayerBuffer; } clearGeometryChanged(); return 0; } bool ExynosDisplay::validateExynosCompositionLayer() { bool isValid = true; ExynosMPP *m2mMpp = mExynosCompositionInfo.mM2mMPP; int sourceSize = (int)m2mMpp->mAssignedSources.size(); if ((mExynosCompositionInfo.mFirstIndex >= 0) && (mExynosCompositionInfo.mLastIndex >= 0)) { sourceSize = mExynosCompositionInfo.mLastIndex - mExynosCompositionInfo.mFirstIndex + 1; if (!mUseDpu && mClientCompositionInfo.mHasCompositionLayer) sourceSize++; } if (m2mMpp->mAssignedSources.size() == 0) { DISPLAY_LOGE("No source images"); isValid = false; } else if (mUseDpu && (((mExynosCompositionInfo.mFirstIndex < 0) || (mExynosCompositionInfo.mLastIndex < 0)) || (sourceSize != (int)m2mMpp->mAssignedSources.size()))) { DISPLAY_LOGE("Invalid index (%d, %d), size(%zu), sourceSize(%d)", mExynosCompositionInfo.mFirstIndex, mExynosCompositionInfo.mLastIndex, m2mMpp->mAssignedSources.size(), sourceSize); isValid = false; } if (isValid == false) { for (int32_t i = mExynosCompositionInfo.mFirstIndex; i <= mExynosCompositionInfo.mLastIndex; i++) { /* break when only framebuffer target is assigned on ExynosCompositor */ if (i == -1) break; if (mLayers[i]->mAcquireFence >= 0) fence_close(mLayers[i]->mAcquireFence, this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_ALL); mLayers[i]->mAcquireFence = -1; } mExynosCompositionInfo.mM2mMPP->requestHWStateChange(MPP_HW_STATE_IDLE); } return isValid; } /** * @return int */ int ExynosDisplay::doExynosComposition() { int ret = NO_ERROR; exynos_image src_img; exynos_image dst_img; if (mExynosCompositionInfo.mHasCompositionLayer) { if (mExynosCompositionInfo.mM2mMPP == NULL) { DISPLAY_LOGE("mExynosCompositionInfo.mM2mMPP is NULL"); return -EINVAL; } mExynosCompositionInfo.mM2mMPP->requestHWStateChange(MPP_HW_STATE_RUNNING); /* mAcquireFence is updated, Update image info */ for (int32_t i = mExynosCompositionInfo.mFirstIndex; i <= mExynosCompositionInfo.mLastIndex; i++) { /* break when only framebuffer target is assigned on ExynosCompositor */ if (i == -1) break; struct exynos_image srcImg, dstImg; mLayers[i]->setSrcExynosImage(&srcImg); dumpExynosImage(eDebugFence, srcImg); mLayers[i]->setDstExynosImage(&dstImg); mLayers[i]->setExynosImage(srcImg, dstImg); } /* For debugging */ if (validateExynosCompositionLayer() == false) { DISPLAY_LOGE("mExynosCompositionInfo is not valid"); return -EINVAL; } if ((ret = mExynosCompositionInfo.mM2mMPP->doPostProcessing( mExynosCompositionInfo.mDstImg)) != NO_ERROR) { DISPLAY_LOGE("exynosComposition doPostProcessing fail ret(%d)", ret); return ret; } for (int32_t i = mExynosCompositionInfo.mFirstIndex; i <= mExynosCompositionInfo.mLastIndex; i++) { /* break when only framebuffer target is assigned on ExynosCompositor */ if (i == -1) break; /* This should be closed by resource lib (libmpp or libacryl) */ mLayers[i]->mAcquireFence = -1; } exynos_image outImage; if ((ret = mExynosCompositionInfo.mM2mMPP->getDstImageInfo(&outImage)) != NO_ERROR) { DISPLAY_LOGE("exynosComposition getDstImageInfo fail ret(%d)", ret); return ret; } android_dataspace dataspace = HAL_DATASPACE_UNKNOWN; if (mColorMode != HAL_COLOR_MODE_NATIVE) dataspace = colorModeToDataspace(mColorMode); mExynosCompositionInfo.setTargetBuffer(this, outImage.bufferHandle, outImage.releaseFenceFd, dataspace); /* * buffer handle, dataspace can be changed by setTargetBuffer() * ExynosImage should be set again according to changed handle and dataspace */ setCompositionTargetExynosImage(COMPOSITION_EXYNOS, &src_img, &dst_img); mExynosCompositionInfo.setExynosImage(src_img, dst_img); DISPLAY_LOGD(eDebugFence, "mExynosCompositionInfo acquireFencefd(%d)", mExynosCompositionInfo.mAcquireFence); if ((ret = mExynosCompositionInfo.mM2mMPP->resetDstReleaseFence()) != NO_ERROR) { DISPLAY_LOGE("exynosComposition resetDstReleaseFence fail ret(%d)", ret); return ret; } } return ret; } bool ExynosDisplay::getHDRException(ExynosLayer* __unused layer) { return false; } int32_t ExynosDisplay::configureHandle(ExynosLayer &layer, int fence_fd, exynos_win_config_data &cfg) { /* TODO : this is hardcoded */ int32_t ret = NO_ERROR; buffer_handle_t handle = NULL; int32_t blending = 0x0100; uint32_t x = 0, y = 0; uint32_t w = WIDTH(layer.mPreprocessedInfo.displayFrame); uint32_t h = HEIGHT(layer.mPreprocessedInfo.displayFrame); ExynosMPP* otfMPP = NULL; ExynosMPP* m2mMPP = NULL; unsigned int luminanceMin = 0; unsigned int luminanceMax = 0; blending = layer.mBlending; otfMPP = layer.mOtfMPP; m2mMPP = layer.mM2mMPP; cfg.compressionInfo = layer.mCompressionInfo; if (layer.mCompressionInfo.type == COMP_TYPE_AFBC) { cfg.comp_src = DPP_COMP_SRC_GPU; } if (otfMPP == nullptr && layer.mExynosCompositionType != HWC2_COMPOSITION_DISPLAY_DECORATION) { HWC_LOGE(this, "%s:: otfMPP is NULL", __func__); return -EINVAL; } if (m2mMPP != NULL) handle = m2mMPP->mDstImgs[m2mMPP->mCurrentDstBuf].bufferHandle; else handle = layer.mLayerBuffer; if ((!layer.isDimLayer()) && handle == NULL) { HWC_LOGE(this, "%s:: invalid handle", __func__); return -EINVAL; } if (layer.mPreprocessedInfo.displayFrame.left < 0) { unsigned int crop = -layer.mPreprocessedInfo.displayFrame.left; DISPLAY_LOGD(eDebugWinConfig, "layer off left side of screen; cropping %u pixels from left edge", crop); x = 0; w -= crop; } else { x = layer.mPreprocessedInfo.displayFrame.left; } if (layer.mPreprocessedInfo.displayFrame.right > (int)mXres) { unsigned int crop = layer.mPreprocessedInfo.displayFrame.right - mXres; DISPLAY_LOGD(eDebugWinConfig, "layer off right side of screen; cropping %u pixels from right edge", crop); w -= crop; } if (layer.mPreprocessedInfo.displayFrame.top < 0) { unsigned int crop = -layer.mPreprocessedInfo.displayFrame.top; DISPLAY_LOGD(eDebugWinConfig, "layer off top side of screen; cropping %u pixels from top edge", crop); y = 0; h -= crop; } else { y = layer.mPreprocessedInfo.displayFrame.top; } if (layer.mPreprocessedInfo.displayFrame.bottom > (int)mYres) { int crop = layer.mPreprocessedInfo.displayFrame.bottom - mYres; DISPLAY_LOGD(eDebugWinConfig, "layer off bottom side of screen; cropping %u pixels from bottom edge", crop); h -= crop; } cfg.layer = &layer; if ((layer.mExynosCompositionType == HWC2_COMPOSITION_DEVICE) && (layer.mCompositionType == HWC2_COMPOSITION_CURSOR)) { cfg.state = cfg.WIN_STATE_CURSOR; } else if (layer.mExynosCompositionType == HWC2_COMPOSITION_DISPLAY_DECORATION) { cfg.state = cfg.WIN_STATE_RCD; assign(cfg.block_area, layer.mBlockingRect.left, layer.mBlockingRect.top, layer.mBlockingRect.right - layer.mBlockingRect.left, layer.mBlockingRect.bottom - layer.mBlockingRect.top); } else { cfg.state = cfg.WIN_STATE_BUFFER; } cfg.dst.x = x; cfg.dst.y = y; cfg.dst.w = w; cfg.dst.h = h; cfg.dst.f_w = mXres; cfg.dst.f_h = mYres; cfg.plane_alpha = layer.mPlaneAlpha; cfg.blending = blending; cfg.assignedMPP = otfMPP; if (layer.isDimLayer()) { if (fence_fd >= 0) { fence_fd = fence_close(fence_fd, this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_ALL); } cfg.state = cfg.WIN_STATE_COLOR; hwc_color_t color = layer.mColor; cfg.color = (color.a << 24) | (color.r << 16) | (color.g << 8) | color.b; DISPLAY_LOGD(eDebugWinConfig, "HWC2: DIM layer is enabled, color: %d, alpha : %f", cfg.color, cfg.plane_alpha); return ret; } VendorGraphicBufferMeta gmeta(handle); if (!layer.mPreprocessedInfo.mUsePrivateFormat) cfg.format = gmeta.format; else cfg.format = layer.mPreprocessedInfo.mPrivateFormat; cfg.buffer_id = gmeta.unique_id; cfg.fd_idma[0] = gmeta.fd; cfg.fd_idma[1] = gmeta.fd1; cfg.fd_idma[2] = gmeta.fd2; cfg.protection = (getDrmMode(gmeta.producer_usage) == SECURE_DRM) ? 1 : 0; exynos_image src_img = layer.mSrcImg; if (m2mMPP != NULL) { DISPLAY_LOGD(eDebugWinConfig, "\tUse m2mMPP, bufIndex: %d", m2mMPP->mCurrentDstBuf); dumpExynosImage(eDebugWinConfig, m2mMPP->mAssignedSources[0]->mMidImg); exynos_image mpp_dst_img; if (m2mMPP->getDstImageInfo(&mpp_dst_img) == NO_ERROR) { dumpExynosImage(eDebugWinConfig, mpp_dst_img); cfg.compressionInfo = mpp_dst_img.compressionInfo; cfg.src.f_w = mpp_dst_img.fullWidth; cfg.src.f_h = mpp_dst_img.fullHeight; cfg.src.x = mpp_dst_img.x; cfg.src.y = mpp_dst_img.y; cfg.src.w = mpp_dst_img.w; cfg.src.h = mpp_dst_img.h; cfg.format = mpp_dst_img.format; cfg.acq_fence = hwcCheckFenceDebug(this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_DPP, mpp_dst_img.releaseFenceFd); if (m2mMPP->mPhysicalType == MPP_MSC) { setFenceName(cfg.acq_fence, FENCE_DPP_SRC_MSC); } else if (m2mMPP->mPhysicalType == MPP_G2D) { setFenceName(cfg.acq_fence, FENCE_DPP_SRC_G2D); } else { setFenceName(cfg.acq_fence, FENCE_DPP_SRC_MPP); } m2mMPP->resetDstReleaseFence(); } else { HWC_LOGE(this, "%s:: Failed to get dst info of m2mMPP", __func__); } cfg.dataspace = mpp_dst_img.dataSpace; cfg.transform = 0; if (hasHdrInfo(layer.mMidImg)) { bool hdr_exception = getHDRException(&layer); uint32_t parcelFdIndex = getBufferNumOfFormat(layer.mMidImg.format, getCompressionType(layer.mMidImg.bufferHandle)); if (parcelFdIndex == 0) { DISPLAY_LOGE("%s:: failed to get parcelFdIndex for midImg with format: %d", __func__, layer.mMidImg.format); return -EINVAL; } if (layer.mBufferHasMetaParcel) { VendorGraphicBufferMeta layer_buffer_gmeta(layer.mLayerBuffer); if (layer_buffer_gmeta.flags & VendorGraphicBufferMeta::PRIV_FLAGS_USES_2PRIVATE_DATA) cfg.fd_idma[parcelFdIndex] = layer_buffer_gmeta.fd1; else if (layer_buffer_gmeta.flags & VendorGraphicBufferMeta::PRIV_FLAGS_USES_3PRIVATE_DATA) cfg.fd_idma[parcelFdIndex] = layer_buffer_gmeta.fd2; } else { cfg.fd_idma[parcelFdIndex] = layer.mMetaParcelFd; } if (!hdr_exception) cfg.hdr_enable = true; else cfg.hdr_enable = false; /* Min/Max luminance should be set as M2M MPP's HDR operations * If HDR is not processed by M2M MPP, M2M's dst image should have source's min/max luminance * */ dstMetaInfo_t metaInfo = m2mMPP->getDstMetaInfo(mpp_dst_img.dataSpace); luminanceMin = metaInfo.minLuminance; luminanceMax = metaInfo.maxLuminance; DISPLAY_LOGD(eDebugMPP, "HWC2: DPP luminance min %d, max %d", luminanceMin, luminanceMax); } else { cfg.hdr_enable = true; } src_img = layer.mMidImg; } else { cfg.src.f_w = src_img.fullWidth; cfg.src.f_h = src_img.fullHeight; cfg.src.x = layer.mPreprocessedInfo.sourceCrop.left; cfg.src.y = layer.mPreprocessedInfo.sourceCrop.top; cfg.src.w = WIDTH(layer.mPreprocessedInfo.sourceCrop) - (cfg.src.x - (uint32_t)layer.mPreprocessedInfo.sourceCrop.left); cfg.src.h = HEIGHT(layer.mPreprocessedInfo.sourceCrop) - (cfg.src.y - (uint32_t)layer.mPreprocessedInfo.sourceCrop.top); cfg.acq_fence = hwcCheckFenceDebug(this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_DPP, fence_fd); setFenceName(cfg.acq_fence, FENCE_DPP_SRC_LAYER); cfg.dataspace = src_img.dataSpace; cfg.transform = src_img.transform; if (hasHdrInfo(src_img)) { bool hdr_exception = getHDRException(&layer); if (!hdr_exception) cfg.hdr_enable = true; else cfg.hdr_enable = false; if (layer.mBufferHasMetaParcel == false) { uint32_t parcelFdIndex = getBufferNumOfFormat(gmeta.format, getCompressionType(handle)); if (parcelFdIndex == 0) { DISPLAY_LOGE("%s:: failed to get parcelFdIndex for srcImg with format: %d", __func__, gmeta.format); return -EINVAL; } cfg.fd_idma[parcelFdIndex] = layer.mMetaParcelFd; } /* * Static info uses 0.0001nit unit for luminace * Display uses 1nit unit for max luminance * and uses 0.0001nit unit for min luminance * Conversion is required */ luminanceMin = src_img.metaParcel.sHdrStaticInfo.sType1.mMinDisplayLuminance; luminanceMax = src_img.metaParcel.sHdrStaticInfo.sType1.mMaxDisplayLuminance/10000; DISPLAY_LOGD(eDebugMPP, "HWC2: DPP luminance min %d, max %d", luminanceMin, luminanceMax); } else { cfg.hdr_enable = true; } } cfg.min_luminance = luminanceMin; cfg.max_luminance = luminanceMax; cfg.needColorTransform = src_img.needColorTransform; /* Adjust configuration */ uint32_t srcMaxWidth, srcMaxHeight, srcWidthAlign, srcHeightAlign = 0; uint32_t srcXAlign, srcYAlign, srcMaxCropWidth, srcMaxCropHeight, srcCropWidthAlign, srcCropHeightAlign = 0; if (otfMPP != nullptr) { srcMaxWidth = otfMPP->getSrcMaxWidth(src_img); srcMaxHeight = otfMPP->getSrcMaxHeight(src_img); srcWidthAlign = otfMPP->getSrcWidthAlign(src_img); srcHeightAlign = otfMPP->getSrcHeightAlign(src_img); srcXAlign = otfMPP->getSrcXOffsetAlign(src_img); srcYAlign = otfMPP->getSrcYOffsetAlign(src_img); srcMaxCropWidth = otfMPP->getSrcMaxCropWidth(src_img); srcMaxCropHeight = otfMPP->getSrcMaxCropHeight(src_img); srcCropWidthAlign = otfMPP->getSrcCropWidthAlign(src_img); srcCropHeightAlign = otfMPP->getSrcCropHeightAlign(src_img); } if (cfg.src.x < 0) cfg.src.x = 0; if (cfg.src.y < 0) cfg.src.y = 0; if (otfMPP != NULL) { if (cfg.src.f_w > srcMaxWidth) cfg.src.f_w = srcMaxWidth; if (cfg.src.f_h > srcMaxHeight) cfg.src.f_h = srcMaxHeight; cfg.src.f_w = pixel_align_down(cfg.src.f_w, srcWidthAlign); cfg.src.f_h = pixel_align_down(cfg.src.f_h, srcHeightAlign); cfg.src.x = pixel_align(cfg.src.x, srcXAlign); cfg.src.y = pixel_align(cfg.src.y, srcYAlign); } if (cfg.src.x + cfg.src.w > cfg.src.f_w) cfg.src.w = cfg.src.f_w - cfg.src.x; if (cfg.src.y + cfg.src.h > cfg.src.f_h) cfg.src.h = cfg.src.f_h - cfg.src.y; if (otfMPP != NULL) { if (cfg.src.w > srcMaxCropWidth) cfg.src.w = srcMaxCropWidth; if (cfg.src.h > srcMaxCropHeight) cfg.src.h = srcMaxCropHeight; cfg.src.w = pixel_align_down(cfg.src.w, srcCropWidthAlign); cfg.src.h = pixel_align_down(cfg.src.h, srcCropHeightAlign); } uint64_t bufSize = gmeta.size * formatToBpp(gmeta.format); uint64_t srcSize = cfg.src.f_w * cfg.src.f_h * formatToBpp(cfg.format); if (!isFormatLossy(gmeta.format) && (bufSize < srcSize)) { DISPLAY_LOGE("%s:: buffer size is smaller than source size, buf(size: %d, format: %d), src(w: %d, h: %d, format: %d)", __func__, gmeta.size, gmeta.format, cfg.src.f_w, cfg.src.f_h, cfg.format); return -EINVAL; } return ret; } int32_t ExynosDisplay::configureOverlay(ExynosLayer *layer, exynos_win_config_data &cfg) { int32_t ret = NO_ERROR; if(layer != NULL) { if ((ret = configureHandle(*layer, layer->mAcquireFence, cfg)) != NO_ERROR) return ret; /* This will be closed by setReleaseFences() using config.acq_fence */ layer->mAcquireFence = -1; } return ret; } int32_t ExynosDisplay::configureOverlay(ExynosCompositionInfo &compositionInfo) { int32_t windowIndex = compositionInfo.mWindowIndex; buffer_handle_t handle = compositionInfo.mTargetBuffer; VendorGraphicBufferMeta gmeta(compositionInfo.mTargetBuffer); if ((windowIndex < 0) || (windowIndex >= (int32_t)mDpuData.configs.size())) { HWC_LOGE(this, "%s:: ExynosCompositionInfo(%d) has invalid data, windowIndex(%d)", __func__, compositionInfo.mType, windowIndex); return -EINVAL; } exynos_win_config_data &config = mDpuData.configs[windowIndex]; if (handle == NULL) { /* config will be set by handleStaticLayers */ if (compositionInfo.mSkipFlag) return NO_ERROR; if (compositionInfo.mType == COMPOSITION_CLIENT) { ALOGW("%s:: ExynosCompositionInfo(%d) has invalid data, handle(%p)", __func__, compositionInfo.mType, handle); if (compositionInfo.mAcquireFence >= 0) { compositionInfo.mAcquireFence = fence_close(compositionInfo.mAcquireFence, this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_FB); } config.state = config.WIN_STATE_DISABLED; return NO_ERROR; } else { HWC_LOGE(this, "%s:: ExynosCompositionInfo(%d) has invalid data, handle(%p)", __func__, compositionInfo.mType, handle); return -EINVAL; } } config.buffer_id = gmeta.unique_id; config.fd_idma[0] = gmeta.fd; config.fd_idma[1] = gmeta.fd1; config.fd_idma[2] = gmeta.fd2; config.protection = (getDrmMode(gmeta.producer_usage) == SECURE_DRM) ? 1 : 0; config.state = config.WIN_STATE_BUFFER; config.assignedMPP = compositionInfo.mOtfMPP; config.dst.f_w = mXres; config.dst.f_h = mYres; config.format = gmeta.format; if (compositionInfo.mType == COMPOSITION_EXYNOS) { config.src.f_w = pixel_align(mXres, G2D_JUSTIFIED_DST_ALIGN); config.src.f_h = pixel_align(mYres, G2D_JUSTIFIED_DST_ALIGN); } else { config.src.f_w = gmeta.stride; config.src.f_h = gmeta.vstride; } config.compressionInfo = compositionInfo.mCompressionInfo; if (compositionInfo.mCompressionInfo.type == COMP_TYPE_AFBC) { if (compositionInfo.mType == COMPOSITION_EXYNOS) config.comp_src = DPP_COMP_SRC_G2D; else if (compositionInfo.mType == COMPOSITION_CLIENT) config.comp_src = DPP_COMP_SRC_GPU; else HWC_LOGE(this, "unknown composition type: %d", compositionInfo.mType); } bool useCompositionCrop = true; if ((mDisplayControl.enableCompositionCrop) && (compositionInfo.mHasCompositionLayer) && (compositionInfo.mFirstIndex >= 0) && (compositionInfo.mLastIndex >= 0)) { hwc_rect merged_rect, src_rect; merged_rect.left = mXres; merged_rect.top = mYres; merged_rect.right = 0; merged_rect.bottom = 0; for (int i = compositionInfo.mFirstIndex; i <= compositionInfo.mLastIndex; i++) { ExynosLayer *layer = mLayers[i]; src_rect.left = layer->mDisplayFrame.left; src_rect.top = layer->mDisplayFrame.top; src_rect.right = layer->mDisplayFrame.right; src_rect.bottom = layer->mDisplayFrame.bottom; merged_rect = expand(merged_rect, src_rect); DISPLAY_LOGD(eDebugWinConfig, "[%d] layer type: [%d, %d] dispFrame [l: %d, t: %d, r: %d, b: %d], mergedRect [l: %d, t: %d, r: %d, b: %d]", i, layer->mCompositionType, layer->mExynosCompositionType, layer->mDisplayFrame.left, layer->mDisplayFrame.top, layer->mDisplayFrame.right, layer->mDisplayFrame.bottom, merged_rect.left, merged_rect.top, merged_rect.right, merged_rect.bottom); } config.src.x = merged_rect.left; config.src.y = merged_rect.top; config.src.w = merged_rect.right - merged_rect.left; config.src.h = merged_rect.bottom - merged_rect.top; ExynosMPP* exynosMPP = config.assignedMPP; if (exynosMPP == NULL) { DISPLAY_LOGE("%s:: assignedMPP is NULL", __func__); useCompositionCrop = false; } else { /* Check size constraints */ uint32_t restrictionIdx = getRestrictionIndex(config.format); uint32_t srcXAlign = exynosMPP->getSrcXOffsetAlign(restrictionIdx); uint32_t srcYAlign = exynosMPP->getSrcYOffsetAlign(restrictionIdx); uint32_t srcWidthAlign = exynosMPP->getSrcCropWidthAlign(restrictionIdx); uint32_t srcHeightAlign = exynosMPP->getSrcCropHeightAlign(restrictionIdx); uint32_t srcMinWidth = exynosMPP->getSrcMinWidth(restrictionIdx); uint32_t srcMinHeight = exynosMPP->getSrcMinHeight(restrictionIdx); if (config.src.w < srcMinWidth) { config.src.x -= (srcMinWidth - config.src.w); if (config.src.x < 0) config.src.x = 0; config.src.w = srcMinWidth; } if (config.src.h < srcMinHeight) { config.src.y -= (srcMinHeight - config.src.h); if (config.src.y < 0) config.src.y = 0; config.src.h = srcMinHeight; } int32_t alignedSrcX = pixel_align_down(config.src.x, srcXAlign); int32_t alignedSrcY = pixel_align_down(config.src.y, srcYAlign); config.src.w += (config.src.x - alignedSrcX); config.src.h += (config.src.y - alignedSrcY); config.src.x = alignedSrcX; config.src.y = alignedSrcY; config.src.w = pixel_align(config.src.w, srcWidthAlign); config.src.h = pixel_align(config.src.h, srcHeightAlign); } config.dst.x = config.src.x; config.dst.y = config.src.y; config.dst.w = config.src.w; config.dst.h = config.src.h; if ((config.src.x < 0) || (config.src.y < 0) || ((config.src.x + config.src.w) > mXres) || ((config.src.y + config.src.h) > mYres)) { useCompositionCrop = false; ALOGW("Invalid composition target crop size: (%d, %d, %d, %d)", config.src.x, config.src.y, config.src.w, config.src.h); } DISPLAY_LOGD(eDebugWinConfig, "composition(%d) config[%d] x : %d, y : %d, w : %d, h : %d", compositionInfo.mType, windowIndex, config.dst.x, config.dst.y, config.dst.w, config.dst.h); } else { useCompositionCrop = false; } if (useCompositionCrop == false) { config.src.x = 0; config.src.y = 0; config.src.w = mXres; config.src.h = mYres; config.dst.x = 0; config.dst.y = 0; config.dst.w = mXres; config.dst.h = mYres; } config.blending = HWC2_BLEND_MODE_PREMULTIPLIED; config.acq_fence = hwcCheckFenceDebug(this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_DPP, compositionInfo.mAcquireFence); config.plane_alpha = 1; config.dataspace = compositionInfo.mSrcImg.dataSpace; config.hdr_enable = true; /* This will be closed by setReleaseFences() using config.acq_fence */ compositionInfo.mAcquireFence = -1; DISPLAY_LOGD(eDebugSkipStaicLayer, "Configure composition target[%d], config[%d]!!!!", compositionInfo.mType, windowIndex); dumpConfig(config); uint64_t bufSize = gmeta.size * formatToBpp(gmeta.format); uint64_t srcSize = config.src.f_w * config.src.f_h * formatToBpp(config.format); if (!isFormatLossy(gmeta.format) && (bufSize < srcSize)) { DISPLAY_LOGE("%s:: buffer size is smaller than source size, buf(size: %d, format: %d), src(w: %d, h: %d, format: %d)", __func__, gmeta.size, gmeta.format, config.src.f_w, config.src.f_h, config.format); return -EINVAL; } return NO_ERROR; } /** * @return int */ int ExynosDisplay::setWinConfigData() { int ret = NO_ERROR; mDpuData.reset(); if (mClientCompositionInfo.mHasCompositionLayer) { if ((ret = configureOverlay(mClientCompositionInfo)) != NO_ERROR) return ret; } if (mExynosCompositionInfo.mHasCompositionLayer) { if ((ret = configureOverlay(mExynosCompositionInfo)) != NO_ERROR) { /* TEST */ //return ret; HWC_LOGE(this, "configureOverlay(ExynosCompositionInfo) is failed"); } } /* TODO loop for number of layers */ for (size_t i = 0; i < mLayers.size(); i++) { if ((mLayers[i]->mExynosCompositionType == HWC2_COMPOSITION_EXYNOS) || (mLayers[i]->mExynosCompositionType == HWC2_COMPOSITION_CLIENT)) continue; if (mLayers[i]->mExynosCompositionType == HWC2_COMPOSITION_DISPLAY_DECORATION) { if (CC_UNLIKELY(mDpuData.rcdConfigs.size() == 0)) { DISPLAY_LOGE("%s:: %zu layer has invalid COMPOSITION_TYPE(%d)", __func__, i, mLayers[i]->mExynosCompositionType); return -EINVAL; } if ((ret = configureOverlay(mLayers[i], mDpuData.rcdConfigs[0])) != NO_ERROR) return ret; continue; } int32_t windowIndex = mLayers[i]->mWindowIndex; if ((windowIndex < 0) || (windowIndex >= (int32_t)mDpuData.configs.size())) { DISPLAY_LOGE("%s:: %zu layer has invalid windowIndex(%d)", __func__, i, windowIndex); return -EINVAL; } DISPLAY_LOGD(eDebugWinConfig, "%zu layer, config[%d]", i, windowIndex); if ((ret = configureOverlay(mLayers[i], mDpuData.configs[windowIndex])) != NO_ERROR) return ret; } return 0; } void ExynosDisplay::printDebugInfos(String8 &reason) { struct timeval tv; gettimeofday(&tv, NULL); reason.appendFormat("errFrameNumber: %" PRId64 " time:%s\n", mErrorFrameCount, getLocalTimeStr(tv).c_str()); ALOGD("%s", reason.c_str()); bool fileOpened = mDebugDumpFileWriter.chooseOpenedFile(); mDebugDumpFileWriter.write(reason); mErrorFrameCount++; android::String8 result; result.appendFormat("Device mGeometryChanged(%" PRIx64 "), mGeometryChanged(%" PRIx64 "), mRenderingState(%d)\n", mDevice->mGeometryChanged, mGeometryChanged, mRenderingState); result.appendFormat("======================= dump composition infos ================================\n"); const ExynosCompositionInfo& clientCompInfo = mClientCompositionInfo; const ExynosCompositionInfo& exynosCompInfo = mExynosCompositionInfo; clientCompInfo.dump(result); exynosCompInfo.dump(result); ALOGD("%s", result.c_str()); mDebugDumpFileWriter.write(result); result.clear(); result.appendFormat("======================= dump exynos layers (%zu) ================================\n", mLayers.size()); ALOGD("%s", result.c_str()); mDebugDumpFileWriter.write(result); result.clear(); for (uint32_t i = 0; i < mLayers.size(); i++) { ExynosLayer *layer = mLayers[i]; layer->printLayer(); if (fileOpened) { layer->dump(result); mDebugDumpFileWriter.write(result); result.clear(); } } if (mIgnoreLayers.size()) { result.appendFormat("======================= dump ignore layers (%zu) ================================\n", mIgnoreLayers.size()); ALOGD("%s", result.c_str()); mDebugDumpFileWriter.write(result); result.clear(); for (uint32_t i = 0; i < mIgnoreLayers.size(); i++) { ExynosLayer *layer = mIgnoreLayers[i]; layer->printLayer(); if (fileOpened) { layer->dump(result); mDebugDumpFileWriter.write(result); result.clear(); } } } result.appendFormat("============================= dump win configs ===================================\n"); ALOGD("%s", result.c_str()); mDebugDumpFileWriter.write(result); result.clear(); for (size_t i = 0; i < mDpuData.configs.size(); i++) { ALOGD("config[%zu]", i); printConfig(mDpuData.configs[i]); if (fileOpened) { result.appendFormat("config[%zu]\n", i); dumpConfig(result, mDpuData.configs[i]); mDebugDumpFileWriter.write(result); result.clear(); } } mDebugDumpFileWriter.flush(); } int32_t ExynosDisplay::validateWinConfigData() { bool flagValidConfig = true; int bufferStateCnt = 0; for (size_t i = 0; i < mDpuData.configs.size(); i++) { exynos_win_config_data &config = mDpuData.configs[i]; if (config.state == config.WIN_STATE_BUFFER) { bool configInvalid = false; /* multiple dma mapping */ for (size_t j = (i+1); j < mDpuData.configs.size(); j++) { exynos_win_config_data &compare_config = mDpuData.configs[j]; if ((config.state == config.WIN_STATE_BUFFER) && (compare_config.state == compare_config.WIN_STATE_BUFFER)) { if ((config.assignedMPP != NULL) && (config.assignedMPP == compare_config.assignedMPP)) { DISPLAY_LOGE("WIN_CONFIG error: duplicated assignedMPP(%s) between win%zu, win%zu", config.assignedMPP->mName.c_str(), i, j); compare_config.state = compare_config.WIN_STATE_DISABLED; flagValidConfig = false; continue; } } } if ((config.src.x < 0) || (config.src.y < 0)|| (config.dst.x < 0) || (config.dst.y < 0)|| (config.src.w <= 0) || (config.src.h <= 0)|| (config.dst.w <= 0) || (config.dst.h <= 0)|| (config.dst.x + config.dst.w > (uint32_t)mXres) || (config.dst.y + config.dst.h > (uint32_t)mYres)) { DISPLAY_LOGE("WIN_CONFIG error: invalid pos or size win%zu", i); configInvalid = true; } if ((config.src.w > config.src.f_w) || (config.src.h > config.src.f_h)) { DISPLAY_LOGE("WIN_CONFIG error: invalid size %zu, %d, %d, %d, %d", i, config.src.w, config.src.f_w, config.src.h, config.src.f_h); configInvalid = true; } /* Source alignment check */ ExynosMPP* exynosMPP = config.assignedMPP; if (exynosMPP == NULL) { DISPLAY_LOGE("WIN_CONFIG error: config %zu assigendMPP is NULL", i); configInvalid = true; } else { uint32_t restrictionIdx = getRestrictionIndex(config.format); uint32_t srcXAlign = exynosMPP->getSrcXOffsetAlign(restrictionIdx); uint32_t srcYAlign = exynosMPP->getSrcYOffsetAlign(restrictionIdx); uint32_t srcWidthAlign = exynosMPP->getSrcCropWidthAlign(restrictionIdx); uint32_t srcHeightAlign = exynosMPP->getSrcCropHeightAlign(restrictionIdx); if ((config.src.x % srcXAlign != 0) || (config.src.y % srcYAlign != 0) || (config.src.w % srcWidthAlign != 0) || (config.src.h % srcHeightAlign != 0)) { DISPLAY_LOGE("WIN_CONFIG error: invalid src alignment : %zu, "\ "assignedMPP: %s, mppType:%d, format(%d), s_x: %d(%d), s_y: %d(%d), s_w : %d(%d), s_h : %d(%d)", i, config.assignedMPP->mName.c_str(), exynosMPP->mLogicalType, config.format, config.src.x, srcXAlign, config.src.y, srcYAlign, config.src.w, srcWidthAlign, config.src.h, srcHeightAlign); configInvalid = true; } } if (configInvalid) { config.state = config.WIN_STATE_DISABLED; flagValidConfig = false; } bufferStateCnt++; } if ((config.state == config.WIN_STATE_COLOR) || (config.state == config.WIN_STATE_CURSOR)) bufferStateCnt++; } if (bufferStateCnt == 0) { DISPLAY_LOGE("WIN_CONFIG error: has no buffer window"); flagValidConfig = false; } if (flagValidConfig) return NO_ERROR; else return -EINVAL; } /** * @return int */ int ExynosDisplay::setDisplayWinConfigData() { return 0; } bool ExynosDisplay::checkConfigChanged(const exynos_dpu_data &lastConfigsData, const exynos_dpu_data &newConfigsData) { if (exynosHWCControl.skipWinConfig == 0) return true; /* HWC doesn't skip WIN_CONFIG if other display is connected */ if ((mDevice->checkNonInternalConnection()) && (mType == HWC_DISPLAY_PRIMARY)) return true; for (size_t i = 0; i < lastConfigsData.configs.size(); i++) { if ((lastConfigsData.configs[i].state != newConfigsData.configs[i].state) || (lastConfigsData.configs[i].fd_idma[0] != newConfigsData.configs[i].fd_idma[0]) || (lastConfigsData.configs[i].fd_idma[1] != newConfigsData.configs[i].fd_idma[1]) || (lastConfigsData.configs[i].fd_idma[2] != newConfigsData.configs[i].fd_idma[2]) || (lastConfigsData.configs[i].dst.x != newConfigsData.configs[i].dst.x) || (lastConfigsData.configs[i].dst.y != newConfigsData.configs[i].dst.y) || (lastConfigsData.configs[i].dst.w != newConfigsData.configs[i].dst.w) || (lastConfigsData.configs[i].dst.h != newConfigsData.configs[i].dst.h) || (lastConfigsData.configs[i].src.x != newConfigsData.configs[i].src.x) || (lastConfigsData.configs[i].src.y != newConfigsData.configs[i].src.y) || (lastConfigsData.configs[i].src.w != newConfigsData.configs[i].src.w) || (lastConfigsData.configs[i].src.h != newConfigsData.configs[i].src.h) || (lastConfigsData.configs[i].format != newConfigsData.configs[i].format) || (lastConfigsData.configs[i].blending != newConfigsData.configs[i].blending) || (lastConfigsData.configs[i].plane_alpha != newConfigsData.configs[i].plane_alpha)) return true; } /* To cover buffer payload changed case */ for (size_t i = 0; i < mLayers.size(); i++) { if(mLayers[i]->mLastLayerBuffer != mLayers[i]->mLayerBuffer) return true; } return false; } int ExynosDisplay::checkConfigDstChanged(const exynos_dpu_data &lastConfigsData, const exynos_dpu_data &newConfigsData, uint32_t index) { if ((lastConfigsData.configs[index].state != newConfigsData.configs[index].state) || (lastConfigsData.configs[index].fd_idma[0] != newConfigsData.configs[index].fd_idma[0]) || (lastConfigsData.configs[index].fd_idma[1] != newConfigsData.configs[index].fd_idma[1]) || (lastConfigsData.configs[index].fd_idma[2] != newConfigsData.configs[index].fd_idma[2]) || (lastConfigsData.configs[index].format != newConfigsData.configs[index].format) || (lastConfigsData.configs[index].blending != newConfigsData.configs[index].blending) || (lastConfigsData.configs[index].plane_alpha != newConfigsData.configs[index].plane_alpha)) { DISPLAY_LOGD(eDebugWindowUpdate, "damage region is skip, but other configuration except dst was changed"); DISPLAY_LOGD(eDebugWindowUpdate, "\tstate[%d, %d], fd[%d, %d], format[0x%8x, 0x%8x], blending[%d, %d], plane_alpha[%f, %f]", lastConfigsData.configs[index].state, newConfigsData.configs[index].state, lastConfigsData.configs[index].fd_idma[0], newConfigsData.configs[index].fd_idma[0], lastConfigsData.configs[index].format, newConfigsData.configs[index].format, lastConfigsData.configs[index].blending, newConfigsData.configs[index].blending, lastConfigsData.configs[index].plane_alpha, newConfigsData.configs[index].plane_alpha); return -1; } if ((lastConfigsData.configs[index].dst.x != newConfigsData.configs[index].dst.x) || (lastConfigsData.configs[index].dst.y != newConfigsData.configs[index].dst.y) || (lastConfigsData.configs[index].dst.w != newConfigsData.configs[index].dst.w) || (lastConfigsData.configs[index].dst.h != newConfigsData.configs[index].dst.h) || (lastConfigsData.configs[index].src.x != newConfigsData.configs[index].src.x) || (lastConfigsData.configs[index].src.y != newConfigsData.configs[index].src.y) || (lastConfigsData.configs[index].src.w != newConfigsData.configs[index].src.w) || (lastConfigsData.configs[index].src.h != newConfigsData.configs[index].src.h)) return 1; else return 0; } /** * @return int */ int ExynosDisplay::deliverWinConfigData() { ATRACE_CALL(); String8 errString; int ret = NO_ERROR; struct timeval tv_s, tv_e; long timediff; ret = validateWinConfigData(); if (ret != NO_ERROR) { errString.appendFormat("Invalid WIN_CONFIG\n"); goto err; } for (size_t i = 0; i < mDpuData.configs.size(); i++) { DISPLAY_LOGD(eDebugWinConfig|eDebugSkipStaicLayer, "deliver config[%zu]", i); dumpConfig(mDpuData.configs[i]); } if (checkConfigChanged(mDpuData, mLastDpuData) == false) { DISPLAY_LOGD(eDebugWinConfig, "Winconfig : same"); #ifndef DISABLE_FENCE if (mLastRetireFence > 0) { mDpuData.retire_fence = hwcCheckFenceDebug(this, FENCE_TYPE_RETIRE, FENCE_IP_DPP, hwc_dup(mLastRetireFence, this, FENCE_TYPE_RETIRE, FENCE_IP_DPP)); } else mDpuData.retire_fence = -1; #endif ret = 0; } else { /* wait for 5 vsync */ int32_t waitTime = mVsyncPeriod / 1000000 * 5; gettimeofday(&tv_s, NULL); if (mUsePowerHints) { mRetireFenceWaitTime = systemTime(); } if (fence_valid(mLastRetireFence)) { ATRACE_NAME("waitLastRetireFence"); if (sync_wait(mLastRetireFence, waitTime) < 0) { DISPLAY_LOGE("%s:: mLastRetireFence(%d) is not released during (%d ms)", __func__, mLastRetireFence, waitTime); if (sync_wait(mLastRetireFence, 1000 - waitTime) < 0) { DISPLAY_LOGE("%s:: mLastRetireFence sync wait error (%d)", __func__, mLastRetireFence); } else { gettimeofday(&tv_e, NULL); tv_e.tv_usec += (tv_e.tv_sec - tv_s.tv_sec) * 1000000; timediff = tv_e.tv_usec - tv_s.tv_usec; DISPLAY_LOGE("%s:: winconfig is delayed over 5 vysnc (fence:%d)(time:%ld)", __func__, mLastRetireFence, timediff); } } } if (mUsePowerHints) { mRetireFenceAcquireTime = systemTime(); } for (size_t i = 0; i < mDpuData.configs.size(); i++) { setFenceInfo(mDpuData.configs[i].acq_fence, this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_DPP, HwcFenceDirection::TO); } if ((ret = mDisplayInterface->deliverWinConfigData()) < 0) { errString.appendFormat("interface's deliverWinConfigData() failed: %s ret(%d)\n", strerror(errno), ret); goto err; } else { mLastDpuData = mDpuData; } for (size_t i = 0; i < mDpuData.configs.size(); i++) { setFenceInfo(mDpuData.configs[i].rel_fence, this, FENCE_TYPE_SRC_RELEASE, FENCE_IP_DPP, HwcFenceDirection::FROM); } setFenceInfo(mDpuData.retire_fence, this, FENCE_TYPE_RETIRE, FENCE_IP_DPP, HwcFenceDirection::FROM); } return ret; err: printDebugInfos(errString); closeFences(); clearDisplay(); mDisplayInterface->setForcePanic(); return ret; } /** * @return int */ int ExynosDisplay::setReleaseFences() { int release_fd = -1; String8 errString; /* * Close release fence for client target buffer * SurfaceFlinger doesn't get release fence for client target buffer */ if ((mClientCompositionInfo.mHasCompositionLayer) && (mClientCompositionInfo.mWindowIndex >= 0) && (mClientCompositionInfo.mWindowIndex < (int32_t)mDpuData.configs.size())) { exynos_win_config_data &config = mDpuData.configs[mClientCompositionInfo.mWindowIndex]; for (int i = mClientCompositionInfo.mFirstIndex; i <= mClientCompositionInfo.mLastIndex; i++) { if (mLayers[i]->mExynosCompositionType != HWC2_COMPOSITION_CLIENT) { if(mLayers[i]->mOverlayPriority < ePriorityHigh) { errString.appendFormat("%d layer compositionType is not client(%d)\n", i, mLayers[i]->mExynosCompositionType); goto err; } else { continue; } } if (mType == HWC_DISPLAY_VIRTUAL) mLayers[i]->mReleaseFence = -1; else mLayers[i]->mReleaseFence = hwcCheckFenceDebug(this, FENCE_TYPE_SRC_RELEASE, FENCE_IP_DPP, hwc_dup(config.rel_fence, this, FENCE_TYPE_SRC_RELEASE, FENCE_IP_DPP)); } config.rel_fence = fence_close(config.rel_fence, this, FENCE_TYPE_SRC_RELEASE, FENCE_IP_FB); } // DPU doesn't close acq_fence, HWC should close it. for (auto &config : mDpuData.configs) { if (config.acq_fence != -1) fence_close(config.acq_fence, this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_DPP); config.acq_fence = -1; } for (auto &config : mDpuData.rcdConfigs) { if (config.acq_fence != -1) fence_close(config.acq_fence, this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_DPP); config.acq_fence = -1; } // DPU doesn't close rel_fence of readback buffer, HWC should close it if (mDpuData.readback_info.rel_fence >= 0) { mDpuData.readback_info.rel_fence = fence_close(mDpuData.readback_info.rel_fence, this, FENCE_TYPE_READBACK_RELEASE, FENCE_IP_FB); } for (size_t i = 0; i < mLayers.size(); i++) { if ((mLayers[i]->mExynosCompositionType == HWC2_COMPOSITION_CLIENT) || (mLayers[i]->mExynosCompositionType == HWC2_COMPOSITION_EXYNOS) || (mLayers[i]->mExynosCompositionType == HWC2_COMPOSITION_DISPLAY_DECORATION)) continue; if ((mLayers[i]->mWindowIndex < 0) || (mLayers[i]->mWindowIndex >= mDpuData.configs.size())) { errString.appendFormat("%s:: layer[%zu] has invalid window index(%d)\n", __func__, i, mLayers[i]->mWindowIndex); goto err; } exynos_win_config_data &config = mDpuData.configs[mLayers[i]->mWindowIndex]; if (mLayers[i]->mOtfMPP != NULL) { mLayers[i]->mOtfMPP->setHWStateFence(-1); } if (mLayers[i]->mM2mMPP != NULL) { if (mLayers[i]->mM2mMPP->mUseM2MSrcFence) mLayers[i]->mReleaseFence = mLayers[i]->mM2mMPP->getSrcReleaseFence(0); else { mLayers[i]->mReleaseFence = hwcCheckFenceDebug(this, FENCE_TYPE_SRC_RELEASE, FENCE_IP_DPP, hwc_dup(config.rel_fence, this, FENCE_TYPE_SRC_RELEASE, FENCE_IP_LAYER)); } mLayers[i]->mM2mMPP->resetSrcReleaseFence(); #ifdef DISABLE_FENCE mLayers[i]->mM2mMPP->setDstAcquireFence(-1); #else DISPLAY_LOGD(eDebugFence, "m2m : win_index(%d), releaseFencefd(%d)", mLayers[i]->mWindowIndex, config.rel_fence); if (config.rel_fence > 0) { release_fd = config.rel_fence; if (release_fd >= 0) { setFenceInfo(release_fd, this, FENCE_TYPE_DST_ACQUIRE, FENCE_IP_DPP, HwcFenceDirection::UPDATE, true); mLayers[i]->mM2mMPP->setDstAcquireFence(release_fd); } else { DISPLAY_LOGE("fail to dup, ret(%d, %s)", errno, strerror(errno)); mLayers[i]->mM2mMPP->setDstAcquireFence(-1); } } else { mLayers[i]->mM2mMPP->setDstAcquireFence(-1); } DISPLAY_LOGD(eDebugFence, "mM2mMPP is used, layer[%zu].releaseFencefd(%d)", i, mLayers[i]->mReleaseFence); #endif } else { #ifdef DISABLE_FENCE mLayers[i]->mReleaseFence = -1; #else DISPLAY_LOGD(eDebugFence, "other : win_index(%d), releaseFencefd(%d)", mLayers[i]->mWindowIndex, config.rel_fence); if (config.rel_fence > 0) { release_fd = hwcCheckFenceDebug(this, FENCE_TYPE_SRC_RELEASE, FENCE_IP_DPP, config.rel_fence); if (release_fd >= 0) mLayers[i]->mReleaseFence = release_fd; else { DISPLAY_LOGE("fail to dup, ret(%d, %s)", errno, strerror(errno)); mLayers[i]->mReleaseFence = -1; } } else { mLayers[i]->mReleaseFence = -1; } DISPLAY_LOGD(eDebugFence, "Direct overlay layer[%zu].releaseFencefd(%d)", i, mLayers[i]->mReleaseFence); #endif } } if (mExynosCompositionInfo.mHasCompositionLayer) { if (mExynosCompositionInfo.mM2mMPP == NULL) { errString.appendFormat("There is exynos composition, but m2mMPP is NULL\n"); goto err; } if (mUseDpu && ((mExynosCompositionInfo.mWindowIndex < 0) || (mExynosCompositionInfo.mWindowIndex >= (int32_t)mDpuData.configs.size()))) { errString.appendFormat("%s:: exynosComposition has invalid window index(%d)\n", __func__, mExynosCompositionInfo.mWindowIndex); goto err; } exynos_win_config_data &config = mDpuData.configs[mExynosCompositionInfo.mWindowIndex]; for (int i = mExynosCompositionInfo.mFirstIndex; i <= mExynosCompositionInfo.mLastIndex; i++) { /* break when only framebuffer target is assigned on ExynosCompositor */ if (i == -1) break; if (mLayers[i]->mExynosCompositionType != HWC2_COMPOSITION_EXYNOS) { errString.appendFormat("%d layer compositionType is not exynos(%d)\n", i, mLayers[i]->mExynosCompositionType); goto err; } if (mExynosCompositionInfo.mM2mMPP->mUseM2MSrcFence) mLayers[i]->mReleaseFence = mExynosCompositionInfo.mM2mMPP->getSrcReleaseFence(i-mExynosCompositionInfo.mFirstIndex); else { mLayers[i]->mReleaseFence = hwc_dup(config.rel_fence, this, FENCE_TYPE_SRC_RELEASE, FENCE_IP_LAYER); } DISPLAY_LOGD(eDebugFence, "exynos composition layer[%d].releaseFencefd(%d)", i, mLayers[i]->mReleaseFence); } mExynosCompositionInfo.mM2mMPP->resetSrcReleaseFence(); if(mUseDpu) { #ifdef DISABLE_FENCE mExynosCompositionInfo.mM2mMPP->setDstAcquireFence(-1); #else if (config.rel_fence > 0) { setFenceInfo(config.rel_fence, this, FENCE_TYPE_DST_ACQUIRE, FENCE_IP_DPP, HwcFenceDirection::UPDATE, true); mExynosCompositionInfo.mM2mMPP->setDstAcquireFence(config.rel_fence); } else { mExynosCompositionInfo.mM2mMPP->setDstAcquireFence(-1); } #endif } } return 0; err: printDebugInfos(errString); closeFences(); mDisplayInterface->setForcePanic(); return -EINVAL; } /** * If display uses outbuf and outbuf is invalid, this function return false. * Otherwise, this function return true. * If outbuf is invalid, display should handle fence of layers. */ bool ExynosDisplay::checkFrameValidation() { return true; } int32_t ExynosDisplay::acceptDisplayChanges() { int32_t type = 0; if (mDropFrameDuringResSwitch) { return HWC2_ERROR_NONE; } if (mRenderingState != RENDERING_STATE_VALIDATED) { DISPLAY_LOGE("%s:: display is not validated : %d", __func__, mRenderingState); return HWC2_ERROR_NOT_VALIDATED; } for (size_t i = 0; i < mLayers.size(); i++) { if (mLayers[i] != NULL) { HDEBUGLOGD(eDebugDefault, "%s, Layer %zu : %d, %d", __func__, i, mLayers[i]->mExynosCompositionType, mLayers[i]->mValidateCompositionType); type = getLayerCompositionTypeForValidationType(i); /* update compositionType * SF updates their state and doesn't call back into HWC HAL */ mLayers[i]->mCompositionType = type; mLayers[i]->mExynosCompositionType = mLayers[i]->mValidateCompositionType; } else { HWC_LOGE(this, "Layer %zu is NULL", i); } } mRenderingState = RENDERING_STATE_ACCEPTED_CHANGE; return HWC2_ERROR_NONE; } int32_t ExynosDisplay::createLayer(hwc2_layer_t* outLayer) { Mutex::Autolock lock(mDRMutex); if (mPlugState == false) { DISPLAY_LOGI("%s : skip createLayer. Display is already disconnected", __func__); return HWC2_ERROR_BAD_DISPLAY; } /* TODO : Implementation here */ ExynosLayer *layer = new ExynosLayer(this); /* TODO : Sort sequence should be added to somewhere */ mLayers.add((ExynosLayer*)layer); /* TODO : Set z-order to max, check outLayer address? */ layer->setLayerZOrder(1000); *outLayer = (hwc2_layer_t)layer; setGeometryChanged(GEOMETRY_DISPLAY_LAYER_ADDED); return HWC2_ERROR_NONE; } int32_t ExynosDisplay::getActiveConfig(hwc2_config_t* outConfig) { Mutex::Autolock lock(mDisplayMutex); return getActiveConfigInternal(outConfig); } int32_t ExynosDisplay::getActiveConfigInternal(hwc2_config_t* outConfig) { *outConfig = mActiveConfig; return HWC2_ERROR_NONE; } int32_t ExynosDisplay::getLayerCompositionTypeForValidationType(uint32_t layerIndex) { int32_t type = -1; if (layerIndex >= mLayers.size()) { DISPLAY_LOGE("invalid layer index (%d)", layerIndex); return type; } if ((mLayers[layerIndex]->mValidateCompositionType == HWC2_COMPOSITION_CLIENT) && (mClientCompositionInfo.mSkipFlag) && (mClientCompositionInfo.mFirstIndex <= (int32_t)layerIndex) && ((int32_t)layerIndex <= mClientCompositionInfo.mLastIndex)) { type = HWC2_COMPOSITION_DEVICE; } else if (mLayers[layerIndex]->mValidateCompositionType == HWC2_COMPOSITION_EXYNOS) { type = HWC2_COMPOSITION_DEVICE; } else if ((mLayers[layerIndex]->mCompositionType == HWC2_COMPOSITION_CURSOR) && (mLayers[layerIndex]->mValidateCompositionType == HWC2_COMPOSITION_DEVICE)) { if (mDisplayControl.cursorSupport == true) type = HWC2_COMPOSITION_CURSOR; else type = HWC2_COMPOSITION_DEVICE; } else if ((mLayers[layerIndex]->mCompositionType == HWC2_COMPOSITION_SOLID_COLOR) && (mLayers[layerIndex]->mValidateCompositionType == HWC2_COMPOSITION_DEVICE)) { type = HWC2_COMPOSITION_SOLID_COLOR; } else if ((mLayers[layerIndex]->mCompositionType == HWC2_COMPOSITION_REFRESH_RATE_INDICATOR) && (mLayers[layerIndex]->mValidateCompositionType == HWC2_COMPOSITION_DEVICE)) { type = HWC2_COMPOSITION_REFRESH_RATE_INDICATOR; } else { type = mLayers[layerIndex]->mValidateCompositionType; } return type; } int32_t ExynosDisplay::getChangedCompositionTypes( uint32_t* outNumElements, hwc2_layer_t* outLayers, int32_t* /*hwc2_composition_t*/ outTypes) { if (mDropFrameDuringResSwitch) { if ((outLayers == NULL) || (outTypes == NULL)) { *outNumElements = 0; } return HWC2_ERROR_NONE; } if (mRenderingState != RENDERING_STATE_VALIDATED) { DISPLAY_LOGE("%s:: display is not validated : %d", __func__, mRenderingState); return HWC2_ERROR_NOT_VALIDATED; } uint32_t count = 0; auto set_out_param = [](ExynosLayer *layer, int32_t type, uint32_t &count, uint32_t num, hwc2_layer_t *out_layers, int32_t *out_types) -> int32_t { if (type == layer->mCompositionType) { return 0; } if (out_layers == NULL || out_types == NULL) { count++; } else { if (count < num) { out_layers[count] = (hwc2_layer_t)layer; out_types[count] = type; count++; } else { return -1; } } return 0; }; int32_t ret = 0; for (size_t i = 0; i < mLayers.size(); i++) { DISPLAY_LOGD(eDebugHWC, "[%zu] layer: mCompositionType(%d), mValidateCompositionType(%d), mExynosCompositionType(%d), skipFlag(%d)", i, mLayers[i]->mCompositionType, mLayers[i]->mValidateCompositionType, mLayers[i]->mExynosCompositionType, mClientCompositionInfo.mSkipFlag); if ((ret = set_out_param(mLayers[i], getLayerCompositionTypeForValidationType(i), count, *outNumElements, outLayers, outTypes)) < 0) { break; } } if (ret == 0) { for (size_t i = 0; i < mIgnoreLayers.size(); i++) { DISPLAY_LOGD(eDebugHWC, "[%zu] ignore layer: mCompositionType(%d), mValidateCompositionType(%d)", i, mIgnoreLayers[i]->mCompositionType, mIgnoreLayers[i]->mValidateCompositionType); if ((ret = set_out_param(mIgnoreLayers[i], mIgnoreLayers[i]->mValidateCompositionType, count, *outNumElements, outLayers, outTypes)) < 0) { break; } } } if (ret < 0) { DISPLAY_LOGE("array size is not valid (%d, %d)", count, *outNumElements); String8 errString; errString.appendFormat("array size is not valid (%d, %d)", count, *outNumElements); printDebugInfos(errString); return ret; } if ((outLayers == NULL) || (outTypes == NULL)) *outNumElements = count; return HWC2_ERROR_NONE; } int32_t ExynosDisplay::getClientTargetSupport(uint32_t width, uint32_t height, int32_t /*android_pixel_format_t*/ format, int32_t /*android_dataspace_t*/ dataspace) { if (width != mXres) return HWC2_ERROR_UNSUPPORTED; if (height != mYres) return HWC2_ERROR_UNSUPPORTED; if (format != HAL_PIXEL_FORMAT_RGBA_8888) return HWC2_ERROR_UNSUPPORTED; if ((dataspace != HAL_DATASPACE_UNKNOWN) && (!mDisplayInterface->supportDataspace(dataspace))) return HWC2_ERROR_UNSUPPORTED; return HWC2_ERROR_NONE; } int32_t ExynosDisplay::getColorModes(uint32_t *outNumModes, int32_t * /*android_color_mode_t*/ outModes) { return mDisplayInterface->getColorModes(outNumModes, outModes); } int32_t ExynosDisplay::getDisplayAttribute( hwc2_config_t config, int32_t /*hwc2_attribute_t*/ attribute, int32_t* outValue) { switch (attribute) { case HWC2_ATTRIBUTE_VSYNC_PERIOD: *outValue = mDisplayConfigs[config].vsyncPeriod; break; case HWC2_ATTRIBUTE_WIDTH: *outValue = mDisplayConfigs[config].width; break; case HWC2_ATTRIBUTE_HEIGHT: *outValue = mDisplayConfigs[config].height; break; case HWC2_ATTRIBUTE_DPI_X: *outValue = mDisplayConfigs[config].Xdpi; break; case HWC2_ATTRIBUTE_DPI_Y: *outValue = mDisplayConfigs[config].Ydpi; break; case HWC2_ATTRIBUTE_CONFIG_GROUP: *outValue = mDisplayConfigs[config].groupId; break; default: ALOGE("unknown display attribute %u", attribute); return HWC2_ERROR_BAD_CONFIG; } return HWC2_ERROR_NONE; } int32_t ExynosDisplay::getDisplayConfigs( uint32_t* outNumConfigs, hwc2_config_t* outConfigs) { return mDisplayInterface->getDisplayConfigs(outNumConfigs, outConfigs); } int32_t ExynosDisplay::getDisplayName(uint32_t* outSize, char* outName) { if (outName == NULL) { *outSize = mDisplayName.size(); return HWC2_ERROR_NONE; } uint32_t strSize = mDisplayName.size(); if (*outSize < strSize) { DISPLAY_LOGE("Invalide outSize(%d), mDisplayName.size(%d)", *outSize, strSize); strSize = *outSize; } std::strncpy(outName, mDisplayName.c_str(), strSize); *outSize = strSize; return HWC2_ERROR_NONE; } int32_t ExynosDisplay::getDisplayRequests( int32_t* /*hwc2_display_request_t*/ outDisplayRequests, uint32_t* outNumElements, hwc2_layer_t* outLayers, int32_t* /*hwc2_layer_request_t*/ outLayerRequests) { /* * This function doesn't check mRenderingState * This can be called in the below rendering state * RENDERING_STATE_PRESENTED: when it is called by canSkipValidate() * RENDERING_STATE_ACCEPTED_CHANGE: when it is called by SF * RENDERING_STATE_VALIDATED: when it is called by validateDisplay() */ String8 errString; *outDisplayRequests = 0; if (mDropFrameDuringResSwitch) { if ((outLayers == NULL) || (outLayerRequests == NULL)) { *outNumElements = 0; } return HWC2_ERROR_NONE; } uint32_t requestNum = 0; if (mClientCompositionInfo.mHasCompositionLayer == true) { if ((mClientCompositionInfo.mFirstIndex < 0) || (mClientCompositionInfo.mFirstIndex >= (int)mLayers.size()) || (mClientCompositionInfo.mLastIndex < 0) || (mClientCompositionInfo.mLastIndex >= (int)mLayers.size())) { errString.appendFormat("%s:: mClientCompositionInfo.mHasCompositionLayer is true " "but index is not valid (firstIndex: %d, lastIndex: %d)\n", __func__, mClientCompositionInfo.mFirstIndex, mClientCompositionInfo.mLastIndex); goto err; } for (int32_t i = mClientCompositionInfo.mFirstIndex; i < mClientCompositionInfo.mLastIndex; i++) { ExynosLayer *layer = mLayers[i]; if (layer->needClearClientTarget()) { if ((outLayers != NULL) && (outLayerRequests != NULL)) { if (requestNum >= *outNumElements) return -1; outLayers[requestNum] = (hwc2_layer_t)layer; outLayerRequests[requestNum] = HWC2_LAYER_REQUEST_CLEAR_CLIENT_TARGET; } requestNum++; } } } if ((outLayers == NULL) || (outLayerRequests == NULL)) *outNumElements = requestNum; return HWC2_ERROR_NONE; err: printDebugInfos(errString); *outNumElements = 0; mDisplayInterface->setForcePanic(); return -EINVAL; } int32_t ExynosDisplay::getDisplayType( int32_t* /*hwc2_display_type_t*/ outType) { switch (mType) { case HWC_DISPLAY_PRIMARY: case HWC_DISPLAY_EXTERNAL: *outType = HWC2_DISPLAY_TYPE_PHYSICAL; return HWC2_ERROR_NONE; case HWC_DISPLAY_VIRTUAL: *outType = HWC2_DISPLAY_TYPE_VIRTUAL; return HWC2_ERROR_NONE; default: DISPLAY_LOGE("Invalid display type(%d)", mType); *outType = HWC2_DISPLAY_TYPE_INVALID; return HWC2_ERROR_NONE; } } int32_t ExynosDisplay::getDozeSupport( int32_t* outSupport) { if (mDisplayInterface->isDozeModeAvailable()) { *outSupport = 1; } else { *outSupport = 0; } return 0; } int32_t ExynosDisplay::getReleaseFences( uint32_t* outNumElements, hwc2_layer_t* outLayers, int32_t* outFences) { if (outNumElements == NULL) { return HWC2_ERROR_BAD_PARAMETER; } Mutex::Autolock lock(mDisplayMutex); uint32_t deviceLayerNum = 0; if (outLayers != NULL && outFences != NULL) { // second pass call for (size_t i = 0; i < mLayers.size(); i++) { if (mLayers[i]->mReleaseFence >= 0) { if (deviceLayerNum < *outNumElements) { // transfer fence ownership to the caller setFenceName(mLayers[i]->mReleaseFence, FENCE_LAYER_RELEASE_DPP); outLayers[deviceLayerNum] = (hwc2_layer_t)mLayers[i]; outFences[deviceLayerNum] = mLayers[i]->mReleaseFence; mLayers[i]->mReleaseFence = -1; DISPLAY_LOGD(eDebugHWC, "[%zu] layer deviceLayerNum(%d), release fence: %d", i, deviceLayerNum, outFences[deviceLayerNum]); } else { // *outNumElements is not from the first pass call. DISPLAY_LOGE("%s: outNumElements %d too small", __func__, *outNumElements); return HWC2_ERROR_BAD_PARAMETER; } deviceLayerNum++; } } } else { // first pass call for (size_t i = 0; i < mLayers.size(); i++) { if (mLayers[i]->mReleaseFence >= 0) { deviceLayerNum++; } } } *outNumElements = deviceLayerNum; return 0; } int32_t ExynosDisplay::canSkipValidate() { if (exynosHWCControl.skipResourceAssign == 0) return SKIP_ERR_CONFIG_DISABLED; /* This is first frame. validateDisplay can't be skipped */ if (mRenderingState == RENDERING_STATE_NONE) return SKIP_ERR_FIRST_FRAME; if (mDevice->mGeometryChanged != 0) { /* validateDisplay() should be called */ return SKIP_ERR_GEOMETRY_CHAGNED; } else { for (uint32_t i = 0; i < mLayers.size(); i++) { if (getLayerCompositionTypeForValidationType(i) == HWC2_COMPOSITION_CLIENT) { return SKIP_ERR_HAS_CLIENT_COMP; } } if ((mClientCompositionInfo.mSkipStaticInitFlag == true) && (mClientCompositionInfo.mSkipFlag == true)) { if (skipStaticLayerChanged(mClientCompositionInfo) == true) return SKIP_ERR_SKIP_STATIC_CHANGED; } if (mClientCompositionInfo.mHasCompositionLayer && mClientCompositionInfo.mTargetBuffer == NULL) { return SKIP_ERR_INVALID_CLIENT_TARGET_BUFFER; } /* * If there is hwc2_layer_request_t * validateDisplay() can't be skipped */ int32_t displayRequests = 0; uint32_t outNumRequests = 0; if ((getDisplayRequests(&displayRequests, &outNumRequests, NULL, NULL) != NO_ERROR) || (outNumRequests != 0)) return SKIP_ERR_HAS_REQUEST; } return NO_ERROR; } bool ExynosDisplay::isFullScreenComposition() { hwc_rect_t dispRect = { INT_MAX, INT_MAX, 0, 0 }; for (auto layer : mLayers) { auto &r = layer->mDisplayFrame; if (r.top < dispRect.top) dispRect.top = r.top; if (r.left < dispRect.left) dispRect.left = r.left; if (r.bottom > dispRect.bottom) dispRect.bottom = r.bottom; if (r.right > dispRect.right) dispRect.right = r.right; } if ((dispRect.right != mXres) || (dispRect.bottom != mYres)) { ALOGD("invalid displayFrame disp=[%d %d %d %d] expected=%dx%d", dispRect.left, dispRect.top, dispRect.right, dispRect.bottom, mXres, mYres); return false; } return true; } void dumpBuffer(const String8& prefix, const exynos_image& image, std::ofstream& configFile) { ATRACE_NAME(prefix.c_str()); if (image.bufferHandle == nullptr) { ALOGE("%s: Buffer handle for %s is NULL", __func__, prefix.c_str()); return; } ALOGI("%s: dumping buffer for %s", __func__, prefix.c_str()); bool failFenceSync = false; String8 infoDump; if (image.acquireFenceFd > 0 && sync_wait(image.acquireFenceFd, 1000) < 0) { infoDump.appendFormat("Failed to sync acquire fence\n"); ALOGE("%s: Failed to wait acquire fence %d, errno=(%d, %s)", __func__, image.acquireFenceFd, errno, strerror(errno)); failFenceSync = true; } if (image.releaseFenceFd > 0 && sync_wait(image.releaseFenceFd, 1000) < 0) { infoDump.appendFormat("Failed to sync release fence\n"); ALOGE("%s: Failed to wait release fence %d, errno=(%d, %s)", __func__, image.releaseFenceFd, errno, strerror(errno)); failFenceSync = true; } VendorGraphicBufferMeta gmeta(image.bufferHandle); String8 infoPath = String8::format("%s/%s-info.txt", kBufferDumpPath, prefix.c_str()); std::ofstream infoFile(infoPath.c_str()); if (!infoFile) { ALOGE("%s: failed to open file %s", __func__, infoPath.c_str()); return; } // TODO(b/261232489): Fix fence sync errors // We currently ignore the fence errors and just dump the buffers // dump buffer info dumpExynosImage(infoDump, image); infoDump.appendFormat("\nfd[%d, %d, %d] size[%d, %d, %d]\n", gmeta.fd, gmeta.fd1, gmeta.fd2, gmeta.size, gmeta.size1, gmeta.size2); infoDump.appendFormat(" offset[%d, %d, %d] format:%d framework_format:%d\n", gmeta.offset, gmeta.offset1, gmeta.offset2, gmeta.format, gmeta.frameworkFormat); infoDump.appendFormat(" width:%d height:%d stride:%d vstride:%d\n", gmeta.width, gmeta.height, gmeta.stride, gmeta.vstride); infoDump.appendFormat(" producer: 0x%" PRIx64 " consumer: 0x%" PRIx64 " flags: 0x%" PRIx32 "\n", gmeta.producer_usage, gmeta.consumer_usage, gmeta.flags); infoFile << infoDump << std::endl; String8 bufferPath = String8::format("%s/%s-%s.raw", kBufferDumpPath, prefix.c_str(), getFormatStr(image.format, image.compressionInfo.type).c_str()); std::ofstream bufferFile(bufferPath.c_str(), std::ios::binary); if (!bufferFile) { ALOGE("%s: failed to open file %s", __func__, bufferPath.c_str()); return; } // dump info that can be loaded by hwc-tester configFile << "buffers {\n"; configFile << " key: \"" << prefix << "\"\n"; configFile << " format: " << getFormatStr(image.format, image.compressionInfo.type) << "\n"; configFile << " width: " << gmeta.width << "\n"; configFile << " height: " << gmeta.height << "\n"; auto usage = gmeta.producer_usage | gmeta.consumer_usage; configFile << " usage: 0x" << std::hex << usage << std::dec << "\n"; configFile << " filepath: \"" << bufferPath << "\"\n"; configFile << "}\n" << std::endl; int bufferNumber = getBufferNumOfFormat(image.format, image.compressionInfo.type); for (int i = 0; i < bufferNumber; ++i) { if (gmeta.fds[i] <= 0) { ALOGE("%s: gmeta.fds[%d]=%d is invalid", __func__, i, gmeta.fds[i]); continue; } if (gmeta.sizes[i] <= 0) { ALOGE("%s: gmeta.sizes[%d]=%d is invalid", __func__, i, gmeta.sizes[i]); continue; } auto addr = mmap(0, gmeta.sizes[i], PROT_READ | PROT_WRITE, MAP_SHARED, gmeta.fds[i], 0); if (addr != MAP_FAILED && addr != NULL) { bufferFile.write(static_cast(addr), gmeta.sizes[i]); munmap(addr, gmeta.sizes[i]); } else { ALOGE("%s: failed to mmap fds[%d]:%d for %s", __func__, i, gmeta.fds[i]); } } } void ExynosDisplay::dumpAllBuffers() { ATRACE_CALL(); // dump layers info String8 infoPath = String8::format("%s/%03d-display-info.txt", kBufferDumpPath, mBufferDumpNum); std::ofstream infoFile(infoPath.c_str()); if (!infoFile) { DISPLAY_LOGE("%s: failed to open file %s", __func__, infoPath.c_str()); ++mBufferDumpNum; return; } String8 displayDump; dumpLocked(displayDump); infoFile << displayDump << std::endl; // dump buffer contents & infos std::vector allLayerKeys; String8 testerConfigPath = String8::format("%s/%03d-hwc-tester-config.textproto", kBufferDumpPath, mBufferDumpNum); std::ofstream configFile(testerConfigPath.c_str()); configFile << std::string(15, '#') << " You can load this config file using hwc-tester to reproduce this frame " << std::string(15, '#') << std::endl; { std::scoped_lock lock(mDRMutex); for (int i = 0; i < mLayers.size(); ++i) { String8 prefix = String8::format("%03d-%d-src", mBufferDumpNum, i); dumpBuffer(prefix, mLayers[i]->mSrcImg, configFile); if (mLayers[i]->mM2mMPP != nullptr) { String8 midPrefix = String8::format("%03d-%d-mid", mBufferDumpNum, i); exynos_image image = mLayers[i]->mMidImg; mLayers[i]->mM2mMPP->getDstImageInfo(&image); dumpBuffer(midPrefix, image, configFile); } configFile << "layers {\n"; configFile << " key: \"" << prefix << "\"\n"; configFile << " composition: " << AidlComposer3::toString(static_cast( mLayers[i]->mRequestedCompositionType)) << "\n"; configFile << " source_crop: {\n"; configFile << " left: " << mLayers[i]->mPreprocessedInfo.sourceCrop.left << "\n"; configFile << " top: " << mLayers[i]->mPreprocessedInfo.sourceCrop.top << "\n"; configFile << " right: " << mLayers[i]->mPreprocessedInfo.sourceCrop.right << "\n"; configFile << " bottom: " << mLayers[i]->mPreprocessedInfo.sourceCrop.bottom << "\n"; configFile << " }\n"; configFile << " display_frame: {\n"; configFile << " left: " << mLayers[i]->mPreprocessedInfo.displayFrame.left << "\n"; configFile << " top: " << mLayers[i]->mPreprocessedInfo.displayFrame.top << "\n"; configFile << " right: " << mLayers[i]->mPreprocessedInfo.displayFrame.right << "\n"; configFile << " bottom: " << mLayers[i]->mPreprocessedInfo.displayFrame.bottom << "\n"; configFile << " }\n"; configFile << " dataspace: " << AidlCommon::toString( static_cast(mLayers[i]->mDataSpace)) << "\n"; configFile << " blend: " << AidlCommon::toString( static_cast(mLayers[i]->mBlending)) << "\n"; configFile << " transform: " << AidlCommon::toString( static_cast(mLayers[i]->mTransform)) << "\n"; configFile << " plane_alpha: " << mLayers[i]->mPlaneAlpha << "\n"; configFile << " z_order: " << mLayers[i]->mZOrder << "\n"; if (mLayers[i]->mRequestedCompositionType == HWC2_COMPOSITION_SOLID_COLOR) { configFile << " color: {\n"; configFile << " r: " << mLayers[i]->mColor.r << "\n"; configFile << " g: " << mLayers[i]->mColor.g << "\n"; configFile << " b: " << mLayers[i]->mColor.b << "\n"; configFile << " a: " << mLayers[i]->mColor.a << "\n"; configFile << " }\n"; } else if (mLayers[i]->mSrcImg.bufferHandle != nullptr) { configFile << " buffer_key: \"" << prefix << "\"\n"; } configFile << "}\n" << std::endl; allLayerKeys.push_back(prefix); } } if (mClientCompositionInfo.mHasCompositionLayer) { String8 prefix = String8::format("%03d-client-target", mBufferDumpNum); exynos_image src, dst; setCompositionTargetExynosImage(COMPOSITION_CLIENT, &src, &dst); dumpBuffer(prefix, src, configFile); } configFile << "timelines {\n"; configFile << " display_id: " << mDisplayId << "\n"; configFile << " width: " << mXres << "\n"; configFile << " height: " << mYres << "\n"; configFile << " color_mode: " << AidlComposer3::toString(static_cast(mColorMode)) << "\n"; configFile << std::endl; for (auto& layerKey : allLayerKeys) { configFile << " layers: {\n"; configFile << " layer_key: \"" << layerKey << "\"\n"; configFile << " }\n"; } configFile << "}" << std::endl; ++mBufferDumpNum; } int32_t ExynosDisplay::presentDisplay(int32_t* outRetireFence) { DISPLAY_ATRACE_CALL(); gettimeofday(&updateTimeInfo.lastPresentTime, NULL); const bool mixedComposition = isMixedComposition(); // store this once here for the whole frame so it's consistent mUsePowerHints = usePowerHintSession(); if (mUsePowerHints) { // adds + removes the tid for adpf tracking mPowerHalHint.trackThisThread(); mPresentStartTime = systemTime(SYSTEM_TIME_MONOTONIC); if (!mValidateStartTime.has_value()) { mValidationDuration = std::nullopt; // load target time here if validation was skipped mExpectedPresentTime = getExpectedPresentTime(mPresentStartTime); auto target = min(mExpectedPresentTime - mPresentStartTime, static_cast(mVsyncPeriod)); mPowerHalHint.signalTargetWorkDuration(target); // if we did not validate (have not sent hint yet) and have data for this case std::optional predictedDuration = getPredictedDuration(false); if (predictedDuration.has_value()) { mPowerHalHint.signalActualWorkDuration(*predictedDuration); } } mRetireFenceWaitTime = std::nullopt; mValidateStartTime = std::nullopt; } int ret = HWC2_ERROR_NONE; String8 errString; thread_local bool setTaskProfileDone = false; if (setTaskProfileDone == false) { if (!SetTaskProfiles(gettid(), {"SFMainPolicy"})) { ALOGW("Failed to add `%d` into SFMainPolicy", gettid()); } setTaskProfileDone = true; } Mutex::Autolock lock(mDisplayMutex); if (!mHpdStatus) { ALOGD("presentDisplay: drop frame: mHpdStatus == false"); } mDropFrameDuringResSwitch = (mGeometryChanged & GEOMETRY_DISPLAY_RESOLUTION_CHANGED) && !isFullScreenComposition(); if (mDropFrameDuringResSwitch) { ALOGD("presentDisplay: drop invalid frame during resolution switch"); } if (!mHpdStatus || mDropFrameDuringResSwitch || mPauseDisplay || mDevice->isInTUI()) { closeFencesForSkipFrame(RENDERING_STATE_PRESENTED); *outRetireFence = -1; mRenderingState = RENDERING_STATE_PRESENTED; applyExpectedPresentTime(); return ret; } /* * buffer handle, dataspace were set by setClientTarget() after validateDisplay * ExynosImage should be set again according to changed handle and dataspace */ exynos_image src_img; exynos_image dst_img; setCompositionTargetExynosImage(COMPOSITION_CLIENT, &src_img, &dst_img); mClientCompositionInfo.setExynosImage(src_img, dst_img); mClientCompositionInfo.setExynosMidImage(dst_img); funcReturnCallback presentRetCallback([&]() { if (ret != HWC2_ERROR_NOT_VALIDATED) presentPostProcessing(); }); if (mSkipFrame) { ALOGI("[%d] presentDisplay is skipped by mSkipFrame", mDisplayId); closeFencesForSkipFrame(RENDERING_STATE_PRESENTED); setGeometryChanged(GEOMETRY_DISPLAY_FORCE_VALIDATE); *outRetireFence = -1; for (size_t i=0; i < mLayers.size(); i++) { mLayers[i]->mReleaseFence = -1; } if (mRenderingState == RENDERING_STATE_NONE) { ALOGD("\tThis is the first frame after power on"); ret = HWC2_ERROR_NONE; } else { ALOGD("\tThis is the second frame after power on"); ret = HWC2_ERROR_NOT_VALIDATED; } mRenderingState = RENDERING_STATE_PRESENTED; mDevice->onRefresh(mDisplayId); return ret; } tryUpdateBtsFromOperationRate(true); if (mRenderingState != RENDERING_STATE_ACCEPTED_CHANGE) { /* * presentDisplay() can be called before validateDisplay() * when HWC2_CAPABILITY_SKIP_VALIDATE is supported */ #ifdef HWC_NO_SUPPORT_SKIP_VALIDATE DISPLAY_LOGE("%s:: Skip validate is not supported. Invalid rendering state : %d", __func__, mRenderingState); goto err; #endif if ((mRenderingState != RENDERING_STATE_NONE) && (mRenderingState != RENDERING_STATE_PRESENTED)) { DISPLAY_LOGE("%s:: invalid rendering state : %d", __func__, mRenderingState); goto err; } if (mDevice->canSkipValidate() == false) goto not_validated; else { for (size_t i=0; i < mLayers.size(); i++) { // Layer's acquire fence from SF mLayers[i]->setSrcAcquireFence(); } DISPLAY_LOGD(eDebugSkipValidate, "validate is skipped"); } if (updateColorConversionInfo() != NO_ERROR) { ALOGE("%s:: updateColorConversionInfo() fail, ret(%d)", __func__, ret); } if (mDisplayControl.earlyStartMPP == true) { /* * HWC should update performanceInfo when validate is skipped * HWC excludes the layer from performance calculation * if there is no buffer update. (using ExynosMPP::canSkipProcessing()) * Therefore performanceInfo should be calculated again if the buffer is updated. */ if ((ret = mDevice->mResourceManager->deliverPerformanceInfo()) != NO_ERROR) { DISPLAY_LOGE("deliverPerformanceInfo() error (%d) in validateSkip case", ret); } startPostProcessing(); } } mRetireFenceAcquireTime = std::nullopt; mDpuData.reset(); if (mConfigRequestState == hwc_request_state_t::SET_CONFIG_STATE_PENDING) { if ((ret = doDisplayConfigPostProcess(mDevice)) != NO_ERROR) { DISPLAY_LOGE("doDisplayConfigPostProcess error (%d)", ret); } } if (updatePresentColorConversionInfo() != NO_ERROR) { ALOGE("%s:: updatePresentColorConversionInfo() fail, ret(%d)", __func__, ret); } if ((mLayers.size() == 0) && (mType != HWC_DISPLAY_VIRTUAL)) { ALOGI("%s:: layer size is 0", __func__); clearDisplay(); *outRetireFence = -1; mLastRetireFence = fence_close(mLastRetireFence, this, FENCE_TYPE_RETIRE, FENCE_IP_DPP); mRenderingState = RENDERING_STATE_PRESENTED; ret = 0; return ret; } if (!checkFrameValidation()) { ALOGW("%s: checkFrameValidation fail", __func__); clearDisplay(); *outRetireFence = -1; mLastRetireFence = fence_close(mLastRetireFence, this, FENCE_TYPE_RETIRE, FENCE_IP_DPP); mRenderingState = RENDERING_STATE_PRESENTED; return ret; } if ((mDisplayControl.earlyStartMPP == false) && ((ret = doExynosComposition()) != NO_ERROR)) { errString.appendFormat("exynosComposition fail (%d)\n", ret); goto err; } // loop for all layer for (size_t i=0; i < mLayers.size(); i++) { /* mAcquireFence is updated, Update image info */ struct exynos_image srcImg, dstImg, midImg; mLayers[i]->setSrcExynosImage(&srcImg); mLayers[i]->setDstExynosImage(&dstImg); mLayers[i]->setExynosImage(srcImg, dstImg); if (mLayers[i]->mExynosCompositionType == HWC2_COMPOSITION_CLIENT) { mLayers[i]->mReleaseFence = -1; mLayers[i]->mAcquireFence = fence_close(mLayers[i]->mAcquireFence, this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_LAYER); } else if (mLayers[i]->mExynosCompositionType == HWC2_COMPOSITION_EXYNOS) { continue; } else { if (mLayers[i]->mOtfMPP != NULL) { mLayers[i]->mOtfMPP->requestHWStateChange(MPP_HW_STATE_RUNNING); } if ((mDisplayControl.earlyStartMPP == false) && (mLayers[i]->mExynosCompositionType == HWC2_COMPOSITION_DEVICE) && (mLayers[i]->mM2mMPP != NULL)) { ExynosMPP* m2mMpp = mLayers[i]->mM2mMPP; midImg = mLayers[i]->mMidImg; m2mMpp->requestHWStateChange(MPP_HW_STATE_RUNNING); if ((ret = m2mMpp->doPostProcessing(midImg)) != NO_ERROR) { HWC_LOGE(this, "%s:: doPostProcessing() failed, layer(%zu), ret(%d)", __func__, i, ret); errString.appendFormat("%s:: doPostProcessing() failed, layer(%zu), ret(%d)\n", __func__, i, ret); goto err; } else { /* This should be closed by lib for each resource */ mLayers[i]->mAcquireFence = -1; } } } } if ((ret = setWinConfigData()) != NO_ERROR) { errString.appendFormat("setWinConfigData fail (%d)\n", ret); goto err; } if ((ret = handleStaticLayers(mClientCompositionInfo)) != NO_ERROR) { mClientCompositionInfo.mSkipStaticInitFlag = false; errString.appendFormat("handleStaticLayers error\n"); goto err; } if (shouldSignalNonIdle()) { mPowerHalHint.signalNonIdle(); } if (!checkUpdateRRIndicatorOnly()) { if (mRefreshRateIndicatorHandler) { mRefreshRateIndicatorHandler->checkOnPresentDisplay(); } } handleWindowUpdate(); setDisplayWinConfigData(); if ((ret = deliverWinConfigData()) != NO_ERROR) { HWC_LOGE(this, "%s:: fail to deliver win_config (%d)", __func__, ret); if (mDpuData.retire_fence > 0) fence_close(mDpuData.retire_fence, this, FENCE_TYPE_RETIRE, FENCE_IP_DPP); mDpuData.retire_fence = -1; } setReleaseFences(); if (mBufferDumpNum < mBufferDumpCount) { dumpAllBuffers(); } if (mDpuData.retire_fence != -1) { #ifdef DISABLE_FENCE if (mDpuData.retire_fence >= 0) fence_close(mDpuData.retire_fence, this, FENCE_TYPE_RETIRE, FENCE_IP_DPP); *outRetireFence = -1; #else *outRetireFence = hwcCheckFenceDebug(this, FENCE_TYPE_RETIRE, FENCE_IP_DPP, mDpuData.retire_fence); #endif setFenceInfo(mDpuData.retire_fence, this, FENCE_TYPE_RETIRE, FENCE_IP_LAYER, HwcFenceDirection::TO); } else *outRetireFence = -1; /* Update last retire fence */ mLastRetireFence = fence_close(mLastRetireFence, this, FENCE_TYPE_RETIRE, FENCE_IP_DPP); mLastRetireFence = hwc_dup((*outRetireFence), this, FENCE_TYPE_RETIRE, FENCE_IP_DPP, true); setFenceName(mLastRetireFence, FENCE_RETIRE); increaseMPPDstBufIndex(); /* Check all of acquireFence are closed */ for (size_t i=0; i < mLayers.size(); i++) { if (mLayers[i]->mAcquireFence != -1) { DISPLAY_LOGE("layer[%zu] fence(%d) type(%d, %d, %d) is not closed", i, mLayers[i]->mAcquireFence, mLayers[i]->mCompositionType, mLayers[i]->mExynosCompositionType, mLayers[i]->mValidateCompositionType); if (mLayers[i]->mM2mMPP != NULL) DISPLAY_LOGE("\t%s is assigned", mLayers[i]->mM2mMPP->mName.c_str()); if (mLayers[i]->mAcquireFence > 0) fence_close(mLayers[i]->mAcquireFence, this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_LAYER); mLayers[i]->mAcquireFence = -1; } } if (mExynosCompositionInfo.mAcquireFence >= 0) { DISPLAY_LOGE("mExynosCompositionInfo mAcquireFence(%d) is not initialized", mExynosCompositionInfo.mAcquireFence); fence_close(mExynosCompositionInfo.mAcquireFence, this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_G2D); mExynosCompositionInfo.mAcquireFence = -1; } if (mClientCompositionInfo.mAcquireFence >= 0) { DISPLAY_LOGE("mClientCompositionInfo mAcquireFence(%d) is not initialized", mClientCompositionInfo.mAcquireFence); fence_close(mClientCompositionInfo.mAcquireFence, this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_FB); mClientCompositionInfo.mAcquireFence = -1; } /* All of release fences are tranferred */ for (size_t i=0; i < mLayers.size(); i++) { setFenceInfo(mLayers[i]->mReleaseFence, this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_LAYER, HwcFenceDirection::TO); } doPostProcessing(); if (!mDevice->validateFences(this)) { ALOGE("%s:: validate fence failed.", __func__); } mDpuData.reset(); mRenderingState = RENDERING_STATE_PRESENTED; if (mConfigRequestState == hwc_request_state_t::SET_CONFIG_STATE_REQUESTED) { /* Do not update mVsyncPeriod */ updateInternalDisplayConfigVariables(mDesiredConfig, false); } if (mUsePowerHints) { // update the "last present" now that we know for sure when this frame is due mLastExpectedPresentTime = mExpectedPresentTime; // we add an offset here to keep the flinger and HWC error terms roughly the same static const constexpr std::chrono::nanoseconds kFlingerOffset = 300us; nsecs_t now = systemTime() + kFlingerOffset.count(); updateAverages(now); nsecs_t duration = now - mPresentStartTime; if (mRetireFenceWaitTime.has_value() && mRetireFenceAcquireTime.has_value()) { duration = now - *mRetireFenceAcquireTime + *mRetireFenceWaitTime - mPresentStartTime; } mPowerHalHint.signalActualWorkDuration(duration + mValidationDuration.value_or(0)); } mPriorFrameMixedComposition = mixedComposition; tryUpdateBtsFromOperationRate(false); return ret; err: printDebugInfos(errString); closeFences(); *outRetireFence = -1; mLastRetireFence = -1; mRenderingState = RENDERING_STATE_PRESENTED; setGeometryChanged(GEOMETRY_ERROR_CASE); mLastDpuData.reset(); mClientCompositionInfo.mSkipStaticInitFlag = false; mExynosCompositionInfo.mSkipStaticInitFlag = false; mDpuData.reset(); if (!mDevice->validateFences(this)) { ALOGE("%s:: validate fence failed.", __func__); } mDisplayInterface->setForcePanic(); ret = -EINVAL; return ret; not_validated: DISPLAY_LOGD(eDebugSkipValidate, "display need validate"); mRenderingState = RENDERING_STATE_NONE; ret = HWC2_ERROR_NOT_VALIDATED; return ret; } int32_t ExynosDisplay::presentPostProcessing() { setReadbackBufferInternal(NULL, -1, false); if (mDpuData.enable_readback) mDevice->signalReadbackDone(); mDpuData.enable_readback = false; for (auto it : mIgnoreLayers) { /* * Directly close without counting down * because it was not counted by validate */ if (it->mAcquireFence > 0) { close(it->mAcquireFence); } it->mAcquireFence = -1; } return NO_ERROR; } int32_t ExynosDisplay::setActiveConfig(hwc2_config_t config) { Mutex::Autolock lock(mDisplayMutex); DISPLAY_LOGD(eDebugDisplayConfig, "%s:: config(%d)", __func__, config); return setActiveConfigInternal(config, false); } int32_t ExynosDisplay::setActiveConfigInternal(hwc2_config_t config, bool force) { if (isBadConfig(config)) return HWC2_ERROR_BAD_CONFIG; if (!force && needNotChangeConfig(config)) { ALOGI("skip same config %d (force %d)", config, force); return HWC2_ERROR_NONE; } DISPLAY_LOGD(eDebugDisplayConfig, "(current %d) : %dx%d, %dms, %d Xdpi, %d Ydpi", mActiveConfig, mXres, mYres, mVsyncPeriod, mXdpi, mYdpi); DISPLAY_LOGD(eDebugDisplayConfig, "(requested %d) : %dx%d, %dms, %d Xdpi, %d Ydpi", config, mDisplayConfigs[config].width, mDisplayConfigs[config].height, mDisplayConfigs[config].vsyncPeriod, mDisplayConfigs[config].Xdpi, mDisplayConfigs[config].Ydpi); if (mDisplayInterface->setActiveConfig(config) < 0) { ALOGE("%s bad config request", __func__); return HWC2_ERROR_BAD_CONFIG; } if ((mXres != mDisplayConfigs[config].width) || (mYres != mDisplayConfigs[config].height)) { mRenderingState = RENDERING_STATE_NONE; setGeometryChanged(GEOMETRY_DISPLAY_RESOLUTION_CHANGED); } updateInternalDisplayConfigVariables(config); return HWC2_ERROR_NONE; } int32_t ExynosDisplay::setClientTarget( buffer_handle_t target, int32_t acquireFence, int32_t /*android_dataspace_t*/ dataspace) { buffer_handle_t handle = NULL; if (target != NULL) handle = target; #ifdef DISABLE_FENCE if (acquireFence >= 0) fence_close(acquireFence, this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_FB); acquireFence = -1; #endif acquireFence = hwcCheckFenceDebug(this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_FB, acquireFence); if (handle == NULL) { DISPLAY_LOGD(eDebugOverlaySupported, "ClientTarget is NULL, skipStaic (%d)", mClientCompositionInfo.mSkipFlag); if (mClientCompositionInfo.mSkipFlag == false) { DISPLAY_LOGE("ClientTarget is NULL"); DISPLAY_LOGE("\t%s:: mRenderingState(%d)",__func__, mRenderingState); } } else { VendorGraphicBufferMeta gmeta(handle); DISPLAY_LOGD(eDebugOverlaySupported, "ClientTarget handle: %p [fd: %d, %d, %d]", handle, gmeta.fd, gmeta.fd1, gmeta.fd2); if ((mClientCompositionInfo.mSkipFlag == true) && ((mClientCompositionInfo.mLastWinConfigData.fd_idma[0] != gmeta.fd) || (mClientCompositionInfo.mLastWinConfigData.fd_idma[1] != gmeta.fd1) || (mClientCompositionInfo.mLastWinConfigData.fd_idma[2] != gmeta.fd2))) { String8 errString; DISPLAY_LOGE("skip flag is enabled but buffer is updated lastConfig[%d, %d, %d], handle[%d, %d, %d]\n", mClientCompositionInfo.mLastWinConfigData.fd_idma[0], mClientCompositionInfo.mLastWinConfigData.fd_idma[1], mClientCompositionInfo.mLastWinConfigData.fd_idma[2], gmeta.fd, gmeta.fd1, gmeta.fd2); DISPLAY_LOGE("last win config"); for (size_t i = 0; i < mLastDpuData.configs.size(); i++) { errString.appendFormat("config[%zu]\n", i); dumpConfig(errString, mLastDpuData.configs[i]); DISPLAY_LOGE("\t%s", errString.c_str()); errString.clear(); } errString.appendFormat("%s:: skip flag is enabled but buffer is updated\n", __func__); printDebugInfos(errString); } } mClientCompositionInfo.setTargetBuffer(this, handle, acquireFence, (android_dataspace)dataspace); setFenceInfo(acquireFence, this, FENCE_TYPE_SRC_RELEASE, FENCE_IP_FB, HwcFenceDirection::FROM); if (handle) { mClientCompositionInfo.mCompressionInfo = getCompressionInfo(handle); mExynosCompositionInfo.mCompressionInfo = getCompressionInfo(handle); } return 0; } int32_t ExynosDisplay::setColorTransform( const float* matrix, int32_t /*android_color_transform_t*/ hint) { if ((hint < HAL_COLOR_TRANSFORM_IDENTITY) || (hint > HAL_COLOR_TRANSFORM_CORRECT_TRITANOPIA)) return HWC2_ERROR_BAD_PARAMETER; ALOGI("%s:: %d, %d", __func__, mColorTransformHint, hint); if (mColorTransformHint != hint) setGeometryChanged(GEOMETRY_DISPLAY_COLOR_TRANSFORM_CHANGED); mColorTransformHint = hint; #ifdef HWC_SUPPORT_COLOR_TRANSFORM int ret = mDisplayInterface->setColorTransform(matrix, hint); if (ret < 0) mColorTransformHint = ret; return ret; #else return HWC2_ERROR_NONE; #endif } int32_t ExynosDisplay::setColorMode(int32_t /*android_color_mode_t*/ mode) { if (mDisplayInterface->setColorMode(mode) < 0) { if (mode == HAL_COLOR_MODE_NATIVE) return HWC2_ERROR_NONE; ALOGE("%s:: is not supported", __func__); return HWC2_ERROR_UNSUPPORTED; } ALOGI("%s:: %d, %d", __func__, mColorMode, mode); if (mColorMode != mode) setGeometryChanged(GEOMETRY_DISPLAY_COLOR_MODE_CHANGED); mColorMode = (android_color_mode_t)mode; return HWC2_ERROR_NONE; } int32_t ExynosDisplay::getRenderIntents(int32_t mode, uint32_t* outNumIntents, int32_t* /*android_render_intent_v1_1_t*/ outIntents) { ALOGI("%s:: mode(%d), outNum(%d), outIntents(%p)", __func__, mode, *outNumIntents, outIntents); return mDisplayInterface->getRenderIntents(mode, outNumIntents, outIntents); } int32_t ExynosDisplay::setColorModeWithRenderIntent(int32_t /*android_color_mode_t*/ mode, int32_t /*android_render_intent_v1_1_t */ intent) { ALOGI("%s:: mode(%d), intent(%d)", __func__, mode, intent); return mDisplayInterface->setColorModeWithRenderIntent(mode, intent); } int32_t ExynosDisplay::getDisplayIdentificationData(uint8_t* outPort, uint32_t* outDataSize, uint8_t* outData) { return mDisplayInterface->getDisplayIdentificationData(outPort, outDataSize, outData); } int32_t ExynosDisplay::getDisplayCapabilities(uint32_t* outNumCapabilities, uint32_t* outCapabilities) { /* If each display has their own capabilities, * this should be described in display module codes */ uint32_t capabilityNum = 0; bool isBrightnessSupported = false; int32_t isDozeSupported = 0; auto ret = getDisplayBrightnessSupport(&isBrightnessSupported); if (ret != HWC2_ERROR_NONE) { ALOGE("%s: failed to getDisplayBrightnessSupport: %d", __func__, ret); return ret; } if (isBrightnessSupported) { capabilityNum++; } ret = getDozeSupport(&isDozeSupported); if (ret != HWC2_ERROR_NONE) { ALOGE("%s: failed to getDozeSupport: %d", __func__, ret); return ret; } if (isDozeSupported) { capabilityNum++; } #ifdef HWC_SUPPORT_COLOR_TRANSFORM capabilityNum++; #endif if (outCapabilities == NULL) { *outNumCapabilities = capabilityNum; return HWC2_ERROR_NONE; } if (capabilityNum != *outNumCapabilities) { ALOGE("%s:: invalid outNumCapabilities(%d), should be(%d)", __func__, *outNumCapabilities, capabilityNum); return HWC2_ERROR_BAD_PARAMETER; } uint32_t index = 0; if (isBrightnessSupported) { outCapabilities[index++] = HWC2_DISPLAY_CAPABILITY_BRIGHTNESS; } if (isDozeSupported) { outCapabilities[index++] = HWC2_DISPLAY_CAPABILITY_DOZE; } #ifdef HWC_SUPPORT_COLOR_TRANSFORM outCapabilities[index++] = HWC2_DISPLAY_CAPABILITY_SKIP_CLIENT_COLOR_TRANSFORM; #endif return HWC2_ERROR_NONE; } int32_t ExynosDisplay::getDisplayBrightnessSupport(bool* outSupport) { if (!mBrightnessController || !mBrightnessController->isSupported()) { *outSupport = false; } else { *outSupport = true; } return HWC2_ERROR_NONE; } void ExynosDisplay::handleTargetOperationRate() { int32_t targetOpRate = mOperationRateManager->getTargetOperationRate(); if (targetOpRate == mBrightnessController->getOperationRate()) return; mDevice->onRefresh(mDisplayId); mBrightnessController->processOperationRate(targetOpRate); } int32_t ExynosDisplay::setDisplayBrightness(float brightness, bool waitPresent) { if (mBrightnessController) { int32_t ret; ret = mBrightnessController->processDisplayBrightness(brightness, mVsyncPeriod, waitPresent); if (ret == NO_ERROR) { setMinIdleRefreshRate(0, RrThrottleRequester::BRIGHTNESS); if (mOperationRateManager) { mOperationRateManager->onBrightness(mBrightnessController->getBrightnessLevel()); handleTargetOperationRate(); } } return ret; } return HWC2_ERROR_UNSUPPORTED; } int32_t ExynosDisplay::ignoreBrightnessUpdateRequests(bool ignore) { if (mBrightnessController) return mBrightnessController->ignoreBrightnessUpdateRequests(ignore); return HWC2_ERROR_UNSUPPORTED; } int32_t ExynosDisplay::setBrightnessNits(const float nits) { if (mBrightnessController) { int32_t ret = mBrightnessController->setBrightnessNits(nits, mVsyncPeriod); if (ret == NO_ERROR) { setMinIdleRefreshRate(0, RrThrottleRequester::BRIGHTNESS); if (mOperationRateManager) mOperationRateManager->onBrightness(mBrightnessController->getBrightnessLevel()); } return ret; } return HWC2_ERROR_UNSUPPORTED; } int32_t ExynosDisplay::setBrightnessDbv(const uint32_t dbv) { if (mBrightnessController) { int32_t ret = mBrightnessController->setBrightnessDbv(dbv, mVsyncPeriod); if (ret == NO_ERROR) { setMinIdleRefreshRate(0, RrThrottleRequester::BRIGHTNESS); if (mOperationRateManager) { mOperationRateManager->onBrightness(mBrightnessController->getBrightnessLevel()); } } return ret; } return HWC2_ERROR_UNSUPPORTED; } int32_t ExynosDisplay::getDisplayConnectionType(uint32_t* outType) { if (mType == HWC_DISPLAY_PRIMARY) *outType = HWC2_DISPLAY_CONNECTION_TYPE_INTERNAL; else if (mType == HWC_DISPLAY_EXTERNAL) *outType = HWC2_DISPLAY_CONNECTION_TYPE_EXTERNAL; else return HWC2_ERROR_BAD_DISPLAY; return HWC2_ERROR_NONE; } int32_t ExynosDisplay::getDisplayVsyncPeriod(hwc2_vsync_period_t* __unused outVsyncPeriod) { Mutex::Autolock lock(mDisplayMutex); return getDisplayVsyncPeriodInternal(outVsyncPeriod); } int32_t ExynosDisplay::getConfigAppliedTime(const uint64_t desiredTime, const uint64_t actualChangeTime, int64_t &appliedTime, int64_t &refreshTime) { uint32_t transientDuration = mDisplayInterface->getConfigChangeDuration(); appliedTime = actualChangeTime; if (desiredTime > appliedTime) { const int64_t originalAppliedTime = appliedTime; const int64_t diff = desiredTime - appliedTime; appliedTime += (diff + mVsyncPeriod - 1) / mVsyncPeriod * mVsyncPeriod; DISPLAY_LOGD(eDebugDisplayConfig, "desired time(%" PRId64 "), applied time(%" PRId64 "->%" PRId64 ")", desiredTime, originalAppliedTime, appliedTime); } else { DISPLAY_LOGD(eDebugDisplayConfig, "desired time(%" PRId64 "), applied time(%" PRId64 ")", desiredTime, appliedTime); } refreshTime = appliedTime - (transientDuration * mVsyncPeriod); return NO_ERROR; } void ExynosDisplay::calculateTimelineLocked( hwc2_config_t config, hwc_vsync_period_change_constraints_t* vsyncPeriodChangeConstraints, hwc_vsync_period_change_timeline_t* outTimeline) { int64_t actualChangeTime = 0; /* actualChangeTime includes transient duration */ mDisplayInterface->getVsyncAppliedTime(config, &actualChangeTime); outTimeline->refreshRequired = true; getConfigAppliedTime(mVsyncPeriodChangeConstraints.desiredTimeNanos, actualChangeTime, outTimeline->newVsyncAppliedTimeNanos, outTimeline->refreshTimeNanos); DISPLAY_LOGD(eDebugDisplayConfig, "requested config : %d(%d)->%d(%d), " "desired %" PRId64 ", newVsyncAppliedTimeNanos : %" PRId64 "", mActiveConfig, mDisplayConfigs[mActiveConfig].vsyncPeriod, config, mDisplayConfigs[config].vsyncPeriod, mVsyncPeriodChangeConstraints.desiredTimeNanos, outTimeline->newVsyncAppliedTimeNanos); } int32_t ExynosDisplay::setActiveConfigWithConstraints(hwc2_config_t config, hwc_vsync_period_change_constraints_t* vsyncPeriodChangeConstraints, hwc_vsync_period_change_timeline_t* outTimeline) { DISPLAY_ATRACE_CALL(); Mutex::Autolock lock(mDisplayMutex); const nsecs_t current = systemTime(SYSTEM_TIME_MONOTONIC); const nsecs_t diffMs = ns2ms(vsyncPeriodChangeConstraints->desiredTimeNanos - current); DISPLAY_LOGD(eDebugDisplayConfig, "config(%d->%d), seamless(%d), diff(%" PRId64 ")", mActiveConfig, config, vsyncPeriodChangeConstraints->seamlessRequired, diffMs); if (CC_UNLIKELY(ATRACE_ENABLED())) ATRACE_NAME(("diff:" + std::to_string(diffMs)).c_str()); if (isBadConfig(config)) return HWC2_ERROR_BAD_CONFIG; if (!isConfigSettingEnabled()) { mPendingConfig = config; DISPLAY_LOGI("%s: config setting disabled, set pending config=%d", __func__, config); return HWC2_ERROR_NONE; } if (mDisplayConfigs[mActiveConfig].groupId != mDisplayConfigs[config].groupId) { if (vsyncPeriodChangeConstraints->seamlessRequired) { DISPLAY_LOGD(eDebugDisplayConfig, "Case : Seamless is not allowed"); return HWC2_ERROR_SEAMLESS_NOT_ALLOWED; } outTimeline->newVsyncAppliedTimeNanos = vsyncPeriodChangeConstraints->desiredTimeNanos; outTimeline->refreshRequired = true; } if (needNotChangeConfig(config)) { outTimeline->refreshRequired = false; outTimeline->newVsyncAppliedTimeNanos = vsyncPeriodChangeConstraints->desiredTimeNanos; return HWC2_ERROR_NONE; } if ((mXres != mDisplayConfigs[config].width) || (mYres != mDisplayConfigs[config].height)) { if ((mDisplayInterface->setActiveConfigWithConstraints(config, true)) != NO_ERROR) { ALOGW("Mode change not possible"); return HWC2_ERROR_BAD_CONFIG; } mRenderingState = RENDERING_STATE_NONE; setGeometryChanged(GEOMETRY_DISPLAY_RESOLUTION_CHANGED); updateInternalDisplayConfigVariables(config, false); } else if (vsyncPeriodChangeConstraints->seamlessRequired) { if ((mDisplayInterface->setActiveConfigWithConstraints(config, true)) != NO_ERROR) { DISPLAY_LOGD(eDebugDisplayConfig, "Case : Seamless is not possible"); return HWC2_ERROR_SEAMLESS_NOT_POSSIBLE; } } DISPLAY_LOGD(eDebugDisplayConfig, "%s : %dx%d, %dms, %d Xdpi, %d Ydpi", __func__, mXres, mYres, mVsyncPeriod, mXdpi, mYdpi); if (mConfigRequestState == hwc_request_state_t::SET_CONFIG_STATE_REQUESTED) { DISPLAY_LOGI("%s, previous request config is processing (desird %d, new request %d)", __func__, mDesiredConfig, config); } /* Config would be requested on present time */ mConfigRequestState = hwc_request_state_t::SET_CONFIG_STATE_PENDING; mVsyncPeriodChangeConstraints = *vsyncPeriodChangeConstraints; mDesiredConfig = config; DISPLAY_ATRACE_INT("Pending ActiveConfig", mDesiredConfig); calculateTimelineLocked(config, vsyncPeriodChangeConstraints, outTimeline); /* mActiveConfig should be changed immediately for internal status */ mActiveConfig = config; mVsyncAppliedTimeLine = *outTimeline; updateBtsFrameScanoutPeriod(getDisplayFrameScanoutPeriodFromConfig(config)); bool earlyWakeupNeeded = checkRrCompensationEnabled(); if (earlyWakeupNeeded) { setEarlyWakeupDisplay(); } if (mRefreshRateIndicatorHandler) { mRefreshRateIndicatorHandler->checkOnSetActiveConfig(mDisplayConfigs[config].refreshRate); } return HWC2_ERROR_NONE; } int32_t ExynosDisplay::setBootDisplayConfig(int32_t config) { return HWC2_ERROR_UNSUPPORTED; } int32_t ExynosDisplay::clearBootDisplayConfig() { return HWC2_ERROR_UNSUPPORTED; } int32_t ExynosDisplay::getPreferredBootDisplayConfig(int32_t *outConfig) { return getPreferredDisplayConfigInternal(outConfig); } int32_t ExynosDisplay::getPreferredDisplayConfigInternal(int32_t *outConfig) { return HWC2_ERROR_UNSUPPORTED; } int32_t ExynosDisplay::setAutoLowLatencyMode(bool __unused on) { return HWC2_ERROR_UNSUPPORTED; } int32_t ExynosDisplay::getSupportedContentTypes(uint32_t* __unused outNumSupportedContentTypes, uint32_t* __unused outSupportedContentTypes) { if (outSupportedContentTypes == NULL) outNumSupportedContentTypes = 0; return HWC2_ERROR_NONE; } int32_t ExynosDisplay::setContentType(int32_t /* hwc2_content_type_t */ contentType) { if (contentType == HWC2_CONTENT_TYPE_NONE) return HWC2_ERROR_NONE; return HWC2_ERROR_UNSUPPORTED; } int32_t ExynosDisplay::getClientTargetProperty( hwc_client_target_property_t *outClientTargetProperty, HwcDimmingStage *outDimmingStage) { outClientTargetProperty->pixelFormat = HAL_PIXEL_FORMAT_RGBA_8888; outClientTargetProperty->dataspace = HAL_DATASPACE_UNKNOWN; if (outDimmingStage != nullptr) *outDimmingStage = HwcDimmingStage::DIMMING_NONE; return HWC2_ERROR_NONE; } bool ExynosDisplay::isBadConfig(hwc2_config_t config) { /* Check invalid config */ const auto its = mDisplayConfigs.find(config); if (its == mDisplayConfigs.end()) { DISPLAY_LOGE("%s, invalid config : %d", __func__, config); return true; } return false; } bool ExynosDisplay::needNotChangeConfig(hwc2_config_t config) { /* getting current config and compare */ /* If same value, return */ if (mActiveConfig == config) { DISPLAY_LOGI("%s, Same config change requested : %d", __func__, config); return true; } return false; } int32_t ExynosDisplay::updateInternalDisplayConfigVariables( hwc2_config_t config, bool updateVsync) { mActiveConfig = config; /* Update internal variables */ getDisplayAttribute(mActiveConfig, HWC2_ATTRIBUTE_WIDTH, (int32_t*)&mXres); getDisplayAttribute(mActiveConfig, HWC2_ATTRIBUTE_HEIGHT, (int32_t*)&mYres); getDisplayAttribute(mActiveConfig, HWC2_ATTRIBUTE_DPI_X, (int32_t*)&mXdpi); getDisplayAttribute(mActiveConfig, HWC2_ATTRIBUTE_DPI_Y, (int32_t*)&mYdpi); mHdrFullScrenAreaThreshold = mXres * mYres * kHdrFullScreen; if (updateVsync) { resetConfigRequestStateLocked(config); } if (mRefreshRateIndicatorHandler) { mRefreshRateIndicatorHandler->checkOnSetActiveConfig(mDisplayConfigs[config].refreshRate); } return NO_ERROR; } void ExynosDisplay::updateBtsFrameScanoutPeriod(int32_t frameScanoutPeriod, bool configApplied) { if (mBtsFrameScanoutPeriod == frameScanoutPeriod) { return; } if (configApplied || frameScanoutPeriod < mBtsFrameScanoutPeriod) { checkBtsReassignResource(frameScanoutPeriod, mBtsFrameScanoutPeriod); mBtsFrameScanoutPeriod = frameScanoutPeriod; ATRACE_INT("btsFrameScanoutPeriod", mBtsFrameScanoutPeriod); } } void ExynosDisplay::tryUpdateBtsFromOperationRate(bool beforeValidateDisplay) { if (mOperationRateManager == nullptr || mBrightnessController == nullptr || mActiveConfig == UINT_MAX) { return; } if (!mDisplayConfigs[mActiveConfig].isOperationRateToBts) { return; } if (beforeValidateDisplay && mBrightnessController->isOperationRatePending()) { uint32_t opRate = mBrightnessController->getOperationRate(); if (opRate) { int32_t operationRatePeriod = nsecsPerSec / opRate; if (operationRatePeriod < mBtsFrameScanoutPeriod) { updateBtsFrameScanoutPeriod(opRate); mBtsPendingOperationRatePeriod = 0; } else if (operationRatePeriod != mBtsFrameScanoutPeriod) { mBtsPendingOperationRatePeriod = operationRatePeriod; } } } if (!beforeValidateDisplay && mBtsPendingOperationRatePeriod && !mBrightnessController->isOperationRatePending()) { /* Do not update during rr transition, it will be updated after setting config done */ if (mConfigRequestState != hwc_request_state_t::SET_CONFIG_STATE_REQUESTED) { updateBtsFrameScanoutPeriod(mBtsPendingOperationRatePeriod, true); } mBtsPendingOperationRatePeriod = 0; } } inline int32_t ExynosDisplay::getDisplayFrameScanoutPeriodFromConfig(hwc2_config_t config) { int32_t frameScanoutPeriodNs; auto vrrConfig = getVrrConfigs(config); if (vrrConfig.has_value() && vrrConfig->isFullySupported) { frameScanoutPeriodNs = vrrConfig->minFrameIntervalNs; } else { getDisplayAttribute(config, HWC2_ATTRIBUTE_VSYNC_PERIOD, &frameScanoutPeriodNs); if (mOperationRateManager && mBrightnessController && mDisplayConfigs[config].isOperationRateToBts) { uint32_t opRate = mBrightnessController->getOperationRate(); if (opRate) { uint32_t opPeriodNs = nsecsPerSec / opRate; frameScanoutPeriodNs = (frameScanoutPeriodNs <= opPeriodNs) ? frameScanoutPeriodNs : opPeriodNs; } } } assert(frameScanoutPeriodNs > 0); return frameScanoutPeriodNs; } uint32_t ExynosDisplay::getBtsRefreshRate() const { return static_cast(round(nsecsPerSec / mBtsFrameScanoutPeriod * 0.1f) * 10); } void ExynosDisplay::updateRefreshRateHint() { if (mRefreshRate) { mPowerHalHint.signalRefreshRate(mPowerModeState.value_or(HWC2_POWER_MODE_OFF), mRefreshRate); } } /* This function must be called within a mDisplayMutex protection */ int32_t ExynosDisplay::resetConfigRequestStateLocked(hwc2_config_t config) { ATRACE_CALL(); assert(isBadConfig(config) == false); mRefreshRate = mDisplayConfigs[config].refreshRate; mVsyncPeriod = getDisplayVsyncPeriodFromConfig(config); updateBtsFrameScanoutPeriod(getDisplayFrameScanoutPeriodFromConfig(config), true); DISPLAY_LOGD(eDebugDisplayConfig, "Update mVsyncPeriod %d by config(%d)", mVsyncPeriod, config); updateRefreshRateHint(); if (mConfigRequestState != hwc_request_state_t::SET_CONFIG_STATE_REQUESTED) { DISPLAY_LOGI("%s: mConfigRequestState (%d) is not REQUESTED", __func__, mConfigRequestState); } else { DISPLAY_LOGD(eDebugDisplayInterfaceConfig, "%s: Change mConfigRequestState (%d) to DONE", __func__, mConfigRequestState); mConfigRequestState = hwc_request_state_t::SET_CONFIG_STATE_DONE; updateAppliedActiveConfig(mActiveConfig, systemTime(SYSTEM_TIME_MONOTONIC)); } return NO_ERROR; } int32_t ExynosDisplay::updateConfigRequestAppliedTime() { if (mConfigRequestState != hwc_request_state_t::SET_CONFIG_STATE_REQUESTED) { DISPLAY_LOGI("%s: mConfigRequestState (%d) is not REQUESTED", __func__, mConfigRequestState); return NO_ERROR; } /* * config change was requested but * it is not applied until newVsyncAppliedTimeNanos * Update time information */ int64_t actualChangeTime = 0; mDisplayInterface->getVsyncAppliedTime(mDesiredConfig, &actualChangeTime); return updateVsyncAppliedTimeLine(actualChangeTime); } int32_t ExynosDisplay::updateVsyncAppliedTimeLine(int64_t actualChangeTime) { ExynosDevice *dev = mDevice; DISPLAY_LOGD(eDebugDisplayConfig,"Vsync applied time is changed (%" PRId64 "-> %" PRId64 ")", mVsyncAppliedTimeLine.newVsyncAppliedTimeNanos, actualChangeTime); getConfigAppliedTime(mVsyncPeriodChangeConstraints.desiredTimeNanos, actualChangeTime, mVsyncAppliedTimeLine.newVsyncAppliedTimeNanos, mVsyncAppliedTimeLine.refreshTimeNanos); if (mConfigRequestState == hwc_request_state_t::SET_CONFIG_STATE_REQUESTED) { mVsyncAppliedTimeLine.refreshRequired = false; } else { mVsyncAppliedTimeLine.refreshRequired = true; } DISPLAY_LOGD(eDebugDisplayConfig,"refresh required(%d), newVsyncAppliedTimeNanos (%" PRId64 ")", mVsyncAppliedTimeLine.refreshRequired, mVsyncAppliedTimeLine.newVsyncAppliedTimeNanos); dev->onVsyncPeriodTimingChanged(getId(), &mVsyncAppliedTimeLine); return NO_ERROR; } int32_t ExynosDisplay::getDisplayVsyncPeriodInternal(hwc2_vsync_period_t* outVsyncPeriod) { /* Getting actual config from DPU */ if (mDisplayInterface->getDisplayVsyncPeriod(outVsyncPeriod) == HWC2_ERROR_NONE) { DISPLAY_LOGD(eDebugDisplayInterfaceConfig, "period : %ld", (long)*outVsyncPeriod); } else { *outVsyncPeriod = mVsyncPeriod; DISPLAY_LOGD(eDebugDisplayInterfaceConfig, "period is mVsyncPeriod: %d", mVsyncPeriod); } return HWC2_ERROR_NONE; } int32_t ExynosDisplay::doDisplayConfigInternal(hwc2_config_t config) { return mDisplayInterface->setActiveConfigWithConstraints(config); } int32_t ExynosDisplay::doDisplayConfigPostProcess(ExynosDevice *dev) { ATRACE_CALL(); uint64_t current = systemTime(SYSTEM_TIME_MONOTONIC); int64_t actualChangeTime = 0; mDisplayInterface->getVsyncAppliedTime(mDesiredConfig, &actualChangeTime); bool needSetActiveConfig = false; DISPLAY_LOGD(eDebugDisplayConfig, "Check time for setActiveConfig (curr: %" PRId64 ", actualChangeTime: %" PRId64 ", desiredTime: %" PRId64 "", current, actualChangeTime, mVsyncPeriodChangeConstraints.desiredTimeNanos); if (actualChangeTime >= mVsyncPeriodChangeConstraints.desiredTimeNanos) { DISPLAY_LOGD(eDebugDisplayConfig, "Request setActiveConfig %d", mDesiredConfig); needSetActiveConfig = true; DISPLAY_ATRACE_INT("Pending ActiveConfig", 0); DISPLAY_ATRACE_INT64("TimeToApplyConfig", 0); } else { DISPLAY_LOGD(eDebugDisplayConfig, "setActiveConfig still pending (mDesiredConfig %d)", mDesiredConfig); DISPLAY_ATRACE_INT("Pending ActiveConfig", mDesiredConfig); DISPLAY_ATRACE_INT64("TimeToApplyConfig", ns2ms(mVsyncPeriodChangeConstraints.desiredTimeNanos - current)); } if (needSetActiveConfig) { int32_t ret = NO_ERROR; if ((ret = doDisplayConfigInternal(mDesiredConfig)) != NO_ERROR) { return ret; } mConfigRequestState = hwc_request_state_t::SET_CONFIG_STATE_REQUESTED; } return updateVsyncAppliedTimeLine(actualChangeTime); } int32_t ExynosDisplay::setOutputBuffer( buffer_handle_t __unused buffer, int32_t __unused releaseFence) { return HWC2_ERROR_NONE; } int ExynosDisplay::clearDisplay(bool needModeClear) { const int ret = mDisplayInterface->clearDisplay(needModeClear); if (ret) DISPLAY_LOGE("fail to clear display"); mClientCompositionInfo.mSkipStaticInitFlag = false; mClientCompositionInfo.mSkipFlag = false; mLastDpuData.reset(); /* Update last retire fence */ mLastRetireFence = fence_close(mLastRetireFence, this, FENCE_TYPE_RETIRE, FENCE_IP_DPP); if (mBrightnessController) { mBrightnessController->onClearDisplay(needModeClear); } return ret; } int32_t ExynosDisplay::setPowerMode( int32_t /*hwc2_power_mode_t*/ mode) { Mutex::Autolock lock(mDisplayMutex); if (!mDisplayInterface->isDozeModeAvailable() && (mode == HWC2_POWER_MODE_DOZE || mode == HWC2_POWER_MODE_DOZE_SUSPEND)) { return HWC2_ERROR_UNSUPPORTED; } if (mode == HWC_POWER_MODE_OFF) { mDevice->mPrimaryBlank = true; clearDisplay(true); ALOGV("HWC2: Clear display (power off)"); } else { mDevice->mPrimaryBlank = false; } if (mode == HWC_POWER_MODE_OFF) mDREnable = false; else mDREnable = mDRDefault; // check the dynamic recomposition thread by following display power status; mDevice->checkDynamicRecompositionThread(); /* TODO: Call display interface */ mDisplayInterface->setPowerMode(mode); ALOGD("%s:: mode(%d))", __func__, mode); mPowerModeState = (hwc2_power_mode_t)mode; if (mode == HWC_POWER_MODE_OFF) { /* It should be called from validate() when the screen is on */ mSkipFrame = true; setGeometryChanged(GEOMETRY_DISPLAY_POWER_OFF); if ((mRenderingState >= RENDERING_STATE_VALIDATED) && (mRenderingState < RENDERING_STATE_PRESENTED)) closeFencesForSkipFrame(RENDERING_STATE_VALIDATED); mRenderingState = RENDERING_STATE_NONE; } else { setGeometryChanged(GEOMETRY_DISPLAY_POWER_ON); } return HWC2_ERROR_NONE; } int32_t ExynosDisplay::setVsyncEnabled( int32_t /*hwc2_vsync_t*/ enabled) { Mutex::Autolock lock(mDisplayMutex); return setVsyncEnabledInternal(enabled); } int32_t ExynosDisplay::setVsyncEnabledInternal( int32_t enabled) { uint32_t val = 0; if (enabled < 0 || enabled > HWC2_VSYNC_DISABLE) return HWC2_ERROR_BAD_PARAMETER; if (enabled == HWC2_VSYNC_ENABLE) { gettimeofday(&updateTimeInfo.lastEnableVsyncTime, NULL); val = 1; if (mVsyncState != HWC2_VSYNC_ENABLE) { /* TODO: remove it once driver can handle on its own */ setEarlyWakeupDisplay(); } } else { gettimeofday(&updateTimeInfo.lastDisableVsyncTime, NULL); } if (mDisplayInterface->setVsyncEnabled(val) < 0) { HWC_LOGE(this, "vsync ioctl failed errno : %d", errno); return HWC2_ERROR_BAD_DISPLAY; } mVsyncState = (hwc2_vsync_t)enabled; return HWC2_ERROR_NONE; } int32_t ExynosDisplay::validateDisplay( uint32_t* outNumTypes, uint32_t* outNumRequests) { DISPLAY_ATRACE_CALL(); gettimeofday(&updateTimeInfo.lastValidateTime, NULL); Mutex::Autolock lock(mDisplayMutex); if (!mHpdStatus) { ALOGD("validateDisplay: drop frame: mHpdStatus == false"); return HWC2_ERROR_NONE; } if (mPauseDisplay) return HWC2_ERROR_NONE; mDropFrameDuringResSwitch = (mGeometryChanged & GEOMETRY_DISPLAY_RESOLUTION_CHANGED) && !isFullScreenComposition(); if (mDropFrameDuringResSwitch) { ALOGD("validateDisplay: drop invalid frame during resolution switch"); *outNumTypes = 0; *outNumRequests = 0; return HWC2_ERROR_NONE; } int ret = NO_ERROR; bool validateError = false; mUpdateEventCnt++; mUpdateCallCnt++; mLastUpdateTimeStamp = systemTime(SYSTEM_TIME_MONOTONIC); if (usePowerHintSession()) { mValidateStartTime = mLastUpdateTimeStamp; mExpectedPresentTime = getExpectedPresentTime(*mValidateStartTime); auto target = min(mExpectedPresentTime - *mValidateStartTime, static_cast(mVsyncPeriod)); mPowerHalHint.signalTargetWorkDuration(target); std::optional predictedDuration = getPredictedDuration(true); if (predictedDuration.has_value()) { mPowerHalHint.signalActualWorkDuration(*predictedDuration); } } checkIgnoreLayers(); if (mLayers.size() == 0) DISPLAY_LOGI("%s:: validateDisplay layer size is 0", __func__); else mLayers.vector_sort(); for (size_t i = 0; i < mLayers.size(); i++) mLayers[i]->setSrcAcquireFence(); tryUpdateBtsFromOperationRate(true); doPreProcessing(); checkLayerFps(); if (exynosHWCControl.useDynamicRecomp == true && mDREnable) { checkDynamicReCompMode(); if (mDevice->isDynamicRecompositionThreadAlive() == false && mDevice->mDRLoopStatus == false) mDevice->dynamicRecompositionThreadCreate(); } if ((ret = mResourceManager->assignResource(this)) != NO_ERROR) { validateError = true; HWC_LOGE(this, "%s:: assignResource() fail, display(%d), ret(%d)", __func__, mDisplayId, ret); String8 errString; errString.appendFormat("%s:: assignResource() fail, display(%d), ret(%d)\n", __func__, mDisplayId, ret); printDebugInfos(errString); mDisplayInterface->setForcePanic(); } if ((ret = skipStaticLayers(mClientCompositionInfo)) != NO_ERROR) { validateError = true; HWC_LOGE(this, "%s:: skipStaticLayers() fail, display(%d), ret(%d)", __func__, mDisplayId, ret); } else { if ((mClientCompositionInfo.mHasCompositionLayer) && (mClientCompositionInfo.mSkipFlag == false)) { /* Initialize compositionType */ for (size_t i = (size_t)mClientCompositionInfo.mFirstIndex; i <= (size_t)mClientCompositionInfo.mLastIndex; i++) { if (mLayers[i]->mOverlayPriority >= ePriorityHigh) continue; mLayers[i]->mValidateCompositionType = HWC2_COMPOSITION_CLIENT; } } } mRenderingState = RENDERING_STATE_VALIDATED; /* * HWC should update performanceInfo even if assignResource is skipped * HWC excludes the layer from performance calculation * if there is no buffer update. (using ExynosMPP::canSkipProcessing()) * Therefore performanceInfo should be calculated again if only the buffer is updated. */ if ((ret = mDevice->mResourceManager->deliverPerformanceInfo()) != NO_ERROR) { HWC_LOGE(NULL,"%s:: deliverPerformanceInfo() error (%d)", __func__, ret); } if ((validateError == false) && (mDisplayControl.earlyStartMPP == true)) { if ((ret = startPostProcessing()) != NO_ERROR) validateError = true; } if (validateError) { setGeometryChanged(GEOMETRY_ERROR_CASE); mClientCompositionInfo.mSkipStaticInitFlag = false; mExynosCompositionInfo.mSkipStaticInitFlag = false; mResourceManager->resetAssignedResources(this, true); mClientCompositionInfo.initializeInfos(this); mExynosCompositionInfo.initializeInfos(this); for (uint32_t i = 0; i < mLayers.size(); i++) { ExynosLayer *layer = mLayers[i]; layer->mOverlayInfo |= eResourceAssignFail; layer->mValidateCompositionType = HWC2_COMPOSITION_CLIENT; addClientCompositionLayer(i); } mResourceManager->assignCompositionTarget(this, COMPOSITION_CLIENT); mResourceManager->assignWindow(this); } resetColorMappingInfoForClientComp(); storePrevValidateCompositionType(); int32_t displayRequests = 0; if ((ret = getChangedCompositionTypes(outNumTypes, NULL, NULL)) != NO_ERROR) { HWC_LOGE(this, "%s:: getChangedCompositionTypes() fail, display(%d), ret(%d)", __func__, mDisplayId, ret); setGeometryChanged(GEOMETRY_ERROR_CASE); } if ((ret = getDisplayRequests(&displayRequests, outNumRequests, NULL, NULL)) != NO_ERROR) { HWC_LOGE(this, "%s:: getDisplayRequests() fail, display(%d), ret(%d)", __func__, mDisplayId, ret); setGeometryChanged(GEOMETRY_ERROR_CASE); } mSkipFrame = false; if ((*outNumTypes == 0) && (*outNumRequests == 0)) return HWC2_ERROR_NONE; if (usePowerHintSession()) { mValidationDuration = systemTime(SYSTEM_TIME_MONOTONIC) - *mValidateStartTime; } return HWC2_ERROR_HAS_CHANGES; } int32_t ExynosDisplay::startPostProcessing() { ATRACE_CALL(); int ret = NO_ERROR; String8 errString; float assignedCapacity = mResourceManager->getAssignedCapacity(MPP_G2D); if (assignedCapacity > (mResourceManager->getM2MCapa(MPP_G2D) * MPP_CAPA_OVER_THRESHOLD)) { errString.appendFormat("Assigned capacity for exynos composition is over restriction (%f)", assignedCapacity); goto err; } if ((ret = doExynosComposition()) != NO_ERROR) { errString.appendFormat("exynosComposition fail (%d)\n", ret); goto err; } // loop for all layer for (size_t i=0; i < mLayers.size(); i++) { if((mLayers[i]->mValidateCompositionType == HWC2_COMPOSITION_DEVICE) && (mLayers[i]->mM2mMPP != NULL)) { /* mAcquireFence is updated, Update image info */ struct exynos_image srcImg, dstImg, midImg; mLayers[i]->setSrcExynosImage(&srcImg); mLayers[i]->setDstExynosImage(&dstImg); mLayers[i]->setExynosImage(srcImg, dstImg); ExynosMPP* m2mMpp = mLayers[i]->mM2mMPP; midImg = mLayers[i]->mMidImg; m2mMpp->requestHWStateChange(MPP_HW_STATE_RUNNING); if ((ret = m2mMpp->doPostProcessing(midImg)) != NO_ERROR) { DISPLAY_LOGE("%s:: doPostProcessing() failed, layer(%zu), ret(%d)", __func__, i, ret); errString.appendFormat("%s:: doPostProcessing() failed, layer(%zu), ret(%d)\n", __func__, i, ret); goto err; } else { /* This should be closed by lib for each resource */ mLayers[i]->mAcquireFence = -1; } } } return ret; err: printDebugInfos(errString); closeFences(); mDisplayInterface->setForcePanic(); return -EINVAL; } int32_t ExynosDisplay::setCursorPositionAsync(uint32_t x_pos, uint32_t y_pos) { mDisplayInterface->setCursorPositionAsync(x_pos, y_pos); return HWC2_ERROR_NONE; } // clang-format off void ExynosDisplay::dumpConfig(const exynos_win_config_data &c) { DISPLAY_LOGD(eDebugWinConfig|eDebugSkipStaicLayer, "\tstate = %u", c.state); if (c.state == c.WIN_STATE_COLOR) { DISPLAY_LOGD(eDebugWinConfig|eDebugSkipStaicLayer, "\t\tx = %d, y = %d, width = %d, height = %d, color = %u, alpha = %f\n", c.dst.x, c.dst.y, c.dst.w, c.dst.h, c.color, c.plane_alpha); } else/* if (c.state != c.WIN_STATE_DISABLED) */{ DISPLAY_LOGD(eDebugWinConfig|eDebugSkipStaicLayer, "\t\tfd = (%d, %d, %d), acq_fence = %d, rel_fence = %d " "src_f_w = %u, src_f_h = %u, src_x = %d, src_y = %d, src_w = %u, src_h = %u, " "dst_f_w = %u, dst_f_h = %u, dst_x = %d, dst_y = %d, dst_w = %u, dst_h = %u, " "format = %u, pa = %f, transform = %d, dataspace = 0x%8x, hdr_enable = %d, blending = %u, " "protection = %u, compression = %s, compression_src = %d, transparent(x:%d, y:%d, w:%u, h:%u), " "block(x:%d, y:%d, w:%u, h:%u), opaque(x:%d, y:%d, w:%u, h:%u)", c.fd_idma[0], c.fd_idma[1], c.fd_idma[2], c.acq_fence, c.rel_fence, c.src.f_w, c.src.f_h, c.src.x, c.src.y, c.src.w, c.src.h, c.dst.f_w, c.dst.f_h, c.dst.x, c.dst.y, c.dst.w, c.dst.h, c.format, c.plane_alpha, c.transform, c.dataspace, c.hdr_enable, c.blending, c.protection, getCompressionStr(c.compressionInfo).c_str(), c.comp_src, c.transparent_area.x, c.transparent_area.y, c.transparent_area.w, c.transparent_area.h, c.block_area.x, c.block_area.y, c.block_area.w, c.block_area.h, c.opaque_area.x, c.opaque_area.y, c.opaque_area.w, c.opaque_area.h); } } void ExynosDisplay::dump(String8& result) { Mutex::Autolock lock(mDisplayMutex); dumpLocked(result); } void ExynosDisplay::dumpLocked(String8& result) { result.appendFormat("[%s] display information size: %d x %d, vsyncState: %d, colorMode: %d, " "colorTransformHint: %d, orientation %d\n", mDisplayName.c_str(), mXres, mYres, mVsyncState, mColorMode, mColorTransformHint, mMountOrientation); mClientCompositionInfo.dump(result); mExynosCompositionInfo.dump(result); result.appendFormat("PanelGammaSource (%d)\n\n", GetCurrentPanelGammaSource()); { Mutex::Autolock lock(mDRMutex); if (mLayers.size()) { result.appendFormat("============================== dump layers ===========================================\n"); for (uint32_t i = 0; i < mLayers.size(); i++) { ExynosLayer *layer = mLayers[i]; layer->dump(result); } } if (mIgnoreLayers.size()) { result.appendFormat("\n============================== dump ignore layers ===========================================\n"); for (uint32_t i = 0; i < mIgnoreLayers.size(); i++) { ExynosLayer *layer = mIgnoreLayers[i]; layer->dump(result); } } } result.appendFormat("\n"); if (mBrightnessController) { mBrightnessController->dump(result); } if (mHistogramController) { mHistogramController->dump(result); } if (mDisplayTe2Manager) { mDisplayTe2Manager->dump(result); } } void ExynosDisplay::dumpConfig(String8 &result, const exynos_win_config_data &c) { result.appendFormat("\tstate = %u\n", c.state); if (c.state == c.WIN_STATE_COLOR) { result.appendFormat("\t\tx = %d, y = %d, width = %d, height = %d, color = %u, alpha = %f\n", c.dst.x, c.dst.y, c.dst.w, c.dst.h, c.color, c.plane_alpha); } else/* if (c.state != c.WIN_STATE_DISABLED) */{ result.appendFormat("\t\tfd = (%d, %d, %d), acq_fence = %d, rel_fence = %d " "src_f_w = %u, src_f_h = %u, src_x = %d, src_y = %d, src_w = %u, src_h = %u, " "dst_f_w = %u, dst_f_h = %u, dst_x = %d, dst_y = %d, dst_w = %u, dst_h = %u, " "format = %u, pa = %f, transform = %d, dataspace = 0x%8x, hdr_enable = %d, blending = %u, " "protection = %u, compression = %s, compression_src = %d, transparent(x:%d, y:%d, w:%u, h:%u), " "block(x:%d, y:%d, w:%u, h:%u), opaque(x:%d, y:%d, w:%u, h:%u)\n", c.fd_idma[0], c.fd_idma[1], c.fd_idma[2], c.acq_fence, c.rel_fence, c.src.f_w, c.src.f_h, c.src.x, c.src.y, c.src.w, c.src.h, c.dst.f_w, c.dst.f_h, c.dst.x, c.dst.y, c.dst.w, c.dst.h, c.format, c.plane_alpha, c.transform, c.dataspace, c.hdr_enable, c.blending, c.protection, getCompressionStr(c.compressionInfo).c_str(), c.comp_src, c.transparent_area.x, c.transparent_area.y, c.transparent_area.w, c.transparent_area.h, c.block_area.x, c.block_area.y, c.block_area.w, c.block_area.h, c.opaque_area.x, c.opaque_area.y, c.opaque_area.w, c.opaque_area.h); } } void ExynosDisplay::printConfig(exynos_win_config_data &c) { ALOGD("\tstate = %u", c.state); if (c.state == c.WIN_STATE_COLOR) { ALOGD("\t\tx = %d, y = %d, width = %d, height = %d, color = %u, alpha = %f\n", c.dst.x, c.dst.y, c.dst.w, c.dst.h, c.color, c.plane_alpha); } else/* if (c.state != c.WIN_STATE_DISABLED) */{ ALOGD("\t\tfd = (%d, %d, %d), acq_fence = %d, rel_fence = %d " "src_f_w = %u, src_f_h = %u, src_x = %d, src_y = %d, src_w = %u, src_h = %u, " "dst_f_w = %u, dst_f_h = %u, dst_x = %d, dst_y = %d, dst_w = %u, dst_h = %u, " "format = %u, pa = %f, transform = %d, dataspace = 0x%8x, hdr_enable = %d, blending = %u, " "protection = %u, compression = %s, compression_src = %d, transparent(x:%d, y:%d, w:%u, h:%u), " "block(x:%d, y:%d, w:%u, h:%u), opaque(x:%d, y:%d, w:%u, h:%u)", c.fd_idma[0], c.fd_idma[1], c.fd_idma[2], c.acq_fence, c.rel_fence, c.src.f_w, c.src.f_h, c.src.x, c.src.y, c.src.w, c.src.h, c.dst.f_w, c.dst.f_h, c.dst.x, c.dst.y, c.dst.w, c.dst.h, c.format, c.plane_alpha, c.transform, c.dataspace, c.hdr_enable, c.blending, c.protection, getCompressionStr(c.compressionInfo).c_str(), c.comp_src, c.transparent_area.x, c.transparent_area.y, c.transparent_area.w, c.transparent_area.h, c.block_area.x, c.block_area.y, c.block_area.w, c.block_area.h, c.opaque_area.x, c.opaque_area.y, c.opaque_area.w, c.opaque_area.h); } } // clang-format on int32_t ExynosDisplay::setCompositionTargetExynosImage(uint32_t targetType, exynos_image *src_img, exynos_image *dst_img) { if (targetType != COMPOSITION_CLIENT && targetType != COMPOSITION_EXYNOS) { return -EINVAL; } const ExynosCompositionInfo& compositionInfo = (targetType == COMPOSITION_CLIENT) ? mClientCompositionInfo : mExynosCompositionInfo; src_img->fullWidth = mXres; src_img->fullHeight = mYres; /* To do */ /* Fb crop should be set hear */ src_img->x = 0; src_img->y = 0; src_img->w = mXres; src_img->h = mYres; if (compositionInfo.mTargetBuffer != NULL) { src_img->bufferHandle = compositionInfo.mTargetBuffer; VendorGraphicBufferMeta gmeta(compositionInfo.mTargetBuffer); src_img->format = gmeta.format; src_img->usageFlags = gmeta.producer_usage; } else { src_img->bufferHandle = NULL; src_img->format = HAL_PIXEL_FORMAT_RGBA_8888; src_img->usageFlags = 0; } src_img->layerFlags = 0x0; src_img->acquireFenceFd = compositionInfo.mAcquireFence; src_img->releaseFenceFd = -1; src_img->dataSpace = compositionInfo.mDataSpace; src_img->blending = HWC2_BLEND_MODE_PREMULTIPLIED; src_img->transform = 0; src_img->compressionInfo = compositionInfo.mCompressionInfo; src_img->planeAlpha = 1; src_img->zOrder = 0; if ((targetType == COMPOSITION_CLIENT) && (mType == HWC_DISPLAY_VIRTUAL)) { if (compositionInfo.mLastIndex < mExynosCompositionInfo.mLastIndex) src_img->zOrder = 0; else src_img->zOrder = 1000; } src_img->needPreblending = compositionInfo.mNeedPreblending; dst_img->fullWidth = mXres; dst_img->fullHeight = mYres; /* To do */ /* Fb crop should be set hear */ dst_img->x = 0; dst_img->y = 0; dst_img->w = mXres; dst_img->h = mYres; dst_img->bufferHandle = NULL; dst_img->format = HAL_PIXEL_FORMAT_RGBA_8888; dst_img->usageFlags = 0; dst_img->layerFlags = 0x0; dst_img->acquireFenceFd = -1; dst_img->releaseFenceFd = -1; dst_img->dataSpace = src_img->dataSpace; if (mColorMode != HAL_COLOR_MODE_NATIVE) dst_img->dataSpace = colorModeToDataspace(mColorMode); dst_img->blending = HWC2_BLEND_MODE_NONE; dst_img->transform = 0; dst_img->compressionInfo = compositionInfo.mCompressionInfo; dst_img->planeAlpha = 1; dst_img->zOrder = src_img->zOrder; return NO_ERROR; } int32_t ExynosDisplay::initializeValidateInfos() { mCursorIndex = -1; for (uint32_t i = 0; i < mLayers.size(); i++) { ExynosLayer *layer = mLayers[i]; layer->mValidateCompositionType = HWC2_COMPOSITION_INVALID; layer->mOverlayInfo = 0; if ((mDisplayControl.cursorSupport == true) && (mLayers[i]->mCompositionType == HWC2_COMPOSITION_CURSOR)) mCursorIndex = i; } exynos_image src_img; exynos_image dst_img; mClientCompositionInfo.initializeInfos(this); setCompositionTargetExynosImage(COMPOSITION_CLIENT, &src_img, &dst_img); mClientCompositionInfo.setExynosImage(src_img, dst_img); mExynosCompositionInfo.initializeInfos(this); setCompositionTargetExynosImage(COMPOSITION_EXYNOS, &src_img, &dst_img); mExynosCompositionInfo.setExynosImage(src_img, dst_img); return NO_ERROR; } int32_t ExynosDisplay::addClientCompositionLayer(uint32_t layerIndex) { bool exynosCompositionChanged = false; int32_t ret = NO_ERROR; DISPLAY_LOGD(eDebugResourceManager, "[%d] layer is added to client composition", layerIndex); if (mClientCompositionInfo.mHasCompositionLayer == false) { mClientCompositionInfo.mFirstIndex = layerIndex; mClientCompositionInfo.mLastIndex = layerIndex; mClientCompositionInfo.mHasCompositionLayer = true; return EXYNOS_ERROR_CHANGED; } else { mClientCompositionInfo.mFirstIndex = min(mClientCompositionInfo.mFirstIndex, (int32_t)layerIndex); mClientCompositionInfo.mLastIndex = max(mClientCompositionInfo.mLastIndex, (int32_t)layerIndex); } DISPLAY_LOGD(eDebugResourceManager, "\tClient composition range [%d] - [%d]", mClientCompositionInfo.mFirstIndex, mClientCompositionInfo.mLastIndex); if ((mClientCompositionInfo.mFirstIndex < 0) || (mClientCompositionInfo.mLastIndex < 0)) { HWC_LOGE(this, "%s:: mClientCompositionInfo.mHasCompositionLayer is true " "but index is not valid (firstIndex: %d, lastIndex: %d)", __func__, mClientCompositionInfo.mFirstIndex, mClientCompositionInfo.mLastIndex); return -EINVAL; } /* handle sandwiched layers */ for (uint32_t i = (uint32_t)mClientCompositionInfo.mFirstIndex + 1; i < (uint32_t)mClientCompositionInfo.mLastIndex; i++) { ExynosLayer *layer = mLayers[i]; if (layer->needClearClientTarget()) { DISPLAY_LOGD(eDebugResourceManager, "\t[%d] layer is opaque and has high or max priority (%d)", i, layer->mOverlayPriority); continue; } if (layer->mValidateCompositionType != HWC2_COMPOSITION_CLIENT) { DISPLAY_LOGD(eDebugResourceManager, "\t[%d] layer changed", i); if (layer->mValidateCompositionType == HWC2_COMPOSITION_EXYNOS) exynosCompositionChanged = true; else { if (layer->mValidateCompositionType == HWC2_COMPOSITION_DEVICE) mWindowNumUsed--; } layer->resetAssignedResource(); layer->mValidateCompositionType = HWC2_COMPOSITION_CLIENT; layer->mOverlayInfo |= eSandwitchedBetweenGLES; } } /* Check Exynos Composition info is changed */ if (exynosCompositionChanged) { DISPLAY_LOGD(eDebugResourceManager, "exynos composition [%d] - [%d] is changed", mExynosCompositionInfo.mFirstIndex, mExynosCompositionInfo.mLastIndex); uint32_t newFirstIndex = ~0; int32_t newLastIndex = -1; if ((mExynosCompositionInfo.mFirstIndex < 0) || (mExynosCompositionInfo.mLastIndex < 0)) { HWC_LOGE(this, "%s:: mExynosCompositionInfo.mHasCompositionLayer should be true(%d) " "but index is not valid (firstIndex: %d, lastIndex: %d)", __func__, mExynosCompositionInfo.mHasCompositionLayer, mExynosCompositionInfo.mFirstIndex, mExynosCompositionInfo.mLastIndex); return -EINVAL; } for (uint32_t i = 0; i < mLayers.size(); i++) { ExynosLayer *exynosLayer = mLayers[i]; if (exynosLayer->mValidateCompositionType == HWC2_COMPOSITION_EXYNOS) { newFirstIndex = min(newFirstIndex, i); newLastIndex = max(newLastIndex, (int32_t)i); } } DISPLAY_LOGD(eDebugResourceManager, "changed exynos composition [%d] - [%d]", newFirstIndex, newLastIndex); /* There is no exynos composition layer */ if (newFirstIndex == (uint32_t)~0) { mExynosCompositionInfo.initializeInfos(this); ret = EXYNOS_ERROR_CHANGED; } else { mExynosCompositionInfo.mFirstIndex = newFirstIndex; mExynosCompositionInfo.mLastIndex = newLastIndex; } } DISPLAY_LOGD(eDebugResourceManager, "\tresult changeFlag(0x%8x)", ret); DISPLAY_LOGD(eDebugResourceManager, "\tClient composition(%d) range [%d] - [%d]", mClientCompositionInfo.mHasCompositionLayer, mClientCompositionInfo.mFirstIndex, mClientCompositionInfo.mLastIndex); DISPLAY_LOGD(eDebugResourceManager, "\tExynos composition(%d) range [%d] - [%d]", mExynosCompositionInfo.mHasCompositionLayer, mExynosCompositionInfo.mFirstIndex, mExynosCompositionInfo.mLastIndex); return ret; } int32_t ExynosDisplay::removeClientCompositionLayer(uint32_t layerIndex) { int32_t ret = NO_ERROR; DISPLAY_LOGD(eDebugResourceManager, "[%d] - [%d] [%d] layer is removed from client composition", mClientCompositionInfo.mFirstIndex, mClientCompositionInfo.mLastIndex, layerIndex); /* Only first layer or last layer can be removed */ if ((mClientCompositionInfo.mHasCompositionLayer == false) || ((mClientCompositionInfo.mFirstIndex != (int32_t)layerIndex) && (mClientCompositionInfo.mLastIndex != (int32_t)layerIndex))) { DISPLAY_LOGE("removeClientCompositionLayer() error, [%d] - [%d], layer[%d]", mClientCompositionInfo.mFirstIndex, mClientCompositionInfo.mLastIndex, layerIndex); return -EINVAL; } if (mClientCompositionInfo.mFirstIndex == mClientCompositionInfo.mLastIndex) { ExynosMPP *otfMPP = mClientCompositionInfo.mOtfMPP; if (otfMPP != NULL) otfMPP->resetAssignedState(); else { DISPLAY_LOGE("mClientCompositionInfo.mOtfMPP is NULL"); return -EINVAL; } mClientCompositionInfo.initializeInfos(this); mWindowNumUsed--; } else if ((int32_t)layerIndex == mClientCompositionInfo.mFirstIndex) mClientCompositionInfo.mFirstIndex++; else mClientCompositionInfo.mLastIndex--; DISPLAY_LOGD(eDebugResourceManager, "\tClient composition(%d) range [%d] - [%d]", mClientCompositionInfo.mHasCompositionLayer, mClientCompositionInfo.mFirstIndex, mClientCompositionInfo.mLastIndex); return ret; } bool ExynosDisplay::hasClientComposition() { return mClientCompositionInfo.mHasCompositionLayer; } int32_t ExynosDisplay::addExynosCompositionLayer(uint32_t layerIndex, float totalUsedCapa) { bool invalidFlag = false; int32_t changeFlag = NO_ERROR; int ret = 0; int32_t startIndex; int32_t endIndex; DISPLAY_LOGD(eDebugResourceManager, "[%d] layer is added to exynos composition", layerIndex); if (mExynosCompositionInfo.mHasCompositionLayer == false) { mExynosCompositionInfo.mFirstIndex = layerIndex; mExynosCompositionInfo.mLastIndex = layerIndex; mExynosCompositionInfo.mHasCompositionLayer = true; return EXYNOS_ERROR_CHANGED; } else { mExynosCompositionInfo.mFirstIndex = min(mExynosCompositionInfo.mFirstIndex, (int32_t)layerIndex); mExynosCompositionInfo.mLastIndex = max(mExynosCompositionInfo.mLastIndex, (int32_t)layerIndex); } DISPLAY_LOGD(eDebugResourceManager, "\tExynos composition range [%d] - [%d]", mExynosCompositionInfo.mFirstIndex, mExynosCompositionInfo.mLastIndex); ExynosMPP *m2mMPP = mExynosCompositionInfo.mM2mMPP; if (m2mMPP == NULL) { DISPLAY_LOGE("exynosComposition m2mMPP is NULL"); return -EINVAL; } startIndex = mExynosCompositionInfo.mFirstIndex; endIndex = mExynosCompositionInfo.mLastIndex; if ((startIndex < 0) || (endIndex < 0) || (startIndex >= (int32_t)mLayers.size()) || (endIndex >= (int32_t)mLayers.size())) { DISPLAY_LOGE("exynosComposition invalid index (%d), (%d)", startIndex, endIndex); return -EINVAL; } int32_t maxPriorityIndex = -1; uint32_t highPriorityIndex = 0; uint32_t highPriorityNum = 0; int32_t highPriorityCheck = 0; std::vector highPriority; highPriority.assign(mLayers.size(), -1); /* handle sandwiched layers */ for (int32_t i = startIndex; i <= endIndex; i++) { ExynosLayer *layer = mLayers[i]; if (layer == NULL) { DISPLAY_LOGE("layer[%d] layer is null", i); continue; } if (layer->mOverlayPriority == ePriorityMax && m2mMPP->mLogicalType == MPP_LOGICAL_G2D_COMBO) { DISPLAY_LOGD(eDebugResourceManager, "\tG2D will be assgined for only [%d] layer", i); invalidFlag = true; maxPriorityIndex = i; continue; } if (layer->mOverlayPriority >= ePriorityHigh) { DISPLAY_LOGD(eDebugResourceManager, "\t[%d] layer has high priority", i); highPriority[highPriorityIndex++] = i; highPriorityNum++; continue; } if (layer->mValidateCompositionType == HWC2_COMPOSITION_EXYNOS) continue; exynos_image src_img; exynos_image dst_img; layer->setSrcExynosImage(&src_img); layer->setDstExynosImage(&dst_img); layer->setExynosMidImage(dst_img); bool isAssignable = false; if ((layer->mSupportedMPPFlag & m2mMPP->mLogicalType) != 0) isAssignable = m2mMPP->isAssignable(this, src_img, dst_img, totalUsedCapa); if (layer->mValidateCompositionType == HWC2_COMPOSITION_CLIENT) { DISPLAY_LOGD(eDebugResourceManager, "\t[%d] layer is client composition", i); invalidFlag = true; } else if (((layer->mSupportedMPPFlag & m2mMPP->mLogicalType) == 0) || (isAssignable == false)) { DISPLAY_LOGD(eDebugResourceManager, "\t[%d] layer is not supported by G2D", i); invalidFlag = true; layer->resetAssignedResource(); layer->mValidateCompositionType = HWC2_COMPOSITION_CLIENT; if ((ret = addClientCompositionLayer(i)) < 0) return ret; changeFlag |= ret; } else if ((layer->mValidateCompositionType == HWC2_COMPOSITION_DEVICE) || (layer->mValidateCompositionType == HWC2_COMPOSITION_INVALID)) { DISPLAY_LOGD(eDebugResourceManager, "\t[%d] layer changed", i); layer->mOverlayInfo |= eSandwitchedBetweenEXYNOS; layer->resetAssignedResource(); if ((ret = m2mMPP->assignMPP(this, layer)) != NO_ERROR) { HWC_LOGE(this, "%s:: %s MPP assignMPP() error (%d)", __func__, m2mMPP->mName.c_str(), ret); return ret; } if (layer->mValidateCompositionType == HWC2_COMPOSITION_DEVICE) mWindowNumUsed--; layer->mValidateCompositionType = HWC2_COMPOSITION_EXYNOS; mExynosCompositionInfo.mFirstIndex = min(mExynosCompositionInfo.mFirstIndex, (int32_t)i); mExynosCompositionInfo.mLastIndex = max(mExynosCompositionInfo.mLastIndex, (int32_t)i); } else { DISPLAY_LOGD(eDebugResourceManager, "\t[%d] layer has known type (%d)", i, layer->mValidateCompositionType); } } if (invalidFlag) { DISPLAY_LOGD(eDebugResourceManager, "\tClient composition range [%d] - [%d]", mClientCompositionInfo.mFirstIndex, mClientCompositionInfo.mLastIndex); DISPLAY_LOGD(eDebugResourceManager, "\tExynos composition range [%d] - [%d], highPriorityNum[%d]", mExynosCompositionInfo.mFirstIndex, mExynosCompositionInfo.mLastIndex, highPriorityNum); if (m2mMPP->mLogicalType == MPP_LOGICAL_G2D_COMBO && maxPriorityIndex >= 0) { startIndex = mExynosCompositionInfo.mFirstIndex; endIndex = mExynosCompositionInfo.mLastIndex; for (int32_t i = startIndex; i <= endIndex; i++) { if (mLayers[i]->mOverlayPriority == ePriorityMax || mLayers[i]->mValidateCompositionType == HWC2_COMPOSITION_CLIENT) continue; mLayers[i]->resetAssignedResource(); mLayers[i]->mValidateCompositionType = HWC2_COMPOSITION_CLIENT; if ((ret = addClientCompositionLayer(i)) < 0) return ret; changeFlag |= ret; } if (mLayers[maxPriorityIndex]->mValidateCompositionType != HWC2_COMPOSITION_EXYNOS) { mLayers[maxPriorityIndex]->mValidateCompositionType = HWC2_COMPOSITION_EXYNOS; mLayers[maxPriorityIndex]->resetAssignedResource(); if ((ret = m2mMPP->assignMPP(this, mLayers[maxPriorityIndex])) != NO_ERROR) { ALOGE("%s:: %s MPP assignMPP() error (%d)", __func__, m2mMPP->mName.c_str(), ret); return ret; } } mExynosCompositionInfo.mFirstIndex = maxPriorityIndex; mExynosCompositionInfo.mLastIndex = maxPriorityIndex; } /* Check if exynos comosition nests GLES composition */ if ((mClientCompositionInfo.mHasCompositionLayer) && (mExynosCompositionInfo.mFirstIndex < mClientCompositionInfo.mFirstIndex) && (mClientCompositionInfo.mFirstIndex < mExynosCompositionInfo.mLastIndex) && (mExynosCompositionInfo.mFirstIndex < mClientCompositionInfo.mLastIndex) && (mClientCompositionInfo.mLastIndex < mExynosCompositionInfo.mLastIndex)) { if ((mClientCompositionInfo.mFirstIndex - mExynosCompositionInfo.mFirstIndex) < (mExynosCompositionInfo.mLastIndex - mClientCompositionInfo.mLastIndex)) { mLayers[mExynosCompositionInfo.mFirstIndex]->resetAssignedResource(); mLayers[mExynosCompositionInfo.mFirstIndex]->mValidateCompositionType = HWC2_COMPOSITION_CLIENT; if ((ret = addClientCompositionLayer(mExynosCompositionInfo.mFirstIndex)) < 0) return ret; mExynosCompositionInfo.mFirstIndex = mClientCompositionInfo.mLastIndex + 1; changeFlag |= ret; } else { mLayers[mExynosCompositionInfo.mLastIndex]->resetAssignedResource(); mLayers[mExynosCompositionInfo.mLastIndex]->mValidateCompositionType = HWC2_COMPOSITION_CLIENT; if ((ret = addClientCompositionLayer(mExynosCompositionInfo.mLastIndex)) < 0) return ret; mExynosCompositionInfo.mLastIndex = (mClientCompositionInfo.mFirstIndex - 1); changeFlag |= ret; } } } if (highPriorityNum > 0 && (m2mMPP->mLogicalType != MPP_LOGICAL_G2D_COMBO)) { for (uint32_t i = 0; i < highPriorityNum; i++) { if ((int32_t)highPriority[i] == mExynosCompositionInfo.mFirstIndex) mExynosCompositionInfo.mFirstIndex++; else if ((int32_t)highPriority[i] == mExynosCompositionInfo.mLastIndex) mExynosCompositionInfo.mLastIndex--; } } if ((mExynosCompositionInfo.mFirstIndex < 0) || (mExynosCompositionInfo.mFirstIndex >= (int)mLayers.size()) || (mExynosCompositionInfo.mLastIndex < 0) || (mExynosCompositionInfo.mLastIndex >= (int)mLayers.size()) || (mExynosCompositionInfo.mFirstIndex > mExynosCompositionInfo.mLastIndex)) { DISPLAY_LOGD(eDebugResourceManager, "\texynos composition is disabled, because of invalid index (%d, %d), size(%zu)", mExynosCompositionInfo.mFirstIndex, mExynosCompositionInfo.mLastIndex, mLayers.size()); mExynosCompositionInfo.initializeInfos(this); changeFlag = EXYNOS_ERROR_CHANGED; } for (uint32_t i = 0; i < highPriorityNum; i++) { if ((mExynosCompositionInfo.mFirstIndex < (int32_t)highPriority[i]) && ((int32_t)highPriority[i] < mExynosCompositionInfo.mLastIndex)) { highPriorityCheck = 1; break; } } if (highPriorityCheck && (m2mMPP->mLogicalType != MPP_LOGICAL_G2D_COMBO)) { startIndex = mExynosCompositionInfo.mFirstIndex; endIndex = mExynosCompositionInfo.mLastIndex; DISPLAY_LOGD(eDebugResourceManager, "\texynos composition is disabled because of sandwitched max priority layer (%d, %d)", mExynosCompositionInfo.mFirstIndex, mExynosCompositionInfo.mLastIndex); for (int32_t i = startIndex; i <= endIndex; i++) { int32_t checkPri = 0; for (uint32_t j = 0; j < highPriorityNum; j++) { if (i == (int32_t)highPriority[j]) { checkPri = 1; break; } } if (checkPri) continue; mLayers[i]->resetAssignedResource(); mLayers[i]->mValidateCompositionType = HWC2_COMPOSITION_CLIENT; if ((ret = addClientCompositionLayer(i)) < 0) HWC_LOGE(this, "%d layer: addClientCompositionLayer() fail", i); } mExynosCompositionInfo.initializeInfos(this); changeFlag = EXYNOS_ERROR_CHANGED; } DISPLAY_LOGD(eDebugResourceManager, "\tresult changeFlag(0x%8x)", changeFlag); DISPLAY_LOGD(eDebugResourceManager, "\tClient composition range [%d] - [%d]", mClientCompositionInfo.mFirstIndex, mClientCompositionInfo.mLastIndex); DISPLAY_LOGD(eDebugResourceManager, "\tExynos composition range [%d] - [%d]", mExynosCompositionInfo.mFirstIndex, mExynosCompositionInfo.mLastIndex); return changeFlag; } bool ExynosDisplay::isPowerModeOff() const { ATRACE_CALL(); Mutex::Autolock lock(mDisplayMutex); return mPowerModeState.has_value() && mPowerModeState.value() == HWC2_POWER_MODE_OFF; } bool ExynosDisplay::isSecureContentPresenting() const { ATRACE_CALL(); Mutex::Autolock lock(mDRMutex); for (uint32_t i = 0; i < mLayers.size(); i++) { ExynosLayer *layer = mLayers[i]; if (layer != NULL && layer->isDrm()) { /* there is some DRM layer */ return true; } } return false; } bool ExynosDisplay::windowUpdateExceptions() { if (mExynosCompositionInfo.mHasCompositionLayer) { DISPLAY_LOGD(eDebugWindowUpdate, "has exynos composition"); return true; } if (mClientCompositionInfo.mHasCompositionLayer) { DISPLAY_LOGD(eDebugWindowUpdate, "has client composition"); return true; } for (size_t i = 0; i < mLayers.size(); i++) { if (mLayers[i]->mM2mMPP != NULL) return true; if (mLayers[i]->mLayerBuffer == NULL) return true; if (mLayers[i]->mTransform != 0) return true; } for (size_t i = 0; i < mDpuData.configs.size(); i++) { exynos_win_config_data &config = mDpuData.configs[i]; if (config.state == config.WIN_STATE_BUFFER) { if (config.src.w/config.dst.w != 1 || config.src.h/config.dst.h != 1) { DISPLAY_LOGD(eDebugWindowUpdate, "Skip reason : scaled"); return true; } } } return false; } int ExynosDisplay::handleWindowUpdate() { int ret = NO_ERROR; // TODO will be implemented unsigned int excp; mDpuData.enable_win_update = false; /* Init with full size */ mDpuData.win_update_region.x = 0; mDpuData.win_update_region.w = mXres; mDpuData.win_update_region.y = 0; mDpuData.win_update_region.h = mYres; if (exynosHWCControl.windowUpdate != 1) return 0; if (mGeometryChanged != 0) { DISPLAY_LOGD(eDebugWindowUpdate, "GEOMETRY chnaged 0x%" PRIx64 "", mGeometryChanged); return 0; } if ((mCursorIndex >= 0) && (mCursorIndex < (int32_t)mLayers.size())) { ExynosLayer *layer = mLayers[mCursorIndex]; /* Cursor layer is enabled */ if (layer->mExynosCompositionType == HWC2_COMPOSITION_DEVICE) { return 0; } } /* exceptions */ if (windowUpdateExceptions()) return 0; hwc_rect mergedRect = {(int)mXres, (int)mYres, 0, 0}; hwc_rect damageRect = {(int)mXres, (int)mYres, 0, 0}; for (size_t i = 0; i < mLayers.size(); i++) { if (mLayers[i]->mExynosCompositionType == HWC2_COMPOSITION_DISPLAY_DECORATION) { continue; } excp = getLayerRegion(mLayers[i], &damageRect, eDamageRegionByDamage); if (excp == eDamageRegionPartial) { DISPLAY_LOGD(eDebugWindowUpdate, "layer(%zu) partial : %d, %d, %d, %d", i, damageRect.left, damageRect.top, damageRect.right, damageRect.bottom); mergedRect = expand(mergedRect, damageRect); } else if (excp == eDamageRegionSkip) { int32_t windowIndex = mLayers[i]->mWindowIndex; if ((ret = checkConfigDstChanged(mDpuData, mLastDpuData, windowIndex)) < 0) { return 0; } else if (ret > 0) { damageRect.left = mLayers[i]->mDisplayFrame.left; damageRect.right = mLayers[i]->mDisplayFrame.right; damageRect.top = mLayers[i]->mDisplayFrame.top; damageRect.bottom = mLayers[i]->mDisplayFrame.bottom; DISPLAY_LOGD(eDebugWindowUpdate, "Skip layer (origin) : %d, %d, %d, %d", damageRect.left, damageRect.top, damageRect.right, damageRect.bottom); mergedRect = expand(mergedRect, damageRect); hwc_rect prevDst = {mLastDpuData.configs[i].dst.x, mLastDpuData.configs[i].dst.y, mLastDpuData.configs[i].dst.x + (int)mLastDpuData.configs[i].dst.w, mLastDpuData.configs[i].dst.y + (int)mLastDpuData.configs[i].dst.h}; mergedRect = expand(mergedRect, prevDst); } else { DISPLAY_LOGD(eDebugWindowUpdate, "layer(%zu) skip", i); continue; } } else if (excp == eDamageRegionFull) { damageRect.left = mLayers[i]->mDisplayFrame.left; damageRect.top = mLayers[i]->mDisplayFrame.top; damageRect.right = mLayers[i]->mDisplayFrame.right; damageRect.bottom = mLayers[i]->mDisplayFrame.bottom; DISPLAY_LOGD(eDebugWindowUpdate, "Full layer update : %d, %d, %d, %d", mLayers[i]->mDisplayFrame.left, mLayers[i]->mDisplayFrame.top, mLayers[i]->mDisplayFrame.right, mLayers[i]->mDisplayFrame.bottom); mergedRect = expand(mergedRect, damageRect); } else { DISPLAY_LOGD(eDebugWindowUpdate, "Partial canceled, Skip reason (layer %zu) : %d", i, excp); return 0; } } if (mergedRect.left == (int32_t)mXres && mergedRect.right == 0 && mergedRect.top == (int32_t)mYres && mergedRect.bottom == 0) { DISPLAY_LOGD(eDebugWindowUpdate, "Partial canceled, All layer skiped" ); return 0; } DISPLAY_LOGD(eDebugWindowUpdate, "Partial(origin) : %d, %d, %d, %d", mergedRect.left, mergedRect.top, mergedRect.right, mergedRect.bottom); if (mergedRect.left < 0) mergedRect.left = 0; if (mergedRect.right > (int32_t)mXres) mergedRect.right = mXres; if (mergedRect.top < 0) mergedRect.top = 0; if (mergedRect.bottom > (int32_t)mYres) mergedRect.bottom = mYres; if (mergedRect.left == 0 && mergedRect.right == (int32_t)mXres && mergedRect.top == 0 && mergedRect.bottom == (int32_t)mYres) { DISPLAY_LOGD(eDebugWindowUpdate, "Partial : Full size"); mDpuData.enable_win_update = true; mDpuData.win_update_region.x = 0; mDpuData.win_update_region.w = mXres; mDpuData.win_update_region.y = 0; mDpuData.win_update_region.h = mYres; DISPLAY_LOGD(eDebugWindowUpdate, "window update end ------------------"); return 0; } mDpuData.enable_win_update = true; mDpuData.win_update_region.x = mergedRect.left; mDpuData.win_update_region.w = WIDTH(mergedRect); mDpuData.win_update_region.y = mergedRect.top; mDpuData.win_update_region.h = HEIGHT(mergedRect); DISPLAY_LOGD(eDebugWindowUpdate, "window update end ------------------"); return 0; } unsigned int ExynosDisplay::getLayerRegion(ExynosLayer *layer, hwc_rect *rect_area, uint32_t regionType) { android::Vector hwcRects; size_t numRects = 0; rect_area->left = INT_MAX; rect_area->top = INT_MAX; rect_area->right = rect_area->bottom = 0; hwcRects = layer->mDamageRects; numRects = layer->mDamageNum; if ((numRects == 0) || (hwcRects.size() == 0)) return eDamageRegionFull; if ((numRects == 1) && (hwcRects[0].left == 0) && (hwcRects[0].top == 0) && (hwcRects[0].right == 0) && (hwcRects[0].bottom == 0)) return eDamageRegionSkip; switch (regionType) { case eDamageRegionByDamage: for (size_t j = 0; j < hwcRects.size(); j++) { hwc_rect_t rect; if ((hwcRects[j].left < 0) || (hwcRects[j].top < 0) || (hwcRects[j].right < 0) || (hwcRects[j].bottom < 0) || (hwcRects[j].left >= hwcRects[j].right) || (hwcRects[j].top >= hwcRects[j].bottom) || (hwcRects[j].right - hwcRects[j].left > WIDTH(layer->mSourceCrop)) || (hwcRects[j].bottom - hwcRects[j].top > HEIGHT(layer->mSourceCrop))) { rect_area->left = INT_MAX; rect_area->top = INT_MAX; rect_area->right = rect_area->bottom = 0; return eDamageRegionFull; } rect.left = layer->mDisplayFrame.left + hwcRects[j].left - layer->mSourceCrop.left; rect.top = layer->mDisplayFrame.top + hwcRects[j].top - layer->mSourceCrop.top; rect.right = layer->mDisplayFrame.left + hwcRects[j].right - layer->mSourceCrop.left; rect.bottom = layer->mDisplayFrame.top + hwcRects[j].bottom - layer->mSourceCrop.top; DISPLAY_LOGD(eDebugWindowUpdate, "Display frame : %d, %d, %d, %d", layer->mDisplayFrame.left, layer->mDisplayFrame.top, layer->mDisplayFrame.right, layer->mDisplayFrame.bottom); DISPLAY_LOGD(eDebugWindowUpdate, "hwcRects : %d, %d, %d, %d", hwcRects[j].left, hwcRects[j].top, hwcRects[j].right, hwcRects[j].bottom); adjustRect(rect, INT_MAX, INT_MAX); /* Get sums of rects */ *rect_area = expand(*rect_area, rect); } return eDamageRegionPartial; break; case eDamageRegionByLayer: if (layer->mLastLayerBuffer != layer->mLayerBuffer) return eDamageRegionFull; else return eDamageRegionSkip; break; default: HWC_LOGE(this, "%s:: Invalid regionType (%d)", __func__, regionType); return eDamageRegionError; break; } return eDamageRegionFull; } uint32_t ExynosDisplay::getRestrictionIndex(int halFormat) { if (isFormatRgb(halFormat)) return RESTRICTION_RGB; else return RESTRICTION_YUV; } void ExynosDisplay::closeFencesForSkipFrame(rendering_state renderingState) { for (size_t i=0; i < mLayers.size(); i++) { if (mLayers[i]->mAcquireFence != -1) { mLayers[i]->mAcquireFence = fence_close(mLayers[i]->mAcquireFence, this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_LAYER); } } if (mDpuData.readback_info.rel_fence >= 0) { mDpuData.readback_info.rel_fence = fence_close(mDpuData.readback_info.rel_fence, this, FENCE_TYPE_READBACK_RELEASE, FENCE_IP_FB); } if (mDpuData.readback_info.acq_fence >= 0) { mDpuData.readback_info.acq_fence = fence_close(mDpuData.readback_info.acq_fence, this, FENCE_TYPE_READBACK_ACQUIRE, FENCE_IP_DPP); } if (renderingState >= RENDERING_STATE_VALIDATED) { if (mDisplayControl.earlyStartMPP == true) { if (mExynosCompositionInfo.mHasCompositionLayer) { /* * m2mMPP's release fence for dst buffer was set to * mExynosCompositionInfo.mAcquireFence by startPostProcessing() * in validate time. * This fence should be passed to display driver * but it wont't because this frame will not be presented. * So fence should be closed. */ mExynosCompositionInfo.mAcquireFence = fence_close(mExynosCompositionInfo.mAcquireFence, this, FENCE_TYPE_DST_RELEASE, FENCE_IP_G2D); } for (size_t i = 0; i < mLayers.size(); i++) { exynos_image outImage; ExynosMPP* m2mMPP = mLayers[i]->mM2mMPP; if ((mLayers[i]->mValidateCompositionType == HWC2_COMPOSITION_DEVICE) && (m2mMPP != NULL) && (m2mMPP->mAssignedDisplay == this) && (m2mMPP->getDstImageInfo(&outImage) == NO_ERROR)) { if (m2mMPP->mPhysicalType == MPP_MSC) { fence_close(outImage.releaseFenceFd, this, FENCE_TYPE_DST_RELEASE, FENCE_IP_MSC); } else if (m2mMPP->mPhysicalType == MPP_G2D) { ALOGD("close(%d)", outImage.releaseFenceFd); fence_close(outImage.releaseFenceFd, this, FENCE_TYPE_DST_RELEASE, FENCE_IP_G2D); } else { DISPLAY_LOGE("[%zu] layer has invalid mppType(%d)", i, m2mMPP->mPhysicalType); fence_close(outImage.releaseFenceFd, this, FENCE_TYPE_DST_RELEASE, FENCE_IP_ALL); } m2mMPP->resetDstReleaseFence(); ALOGD("reset buf[%d], %d", m2mMPP->mCurrentDstBuf, m2mMPP->mDstImgs[m2mMPP->mCurrentDstBuf].acrylicReleaseFenceFd); } } } } if (renderingState >= RENDERING_STATE_PRESENTED) { /* mAcquireFence is set after validate */ mClientCompositionInfo.mAcquireFence = fence_close(mClientCompositionInfo.mAcquireFence, this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_FB); } } void ExynosDisplay::closeFences() { for (auto &config : mDpuData.configs) { if (config.acq_fence != -1) fence_close(config.acq_fence, this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_DPP); config.acq_fence = -1; if (config.rel_fence >= 0) fence_close(config.rel_fence, this, FENCE_TYPE_SRC_RELEASE, FENCE_IP_DPP); config.rel_fence = -1; } for (auto &config : mDpuData.rcdConfigs) { if (config.acq_fence != -1) fence_close(config.acq_fence, this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_DPP); config.acq_fence = -1; if (config.rel_fence >= 0) fence_close(config.rel_fence, this, FENCE_TYPE_SRC_RELEASE, FENCE_IP_DPP); config.rel_fence = -1; } for (size_t i = 0; i < mLayers.size(); i++) { if (mLayers[i]->mReleaseFence > 0) { fence_close(mLayers[i]->mReleaseFence, this, FENCE_TYPE_SRC_RELEASE, FENCE_IP_LAYER); mLayers[i]->mReleaseFence = -1; } if ((mLayers[i]->mExynosCompositionType == HWC2_COMPOSITION_DEVICE) && (mLayers[i]->mM2mMPP != NULL)) { mLayers[i]->mM2mMPP->closeFences(); } } if (mExynosCompositionInfo.mHasCompositionLayer) { if (mExynosCompositionInfo.mM2mMPP == NULL) { DISPLAY_LOGE("There is exynos composition, but m2mMPP is NULL"); return; } mExynosCompositionInfo.mM2mMPP->closeFences(); } for (size_t i=0; i < mLayers.size(); i++) { if (mLayers[i]->mAcquireFence != -1) { mLayers[i]->mAcquireFence = fence_close(mLayers[i]->mAcquireFence, this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_LAYER); } } mExynosCompositionInfo.mAcquireFence = fence_close(mExynosCompositionInfo.mAcquireFence, this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_G2D); mClientCompositionInfo.mAcquireFence = fence_close(mClientCompositionInfo.mAcquireFence, this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_FB); if (mDpuData.retire_fence > 0) fence_close(mDpuData.retire_fence, this, FENCE_TYPE_RETIRE, FENCE_IP_DPP); mDpuData.retire_fence = -1; mLastRetireFence = fence_close(mLastRetireFence, this, FENCE_TYPE_RETIRE, FENCE_IP_DPP); if (mDpuData.readback_info.rel_fence >= 0) { mDpuData.readback_info.rel_fence = fence_close(mDpuData.readback_info.rel_fence, this, FENCE_TYPE_READBACK_RELEASE, FENCE_IP_FB); } if (mDpuData.readback_info.acq_fence >= 0) { mDpuData.readback_info.acq_fence = fence_close(mDpuData.readback_info.acq_fence, this, FENCE_TYPE_READBACK_ACQUIRE, FENCE_IP_DPP); } } void ExynosDisplay::setHWCControl(uint32_t ctrl, int32_t val) { switch (ctrl) { case HWC_CTL_ENABLE_COMPOSITION_CROP: mDisplayControl.enableCompositionCrop = (unsigned int)val; break; case HWC_CTL_ENABLE_EXYNOSCOMPOSITION_OPT: mDisplayControl.enableExynosCompositionOptimization = (unsigned int)val; break; case HWC_CTL_ENABLE_CLIENTCOMPOSITION_OPT: mDisplayControl.enableClientCompositionOptimization = (unsigned int)val; break; case HWC_CTL_USE_MAX_G2D_SRC: mDisplayControl.useMaxG2DSrc = (unsigned int)val; break; case HWC_CTL_ENABLE_HANDLE_LOW_FPS: mDisplayControl.handleLowFpsLayers = (unsigned int)val; break; case HWC_CTL_ENABLE_EARLY_START_MPP: mDisplayControl.earlyStartMPP = (unsigned int)val; break; default: ALOGE("%s: unsupported HWC_CTL (%d)", __func__, ctrl); break; } } int32_t ExynosDisplay::getHdrCapabilities(uint32_t* outNumTypes, int32_t* outTypes, float* outMaxLuminance, float* outMaxAverageLuminance, float* outMinLuminance) { DISPLAY_LOGD(eDebugHWC, "HWC2: %s, %d", __func__, __LINE__); if (outNumTypes == NULL || outMaxLuminance == NULL || outMaxAverageLuminance == NULL || outMinLuminance == NULL) { return HWC2_ERROR_BAD_PARAMETER; } if (outTypes == NULL) { /* * This function is always called twice. * outTypes is NULL in the first call and * outType is valid pointer in the second call. * Get information only in the first call. * Use saved information in the second call. */ if (mDisplayInterface->updateHdrCapabilities() != NO_ERROR) return HWC2_ERROR_BAD_CONFIG; } *outMaxLuminance = mMaxLuminance; *outMaxAverageLuminance = mMaxAverageLuminance; *outMinLuminance = mMinLuminance; if (outTypes == NULL) { *outNumTypes = mHdrTypes.size(); } else { if (*outNumTypes != mHdrTypes.size()) { ALOGE("%s:: Invalid parameter (outNumTypes: %d, mHdrTypes size: %zu", __func__, *outNumTypes, mHdrTypes.size()); return HWC2_ERROR_BAD_PARAMETER; } for(uint32_t i = 0; i < *outNumTypes; i++) { outTypes[i] = mHdrTypes[i]; } } return HWC2_ERROR_NONE; } int32_t ExynosDisplay::getMountOrientation(HwcMountOrientation *orientation) { if (!orientation) return HWC2_ERROR_BAD_PARAMETER; *orientation = mMountOrientation; return HWC2_ERROR_NONE; } std::optional ExynosDisplay::getVrrConfigs(hwc2_config_t config) { if (isBadConfig(config)) { return std::nullopt; } return mDisplayConfigs[config].vrrConfig; } // Support DDI scalser void ExynosDisplay::setDDIScalerEnable(int __unused width, int __unused height) { } int ExynosDisplay::getDDIScalerMode(int __unused width, int __unused height) { return 1; // WQHD } void ExynosDisplay::increaseMPPDstBufIndex() { for (size_t i=0; i < mLayers.size(); i++) { if((mLayers[i]->mExynosCompositionType == HWC2_COMPOSITION_DEVICE) && (mLayers[i]->mM2mMPP != NULL)) { mLayers[i]->mM2mMPP->increaseDstBuffIndex(); } } if ((mExynosCompositionInfo.mHasCompositionLayer) && (mExynosCompositionInfo.mM2mMPP != NULL)) { mExynosCompositionInfo.mM2mMPP->increaseDstBuffIndex(); } } int32_t ExynosDisplay::getReadbackBufferAttributes(int32_t* /*android_pixel_format_t*/ outFormat, int32_t* /*android_dataspace_t*/ outDataspace) { int32_t ret = mDisplayInterface->getReadbackBufferAttributes(outFormat, outDataspace); if (ret == NO_ERROR) { /* Interface didn't specific set dataspace */ if (*outDataspace == HAL_DATASPACE_UNKNOWN) *outDataspace = colorModeToDataspace(mColorMode); /* Set default value */ if (*outDataspace == HAL_DATASPACE_UNKNOWN) *outDataspace = HAL_DATASPACE_V0_SRGB; mDisplayControl.readbackSupport = true; ALOGI("readback info: format(0x%8x), dataspace(0x%8x)", *outFormat, *outDataspace); } else { mDisplayControl.readbackSupport = false; ALOGI("readback is not supported, ret(%d)", ret); ret = HWC2_ERROR_UNSUPPORTED; } return ret; } int32_t ExynosDisplay::setReadbackBuffer(buffer_handle_t buffer, int32_t releaseFence, bool requestedService) { Mutex::Autolock lock(mDisplayMutex); int32_t ret = NO_ERROR; if (buffer == nullptr) return HWC2_ERROR_BAD_PARAMETER; if (mDisplayControl.readbackSupport) { mDpuData.enable_readback = true; } else { DISPLAY_LOGE("readback is not supported but setReadbackBuffer is called, buffer(%p), releaseFence(%d)", buffer, releaseFence); if (releaseFence >= 0) releaseFence = fence_close(releaseFence, this, FENCE_TYPE_READBACK_RELEASE, FENCE_IP_FB); mDpuData.enable_readback = false; ret = HWC2_ERROR_UNSUPPORTED; } setReadbackBufferInternal(buffer, releaseFence, requestedService); return ret; } void ExynosDisplay::setReadbackBufferInternal(buffer_handle_t buffer, int32_t releaseFence, bool requestedService) { if (mDpuData.readback_info.rel_fence >= 0) { mDpuData.readback_info.rel_fence = fence_close(mDpuData.readback_info.rel_fence, this, FENCE_TYPE_READBACK_RELEASE, FENCE_IP_FB); DISPLAY_LOGE("previous readback release fence is not delivered to display device"); } if (releaseFence >= 0) { setFenceInfo(releaseFence, this, FENCE_TYPE_READBACK_RELEASE, FENCE_IP_FB, HwcFenceDirection::FROM); } mDpuData.readback_info.rel_fence = releaseFence; if (buffer != NULL) mDpuData.readback_info.handle = buffer; mDpuData.readback_info.requested_from_service = requestedService; } int32_t ExynosDisplay::getReadbackBufferFence(int32_t* outFence) { /* * acq_fence was not set or * it was already closed by error or frame skip */ if (mDpuData.readback_info.acq_fence < 0) { *outFence = -1; return HWC2_ERROR_UNSUPPORTED; } *outFence = mDpuData.readback_info.acq_fence; /* Fence will be closed by caller of this function */ mDpuData.readback_info.acq_fence = -1; return NO_ERROR; } int32_t ExynosDisplay::setReadbackBufferAcqFence(int32_t acqFence) { if (mDpuData.readback_info.acq_fence >= 0) { mDpuData.readback_info.acq_fence = fence_close(mDpuData.readback_info.acq_fence, this, FENCE_TYPE_READBACK_ACQUIRE, FENCE_IP_DPP); DISPLAY_LOGE("previous readback out fence is not delivered to framework"); } mDpuData.readback_info.acq_fence = acqFence; if (acqFence >= 0) { /* * Requtester of readback will get acqFence after presentDisplay * so validateFences should not check this fence * in presentDisplay so this function sets pendingAllowed parameter. */ setFenceInfo(acqFence, this, FENCE_TYPE_READBACK_ACQUIRE, FENCE_IP_DPP, HwcFenceDirection::FROM, true); } return NO_ERROR; } void ExynosDisplay::initDisplayInterface(uint32_t __unused interfaceType) { mDisplayInterface = std::make_unique(); mDisplayInterface->init(this); } int32_t ExynosDisplay::uncacheLayerBuffers(ExynosLayer* layer, const std::vector& buffers, std::vector& outClearableBuffers) { if (mPowerModeState.has_value() && mPowerModeState.value() == HWC2_POWER_MODE_OFF) { for (auto buffer : buffers) { if (layer->mLayerBuffer == buffer) { layer->mLayerBuffer = nullptr; } if (layer->mLastLayerBuffer == buffer) { layer->mLastLayerBuffer = nullptr; } } outClearableBuffers = buffers; return mDisplayInterface->uncacheLayerBuffers(layer, outClearableBuffers); } return NO_ERROR; } void ExynosDisplay::traceLayerTypes() { size_t g2d_count = 0; size_t dpu_count = 0; size_t gpu_count = 0; size_t skip_count = 0; size_t rcd_count = 0; for(auto const& layer: mLayers) { switch (layer->mExynosCompositionType) { case HWC2_COMPOSITION_EXYNOS: g2d_count++; break; case HWC2_COMPOSITION_CLIENT: if (layer->mCompositionType == HWC2_COMPOSITION_DEVICE) { skip_count++; } else { gpu_count++; } break; case HWC2_COMPOSITION_DEVICE: dpu_count++; break; case HWC2_COMPOSITION_DISPLAY_DECORATION: ++rcd_count; break; default: ALOGW("%s: Unknown layer composition type: %d", __func__, layer->mExynosCompositionType); break; } } DISPLAY_ATRACE_INT("HWComposer: DPU Layer", dpu_count); DISPLAY_ATRACE_INT("HWComposer: G2D Layer", g2d_count); DISPLAY_ATRACE_INT("HWComposer: GPU Layer", gpu_count); DISPLAY_ATRACE_INT("HWComposer: RCD Layer", rcd_count); DISPLAY_ATRACE_INT("HWComposer: DPU Cached Layer", skip_count); DISPLAY_ATRACE_INT("HWComposer: SF Cached Layer", mIgnoreLayers.size()); DISPLAY_ATRACE_INT("HWComposer: Total Layer", mLayers.size() + mIgnoreLayers.size()); } void ExynosDisplay::updateBrightnessState() { static constexpr float kMaxCll = 10000.0; bool clientRgbHdr = false; bool instantHbm = false; bool sdrDim = false; BrightnessController::HdrLayerState hdrState = BrightnessController::HdrLayerState::kHdrNone; for (size_t i = 0; i < mLayers.size(); i++) { if (mLayers[i]->mIsHdrLayer) { // TODO(longling): Below code block for RGB HDR is obsolete also // need some fix (mExynosCompositionType is invalid at this time) if (mLayers[i]->isLayerFormatRgb()) { auto meta = mLayers[i]->getMetaParcel(); if ((meta != nullptr) && (meta->eType & VIDEO_INFO_TYPE_HDR_STATIC) && meta->sHdrStaticInfo.sType1.mMaxContentLightLevel >= kMaxCll) { // if there are one or more such layers and any one of them // is composed by GPU, we won't dim sdr layers if (mLayers[i]->mExynosCompositionType == HWC2_COMPOSITION_CLIENT) { clientRgbHdr = true; } else { instantHbm = true; } } } // If any HDR layer is large, keep the state as kHdrLarge if (hdrState != BrightnessController::HdrLayerState::kHdrLarge && mLayers[i]->getDisplayFrameArea() >= mHdrFullScrenAreaThreshold) { hdrState = BrightnessController::HdrLayerState::kHdrLarge; } else if (hdrState == BrightnessController::HdrLayerState::kHdrNone) { hdrState = BrightnessController::HdrLayerState::kHdrSmall; } // else keep the state (kHdrLarge or kHdrSmall) unchanged. } // SDR layers could be kept dimmed for a while after HDR is gone (DM // will animate the display brightness from HDR brightess to SDR brightness). if (mLayers[i]->mBrightness < 1.0) { sdrDim = true; } } if (mBrightnessController) { mBrightnessController->updateFrameStates(hdrState, sdrDim); mBrightnessController->processInstantHbm(instantHbm && !clientRgbHdr); mBrightnessController->updateCabcMode(); } } void ExynosDisplay::cleanupAfterClientDeath() { // Invalidate the client target buffer because it will be freed when the client dies mClientCompositionInfo.mTargetBuffer = NULL; // Invalidate the skip static flag so that we have to get a new target buffer first // before we can skip the static layers mClientCompositionInfo.mSkipStaticInitFlag = false; mClientCompositionInfo.mSkipFlag = false; } int32_t ExynosDisplay::flushDisplayBrightnessChange() { if (mBrightnessController) { setMinIdleRefreshRate(0, RrThrottleRequester::BRIGHTNESS); if (mOperationRateManager) { mOperationRateManager->onBrightness(mBrightnessController->getBrightnessLevel()); handleTargetOperationRate(); } return mBrightnessController->applyPendingChangeViaSysfs(mVsyncPeriod); } return NO_ERROR; } // we can cache the value once it is known to avoid the lock after boot // we also only actually check once per frame to keep the value internally consistent bool ExynosDisplay::usePowerHintSession() { if (!mUsePowerHintSession.has_value() && mPowerHalHint.checkPowerHintSessionReady()) { mUsePowerHintSession = mPowerHalHint.usePowerHintSession(); } return mUsePowerHintSession.value_or(false); } nsecs_t ExynosDisplay::getExpectedPresentTime(nsecs_t startTime) { ExynosDisplay *primaryDisplay = mDevice->getDisplay(HWC_DISPLAY_PRIMARY); if (primaryDisplay) { nsecs_t out = primaryDisplay->getPendingExpectedPresentTime(); if (out != 0) { return out; } } return getPredictedPresentTime(startTime); } nsecs_t ExynosDisplay::getPredictedPresentTime(nsecs_t startTime) { auto lastRetireFenceSignalTime = getSignalTime(mLastRetireFence); auto expectedPresentTime = startTime - 1; if (lastRetireFenceSignalTime != SIGNAL_TIME_INVALID && lastRetireFenceSignalTime != SIGNAL_TIME_PENDING) { expectedPresentTime = lastRetireFenceSignalTime + mVsyncPeriod; mRetireFencePreviousSignalTime = lastRetireFenceSignalTime; } else if (sync_wait(mLastRetireFence, 0) < 0) { // if last retire fence was not signaled, then try previous signal time if (mRetireFencePreviousSignalTime.has_value()) { expectedPresentTime = *mRetireFencePreviousSignalTime + 2 * mVsyncPeriod; } // the last retire fence acquire time can be useful where the previous fence signal time may // not be recorded yet, but it's only accurate when there is fence wait time if (mRetireFenceAcquireTime.has_value()) { expectedPresentTime = max(expectedPresentTime, *mRetireFenceAcquireTime + 2 * mVsyncPeriod); } } if (expectedPresentTime < startTime) { ALOGV("Could not predict expected present time, fall back on target of one vsync"); expectedPresentTime = startTime + mVsyncPeriod; } return expectedPresentTime; } nsecs_t ExynosDisplay::getSignalTime(int32_t fd) const { if (fd == -1) { return SIGNAL_TIME_INVALID; } struct sync_file_info *finfo = sync_file_info(fd); if (finfo == nullptr) { return SIGNAL_TIME_INVALID; } if (finfo->status != 1) { const auto status = finfo->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); 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 nsecs_t(timestamp); } std::optional ExynosDisplay::getPredictedDuration(bool duringValidation) { AveragesKey beforeFenceKey(mLayers.size(), duringValidation, true); AveragesKey afterFenceKey(mLayers.size(), duringValidation, false); if (mRollingAverages.count(beforeFenceKey) == 0 || mRollingAverages.count(afterFenceKey) == 0) { return std::nullopt; } nsecs_t beforeReleaseFence = mRollingAverages[beforeFenceKey].average; nsecs_t afterReleaseFence = mRollingAverages[afterFenceKey].average; return std::make_optional(afterReleaseFence + beforeReleaseFence); } void ExynosDisplay::updateAverages(nsecs_t endTime) { if (!mRetireFenceWaitTime.has_value() || !mRetireFenceAcquireTime.has_value()) { return; } nsecs_t beforeFenceTime = mValidationDuration.value_or(0) + (*mRetireFenceWaitTime - mPresentStartTime); nsecs_t afterFenceTime = endTime - *mRetireFenceAcquireTime; mRollingAverages[AveragesKey(mLayers.size(), mValidationDuration.has_value(), true)].insert( beforeFenceTime); mRollingAverages[AveragesKey(mLayers.size(), mValidationDuration.has_value(), false)].insert( afterFenceTime); } int32_t ExynosDisplay::getRCDLayerSupport(bool &outSupport) const { outSupport = mDebugRCDLayerEnabled && mDpuData.rcdConfigs.size() > 0; return NO_ERROR; } int32_t ExynosDisplay::setDebugRCDLayerEnabled(bool enable) { mDebugRCDLayerEnabled = enable; return NO_ERROR; } int32_t ExynosDisplay::getDisplayIdleTimerSupport(bool &outSupport) { return mDisplayInterface->getDisplayIdleTimerSupport(outSupport); } int32_t ExynosDisplay::getDisplayMultiThreadedPresentSupport(bool &outSupport) { outSupport = mDisplayControl.multiThreadedPresent; return NO_ERROR; } bool ExynosDisplay::isMixedComposition() { for (size_t i = 0; i < mLayers.size(); i++) { if (mLayers[i]->mBrightness < 1.0) { return true; } } return false; } int ExynosDisplay::lookupDisplayConfigs(const int32_t &width, const int32_t &height, const int32_t &fps, const int32_t &vsyncRate, int32_t *outConfig) { if (!fps || !vsyncRate) return HWC2_ERROR_BAD_CONFIG; constexpr auto nsecsPerMs = std::chrono::nanoseconds(1ms).count(); const auto vsyncPeriod = nsecsPerSec / vsyncRate; for (auto const& [config, mode] : mDisplayConfigs) { long delta = abs(vsyncPeriod - mode.vsyncPeriod); if ((width == 0 || width == mode.width) && (height == 0 || height == mode.height) && (delta < nsecsPerMs) && (fps == mode.refreshRate)) { ALOGD("%s: found display config for mode: %dx%d@%d:%d config=%d", __func__, width, height, fps, vsyncRate, config); *outConfig = config; return HWC2_ERROR_NONE; } } return HWC2_ERROR_BAD_CONFIG; } int ExynosDisplay::lookupDisplayConfigsRelaxed(const int32_t &width, const int32_t &height, const int32_t &fps, int32_t *outConfig) { if (fps <= 1) return HWC2_ERROR_BAD_CONFIG; const auto vsyncPeriod = nsecsPerSec / fps; const auto vsyncPeriodMin = nsecsPerSec / (fps + 1); const auto vsyncPeriodMax = nsecsPerSec / (fps - 1); // Search for exact match in resolution and vsync for (auto const& [config, mode] : mDisplayConfigs) { if (mode.width == width && mode.height == height && mode.vsyncPeriod == vsyncPeriod) { ALOGD("%s: found exact match for mode %dx%d@%d -> config=%d", __func__, width, height, fps, config); *outConfig = config; return HWC2_ERROR_NONE; } } // Search for exact match in resolution, allow small variance in vsync for (auto const& [config, mode] : mDisplayConfigs) { if (mode.width == width && mode.height == height && mode.vsyncPeriod >= vsyncPeriodMin && mode.vsyncPeriod <= vsyncPeriodMax) { ALOGD("%s: found close match for mode %dx%d@%d -> config=%d", __func__, width, height, fps, config); *outConfig = config; return HWC2_ERROR_NONE; } } // Search for smaller resolution, allow small variance in vsync // mDisplayConfigs is sorted, so this will give the largest available option for (auto const& [config, mode] : mDisplayConfigs) { if (mode.width <= width && mode.height <= height && mode.vsyncPeriod >= vsyncPeriodMin && mode.vsyncPeriod <= vsyncPeriodMax) { ALOGD("%s: found relaxed match for mode %dx%d@%d -> config=%d", __func__, width, height, fps, config); *outConfig = config; return HWC2_ERROR_NONE; } } return HWC2_ERROR_BAD_CONFIG; } FILE *ExynosDisplay::RotatingLogFileWriter::openLogFile(const std::string &filename, const std::string &mode) { FILE *file = nullptr; auto fullpath = std::string(ERROR_LOG_PATH0) + "/" + filename; file = fopen(fullpath.c_str(), mode.c_str()); if (file != nullptr) { return file; } ALOGE("Fail to open file %s, error: %s", fullpath.c_str(), strerror(errno)); fullpath = std::string(ERROR_LOG_PATH1) + "/" + filename; file = fopen(fullpath.c_str(), mode.c_str()); if (file == nullptr) { ALOGE("Fail to open file %s, error: %s", fullpath.c_str(), strerror(errno)); } return file; } std::optional ExynosDisplay::RotatingLogFileWriter::getLastModifiedTimestamp( const std::string &filename) { struct stat fileStat; auto fullpath = std::string(ERROR_LOG_PATH0) + "/" + filename; if (stat(fullpath.c_str(), &fileStat) == 0) { return fileStat.st_mtim.tv_sec * nsecsPerSec + fileStat.st_mtim.tv_nsec; } fullpath = std::string(ERROR_LOG_PATH1) + "/" + filename; if (stat(fullpath.c_str(), &fileStat) == 0) { return fileStat.st_mtim.tv_sec * nsecsPerSec + fileStat.st_mtim.tv_nsec; } return std::nullopt; } bool ExynosDisplay::RotatingLogFileWriter::chooseOpenedFile() { if (mLastFileIndex < 0) { // HWC could be restarted, so choose to open new file or continue the last modified file int chosenIndex = 0; nsecs_t lastModifTimestamp = 0; for (int i = 0; i < mMaxFileCount; ++i) { auto timestamp = getLastModifiedTimestamp(mPrefixName + std::to_string(i) + mExtension); if (!timestamp.has_value()) { chosenIndex = i; break; } if (i == 0 || lastModifTimestamp < *timestamp) { chosenIndex = i; lastModifTimestamp = *timestamp; } } auto filename = mPrefixName + std::to_string(chosenIndex) + mExtension; mFile = openLogFile(filename, "ab"); if (mFile == nullptr) { ALOGE("Unable to open log file for %s", filename.c_str()); return false; } mLastFileIndex = chosenIndex; } // Choose to use the same last file or move on to the next file for (int i = 0; i < 2; ++i) { if (mFile == nullptr) { mFile = openLogFile(mPrefixName + std::to_string(mLastFileIndex) + mExtension, (i == 0) ? "ab" : "wb"); } if (mFile != nullptr) { auto fileSize = ftell(mFile); if (fileSize < mThresholdSizePerFile) return true; fclose(mFile); mFile = nullptr; } mLastFileIndex = (mLastFileIndex + 1) % mMaxFileCount; } return false; } void ExynosDisplay::invalidate() { mDevice->onRefresh(mDisplayId); } bool ExynosDisplay::checkHotplugEventUpdated(bool &hpdStatus) { if (mDisplayInterface == nullptr) { ALOGW("%s: mDisplayInterface == nullptr", __func__); return false; } hpdStatus = mDisplayInterface->readHotplugStatus(); int hotplugErrorCode = mDisplayInterface->readHotplugErrorCode(); DISPLAY_LOGI("[%s] mDisplayId(%d), mIndex(%d), HPD Status(previous :%d, current : %d), " "hotplugErrorCode=%d", __func__, mDisplayId, mIndex, mHpdStatus, hpdStatus, hotplugErrorCode); return (mHpdStatus != hpdStatus) || (hotplugErrorCode != 0); } void ExynosDisplay::handleHotplugEvent(bool hpdStatus) { mHpdStatus = hpdStatus; } void ExynosDisplay::hotplug() { int hotplugErrorCode = mDisplayInterface->readHotplugErrorCode(); mDisplayInterface->resetHotplugErrorCode(); mDevice->onHotPlug(mDisplayId, mHpdStatus, hotplugErrorCode); ALOGI("HPD callback(%s, mDisplayId %d, hotplugErrorCode=%d) was called", mHpdStatus ? "connection" : "disconnection", mDisplayId, hotplugErrorCode); } void ExynosDisplay::contentProtectionUpdated(HdcpLevels hdcpLevels) { mDevice->onContentProtectionUpdated(mDisplayId, hdcpLevels); } ExynosDisplay::SysfsBasedRRIHandler::SysfsBasedRRIHandler(ExynosDisplay* display) : mDisplay(display), mLastRefreshRate(0), mLastCallbackTime(0), mIgnoringLastUpdate(false), mCanIgnoreIncreaseUpdate(false) {} int32_t ExynosDisplay::SysfsBasedRRIHandler::init() { auto path = String8::format(kRefreshRateStatePathFormat, mDisplay->mIndex); mFd.Set(open(path.c_str(), O_RDONLY)); if (mFd.get() < 0) { ALOGE("Failed to open sysfs(%s) for refresh rate debug event: %s", path.c_str(), strerror(errno)); return -errno; } auto ret = mDisplay->mDevice->mDeviceInterface->registerSysfsEventHandler(shared_from_this()); if (ret != NO_ERROR) { ALOGE("%s: Failed to register sysfs event handler: %d", __func__, ret); return ret; } setAllowWakeup(true); // Call the callback immediately handleSysfsEvent(); return NO_ERROR; } int32_t ExynosDisplay::SysfsBasedRRIHandler::disable() { setAllowWakeup(false); return mDisplay->mDevice->mDeviceInterface->unregisterSysfsEventHandler(getFd()); } void ExynosDisplay::SysfsBasedRRIHandler::updateRefreshRateLocked(int refreshRate) { ATRACE_CALL(); ATRACE_INT("Refresh rate indicator event", refreshRate); // Ignore refresh rate increase that is caused by refresh rate indicator update but there's // no update for the other layers if (mCanIgnoreIncreaseUpdate && refreshRate > mLastRefreshRate && mLastRefreshRate > 0 && mDisplay->getLastLayerUpdateTime() < mLastCallbackTime) { mIgnoringLastUpdate = true; mCanIgnoreIncreaseUpdate = false; return; } mIgnoringLastUpdate = false; if (refreshRate == mLastRefreshRate) { return; } mLastRefreshRate = refreshRate; mLastCallbackTime = systemTime(CLOCK_MONOTONIC); mDisplay->mDevice->onRefreshRateChangedDebug(mDisplay->mDisplayId, s2ns(1) / mLastRefreshRate); mCanIgnoreIncreaseUpdate = true; } void ExynosDisplay::SysfsBasedRRIHandler::handleSysfsEvent() { ATRACE_CALL(); std::scoped_lock lock(mMutex); char buffer[1024]; lseek(mFd.get(), 0, SEEK_SET); int ret = read(mFd.get(), &buffer, sizeof(buffer)); if (ret < 0) { ALOGE("%s: Failed to read refresh rate from fd %d: %s", __func__, mFd.get(), strerror(errno)); return; } std::string_view bufferView(buffer); auto pos = bufferView.find('@'); if (pos == std::string::npos) { ALOGE("%s: Failed to parse refresh rate event (invalid format)", __func__); return; } int refreshRate = 0; std::from_chars(bufferView.data() + pos + 1, bufferView.data() + bufferView.size() - 1, refreshRate); updateRefreshRateLocked(refreshRate); } void ExynosDisplay::SysfsBasedRRIHandler::updateRefreshRate(int refreshRate) { std::scoped_lock lock(mMutex); updateRefreshRateLocked(refreshRate); } void ExynosDisplay::SysfsBasedRRIHandler::setAllowWakeup(const bool enabled) { auto path = String8::format(kRefreshRateAllowWakeupStateChangePathFormat, mDisplay->mIndex); std::ofstream ofs(path); if (ofs.is_open()) { ofs << enabled; if (ofs.fail()) { ALOGW("%s: Failed to write %d to allow wakeup node: %d", __func__, enabled, errno); } } else { ALOGW("%s: Failed to open allow wakeup node: %d", __func__, errno); } } int32_t ExynosDisplay::setRefreshRateChangedCallbackDebugEnabled(bool enabled) { if ((!!mRefreshRateIndicatorHandler) == enabled) { ALOGW("%s: RefreshRateChangedCallbackDebug is already %s", __func__, enabled ? "enabled" : "disabled"); return NO_ERROR; } int32_t ret = NO_ERROR; if (enabled) { if (mType == HWC_DISPLAY_PRIMARY) { mRefreshRateIndicatorHandler = std::make_shared(this); } else { mRefreshRateIndicatorHandler = std::make_shared(this); } if (!mRefreshRateIndicatorHandler) { ALOGE("%s: Failed to create refresh rate debug handler", __func__); return -ENOMEM; } ret = mRefreshRateIndicatorHandler->init(); if (ret != NO_ERROR) { ALOGE("%s: Failed to initialize refresh rate debug handler: %d", __func__, ret); mRefreshRateIndicatorHandler.reset(); return ret; } } else { ret = mRefreshRateIndicatorHandler->disable(); mRefreshRateIndicatorHandler.reset(); } return ret; } nsecs_t ExynosDisplay::getLastLayerUpdateTime() { Mutex::Autolock lock(mDRMutex); nsecs_t time = 0; for (size_t i = 0; i < mLayers.size(); ++i) { // The update from refresh rate indicator layer should be ignored if (mLayers[i]->mRequestedCompositionType == HWC2_COMPOSITION_REFRESH_RATE_INDICATOR) continue; time = max(time, mLayers[i]->mLastUpdateTime); } return time; } void ExynosDisplay::SysfsBasedRRIHandler::checkOnPresentDisplay() { // Update refresh rate indicator if the last update event is ignored to make sure that // the refresh rate caused by the current frame update will be applied immediately since // we may not receive the sysfs event if the refresh rate is the same as the last ignored one. if (!mIgnoringLastUpdate) { return; } handleSysfsEvent(); } ExynosDisplay::ActiveConfigBasedRRIHandler::ActiveConfigBasedRRIHandler(ExynosDisplay* display) : mDisplay(display), mLastRefreshRate(0) {} int32_t ExynosDisplay::ActiveConfigBasedRRIHandler::init() { updateRefreshRate(mDisplay->mRefreshRate); return NO_ERROR; } void ExynosDisplay::ActiveConfigBasedRRIHandler::updateRefreshRate(int refreshRate) { if (mLastRefreshRate == refreshRate) { return; } mLastRefreshRate = refreshRate; mDisplay->mDevice->onRefreshRateChangedDebug(mDisplay->mDisplayId, s2ns(1) / refreshRate); } void ExynosDisplay::ActiveConfigBasedRRIHandler::checkOnSetActiveConfig(int refreshRate) { updateRefreshRate(refreshRate); } bool ExynosDisplay::checkUpdateRRIndicatorOnly() { mUpdateRRIndicatorOnly = false; // mGeometryChanged & mBufferUpdates have already excluded any changes from RR Indicator layer. // GEOMETRY_LAYER_TYPE_CHANGED still needs to be excluded since SF sometimes could retry to use // DEVICE composition type again if it falls back to CLIENT at previous frame. if ((mGeometryChanged & ~GEOMETRY_LAYER_TYPE_CHANGED) > 0 || mBufferUpdates > 0) { return false; } Mutex::Autolock lock(mDRMutex); for (size_t i = 0; i < mLayers.size(); ++i) { const auto& layer = mLayers[i]; if (layer->mRequestedCompositionType == HWC2_COMPOSITION_REFRESH_RATE_INDICATOR) { // Sometimes, HWC could call onRefresh() so that SF would call another present without // any changes on geometry or buffers. Thus, this function should return true if only // there's any update on geometry or buffer of RR Indicator layer. mUpdateRRIndicatorOnly = ((layer->mGeometryChanged) & ~GEOMETRY_LAYER_TYPE_CHANGED) > 0 || (layer->mLastLayerBuffer != layer->mLayerBuffer); return mUpdateRRIndicatorOnly; } } return false; } bool ExynosDisplay::isUpdateRRIndicatorOnly() { return mUpdateRRIndicatorOnly; } uint32_t ExynosDisplay::getPeakRefreshRate() { uint32_t opRate = mBrightnessController->getOperationRate(); return opRate ?: mPeakRefreshRate; } VsyncPeriodNanos ExynosDisplay::getVsyncPeriod(const int32_t config) { const auto& it = mDisplayConfigs.find(config); if (it == mDisplayConfigs.end()) return 0; return it->second.vsyncPeriod; } uint32_t ExynosDisplay::getRefreshRate(const int32_t config) { const auto& it = mDisplayConfigs.find(config); if (it == mDisplayConfigs.end()) return 0; return it->second.refreshRate; } uint32_t ExynosDisplay::getConfigId(const int32_t refreshRate, const int32_t width, const int32_t height) { for (auto entry : mDisplayConfigs) { auto config = entry.first; auto displayCfg = entry.second; if (getRefreshRate(config) == refreshRate && displayCfg.width == width && displayCfg.height == height) { return config; } } return UINT_MAX; } void ExynosDisplay::resetColorMappingInfoForClientComp() { if (mType != HWC_DISPLAY_PRIMARY) return; int32_t ret = NO_ERROR; for (uint32_t i = 0; i < mLayers.size(); i++) { ExynosLayer *layer = mLayers[i]; if (layer->mPrevValidateCompositionType != HWC2_COMPOSITION_CLIENT && layer->mValidateCompositionType == HWC2_COMPOSITION_CLIENT) { if ((ret = resetColorMappingInfo(layer)) != NO_ERROR) { DISPLAY_LOGE("%s:: resetColorMappingInfo() idx=%d error(%d)", __func__, i, ret); } } } // when no GPU composition, resets the mapping info of client composition info if (mClientCompositionInfo.mPrevHasCompositionLayer && !mClientCompositionInfo.mHasCompositionLayer) { if ((ret = resetColorMappingInfo(&mClientCompositionInfo)) != NO_ERROR) { DISPLAY_LOGE("%s:: resetColorMappingInfo() for client target error(%d)", __func__, ret); } } } void ExynosDisplay::storePrevValidateCompositionType() { for (uint32_t i = 0; i < mIgnoreLayers.size(); i++) { ExynosLayer *layer = mIgnoreLayers[i]; layer->mPrevValidateCompositionType = layer->mValidateCompositionType; } for (uint32_t i = 0; i < mLayers.size(); i++) { ExynosLayer *layer = mLayers[i]; layer->mPrevValidateCompositionType = layer->mValidateCompositionType; } mClientCompositionInfo.mPrevHasCompositionLayer = mClientCompositionInfo.mHasCompositionLayer; } displaycolor::DisplayType ExynosDisplay::getDcDisplayType() const { switch (mType) { case HWC_DISPLAY_PRIMARY: return mIndex == 0 ? displaycolor::DisplayType::DISPLAY_PRIMARY : displaycolor::DisplayType::DISPLAY_SECONDARY; case HWC_DISPLAY_EXTERNAL: return displaycolor::DisplayType::DISPLAY_EXTERNAL; case HWC_DISPLAY_VIRTUAL: default: DISPLAY_LOGE("%s: Unsupported display type(%d)", __func__, mType); assert(false); return displaycolor::DisplayType::DISPLAY_PRIMARY; } }