vsync信号

Posted Achillisjack

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vsync信号相关的知识,希望对你有一定的参考价值。

上个小节说明了HWC模块的加载等操作,并且也说明了HWC模块主要用于产生vsync信号,现在的问题是, vsync信号是如何产生的呢? 又是如何传输到SurfaceFlinger 中,处理的呢?在上面的论述中,HWComposer 的构造方法在加载完FB模块打开设备文件以及加载HWC模块之后,会注册vsync信号,

mHwc->registerProcs(mHwc, &mCBContext->procs);

HWComposer_hwc1.h中的mCBContext变量申明如下,

cb_context*                     mCBContext;

是cb_context 的结构体,该结构体定义如下,

HWComposer_hwc1.cpp中的cb_context结构体定义如下,

struct HWComposer::cb_context 
    struct callbacks : public hwc_procs_t 
        // these are here to facilitate the transition when adding
        // new callbacks (an implementation can check for NULL before
        // calling a new callback).
        void (*zero[4])(void);
    ;
    callbacks procs;
    HWComposer* hwc;
;

里面有2个变量,一个指向HWComposer对象,1个结构体callbacks,这个结构体会传给HAL层的HWC模块,这样,当有vsync信号时,就可以通过这个结构体进行回调。callbacks由继承自 hwc_procs_t, hwcomposer.h中的 hwc_procs_t的定义如下,

typedef struct hwc_procs   
void (*invalidate)(const struct hwc_procs* procs);  
void (*vsync)(const struct hwc_procs* procs, int disp, int64_t timestamp);  
void (*hotplug)(const struct hwc_procs* procs, int disp, int connected);  
 hwc_procs_t;  

invalidate方法:会触发屏幕刷新,在invalidate被调用不久会调用prepare和set,但是不保证调用了invalidate屏幕就一定会刷新,比如之前屏幕已经刷新过了,就不会在刷新。 

vsync方法:是分析这部分代码的目的,在收到一个vsync事件,并且HWC_EVENT_VSYNC是enabled的,这个vsync方法会被hwcomposer hal模块调用,其中的参数disp标示这个vsync事件是属于那个显示设备。 

hotplug方法:在一个显示设备连接或断开时会调用hotplug,主显示设备一直都是连接的,这个方法不会被调用,主要是针对可热插拔的设备。 

 对应的HWC的hwc_registerProcs方法逻辑如下,

hwc_context_t* ctx = (hwc_context_t*)(dev);
•••
ctx->proc = procs; //vsync回调HWComposer的vsync函数。
// Now that we have the functions needed, kick off
// the uevent & vsync threads
init_uevent_thread(ctx);
init_vsync_thread(ctx);

也就是说,HWC模块调用proc中的方法时,实际上是回调hwc_procs 结构体(mCBContext)中对应的方法。当有回调函数注册时,会开启uevent,vsync两个线程。uevent线程跟屏幕invalidate有关, vsync和vsync信号相关,主要分析vsync线程。HWC模块的hwc_vsync.cpp的init_vsync_thread方法调用流程图如下,


init_vsync_thread方法如下,

int ret;
pthread_t vsync_thread;
ALOGI("Initializing VSYNC Thread");
ret = pthread_create(&vsync_thread, NULL, vsync_loop, (void*) ctx);

创建vsync线程,入口方法为vsync_loop。该方法逻辑如下,

1,当不是模拟vsync的情况,当有多个显示设备时,对每个显示设备都要执行回调,发出通知,

do 
   int err = poll(*pfd, (int)(num_displays * num_events), -1);
•••

在该线程运行过程中通过ioctl系统调用进入到fb驱动等待VSync中断,如果VSync中断到来,则ioctl函数从驱动中返回。

2,使用模拟vsync的情况,通常是明确通过属性设置了或在开机时vsync时间戳节点还没打开,这时候会使用模拟vsync,模拟vsync指发给主显示设备。

do 
   usleep(16666);
   uint64_t timestamp = systemTime();
   ctx->proc->vsync(ctx->proc, HWC_DISPLAY_PRIMARY, timestamp);
 while (true);

vsync信号间隔16.66ms发送一次,也就是说1秒钟发送60次vsync信号。

回调mCBContext中的vsync方法,在HWComposer_hwc1.cpp中的HWComposer构造方法中,

mCBContext->hwc = this;
mCBContext->procs.invalidate = &hook_invalidate;
mCBContext->procs.vsync = &hook_vsync;

mCBContext中的vsync方法 指向的是HWComposer的hook_vsync方法,如下,

cb_context* ctx = reinterpret_cast<cb_context*>(
            const_cast<hwc_procs_t*>(procs));
ctx->hwc->vsync(disp, timestamp);

绕了一圈,最后还是调用HWComposer的vsync方法,调用流程图如下,

HWComposer的vsync方法如下,

char tag[16];
snprintf(tag, sizeof(tag), "HW_VSYNC_%1u", disp);
ATRACE_INT(tag, ++mVSyncCounts[disp] & 1);

mEventHandler.onVSyncReceived(this, disp, timestamp);

mEventHandler是什么对象呢? 在SurfaceFlinger_hwc1.cpp 的SurfaceFlinger的init方法构造HWComposer对象时,

mHwc.reset(new HWComposer(this,
    *static_cast<HWComposer::EventHandler *>(this)));
mHwc->registerCallback(this, mComposerSequenceId);

传入的是SurfaceFlinger对象,因此mEventHandler 是SurfaceFlinger对象。

HWComposer直接通过mEventHandler把vsync信号传到surfaceflinger。onVSyncReceived方法如下,

bool needsHwVsync = false;
 // Scope for the lock
   Mutex::Autolock _l(mHWVsyncLock);
   if (type == 0 && mPrimaryHWVsyncEnabled) 
       needsHwVsync = mPrimaryDispSync.addResyncSample(timestamp);
   

if (needsHwVsync) 
   enableHardwareVsync();
 else 
   disableHardwareVsync(false);

逻辑很简单,首先调用DispSync的addResyncSample方法判断是否需要Vsync信号,如果需要就调用enableHardwareVsync方法进行处理,否则调用disableHardwareVsync方法进行处理。

以上是关于vsync信号的主要内容,如果未能解决你的问题,请参考以下文章

ctrl-x 在终端中使用时会发送哪个信号?

vsync信号

vsync信号

QT中QLineEdit的editingFinished()信号在按下回车时会触发两次的解决办法

Win10系列:JavaScript综合实例1

非main goroutine的退出及调度循环(15)