美文网首页
Android-View绘制原理(12)-RenderThrea

Android-View绘制原理(12)-RenderThrea

作者: 代码多哥 | 来源:发表于2023-08-03 11:56 被阅读0次

    上一篇文章介绍了HardwareRendere在初始化的时候,涉及到了一个组件RenderThread并简要的分析了一下,这篇文章将继续深入的分析一下这个RenderThread,介绍一下它的几个重要特性和功能

    1 Thread

    RenderThread首先是继承自ThreadBase,是一个真实的线程。
    frameworks/base/libs/hwui/renderthread/RenderThread.h

    class RenderThread : private ThreadBase {
        PREVENT_COPY_AND_ASSIGN(RenderThread);
    

    frameworks/base/libs/hwui/thread/ThreadBase.h

    ThreadBase()
                : Thread(false)
                , mLooper(new Looper(false))
                , mQueue([this]() { mLooper->wake(); }, mLock) {}
    

    ThreadBase是一个hwui中定义的一个线程类,它由一个消息循环Looper,工作队列mQueue,和一个锁mLock组成,继承自util/Thread,通过系统调用pthread_create创建的线程,它的入口是一个无限循环函数_threadLoop.。

    system/core/libutils/Threads.cpp

     res = androidCreateRawThreadEtc(_threadLoop,
                    this, name, priority, stack, &mThread);
    
    int Thread::_threadLoop(void* user) {
        Thread* const self = static_cast<Thread*>(user);
        sp<Thread> strong(self->mHoldSelf);
         ...
        do {
               bool result;
                result = self->threadLoop();
            }
       ...
        } while(strong != nullptr);
    
        return 0;
    }
    

    每次循环都去执行threadLoop方法。在hwui的ThreadBase里面实现了这方法。
    frameworks/base/libs/hwui/thread/ThreadBase.h

    virtual bool threadLoop() override {
            Looper::setForThread(mLooper);
            while (!exitPending()) {
                waitForWork();
                processQueue();
            }
            Looper::setForThread(nullptr);
            return false;
        }
    

    在第一次调用threadLoop的时候,会进入到ThreadBase自身的一个无限循环中,所以threadLoop只会执行一次。 在这一次执行中,会给当前的线程绑定一个Looper。在ThreadBase中会去调用waitForWork等到looper中的消息,收到消息线程被唤醒后,执行processQueue。处理任务队列中的任务。

    void waitForWork() {
            nsecs_t nextWakeup;
            {
                std::unique_lock lock{mLock};
                nextWakeup = mQueue.nextWakeup(lock);
            }
            int timeout = -1;
            if (nextWakeup < std::numeric_limits<nsecs_t>::max()) {
                timeout = ns2ms(nextWakeup - WorkQueue::clock::now());
                if (timeout < 0) timeout = 0;
            }
            int result = mLooper->pollOnce(timeout);
            LOG_ALWAYS_FATAL_IF(result == Looper::POLL_ERROR, "RenderThread Looper POLL_ERROR!");
        }
    

    这里会一直阻塞到mLooper->pollOnce返回。之后就执行processQueue处理任务队列mQueue. 任务队列的类型是WorkQueue

     void processQueue() { mQueue.process(); }
    

    frameworks/base/libs/hwui/thread/WorkQueue.h

    void process() {
            auto now = clock::now();
            std::vector<WorkItem> toProcess;
            {
                std::unique_lock _lock{mLock};
                if (mWorkQueue.empty()) return;
                toProcess = std::move(mWorkQueue);
                auto moveBack = find_if(std::begin(toProcess), std::end(toProcess),
                                        [&now](WorkItem& item) { return item.runAt > now; });
                if (moveBack != std::end(toProcess)) {
                    mWorkQueue.reserve(std::distance(moveBack, std::end(toProcess)) + 5);
                    std::move(moveBack, std::end(toProcess), std::back_inserter(mWorkQueue));
                    toProcess.erase(moveBack, std::end(toProcess));
                }
            }
            for (auto& item : toProcess) {
                item.work();
            }
        }
    

    这个队列筛选出所有可以处理的WorkItem(runAt < now),然后再循环处理它们。当有任务需要交给这个线程执行的时候,可以获取到这个mQueue,然后调用添加任务的方法,比如post方法

        template <class F>
        void postAt(nsecs_t time, F&& func) {
            enqueue(WorkItem{time, std::function<void()>(std::forward<F>(func))});
        }
    

    然后调用到入队的enqueue方法

     void enqueue(WorkItem&& item) {
            bool needsWakeup;
            {
                std::unique_lock _lock{mLock};
                auto insertAt = std::find_if(
                        std::begin(mWorkQueue), std::end(mWorkQueue),
                        [time = item.runAt](WorkItem & item) { return item.runAt > time; });
                needsWakeup = std::begin(mWorkQueue) == insertAt;
                mWorkQueue.emplace(insertAt, std::move(item));
            }
            if (needsWakeup) {
                mWakeFunc();
            }
        }
    

    如果新的这任务放到队列的头部的话,就调用mWakeFunc方法,这个方法是构造Queue的时候传入的,回看上面ThreadBase的构造方法,传入的是 this { mLooper->wake()},因此会唤醒mLooper的pollOnce,从而线程开始处理工作队列,整个RenderThread线程的执行流程就理顺了,跟java层的HandlerThread很相似。

    2 threadLoop

    RenderThread 重写了threadLoop方法,所以执行的是它自己的threadLoop方法

    bool RenderThread::threadLoop() {
        ...
        initThreadLocals();
        while (true) {
            waitForWork();
            processQueue();
            ...
            if (!mFrameCallbackTaskPending && !mVsyncRequested && mFrameCallbacks.size()) {
                requestVsync();
            }
        }
    
        return false;
    }
    

    主要的流程和父类的threadLoop差不多,只是这里多了几个处理,一个是初始化,另外一个是处理完工作队列后,在一定条件下调用requestVsync,也就是说再处理完一个绘制任务后,再RenderThread里也可能会请求vsync信号,来看一下RenderThread的Vsync机制。

    void RenderThread::initThreadLocals() {
        setupFrameInterval();
        initializeChoreographer();
        mEglManager = new EglManager();
        mRenderState = new RenderState(*this);
        mVkManager = VulkanManager::getInstance();
        mCacheManager = new CacheManager();
    }
    
    void RenderThread::setupFrameInterval() {
        nsecs_t frameIntervalNanos = DeviceInfo::getVsyncPeriod();
        mTimeLord.setFrameInterval(frameIntervalNanos);
        mDispatchFrameDelay = static_cast<nsecs_t>(frameIntervalNanos * .25f);
    }
    

    setupFrameInterval比较简单,它读取的 DeviceInfo::getVsyncPeriod(),这个是在Java层初始化HardwareRenderer的时候设置到C层。接着调用initializeChoreographer初始话C层的Choreographer. 那这里就是关于Vsync的逻辑了。

    void RenderThread::initializeChoreographer() {
            ...
            mChoreographer = AChoreographer_create();
            LOG_ALWAYS_FATAL_IF(mChoreographer == nullptr, "Initialization of Choreographer failed");
            AChoreographer_registerRefreshRateCallback(mChoreographer,
                                                       RenderThread::refreshRateCallback, this);
    
            // Register the FD
            mLooper->addFd(AChoreographer_getFd(mChoreographer), 0, Looper::EVENT_INPUT,
                           RenderThread::choreographerCallback, this);
            mVsyncSource = new ChoreographerSource(this);
    }
    
    

    通过AChoreographer_create方法,创建一个C层的Choreographer对象,并调用AChoreographer_registerRefreshRateCallback注册刷新率更新的回调,当屏幕刷新率变化之后调用setupFrameInterval方法。然后让当前RenderThread的looper去观察mChoreographer文件描述符,如果由输入事件,则调用choreographerCallback方法。最后生成一个ChoreographerSource对象。C层的AChoreographer处理VSync的流程我们先不看,我们来看看什么RenderThread需要监听Vsync。 在threadLoop中,当执行完一次任务后(processQueue),会判断是否需要requestVsync。它的条件是

      if (mPendingRegistrationFrameCallbacks.size() && !mFrameCallbackTaskPending) {
                mVsyncSource->drainPendingEvents();
                mFrameCallbacks.insert(mPendingRegistrationFrameCallbacks.begin(),
                                       mPendingRegistrationFrameCallbacks.end());
                mPendingRegistrationFrameCallbacks.clear();
                requestVsync();
            }
    

    也就是当mPendingRegistrationFrameCallbacks不为空的时候且还没有执行回调的,会去requestVsync。 这是因为processQueue仅仅只完成了CPU的工作,GPU和HWComposer是否完成显示是未知的,而mPendingRegistrationFrameCallbacks是延迟绘制任务,它通常是由动画导致的,需要等到一帧显示完毕才能回调,因此这里就需要去监听Vsync,收到Vsync之后才去回调callback。RenderThread提供注册的方法:

        void RenderThread::postFrameCallback(IFrameCallback* callback) {
        mPendingRegistrationFrameCallbacks.insert(callback);
    }
    

    因此如果没有mPendingRegistrationFrameCallbacks的话,EventThread就不需要请求Vsync了

    3 requireGlContext

    这个方法是准备GPU绘制的上下文,需要在绘制之前准备好向GPU提交数据的相关能力

    void RenderThread::requireGlContext() {
        if (mEglManager->hasEglContext()) {
            return;
        }
        mEglManager->initialize();
    
        sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
        LOG_ALWAYS_FATAL_IF(!glInterface.get());
    
        GrContextOptions options;
        initGrContextOptions(options);
        auto glesVersion = reinterpret_cast<const char*>(glGetString(GL_VERSION));
        auto size = glesVersion ? strlen(glesVersion) : -1;
        cacheManager().configureContext(&options, glesVersion, size);
        sk_sp<GrDirectContext> grContext(GrDirectContext::MakeGL(std::move(glInterface), options));
        LOG_ALWAYS_FATAL_IF(!grContext.get());
        setGrContext(grContext);
    }
    
    • mEglManager->initialize();加载EGL驱动,初始化EGL
    • glInterface(GrGLCreateNativeInterface()),GrGLCreateNativeInterface()里封装所有的openGL接口external/skia/src/gpu/gl/egl/GrGLMakeEGLInterface.cpp
    sk_sp<const GrGLInterface> GrGLMakeEGLInterface() {
        return GrGLMakeAssembledInterface(nullptr, egl_get_gl_proc);
    }
    
    static GrGLFuncPtr egl_get_gl_proc(void* ctx, const char name[]) {
        ...
        #define M(X) if (0 == strcmp(#X, name)) { return (GrGLFuncPtr) X; }
        M(eglGetCurrentDisplay)
        M(eglQueryString)
        ....
        #undef M
        return eglGetProcAddress(name);
     }
    
    • grContext(GrDirectContext::MakeGL(std::move(glInterface), options)); 创建GPU绘制的上下文GrDirectContext,并初始化它的成员fGpu,它将负责向GPU提交数据。
    sk_sp<GrDirectContext> GrDirectContext::MakeGL(sk_sp<const GrGLInterface> glInterface,
                                                   const GrContextOptions& options) {
        sk_sp<GrDirectContext> direct(new GrDirectContext(GrBackendApi::kOpenGL, options));
        ...
        direct->fGpu = GrGLGpu::Make(std::move(glInterface), options, direct.get());
        if (!direct->init()) {
            return nullptr;
        }
        return direct;
    }
    

    到这里EventThread里就具备了请求GPU进行OpenGL渲染的能力了。

    4 总结

    本文主要介绍了EventThread这个组件相关的功能,主要包含了以下内容

    • 线程和任务队列原理
    • vsync机制
    • 初始化EGLManager,GrDirectContext,并创建好了向GPU提交数据的GrGLGpu对象。

    相关文章

      网友评论

          本文标题:Android-View绘制原理(12)-RenderThrea

          本文链接:https://www.haomeiwen.com/subject/gjsmpdtx.html