• Android 显示surfaceFlinger vsync 获取


    vsync 的概念

    vsync简单理解就是一帧图像在显示设备这边显示完成之后(图像从左上角扫描到了右下角了) 发送的第一个硬件vsync信号, 显示设备重新回到左上角开始显示的时候会在发第二个vsync信号。在发送第一个vsync信号出来的时候,上层要开始准备合成处理好的图像buffer。而且这个必须在下一个vsync到达之前准备完成 否则显示会出现异常。 vsync 信号主要应用app刷新,视频显示刷新。

    问题:

    1. vsync信号是硬件发出的,那上层应该怎么去获取?
    2. vsync 如何从底层传递到应用?

    vsync应用层获取的方式

    网上资料,有些太旧运行不了,最好的参考的代码是google的源码的test。

    • google 参考方法

      源码位置:frameworks\native\services\surfaceflinger\tests\vsync\vsync.cpp

    简要流程:硬件的vsync抽象在上层是一个fd。当有vsync的时候,上层looper会调用receiver 回调,在回调中就可以获取到count 和timestamp。回调函数必须是 下面三个参数这样的。

    int receiver(int /*fd*/, int /*events*/, void* data)
    
    • 1

    addFd 将displayEvent的fd、要添加的事件也即有事件输入,有事件之后的回调函数,disPlayEvent自身(用于传递到回调函数获取)

    实现时需要创建一个线程,在线程中队DisplayEventReceiver的fd进行pollOnce 操作。
    fd没有poll_input的消息时,线程会堵塞。会消息的时候pooll返回 然后调用回调函数receiver,在receriver可以获取vysnc的count和timestamp。

        DisplayEventReceiver myDisplayEvent;
        sp loop = new Looper(false);
        loop->addFd(myDisplayEvent.getFd(), 0, ALOOPER_EVENT_INPUT, receiver, &myDisplayEvent);
    
    int receiver(int /*fd*/, int /*events*/, void* data) {
        DisplayEventReceiver* q = (DisplayEventReceiver*)data;
        ssize_t n;
        DisplayEventReceiver::Event buffer[1];
        static nsecs_t oldTimeStamp = 0;
        while ((n = q->getEvents(buffer, 1)) > 0) {
            for (int i = 0; i < n; i++) {
                ALOGD("buffer[i].header.type is :%d \n", buffer[i].header.type);
                if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
                    ALOGD("event vsync: count=%d\t", buffer[i].vsync.count);
                }
                if (oldTimeStamp) {
                    float t = float(buffer[i].header.timestamp - oldTimeStamp) / s2ns(1);
                    ALOGD("%f ms (%f Hz)\n", t * 1000, 1.0 / t);
                }
                oldTimeStamp = buffer[i].header.timestamp;
            }
        }
        return 1;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    vsync 信号传递

    vsync 从hwc 获取到之后 回调到surfaceflinger, surfaceFlinger 通过socket发送到应用。

    • 应用注册fd到surfacefinger流程

      主要在DisplayEventReceiver构造函数中实现。

        sp sf(ComposerService::getComposerService());
        if (sf != nullptr) {
            mEventConnection = sf->createDisplayEventConnection(vsyncSource, eventRegistration);
            if (mEventConnection != nullptr) {
                mDataChannel = std::make_unique();
                mEventConnection->stealReceiveChannel(mDataChannel.get());
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    构造函数流程:

    1. 获取surfaceflinger的服务。
    2. 通过surfaceflinger的createDisplayEventConnection的函数创建一个gui::BitTube对象,
      gui::BitTube管理一对无名的socket, 这个socket会对BitTube的mReceived 和 mSendFd
      进行初始化。
    3. 应用端也会创建一个gui::BitTube,并传递到surfaceflinger中,surfaceflinger 中将已经创建好的BitTube中mReceived赋值给外部创建好BitTube中的mReceived。
      这样外部应用端和surfaceFlinger建立了连续。发送到surfaceflinger BitTube的mSendFd的消息,应用端可以监听并接收。

    DisplayEventDispatcher继承自LooperCallback,看LooperCallback的实现可以知道addFd
    的有INPUT的event后回调到的是handleEvent。

    class DisplayEventDispatcher : public LooperCallback
    
        if (mLooper != nullptr) {
            int rc = mLooper->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT, this, NULL);
            if (rc < 0) {
                return UNKNOWN_ERROR;
            }
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • surfaceFlinger 注册vsync回调到hwc以及 回调上报的路径
    1. 首先hwc是一个hal层的服务 其接口定义在这个目录 hardware/interfaces/graphics/composer/2.1。其对应的service 是android.hardware.graphics.composer@2.1-service。 surfaceflinger提供这个composer的service提供的接口 可以注册回调到hwcomposer 的hal层中。
    mCompositionEngine->getHwComposer().setCallback(this);
    
     mComposer->registerCallback(
                sp::make(callback, mComposer->isVsyncPeriodSwitchSupported()));
    
    
    void Composer::registerCallback(const sp& callback)
    {
        android::hardware::setMinSchedulerPolicy(callback, SCHED_FIFO, 2);
        auto ret = [&]() {
            if (mClient_2_4) {
                return mClient_2_4->registerCallback_2_4(callback);
            }
            return mClient->registerCallback(callback);
        }();
        if (!ret.isOk()) {
            ALOGE("failed to register IComposerCallback");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    而在hal层的 vsync的实现各不相同。 有一种实现方式运行一个线程,然后在线程中使用
    drmWaitVBlank等待vsync,vsync产生后回调到之前surfaceflinger 注册进来的callback中。

    在surfaceflinger中会创建一个DispSyncThread线程,这个线程收到vsync事件后也就是回调到onComposerHalVsync中时。通过之前注册进来的EventThread的callback
    构造了一个DisplayEventReceiver::Event类型事件,类型为DISPLAY_EVENT_VSYNC,还有displayId,时间戳以及接收到的次数。构造好之后会唤醒EventThread线程,这个线程从mPendingEvents中的事件调用dispatchEvent将事件分发给监听者,监听者即是向EventThread请求了Vsync的EventThreadConnection并addfd的配置回调函数的。

    void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp,
                                            std::optional vsyncPeriod)
        mScheduler->addResyncSample(timestamp, vsyncPeriod, &periodFlushed);
    
    
    • 1
    • 2
    • 3
    • 4

    参考链接:
    https://blog.csdn.net/qq_34211365/article/details/105123790

  • 相关阅读:
    Linux磁盘管理(CentOS)
    Vue中的样式绑定
    Linux学习-74-NTFS文件系统安装教程
    virtio frontend and backend
    [Go疑难杂症]为什么nil不等于nil
    【JQuery】JQuery入门——模拟用户分组以及页面换肤
    DC-DC升压变换器 直流隔离低压升高压输出60V/100V/200V/250V/300V/400V/500V/800V
    Mybatis的二级缓存 (默认方式)
    Android 发布自己的sdk
    Qt 学习(四) —— QCheckBox复选框
  • 原文地址:https://blog.csdn.net/H2008066215019910120/article/details/133002550