6076 lines
228 KiB
C++
6076 lines
228 KiB
C++
/*
|
|
* 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 <linux/fb.h>
|
|
#include "ExynosDisplay.h"
|
|
|
|
#include <aidl/android/hardware/power/IPower.h>
|
|
#include <aidl/google/hardware/power/extension/pixel/IPowerExt.h>
|
|
#include <android-base/parsebool.h>
|
|
#include <android-base/properties.h>
|
|
#include <android/binder_manager.h>
|
|
#include <cutils/properties.h>
|
|
#include <hardware/hwcomposer_defs.h>
|
|
#include <linux/fb.h>
|
|
#include <processgroup/processgroup.h>
|
|
#include <sync/sync.h>
|
|
#include <sys/ioctl.h>
|
|
#include <utils/CallStack.h>
|
|
|
|
#include <future>
|
|
#include <map>
|
|
|
|
#include "BrightnessController.h"
|
|
#include "ExynosExternalDisplay.h"
|
|
#include "ExynosLayer.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;
|
|
|
|
extern struct exynos_hwc_control exynosHWCControl;
|
|
extern struct update_time_info updateTimeInfo;
|
|
|
|
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)
|
|
: Worker("DisplayHints", HAL_PRIORITY_URGENT_DISPLAY),
|
|
mNeedUpdateRefreshRateHint(false),
|
|
mLastRefreshRateHint(0),
|
|
mIdleHintIsEnabled(false),
|
|
mForceUpdateIdleHint(false),
|
|
mIdleHintDeadlineTime(0),
|
|
mIdleHintSupportIsChecked(false),
|
|
mIdleHintIsSupported(false),
|
|
mPowerModeState(HWC2_POWER_MODE_OFF),
|
|
mVsyncPeriod(16666666),
|
|
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<PowerHalHintWorker *>(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");
|
|
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");
|
|
return -EINVAL;
|
|
}
|
|
|
|
AIBinder_linkToDeath(pwExtBinder.get(), mDeathRecipient.get(), reinterpret_cast<void *>(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(int refreshRate) {
|
|
int32_t ret = NO_ERROR;
|
|
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(int 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(
|
|
hwc2_power_mode_t powerMode, uint32_t vsyncPeriod) {
|
|
int32_t ret = NO_ERROR;
|
|
|
|
/* TODO: add refresh rate buckets, tracked in b/181100731 */
|
|
int refreshRate = round(nsecsPerSec / vsyncPeriod * 0.1f) * 10;
|
|
// 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;
|
|
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 (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(int64_t deadlineTime, bool forceUpdate) {
|
|
int32_t ret = checkIdleHintSupport();
|
|
if (ret != NO_ERROR) {
|
|
return ret;
|
|
}
|
|
|
|
bool enableIdleHint =
|
|
(deadlineTime < systemTime(SYSTEM_TIME_MONOTONIC) && CC_LIKELY(deadlineTime > 0));
|
|
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<WorkDuration> 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<long long>(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<int64_t>(static_cast<long double>(mLastTargetDurationReported) /
|
|
mTargetWorkDuration * actualDurationNanos);
|
|
}
|
|
}
|
|
|
|
mActualWorkDuration = reportedDurationNs;
|
|
WorkDuration duration = {.durationNanos = reportedDurationNs, .timeStampNanos = systemTime()};
|
|
|
|
if (sTraceHintSessionData) {
|
|
ATRACE_INT64("Measured duration", actualDurationNanos);
|
|
ATRACE_INT64("Target error term", mTargetWorkDuration - actualDurationNanos);
|
|
|
|
ATRACE_INT64("Reported duration", reportedDurationNs);
|
|
ATRACE_INT64("Reported target", mLastTargetDurationReported);
|
|
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) ATRACE_INT64("Time target", mTargetWorkDuration);
|
|
bool shouldSignal = false;
|
|
if (!sNormalizeTarget) {
|
|
shouldSignal = needUpdateTargetWorkDurationLocked();
|
|
if (shouldSignal && mActualWorkDuration.has_value() && sTraceHintSessionData) {
|
|
ATRACE_INT64("Target error term", *mActualWorkDuration - mTargetWorkDuration);
|
|
}
|
|
}
|
|
Unlock();
|
|
if (shouldSignal) {
|
|
Signal();
|
|
}
|
|
}
|
|
|
|
void ExynosDisplay::PowerHalHintWorker::signalRefreshRate(hwc2_power_mode_t powerMode,
|
|
uint32_t vsyncPeriod) {
|
|
Lock();
|
|
mPowerModeState = powerMode;
|
|
mVsyncPeriod = vsyncPeriod;
|
|
mNeedUpdateRefreshRateHint = true;
|
|
Unlock();
|
|
|
|
Signal();
|
|
}
|
|
|
|
void ExynosDisplay::PowerHalHintWorker::signalIdle() {
|
|
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;
|
|
uint32_t vsyncPeriod = mVsyncPeriod;
|
|
|
|
/*
|
|
* 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, vsyncPeriod);
|
|
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<int> 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<uid_t>(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<std::string> 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<bool> 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(ExynosLayer * const *lhs, ExynosLayer *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()
|
|
{
|
|
return sort(compare);
|
|
}
|
|
|
|
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, */
|
|
/* mCompressed 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)
|
|
mCompressed = false;
|
|
else
|
|
mCompressed = true;
|
|
|
|
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::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::setCompressed(bool compressed)
|
|
{
|
|
mCompressed = compressed;
|
|
}
|
|
|
|
bool ExynosCompositionInfo::getCompressed()
|
|
{
|
|
return mCompressed;
|
|
}
|
|
|
|
void ExynosCompositionInfo::dump(String8& result)
|
|
{
|
|
result.appendFormat("CompositionInfo (%d)\n", mType);
|
|
result.appendFormat("mHasCompositionLayer(%d)\n", mHasCompositionLayer);
|
|
if (mHasCompositionLayer) {
|
|
result.appendFormat("\tfirstIndex: %d, lastIndex: %d, dataSpace: 0x%8x, compressed: %d, windowIndex: %d\n",
|
|
mFirstIndex, mLastIndex, mDataSpace, mCompressed, 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.string());
|
|
if (mM2mMPP != NULL)
|
|
result.appendFormat("\t%s\n", mM2mMPP->mName.string());
|
|
}
|
|
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 index, ExynosDevice *device)
|
|
: mDisplayId(HWC_DISPLAY_PRIMARY),
|
|
mType(HWC_NUM_DISPLAY_TYPES),
|
|
mIndex(index),
|
|
mDeconNodeName(""),
|
|
mXres(1440),
|
|
mYres(2960),
|
|
mXdpi(25400),
|
|
mYdpi(25400),
|
|
mVsyncPeriod(16666666),
|
|
mBtsVsyncPeriod(16666666),
|
|
mDevice(device),
|
|
mDisplayName(""),
|
|
mPlugState(false),
|
|
mHasSingleBuffer(false),
|
|
mResourceManager(NULL),
|
|
mClientCompositionInfo(COMPOSITION_CLIENT),
|
|
mExynosCompositionInfo(COMPOSITION_EXYNOS),
|
|
mGeometryChanged(0x0),
|
|
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),
|
|
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(getDisplayId(mDisplayId, mIndex)) {
|
|
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;
|
|
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);
|
|
|
|
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);
|
|
}
|
|
#ifndef HWC_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;
|
|
for (size_t i = 0; i < mLayers.size(); i++) {
|
|
auto w = WIDTH(mLayers[i]->mPreprocessedInfo.displayFrame);
|
|
auto h = HEIGHT(mLayers[i]->mPreprocessedInfo.displayFrame);
|
|
incomingPixels += w * h;
|
|
}
|
|
|
|
/* Mode Switch is not required if total pixels are not more than the threshold */
|
|
unsigned int lcdSize = mXres * mYres;
|
|
if (incomingPixels <= lcdSize) {
|
|
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;
|
|
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.string());
|
|
}
|
|
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::skipSignalIdleForVideoLayer(void) {
|
|
/* ignore the frame update in case we have video layer but ui layer is not updated */
|
|
for (size_t i = 0; i < mLayers.size(); i++) {
|
|
if (!mLayers[i]->isLayerFormatYuv() &&
|
|
mLayers[i]->mLastLayerBuffer != mLayers[i]->mLayerBuffer) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @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.mSrcImg,
|
|
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.compression = layer.mCompressed;
|
|
if (layer.mCompressed) {
|
|
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.compression = mpp_dst_img.compressed;
|
|
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.compression = compositionInfo.mCompressed;
|
|
if (compositionInfo.mCompressed) {
|
|
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)
|
|
{
|
|
FILE *pFile = NULL;
|
|
struct timeval tv;
|
|
gettimeofday(&tv, NULL);
|
|
reason.appendFormat("errFrameNumber: %" PRId64 " time:%s\n", mErrorFrameCount,
|
|
getLocalTimeStr(tv).string());
|
|
ALOGD("%s", reason.string());
|
|
|
|
if (mErrorFrameCount < HWC_PRINT_FRAME_NUM) {
|
|
char filePath[128];
|
|
sprintf(filePath, "%s/%s_hwc_debug%d.dump", ERROR_LOG_PATH0, mDisplayName.string(), (int)mErrorFrameCount);
|
|
pFile = fopen(filePath, "wb");
|
|
if (pFile == NULL) {
|
|
ALOGE("Fail to open file %s, error: %s", filePath, strerror(errno));
|
|
sprintf(filePath, "%s/%s_hwc_debug%d.dump", ERROR_LOG_PATH1, mDisplayName.string(), (int)mErrorFrameCount);
|
|
pFile = fopen(filePath, "wb");
|
|
}
|
|
if (pFile == NULL) {
|
|
ALOGE("Fail to open file %s, error: %s", filePath, strerror(errno));
|
|
} else {
|
|
ALOGI("%s was created", filePath);
|
|
fwrite(reason.string(), 1, reason.size(), pFile);
|
|
}
|
|
}
|
|
mErrorFrameCount++;
|
|
|
|
android::String8 result;
|
|
result.appendFormat("Device mGeometryChanged(%" PRIx64 "), mGeometryChanged(%" PRIx64 "), mRenderingState(%d)\n",
|
|
mDevice->mGeometryChanged, mGeometryChanged, mRenderingState);
|
|
result.appendFormat("======================= dump composition infos ================================\n");
|
|
ExynosCompositionInfo clientCompInfo = mClientCompositionInfo;
|
|
ExynosCompositionInfo exynosCompInfo = mExynosCompositionInfo;
|
|
clientCompInfo.dump(result);
|
|
exynosCompInfo.dump(result);
|
|
ALOGD("%s", result.string());
|
|
if (pFile != NULL) {
|
|
fwrite(result.string(), 1, result.size(), pFile);
|
|
}
|
|
result.clear();
|
|
|
|
result.appendFormat("======================= dump exynos layers (%zu) ================================\n",
|
|
mLayers.size());
|
|
ALOGD("%s", result.string());
|
|
if (pFile != NULL) {
|
|
fwrite(result.string(), 1, result.size(), pFile);
|
|
}
|
|
result.clear();
|
|
for (uint32_t i = 0; i < mLayers.size(); i++) {
|
|
ExynosLayer *layer = mLayers[i];
|
|
layer->printLayer();
|
|
if (pFile != NULL) {
|
|
layer->dump(result);
|
|
fwrite(result.string(), 1, result.size(), pFile);
|
|
result.clear();
|
|
}
|
|
}
|
|
|
|
if (mIgnoreLayers.size()) {
|
|
result.appendFormat("======================= dump ignore layers (%zu) ================================\n",
|
|
mIgnoreLayers.size());
|
|
ALOGD("%s", result.string());
|
|
if (pFile != NULL) {
|
|
fwrite(result.string(), 1, result.size(), pFile);
|
|
}
|
|
result.clear();
|
|
for (uint32_t i = 0; i < mIgnoreLayers.size(); i++) {
|
|
ExynosLayer *layer = mIgnoreLayers[i];
|
|
layer->printLayer();
|
|
if (pFile != NULL) {
|
|
layer->dump(result);
|
|
fwrite(result.string(), 1, result.size(), pFile);
|
|
result.clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
result.appendFormat("============================= dump win configs ===================================\n");
|
|
ALOGD("%s", result.string());
|
|
if (pFile != NULL) {
|
|
fwrite(result.string(), 1, result.size(), pFile);
|
|
}
|
|
result.clear();
|
|
for (size_t i = 0; i < mDpuData.configs.size(); i++) {
|
|
ALOGD("config[%zu]", i);
|
|
printConfig(mDpuData.configs[i]);
|
|
if (pFile != NULL) {
|
|
result.appendFormat("config[%zu]\n", i);
|
|
dumpConfig(result, mDpuData.configs[i]);
|
|
fwrite(result.string(), 1, result.size(), pFile);
|
|
result.clear();
|
|
}
|
|
}
|
|
if (pFile != NULL) {
|
|
fclose(pFile);
|
|
}
|
|
}
|
|
|
|
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.string(), 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.string(), 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 (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 {
|
|
type = mLayers[layerIndex]->mValidateCompositionType;
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
int32_t ExynosDisplay::getChangedCompositionTypes(
|
|
uint32_t* outNumElements, hwc2_layer_t* outLayers,
|
|
int32_t* /*hwc2_composition_t*/ outTypes) {
|
|
|
|
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, 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;
|
|
|
|
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;
|
|
}
|
|
|
|
int32_t ExynosDisplay::presentDisplay(int32_t* outRetireFence) {
|
|
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<nsecs_t>(mVsyncPeriod));
|
|
mPowerHalHint.signalTargetWorkDuration(target);
|
|
// if we did not validate (have not sent hint yet) and have data for this case
|
|
std::optional<nsecs_t> 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);
|
|
|
|
bool dropFrame = false;
|
|
if ((mGeometryChanged & GEOMETRY_DISPLAY_RESOLUTION_CHANGED) &&
|
|
!isFullScreenComposition()) {
|
|
ALOGD("presentDisplay: drop invalid frame during resolution switch");
|
|
dropFrame = true;
|
|
}
|
|
|
|
if (dropFrame || 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;
|
|
}
|
|
|
|
if (mRenderingState != RENDERING_STATE_ACCEPTED_CHANGE) {
|
|
/*
|
|
* presentDisplay() can be called before validateDisplay()
|
|
* when HWC2_CAPABILITY_SKIP_VALIDATE is supported
|
|
*/
|
|
#ifndef HWC_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");
|
|
}
|
|
|
|
updateBrightnessState();
|
|
|
|
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)) {
|
|
clearDisplay();
|
|
*outRetireFence = -1;
|
|
mLastRetireFence = fence_close(mLastRetireFence, this,
|
|
FENCE_TYPE_RETIRE, FENCE_IP_DPP);
|
|
mRenderingState = RENDERING_STATE_PRESENTED;
|
|
ret = 0;
|
|
return ret;
|
|
}
|
|
|
|
if (!checkFrameValidation()) {
|
|
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;
|
|
srcImg = mLayers[i]->mSrcImg;
|
|
midImg = mLayers[i]->mMidImg;
|
|
m2mMpp->requestHWStateChange(MPP_HW_STATE_RUNNING);
|
|
if ((ret = m2mMpp->doPostProcessing(srcImg, 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 (mGeometryChanged != 0 || !skipSignalIdleForVideoLayer()) {
|
|
mPowerHalHint.signalIdle();
|
|
}
|
|
|
|
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 (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.string());
|
|
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;
|
|
|
|
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.string());
|
|
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.mCompressed = isAFBCCompressed(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;
|
|
}
|
|
|
|
int32_t ExynosDisplay::setDisplayBrightness(float brightness, bool waitPresent)
|
|
{
|
|
if (mBrightnessController) {
|
|
return mBrightnessController->processDisplayBrightness(brightness, mVsyncPeriod,
|
|
waitPresent);
|
|
}
|
|
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::calculateTimeline(
|
|
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)
|
|
{
|
|
ATRACE_CALL();
|
|
Mutex::Autolock lock(mDisplayMutex);
|
|
|
|
DISPLAY_LOGD(eDebugDisplayConfig,
|
|
"config(%d), seamless(%d), "
|
|
"desiredTime(%" PRId64 ")",
|
|
config, vsyncPeriodChangeConstraints->seamlessRequired,
|
|
vsyncPeriodChangeConstraints->desiredTimeNanos);
|
|
|
|
if (isBadConfig(config)) return HWC2_ERROR_BAD_CONFIG;
|
|
|
|
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 (mDesiredConfig: %d)", __func__,
|
|
mDesiredConfig);
|
|
}
|
|
/* Config would be requested on present time */
|
|
mConfigRequestState = hwc_request_state_t::SET_CONFIG_STATE_PENDING;
|
|
mVsyncPeriodChangeConstraints = *vsyncPeriodChangeConstraints;
|
|
mDesiredConfig = config;
|
|
|
|
calculateTimeline(config, vsyncPeriodChangeConstraints, outTimeline);
|
|
|
|
/* mActiveConfig should be changed immediately for internal status */
|
|
mActiveConfig = config;
|
|
mVsyncAppliedTimeLine = *outTimeline;
|
|
uint32_t vsync_period = getDisplayVsyncPeriodFromConfig(config);
|
|
updateBtsVsyncPeriod(vsync_period);
|
|
|
|
bool earlyWakeupNeeded = checkRrCompensationEnabled();
|
|
if (earlyWakeupNeeded) {
|
|
setEarlyWakeupDisplay();
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
void ExynosDisplay::updateBtsVsyncPeriod(uint32_t vsyncPeriod, bool configApplied) {
|
|
if (configApplied || vsyncPeriod < mBtsVsyncPeriod) {
|
|
checkBtsReassignResource(vsyncPeriod, mBtsVsyncPeriod);
|
|
mBtsVsyncPeriod = vsyncPeriod;
|
|
}
|
|
}
|
|
|
|
uint32_t ExynosDisplay::getBtsRefreshRate() const {
|
|
return static_cast<uint32_t>(round(nsecsPerSec / mBtsVsyncPeriod * 0.1f) * 10);
|
|
}
|
|
|
|
void ExynosDisplay::updateRefreshRateHint() {
|
|
if (mVsyncPeriod) {
|
|
mPowerHalHint.signalRefreshRate(mPowerModeState.value_or(HWC2_POWER_MODE_OFF),
|
|
mVsyncPeriod);
|
|
}
|
|
}
|
|
|
|
/* This function must be called within a mDisplayMutex protection */
|
|
int32_t ExynosDisplay::resetConfigRequestStateLocked(hwc2_config_t config) {
|
|
ATRACE_CALL();
|
|
|
|
mVsyncPeriod = getDisplayVsyncPeriodFromConfig(config);
|
|
updateBtsVsyncPeriod(mVsyncPeriod, 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));
|
|
|
|
std::lock_guard<std::mutex> lock(mPeakRefreshRateMutex);
|
|
bool isPeakRefreshRate = isCurrentPeakRefreshRate();
|
|
ATRACE_INT("isPeakRefreshRate", isPeakRefreshRate);
|
|
if (isPeakRefreshRate && mNotifyPeakRefreshRate) {
|
|
mPeakRefreshRateCondition.notify_one();
|
|
mNotifyPeakRefreshRate = false;
|
|
}
|
|
}
|
|
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)
|
|
{
|
|
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");
|
|
needSetActiveConfig = true;
|
|
ATRACE_INT("Pending ActiveConfig", 0);
|
|
} else {
|
|
DISPLAY_LOGD(eDebugDisplayConfig, "setActiveConfig still pending (mDesiredConfig %d)",
|
|
mDesiredConfig);
|
|
ATRACE_INT("Pending ActiveConfig", mDesiredConfig);
|
|
}
|
|
|
|
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) {
|
|
|
|
ATRACE_CALL();
|
|
gettimeofday(&updateTimeInfo.lastValidateTime, NULL);
|
|
Mutex::Autolock lock(mDisplayMutex);
|
|
|
|
if (mPauseDisplay) return HWC2_ERROR_NONE;
|
|
|
|
if ((mGeometryChanged & GEOMETRY_DISPLAY_RESOLUTION_CHANGED) &&
|
|
!isFullScreenComposition()) {
|
|
ALOGD("validateDisplay: drop invalid frame during resolution switch");
|
|
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<nsecs_t>(mVsyncPeriod));
|
|
mPowerHalHint.signalTargetWorkDuration(target);
|
|
std::optional<nsecs_t> 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();
|
|
|
|
doPreProcessing();
|
|
checkLayerFps();
|
|
if (exynosHWCControl.useDynamicRecomp == true && mDREnable)
|
|
checkDynamicReCompMode();
|
|
|
|
if (exynosHWCControl.useDynamicRecomp == true &&
|
|
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();
|
|
}
|
|
|
|
updateBrightnessState();
|
|
|
|
if ((ret = updateColorConversionInfo()) != NO_ERROR) {
|
|
validateError = true;
|
|
DISPLAY_LOGE("%s:: updateColorConversionInfo() fail, ret(%d)",
|
|
__func__, ret);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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()
|
|
{
|
|
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;
|
|
srcImg = mLayers[i]->mSrcImg;
|
|
midImg = mLayers[i]->mMidImg;
|
|
m2mMpp->requestHWStateChange(MPP_HW_STATE_RUNNING);
|
|
if ((ret = m2mMpp->doPostProcessing(srcImg, 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 = %d, 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, c.compression, 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);
|
|
result.appendFormat("[%s] display information size: %d x %d, vsyncState: %d, colorMode: %d, "
|
|
"colorTransformHint: %d, orientation %d\n",
|
|
mDisplayName.string(), mXres, mYres, mVsyncState, mColorMode,
|
|
mColorTransformHint, mMountOrientation);
|
|
mClientCompositionInfo.dump(result);
|
|
mExynosCompositionInfo.dump(result);
|
|
|
|
result.appendFormat("PanelGammaSource (%d)\n\n", GetCurrentPanelGammaSource());
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
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 = %d, 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,
|
|
c.compression, 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 = %d, 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,
|
|
c.compression, 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)
|
|
{
|
|
std::optional<ExynosCompositionInfo> compositionInfo;
|
|
|
|
if (targetType == COMPOSITION_CLIENT)
|
|
compositionInfo = mClientCompositionInfo;
|
|
else if (targetType == COMPOSITION_EXYNOS)
|
|
compositionInfo = mExynosCompositionInfo;
|
|
|
|
if (!compositionInfo) return -EINVAL;
|
|
|
|
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->compressed = compositionInfo->mCompressed;
|
|
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;
|
|
}
|
|
|
|
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->compressed = compositionInfo->mCompressed;
|
|
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;
|
|
}
|
|
|
|
|
|
int32_t ExynosDisplay::addExynosCompositionLayer(uint32_t layerIndex)
|
|
{
|
|
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<int32_t> 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);
|
|
|
|
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.string(), 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.string(), 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::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 <hwc_rect_t> 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;
|
|
}
|
|
|
|
// 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<ExynosDisplayInterface>();
|
|
mDisplayInterface->init(this);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
ATRACE_INT("HWComposer: DPU Layer", dpu_count);
|
|
ATRACE_INT("HWComposer: G2D Layer", g2d_count);
|
|
ATRACE_INT("HWComposer: GPU Layer", gpu_count);
|
|
ATRACE_INT("HWComposer: RCD Layer", rcd_count);
|
|
ATRACE_INT("HWComposer: DPU Cached Layer", skip_count);
|
|
ATRACE_INT("HWComposer: SF Cached Layer", mIgnoreLayers.size());
|
|
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) {
|
|
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) {
|
|
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<nsecs_t> 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);
|
|
}
|
|
|
|
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,
|
|
int32_t *outConfig) {
|
|
if (!fps)
|
|
return HWC2_ERROR_BAD_CONFIG;
|
|
|
|
constexpr auto nsecsPerSec = std::chrono::nanoseconds(1s).count();
|
|
constexpr auto nsecsPerMs = std::chrono::nanoseconds(1ms).count();
|
|
|
|
const auto vsyncPeriod = nsecsPerSec / fps;
|
|
|
|
for (auto const& [config, mode] : mDisplayConfigs) {
|
|
long delta = abs(vsyncPeriod - mode.vsyncPeriod);
|
|
if ((width == mode.width) && (height == mode.height) && (delta < nsecsPerMs)) {
|
|
ALOGD("%s: found display config for mode: %dx%d@%d=%d",
|
|
__func__, width, height, fps, config);
|
|
*outConfig = config;
|
|
return HWC2_ERROR_NONE;
|
|
}
|
|
}
|
|
return HWC2_ERROR_BAD_CONFIG;
|
|
}
|