Files
BPI-CM9-Linux/aml-comp/vendor/amlogic/meson_display/common/hwc/HwcVsyncBase.cpp
T
2026-08-24 10:03:24 +08:00

375 lines
10 KiB
C++

/*
* Copyright (c) 2017 Amlogic, Inc. All rights reserved.
*
* This source code is subject to the terms and conditions defined in the
* file 'LICENSE' which is part of this source code package.
*
* Description:
*/
#ifdef __ANDROID__
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <utils/Trace.h>
#endif
#include <MesonLog.h>
#include <DebugHelper.h>
#include <inttypes.h>
#include <cmath>
#include "HwcVsyncBase.h"
#include "HwDisplayCrtcBase.h"
#define SF_VSYNC_DFT_PERIOD 60
#define VT_OFFSET_HIGH_RFRESHRATE 1000000
#define VT_OFFSET_LOW_RFRESHRATE 5000000
HwcVsyncBase::HwcVsyncBase(int vsyncType) {
mSoftVsync = true;
mVsyncType = vsyncType;
mEnabled = false;
mMixVsync = false;
mMixRebase = false;
mPreTimeStamp = 0;
mReqPeriod = 0;
mVsyncTime = 0;
mExit = false;
mObserver = NULL;
mMixOffset = 0;
mCurVsyncPeriod = 0;
mMixRebaseCount = 0;
int ret;
ret = pthread_create(&hw_vsync_thread, NULL, vsyncThread, this);
if (ret) {
MESON_LOGE("failed to start vsync thread: %s", strerror(ret));
return;
}
char name[64];
snprintf(name, 64, "HwcVsync-%d", mVsyncType);
pthread_setname_np(hw_vsync_thread, name);
}
HwcVsyncBase::~HwcVsyncBase() {
std::unique_lock<std::mutex> stateLock(mStatLock);
mExit = true;
stateLock.unlock();
mStateCondition.notify_all();
pthread_join(hw_vsync_thread, NULL);
}
int32_t HwcVsyncBase::setObserver(HwcVsyncObserver * observer) {
mObserver = observer;
return 0;
}
#ifndef __ANDROID__
nsecs_t HwcVsyncBase::systemTime(int clock)
{
struct timespec time_now = {0, 0};
clock_gettime(clock, &time_now);
return (nsecs_t)(time_now.tv_sec * 1000000000 + time_now.tv_nsec);
}
#endif
int32_t HwcVsyncBase::setVsyncMode(enum vsync_mode_t type,
std::shared_ptr<HwDisplayCrtcBase> & crtc) {
std::unique_lock<std::mutex> stateLock(mStatLock);
if (crtc.get()) mCrtc = crtc;
if (type == VSYNC_MODE_VT) {
#ifndef HWC_VT_HW_VSYNC
type = VSYNC_MODE_MIX;
#endif
}
/* default mode set to HW */
mMixVsync = false;
mSoftVsync = false;
if (VSYNC_MODE_SOFTWARE == type) {
mSoftVsync = true;
mCrtc.reset();
} else if (VSYNC_MODE_MIX == type) {
mMixVsync = true;
mMixRebase = true;
mMixRebaseCount = 0;
}
stateLock.unlock();
mStateCondition.notify_all();
return 0;
}
int32_t HwcVsyncBase::setPeriod(nsecs_t period) {
#ifdef __ANDROID__
ATRACE_CALL();
#endif
std::unique_lock<std::mutex> stateLock(mStatLock);
if (mReqPeriod != period) {
MESON_LOGD("Vsync-type [%d] Update period %" PRId64 "->%" PRId64 "", mVsyncType, mReqPeriod, period);
mReqPeriod = period;
if (mVsyncType == DISPLAY_DEFAULT) {
mMixRebase = true;
mMixRebaseCount = 0;
}
}
stateLock.unlock();
mStateCondition.notify_all();
return 0;
}
int32_t HwcVsyncBase::setEnabled(bool enabled) {
std::unique_lock<std::mutex> stateLock(mStatLock);
mEnabled = enabled;
if (mMixVsync == true && mVsyncType != DISPLAY_DEFAULT) {
mMixRebase = true;
mMixRebaseCount = 0;
}
stateLock.unlock();
mStateCondition.notify_all();
return 0;
}
void * HwcVsyncBase::vsyncThread(void * data) {
HwcVsyncBase* pThis = (HwcVsyncBase*)data;
char name[64];
snprintf(name, 64, "VsyncThread-%d", pThis->mVsyncType);
set_thread_debug_info(name);
MESON_LOGV("HwDisplayVsync: vsyncThread start - (%p).", pThis);
// set HwcVsync thread to SCHED_FIFO to minimize jitter
struct sched_param param = {0};
param.sched_priority = 2;
if (sched_setscheduler(0, SCHED_FIFO, &param) != 0) {
MESON_LOGE("Couldn't set SCHED_FIFO for videotunnelthread");
}
while (true) {
{
std::unique_lock<std::mutex> stateLock(pThis->mStatLock);
while (!pThis->mEnabled && !pThis->mExit) {
pThis->mStateCondition.wait(stateLock);
}
if (pThis->mExit) {
MESON_LOGD("exit vsync loop");
pthread_exit(0);
return NULL;
}
}
nsecs_t timestamp = 0;
int32_t ret;
if (pThis->mSoftVsync) {
ret = pThis->waitSoftwareVsync(timestamp);
} else if (pThis->mMixVsync) {
ret = pThis->waitMixVsync(timestamp);
} else {
ret = pThis->waitHwVsync(timestamp);
}
nsecs_t period;
if (pThis->mPreTimeStamp != 0 && pThis->mVsyncType != DISPLAY_VIDEOTUNNEL) {
period = timestamp - pThis->mPreTimeStamp;
} else {
period = pThis->mReqPeriod;
}
pThis->mPreTimeStamp = timestamp;
if (DebugHelper::getInstance().enableVsyncDetail()) {
if (pThis->mPreTimeStamp != 0)
MESON_LOGD("wait for vsync success, period: %" PRId64 ", timestamp (%" PRId64 ")",
period, timestamp);
}
if (ret == 0 && pThis->mObserver) {
if (pThis->mEnabled) {
pThis->mObserver->onVsync(timestamp, period, pThis->mVsyncType);
}
} else {
if (ret != 0) {
MESON_LOGE("wait for hw vsync error:%d", ret);
// wait vblank error we may in a mode switch
// sleep 16 ms and then try again
usleep(16000);
}
}
}
return NULL;
}
int32_t HwcVsyncBase::waitVsync(nsecs_t& vsync_timestamp, nsecs_t& period) {
int ret;
if (mSoftVsync) {
ret = waitSoftwareVsync(vsync_timestamp);
} else if (mMixVsync) {
ret = waitMixVsync(vsync_timestamp);
} else {
ret = waitHwVsync(vsync_timestamp);
}
if (mPreTimeStamp != 0)
period = vsync_timestamp - mPreTimeStamp;
else
period = 0;
mPreTimeStamp = vsync_timestamp;
return ret;
}
int32_t HwcVsyncBase::waitHwVsync(nsecs_t& vsync_timestamp) {
#ifdef __ANDROID__
ATRACE_CALL();
#endif
std::unique_lock<std::mutex> stateLock(mStatLock);
if (!mCrtc.get()) {
return -EFAULT;
}
std::shared_ptr<HwDisplayCrtcBase> localCrtc = mCrtc;
stateLock.unlock();
int32_t ret = localCrtc->waitVBlank(mVsyncTime);
stateLock.lock();
vsync_timestamp = mVsyncTime;
localCrtc.reset();
stateLock.unlock();
return ret;
}
int32_t HwcVsyncBase::waitSoftwareVsync(nsecs_t& vsync_timestamp) {
#ifdef __ANDROID__
ATRACE_CALL();
#endif
static nsecs_t vsync_time = 0;
static nsecs_t old_vsync_period = 0;
nsecs_t now = systemTime(CLOCK_MONOTONIC);
std::unique_lock<std::mutex> stateLock(mStatLock);
mReqPeriod = (mReqPeriod <= 0) ? (1e9/SF_VSYNC_DFT_PERIOD) : mReqPeriod;
//cal the last vsync time with old period
if (mReqPeriod != old_vsync_period) {
if (old_vsync_period > 0) {
vsync_time = vsync_time +
((now - vsync_time) / old_vsync_period) * old_vsync_period;
}
old_vsync_period = mReqPeriod;
}
//set to next vsync time
vsync_time += mReqPeriod;
// we missed, find where the next vsync should be
if (vsync_time - now < 0) {
vsync_time = now + (mReqPeriod -
((now - vsync_time) % mReqPeriod));
}
stateLock.unlock();
struct timespec spec;
spec.tv_sec = vsync_time / 1000000000;
spec.tv_nsec = vsync_time % 1000000000;
int err;
do {
err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL);
} while (err == EINTR);
vsync_timestamp = vsync_time;
return err;
}
int32_t HwcVsyncBase::waitMixVsync(nsecs_t& vsync_timestamp) {
#ifdef __ANDROID__
ATRACE_CALL();
#endif
mReqPeriod = (mReqPeriod <= 0) ? (1e9/SF_VSYNC_DFT_PERIOD) : mReqPeriod;
if (mCurVsyncPeriod != mReqPeriod || mMixRebase) {
int32_t ret = -EFAULT;
MESON_LOGD("[%s] waitVBlank to get hw vsync timestamp", __func__);
std::unique_lock<std::mutex> stateLock(mStatLock);
if (!mCrtc.get())
return ret;
std::shared_ptr<HwDisplayCrtcBase> localCrtc = mCrtc;
stateLock.unlock();
ret = localCrtc->waitVBlank(mVsyncTime);
if (ret != 0) {
mMixRebaseCount = 0;
return ret;
}
stateLock.lock();
// videotunnel vsync offset
if (mEnabled) {
if (mVsyncType == DISPLAY_VIDEOTUNNEL) {
if (std::floor(1e9/mReqPeriod) > SF_VSYNC_DFT_PERIOD)
mMixOffset = VT_OFFSET_HIGH_RFRESHRATE;
else
mMixOffset = VT_OFFSET_LOW_RFRESHRATE;
} else {
if (std::floor(1e9/mReqPeriod) < SF_VSYNC_DFT_PERIOD / 2)
mMixOffset = VT_OFFSET_LOW_RFRESHRATE - VT_OFFSET_HIGH_RFRESHRATE - VT_OFFSET_HIGH_RFRESHRATE;
else
mMixOffset = VT_OFFSET_HIGH_RFRESHRATE;
}
}
mVsyncTime += mMixOffset;
mCurVsyncPeriod = mReqPeriod;
mMixRebaseCount ++;
// The hardware VSync switch will be completed after four VSync cycles
if (mMixRebaseCount >= 4) {
mMixRebase = false;
}
localCrtc.reset();
stateLock.unlock();
} else {
nsecs_t now = systemTime(CLOCK_MONOTONIC);
mVsyncTime = mVsyncTime + mCurVsyncPeriod +
(now - mVsyncTime ) /mCurVsyncPeriod * mCurVsyncPeriod;
struct timespec spec;
spec.tv_sec = mVsyncTime / 1000000000;
spec.tv_nsec = mVsyncTime % 1000000000;
int err;
do {
err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL);
} while (err == EINTR);
}
vsync_timestamp = mVsyncTime;
return 0;
}
void HwcVsyncBase::dump(std::string &dumpstr) {
char str[100] = {0};
snprintf(str, 100, "HwcVsync mode(%s) period(%" PRId64 ") \n",
mSoftVsync ? "soft": (mMixVsync ? "mix" : "hw"), mReqPeriod);
dumpstr.append(str);
memset(str, 0, sizeof(str));
snprintf(str, 100, " mEnabled:%d, mExit:%d\n", mEnabled, mExit);
dumpstr.append(str);
memset(str, 0, sizeof(str));
if (mMixVsync) {
snprintf(str, 100, " mMixOffset:%" PRId64 "\n", mMixOffset);
dumpstr.append(str);
memset(str, 0, sizeof(str));
}
if (mObserver) {
snprintf(str, 100, " mObserver:%p\n", mObserver);
dumpstr.append(str);
memset(str, 0, sizeof(str));
}
}