408 lines
11 KiB
C++
408 lines
11 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 <sys/prctl.h>
|
|
|
|
#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;
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
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);
|
|
|
|
prctl(PR_SET_NAME, "vsyncThread");
|
|
// set HwcVsync thread to SCHED_FIFO to minimize jitter
|
|
struct sched_param param = {0};
|
|
param.sched_priority = 2;
|
|
if (sched_setscheduler(0, SCHED_FIFO, ¶m) != 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;
|
|
bool isSoft = false;
|
|
bool isMix = false;
|
|
{
|
|
std::unique_lock<std::mutex> stateLock(mStatLock);
|
|
isSoft = mSoftVsync;
|
|
isMix = mMixVsync;
|
|
}
|
|
|
|
if (isSoft) {
|
|
ret = waitSoftwareVsync(vsync_timestamp);
|
|
} else if (isMix) {
|
|
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::shared_ptr<HwDisplayCrtcBase> localCrtc;
|
|
{
|
|
std::unique_lock<std::mutex> stateLock(mStatLock);
|
|
if (!mCrtc.get()) {
|
|
return -EFAULT;
|
|
}
|
|
localCrtc = mCrtc;
|
|
}
|
|
|
|
int32_t ret = localCrtc->waitVBlank(mVsyncTime);
|
|
|
|
{
|
|
std::unique_lock<std::mutex> stateLock(mStatLock);
|
|
vsync_timestamp = mVsyncTime;
|
|
localCrtc.reset();
|
|
}
|
|
|
|
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));
|
|
}
|
|
}
|
|
|
|
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) {
|
|
bool softVsync = false;
|
|
bool mixVsync = false;
|
|
bool enabled = false;
|
|
bool exit = false;
|
|
nsecs_t reqPeriod = 0;
|
|
nsecs_t mixOffset = 0;
|
|
HwcVsyncObserver *observer = nullptr;
|
|
{
|
|
std::unique_lock<std::mutex> stateLock(mStatLock);
|
|
softVsync = mSoftVsync;
|
|
mixVsync = mMixVsync;
|
|
enabled = mEnabled;
|
|
exit = mExit;
|
|
reqPeriod = mReqPeriod;
|
|
mixOffset = mMixOffset;
|
|
observer = mObserver;
|
|
}
|
|
|
|
char str[100] = {0};
|
|
snprintf(str, 100, "HwcVsync mode(%s) period(%" PRId64 ") \n",
|
|
softVsync ? "soft" : (mixVsync ? "mix" : "hw"), reqPeriod);
|
|
dumpstr.append(str);
|
|
memset(str, 0, sizeof(str));
|
|
|
|
snprintf(str, 100, " mEnabled:%d, mExit:%d\n", enabled, exit);
|
|
dumpstr.append(str);
|
|
memset(str, 0, sizeof(str));
|
|
|
|
if (mixVsync) {
|
|
snprintf(str, 100, " mMixOffset:%" PRId64 "\n", mixOffset);
|
|
dumpstr.append(str);
|
|
memset(str, 0, sizeof(str));
|
|
}
|
|
|
|
if (observer) {
|
|
snprintf(str, 100, " mObserver:%p\n", observer);
|
|
dumpstr.append(str);
|
|
memset(str, 0, sizeof(str));
|
|
}
|
|
}
|