/* * 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 #endif #include #include #include #include #include #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 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 & crtc) { std::unique_lock 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 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 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 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 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 localCrtc; { std::unique_lock stateLock(mStatLock); if (!mCrtc.get()) { return -EFAULT; } localCrtc = mCrtc; } int32_t ret = localCrtc->waitVBlank(mVsyncTime); { std::unique_lock 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 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 stateLock(mStatLock); if (!mCrtc.get()) return ret; std::shared_ptr 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 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)); } }