【硬件加速】1、硬件加速渲染环境初始化过程分析【Android 13】

在这里插入图片描述

本系列分析硬件加速的基础知识参考了博客:

Android应用程序UI硬件加速渲染技术简要介绍和学习计划_硬件ui_罗升阳的博客-CSDN博客

虽然旧的Android版本代码和本系列分析所用的Android13代码差异很大,但是很多设计思想和流程体系还是相通的。

在Android应用程序中,我们是通过Canvas API来绘制UI元素的。在硬件加速渲染环境中,这些Canvas API调用最终会转化为Open GL API调用(转化过程对应用程序来说是透明的)。由于Open GL API调用要求发生在Open GL环境中,因此在每当有新的Activity窗口启动时,系统都会为其初始化好Open GL环境。这篇文章就详细分析这个Open GL环境的初始化过程。

Open GL环境也称为Open GL渲染上下文。一个Open GL渲染上下文只能与一个线程关联。在一个Open GL渲染上下文创建的Open GL对象一般来说只能在关联的Open GL线程中操作。这样就可以避免发生多线程并发访问发生的冲突问题。这与大多数的UI架构限制UI操作只能发生在UI线程的原理是差不多的。

在Android 5.0之前,Android应用程序的主线程同时也是一个Open GL线程。但是从Android 5.0之后,Android应用程序的Open GL线程就独立出来了,称为Render Thread,如图所示:

在这里插入图片描述

Render Thread有一个Work Queue,Main Thread通过一个代理对象Render Proxy向这个Work Queue发送一个drawFrame命令,从而驱使Render Thread执行一次渲染操作。因此,Android应用程序UI硬件加速渲染环境的初始化过程任务之一就是要创建一个Render Thread。

一个Android应用程序可能存在多个Activity组件。在Android系统中,每一个Activity组件都是一个独立渲染的窗口。由于一个Android应用程序只有一个Render Thread,因此当Main Thread向Render Thread发出渲染命令时,Render Thread要知道当前要渲染的窗口是什么。从这个角度看,Android应用程序UI硬件加速渲染环境的初始化过程任务之二就是要告诉Render Thread当前要渲染的窗口是什么。

Java层的Activity窗口到了Open GL这一层,被抽象为一个ANativeWindow,封装在EGLSurface中,这个EGLSurface描述的是一个绘图表面。一旦Render Thread知道了当前要渲染的窗口,它就将可以将该窗口绑定到Open GL渲染上下文中去,从而使得后面的渲染操作都是针对被绑定的窗口的,如图所示:

在这里插入图片描述

将EGLSurface绑定到Open GL渲染上下文之后,就可以通过dequeueBuffer操作向BufferQueue请求一个图形缓冲区进行绘制,绘制完成后,再通过queueBuffer操作将这个图形缓冲区返回给BufferQueue,最终这个图形缓冲区会被提交给SurfaceFlinger来进行合成显示。

接下来,我们就结合源代码分析Android应用程序UI硬件加速渲染环境的初始化过程,主要的关注点就是创建Render Thread的过程和创建EGLSurface的过程。

App向WMS注册窗口的起点为ViewRootImpl.setView,同时这个方法也是硬件加速渲染环境初始化的起点。

1 ViewRootImpl.setView

    /**
     * We have one child
     */
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
            int userId) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                // ......
                
                if (view instanceof RootViewSurfaceTaker) {
                    mSurfaceHolderCallback =
                            ((RootViewSurfaceTaker)view).willYouTakeTheSurface();
                    if (mSurfaceHolderCallback != null) {
                        mSurfaceHolder = new TakenSurfaceHolder();
                        mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);
                        mSurfaceHolder.addCallback(mSurfaceHolderCallback);
                    }
                }
                
                // ......

                // If the application owns the surface, don't enable hardware acceleration
                if (mSurfaceHolder == null) {
                    // While this is supposed to enable only, it can effectively disable
                    // the acceleration too.
                    enableHardwareAcceleration(attrs);
                    // ......
                }

                // ......
            }
        }
    }

参数view描述的是当前正在创建的窗口的根View,对于Activity来说就是DecorView,DeocrView是实现了接口RootViewSurfaceTaker的:

public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks

willYouTakeTheSurface表示这个View是否有自己的Surface,典型的例子是SurfaceView,由SurfaceView自己控制Surface的尺寸、格式等。

对于DecorView,这个接口返回的是null,那么成员变量mSurfaceHolder就不会被初始化,那么继续调用enableHardwareAcceleration方法来开启硬件加速。

2 ViewRootImpl.enableHardwareAcceleration

    @UnsupportedAppUsage
    private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {
        // ......

        // Try to enable hardware acceleration if requested
        boolean hardwareAccelerated =
                (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
        hardwareAccelerated = ViewDebugManager.getInstance().debugForceHWDraw(hardwareAccelerated);

        if (hardwareAccelerated) {
            // Persistent processes (including the system) should not do
            // accelerated rendering on low-end devices.  In that case,
            // sRendererDisabled will be set.  In addition, the system process
            // itself should never do accelerated rendering.  In that case, both
            // sRendererDisabled and sSystemRendererDisabled are set.  When
            // sSystemRendererDisabled is set, PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED
            // can be used by code on the system process to escape that and enable
            // HW accelerated drawing.  (This is basically for the lock screen.)

            final boolean forceHwAccelerated = (attrs.privateFlags &
                    WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED) != 0;

            if (ThreadedRenderer.sRendererEnabled || forceHwAccelerated) {
                // ......
                
                mAttachInfo.mThreadedRenderer = ThreadedRenderer.create(mContext, translucent,
                        attrs.getTitle().toString());
                
                // ......
            }
        }
    }

虽然硬件加速渲染是个好东西,但是也不是每一个需要绘制UI的进程都必需的。这样做是考虑到两个因素。第一个因素是并不是所有的Canvas API都可以被GPU支持。如果应用程序使用到了这些不被GPU支持的API,那么就需要禁用硬件加速渲染。第二个因素是支持硬件加速渲染的代价是增加了内存开销。

大部分时候都是根据Activity窗口自己是否请求了硬件加速渲染而决定是否要为其开启硬件加速,可以通过AndroidManifest.xml中为相关Activity定义以下属性:

	android.R.attr#hardwareAccelerated

为true来设置某个Activity窗口支持硬件加速,这会使窗口属性LayoutParams的成员变量flags添加FLAG_HARDWARE_ACCELERATED标志位。

但即使Activity窗口声明支持硬件加速,也是要设备本身支持硬件加速渲染才行。这里看到ThreadedRenderer中代表硬件加速渲染开关的成员变量sRendererEnabled默认是开启的:

    public static boolean sRendererEnabled = true;

最后,如果当前创建的窗口支持硬件加速渲染,那么就会调用HardwareRenderer类的静态成员函数create创建一个HardwareRenderer对象,并且保存在与该窗口关联的一个AttachInfo对象的成员变量的成员变量mHardwareRenderer对象。这个HardwareRenderer对象以后将负责执行窗口硬件加速渲染的相关操作。

3 ThreadedRenderer.create

    /**
     * Creates a threaded renderer using OpenGL.
     *
     * @param translucent True if the surface is translucent, false otherwise
     *
     * @return A threaded renderer backed by OpenGL.
     */
    public static ThreadedRenderer create(Context context, boolean translucent, String name) {
        return new ThreadedRenderer(context, translucent, name);
    }

    ThreadedRenderer(Context context, boolean translucent, String name) {
        super();
        // ......
    }

创建一个使用OpenGL的线程渲染器。

继续看下其父类HardwareRenderer的构造方法:

    /**
     * Creates a new instance of a HardwareRenderer. The HardwareRenderer will default
     * to opaque with no light source configured.
     */
    public HardwareRenderer() {
        ProcessInitializer.sInstance.initUsingContext();
        mRootNode = RenderNode.adopt(nCreateRootRenderNode());
        mRootNode.setClipToBounds(false);
        mNativeProxy = nCreateProxy(!mOpaque, mRootNode.mNativeRenderNode);
        if (mNativeProxy == 0) {
            throw new OutOfMemoryError("Unable to create hardware renderer");
        }
        Cleaner.create(this, new DestroyContextRunnable(mNativeProxy));
        ProcessInitializer.sInstance.init(mNativeProxy);
    }

1)、调用ThreadedRenderer类的成员函数nCreateRootRenderNode在Native层创建了一个Render Node,并且通过Java层的RenderNode类的静态成员函数adopt将其封装在一个Java层的Render Node中。这个Render Node即为窗口的Root Render Node。

2)、调用ThreadedRenderer类的成员函数nCreateProxy在Native层创建了一个Render Proxy对象。该Render Proxy对象以后将负责从Main Thread向Render Thread发送命令。

4 创建RootRenderNode

4.1 android_view_ThreadedRenderer_createRootRenderNode

窗口的Root Render Node是通过调用ThreadedRenderer类的成员函数nCreateRootRenderNode创建的。这是一个JNI函数,由Native层的函数android_view_ThreadedRenderer_createRootRenderNode实现,如下所示:

// frameworks\base\libs\hwui\jni\android_graphics_HardwareRenderer.cpp

static jlong android_view_ThreadedRenderer_createRootRenderNode(JNIEnv* env, jobject clazz) {
    RootRenderNode* node = new RootRenderNode(std::make_unique<JvmErrorReporter>(env));
    node->incStrong(0);
    node->setName("RootRenderNode");
    return reinterpret_cast<jlong>(node);
}

从这里就可以看出,窗口在Native层的根RenderNode实际上是一个RootRenderNode对象。

接着返回该RootRenderNode的地址。

4.2 创建RenderNode

// frameworks\base\graphics\java\android\graphics\RenderNode.java

	public static RenderNode adopt(long nativePtr) {
        return new RenderNode(nativePtr);
    }

    private RenderNode(long nativePtr) {
        mNativeRenderNode = nativePtr;
        NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativeRenderNode);
        mAnimationHost = null;
    }

这一步就更简单了,根据从C/C++层传过来的RootRenderNode对象的地址创建一个Java层的RenderNode对象,将该地址保存在其long型的成员变量mNativeRenderNode中。

5 创建RenderProxy

窗口在Main Thread线程中使用的Render Proxy对象是通过调用ThreadedRenderer类的成员函数nCreateProxy创建的。这是一个JNI函数,由Native层的函数android_view_ThreadedRenderer_createProxy实现,如下所示:

// frameworks\base\libs\hwui\jni\android_graphics_HardwareRenderer.cpp

static jlong android_view_ThreadedRenderer_createProxy(JNIEnv* env, jobject clazz,
        jboolean translucent, jlong rootRenderNodePtr) {
    RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootRenderNodePtr);
    ContextFactoryImpl factory(rootRenderNode);
    RenderProxy* proxy = new RenderProxy(translucent, rootRenderNode, &factory);
    return (jlong) proxy;
}

参数rootRenderNodePtr指向前面创建的RootRenderNode对象。有了这个RootRenderNode对象之后,函数android_view_ThreadedRenderer_createProxy就创建了一个RenderProxy对象。

RenderProxy对象的构造函数如下所示:

// frameworks\base\libs\hwui\renderthread\RenderProxy.cpp

RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode,
                         IContextFactory* contextFactory)
        : mRenderThread(RenderThread::getInstance()), mContext(nullptr) {
    mContext = mRenderThread.queue().runSync([&]() -> CanvasContext* {
        return CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory);
    });
    mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode,
                              pthread_gettid_np(pthread_self()), getRenderThreadTid());
}

RenderProxy类有三个重要的成员变量mRenderThread、mContext和mDrawFrameTask,它们的类型分别为RenderThread、CanvasContext和DrawFrameTask。其中,mRenderThread描述的就是Render Thread,mContext描述的是一个画布上下文,mDrawFrameTask描述的是一个用来执行渲染任务的Task。接下来我们就重点分析这三个成员变量的初始化过程。

6 创建RenderThread

RenderProxy类的成员变量mRenderThread指向的Render Thread是通过调用RenderThread类的静态成员函数getInstance获得的。从名字我们就可以看出,RenderThread类的静态成员函数getInstance返回的是一个RenderThread单例。也就是说,在一个Android应用程序进程中,只有一个Render Thread存在。

为了更好地了解Render Thread是如何运行的,我们继续分析Render Thread的创建过程,如下所示:

// frameworks\base\libs\hwui\renderthread\RenderThread.cpp

RenderThread& RenderThread::getInstance() {
    [[clang::no_destroy]] static sp<RenderThread> sInstance = []() {
        sp<RenderThread> thread = sp<RenderThread>::make();
        thread->start("RenderThread");
        return thread;
    }();
    gHasRenderThreadInstance = true;
    return *sInstance;
}

首先创建一个RenderThread,然后看到RenderThread继承自ThreadBase,ThreadBase又继承自Thread:

// frameworks\base\libs\hwui\renderthread\RenderThread.h
class RenderThread : private ThreadBase
    
// frameworks\base\libs\hwui\thread\ThreadBase.h
class ThreadBase : public Thread {
    PREVENT_COPY_AND_ASSIGN(ThreadBase);

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

    // ......

    void start(const char* name = "ThreadBase") { Thread::run(name); }
    
    // ......
}

1)、RenderThread类的成员变量mLooper指向一个Looper对象,Render Thread通过它来创建一个消息驱动运行模型,类似于Main Thread的消息驱动运行模型。

2)、RenderThread类的成员变量mQueue指向一个WorkQueue对象,WorkQueue顾名思义是一个任务列表,它管理一个WorkItem的队列,每一个WorkItem代表一个待完成的任务。

3)、RenderThread类是从Thread类继承下来的,当我们调用它的成员函数start进而调用Thread.run函数的时候,就会创建一个新的线程。这个新的线程的入口函数为RenderThread类的成员函数threadLoop,它的实现如下所示:

// frameworks\base\libs\hwui\renderthread\RenderThread.cpp

bool RenderThread::threadLoop() {
    // ......
    
    initThreadLocals();

    while (true) {
        waitForWork();
        processQueue();

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

        if (!mFrameCallbackTaskPending && !mVsyncRequested && mFrameCallbacks.size()) {
            // TODO: Clean this up. This is working around an issue where a combination
            // of bad timing and slow drawing can result in dropping a stale vsync
            // on the floor (correct!) but fails to schedule to listen for the
            // next vsync (oops), so none of the callbacks are run.
            requestVsync();
        }
    }

    return false;
}

接下来分析一下RenderThread的工作机制,也就是RenderThread.threadLoop的主要内容。在分析initThreadLocals函数前,先看一下WorkQueue和Looper的部分。

6.1 WorkQueue

RenderThread的成员变量mWorkQueue就是一个包含WorkItem类型对象的队列,WorkItem代表着每一个任务:

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

	std::vector<WorkItem> mWorkQueue;

    struct WorkItem {
        WorkItem() = delete;
        WorkItem(const WorkItem& other) = delete;
        WorkItem& operator=(const WorkItem& other) = delete;
        WorkItem(WorkItem&& other) = default;
        WorkItem& operator=(WorkItem&& other) = default;

        WorkItem(nsecs_t runAt, std::function<void()>&& work)
                : runAt(runAt), work(std::move(work)) {}

        nsecs_t runAt;
        std::function<void()> work;
    };

其中WorkItem.runAt表示当前任务在何时将会得到处理。

6.1.1 添加任务

当其它线程需要调度Render Thread,就会向它的任务队列增加一个任务,然后唤醒Render Thread进行处理。具体添加的方式,我们到后面一并分析。

WorkQueue类提供了一些函数来向WorkQueue增加一个WorkItem,它们的实现如下所示:

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

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

    template <class F>
    void postDelayed(nsecs_t delay, F&& func) {
        enqueue(WorkItem{clock::now() + delay, std::function<void()>(std::forward<F>(func))});
    }

    template <class F>
    void post(F&& func) {
        postAt(0, std::forward<F>(func));
    }

    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();
        }
    }

1)、postAt和postDelayed的区别是,postDelayed为创建的WorkItem的执行时间runAt,添加了额外的延迟,即传参delay代表的时间。

2)、这些函数最终调用的都是enqueue函数,内容也很简单,找到mWorkQueue中的第一个执行时间比当前要入的WorkItem的执行时间更晚的那个WorkItem,如果它是mWorkQueue队列中的第一个,说明当前要插入的WorkItem才是最早应该执行的WorkItem,那么将needsWakeup置为true,接着唤醒RenderThread。另外我们从这里也能看到WorkQueue中的WorkItem是按照执行时间从早到晚进行排列的。

3)、最终调用mWakeFunc函数唤醒RenderThread,后面分析会看到这个函数的内容。

6.1.2 处理任务

Render Thread通过成员函数processQueue,进而调用WorkQueue的成员函数process处理待处理的WorkItem。

processQueue定义在ThreadBase.h中:

// frameworks\base\libs\hwui\thread\ThreadBase.h
	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();
        }
    }

从上面的分析中,我们知道了WorkQueue.mWorkQueue中的WorkItem,是按照执行时间从早到晚进行排序的,那么这里的内容就是,从mWorkQueue中找到所有执行时间不小于当前时间的WorkItem,依次执行它们的work函数。

work函数的内容具体是什么,则要到下面分析向WorkQueue添加任务的具体场景时,才能得知。

6.2 RenderThread线程的睡眠和唤醒

6.2.1 RenderThread线程的睡眠

RenderThread线程的睡眠是通过定义在ThreadBase.h中的waitForWork完成的:

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

	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!");
    }

    nsecs_t nextWakeup(std::unique_lock<std::mutex>& lock) {
        if (mWorkQueue.empty()) {
            return std::numeric_limits<nsecs_t>::max();
        } else {
            return std::begin(mWorkQueue)->runAt;
        }
    }

waitForWork函数的内容为,从通过nextWakeup函数从WorkQueue中拿出最先执行的那个WorkItem,在它的执行时间到来之前睡眠。

1)、mWorkQueue为空,表示当前没有可执行的WorkItem,那么当前线程就一直睡眠,直到Looper.wake唤醒。

2)、mWorkQueue不为空,且当前有可执行的WorkItem,如果就拿WorkItem的执行时间减去当前时间,如果小于0,说明已经到了下一个WorkItem该执行的时间了,那么无需等待,否则设置一个延时,到时后再唤醒当前线程。

6.2.2 RenderThread线程的唤醒

RenderThread线程的唤醒的情况有两种其实上面都已经分析过了:

1)、一是向WorkQueue中添加任务时,如果该任务根据执行时间排在了WorkQueue的首位,那么就需要唤醒当前当前线程立即处理该任务。

2)、二是waitForWork函数会从WorkQueue中拿出第一个WorkItem,在它的执行时间到来之前睡眠,执行时间到来的时候唤醒。

3)、屏幕产生Vsync信号的时候,这种方式下面会看到。

这里看一下第一种情况,向WorkQueue添加任务的时候,唤醒线程的mWakeFunc函数的实现:

    void enqueue(WorkItem&& item) {
        bool needsWakeup;
        {
            // ......
        }
        if (needsWakeup) {
            mWakeFunc();
        }
    }

WorkQueue的成员变量mWakeFunc定义为:

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

	std::function<void()> mWakeFunc;

    WorkQueue(std::function<void()>&& wakeFunc, std::mutex& lock)
            : mWakeFunc(move(wakeFunc)), mLock(lock) {}

从WorkQueue的构造函数中赋值,那么往回看WorkQueue创建的地方:

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

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

看到mWakeFunc的内容正是:

	mLooper->wake()

用来唤醒睡眠在ThreadBase.waitForWork的RenderThread线程。

6.3 RenderThread初始化

回到RenderThread类的成员函数threadLoop中,我们再来看Render Thread在进入无限循环之前调用的RenderThread类的成员函数initThreadLocals,它的实现如下所示:

// frameworks\base\libs\hwui\renderthread\RenderThread.cpp

void RenderThread::initThreadLocals() {
    setupFrameInterval();
    initializeChoreographer();
    mEglManager = new EglManager();
    mRenderState = new RenderState(*this);
    mVkManager = VulkanManager::getInstance();
    mCacheManager = new CacheManager();
}

RenderThread类的成员函数initThreadLocals首先调用另外一个成员函数initializeChoreographer创建和初始化一个mChoreographer对象,用来接收Vsync信号。接着又会分别创建一个EglManager对象和一个RenderState对象,并且保存在成员变量mEglManager和mRenderState中。前者用在初始化Open GL渲染上下文需要用到,而后者用来记录Render Thread当前的一些渲染状态。

接下来我们主要关注Choreographer对象的创建和初始化过程,即RenderThread类的成员函数initializeChoreographer的实现,如下所示:

// frameworks\base\libs\hwui\renderthread\RenderThread.cpp

void RenderThread::initializeChoreographer() {
    LOG_ALWAYS_FATAL_IF(mVsyncSource, "Initializing a second Choreographer?");

    if (!Properties::isolatedProcess) {
        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);
    } else {
        mVsyncSource = new DummyVsyncSource(this);
    }
}

1)、AChoreographer_create函数用来创建一个AChoreographer的实例。

2)、AChoreographer_registerRefreshRateCallback注册一个当显示刷新率改变时执行的回调。

3)、创建的Choreographer对象关联的文件描述符被注册到了Render Thread的消息循环中。这意味着屏幕产生Vsync信号时,SurfaceFlinger服务(Vsync信号由SurfaceFlinger服务进行管理和分发)会通过上述文件描述符号唤醒Render Thread。这时候Render Thread就会调用RenderThread类的静态成员函数choreographerCallback。

4)、初始化ChoreographerSource类型的成员变量mVsyncSource,可以通过该成员变量来添加Vsync回调。

接下来分析一下Choreographer的工作机制。

6.4 Choreographer工作机制

6.4.1 Vsync信号到来流程

6.4.1.1 RenderThread.choreographerCallback

RenderThread类的成员函数choreographerCallback的实现如下所示:

int RenderThread::choreographerCallback(int fd, int events, void* data) {
    // ......
    
    RenderThread* rt = reinterpret_cast<RenderThread*>(data);
    AChoreographer_handlePendingEvents(rt->mChoreographer, data);

    return 1;
}
6.4.1.2 AChoreographer.AChoreographer_handlePendingEvents

AChoreographer_handlePendingEvents定义如下所示:

// frameworks\native\libs\nativedisplay\AChoreographer.cpp

void AChoreographer_handlePendingEvents(AChoreographer* choreographer, void* data) {
    // Pass dummy fd and events args to handleEvent, since the underlying
    // DisplayEventDispatcher doesn't need them outside of validating that a
    // Looper instance didn't break, but these args circumvent those checks.
    Choreographer* impl = AChoreographer_to_Choreographer(choreographer);
    impl->handleEvent(-1, Looper::EVENT_INPUT, data);
}

static inline Choreographer* AChoreographer_to_Choreographer(AChoreographer* choreographer) {
    return reinterpret_cast<Choreographer*>(choreographer);
}

将传参AChoreographer强转为Choreographer类型,然后调用Choreographer的handleEvent函数。

由于Choreographer是继承DisplayEventDispatcher的:

class Choreographer : public DisplayEventDispatcher, public MessageHandler 

则调用的是DisplayEventDispatcher.handleEvent。

6.4.1.3 DisplayEventDispatcher.handleEvent
// frameworks\native\libs\gui\DisplayEventDispatcher.cpp

int DisplayEventDispatcher::handleEvent(int, int events, void*) {
    // ......

    // Drain all pending events, keep the last vsync.
    // ,.....
    if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount, &vsyncEventData)) {
        ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64
              ", displayId=%s, count=%d, vsyncId=%" PRId64,
              this, ns2ms(vsyncTimestamp), to_string(vsyncDisplayId).c_str(), vsyncCount,
              vsyncEventData.preferredVsyncId());
        mWaitingForVsync = false;
        mLastVsyncCount = vsyncCount;
        dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount, vsyncEventData);
    }

    // ,.....

    return 1; // keep the callback
}

processPendingEvents函数用来处理DisplayEventReceiver接收到的所有事件,包括Vsync信号事件,热插拔事件等,如果接收的事件中含有Vsync信号事件,就会返回true,然后调用Choreographer的dispatchVsync函数。

6.4.1.4 Choreographer.dispatchVsync
// frameworks\native\libs\nativedisplay\AChoreographer.cpp

void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t,
                                  VsyncEventData vsyncEventData) {
    std::vector<FrameCallback> callbacks{};
    {
        std::lock_guard<std::mutex> _l{mLock};
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
        while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) {
            callbacks.push_back(mFrameCallbacks.top());
            mFrameCallbacks.pop();
        }
    }
    mLastVsyncEventData = vsyncEventData;
    for (const auto& cb : callbacks) {
        if (cb.vsyncCallback != nullptr) {
            // ......
            cb.vsyncCallback(reinterpret_cast<const AChoreographerFrameCallbackData*>(
                                     &frameCallbackData),
                             cb.data);
            mInCallback = false;
        } else if (cb.callback64 != nullptr) {
            cb.callback64(timestamp, cb.data);
        } else if (cb.callback != nullptr) {
            cb.callback(timestamp, cb.data);
        }
    }
}

Choreographer的dispatchVsync函数主要内容为遍历Choreographer的成员变量mFrameCallbacks,执行其中每一个FrameCallback对象的回调函数。Choreographer的成员变量mFrameCallbacks和FrameCallback结构体定义如下:

// frameworks\native\libs\nativedisplay\AChoreographer.cpp

    std::priority_queue<FrameCallback> mFrameCallbacks;

struct FrameCallback {
    AChoreographer_frameCallback callback;
    AChoreographer_frameCallback64 callback64;
    AChoreographer_vsyncCallback vsyncCallback;
    void* data;
    nsecs_t dueTime;

    // ......
};

回调有三种,定义如下:

// frameworks\native\include\android\choreographer.h

/**
 * Prototype of the function that is called when a new frame is being rendered.
 * It's passed the time that the frame is being rendered as nanoseconds in the
 * CLOCK_MONOTONIC time base, as well as the data pointer provided by the
 * application that registered a callback. All callbacks that run as part of
 * rendering a frame will observe the same frame time, so it should be used
 * whenever events need to be synchronized (e.g. animations).
 */
typedef void (*AChoreographer_frameCallback)(long frameTimeNanos, void* data);

/**
 * Prototype of the function that is called when a new frame is being rendered.
 * It's passed the time that the frame is being rendered as nanoseconds in the
 * CLOCK_MONOTONIC time base, as well as the data pointer provided by the
 * application that registered a callback. All callbacks that run as part of
 * rendering a frame will observe the same frame time, so it should be used
 * whenever events need to be synchronized (e.g. animations).
 */
typedef void (*AChoreographer_frameCallback64)(int64_t frameTimeNanos, void* data);

/**
 * Prototype of the function that is called when a new frame is being rendered.
 * It's passed the frame data that should not outlive the callback, as well as the data pointer
 * provided by the application that registered a callback.
 */
typedef void (*AChoreographer_vsyncCallback)(
        const AChoreographerFrameCallbackData* callbackData, void* data);
  • AChoreographer_frameCallback,渲染新帧时调用的函数原型。 它传递了帧在 CLOCK_MONOTONIC 时基中呈现为纳秒的时间,以及注册回调的应用程序提供的数据指针。 作为渲染帧的一部分运行的所有回调将看到相同的帧时间,因此只要需要同步事件(例如动画),就应该使用它。
  • AChoreographer_frameCallback64,同上。
  • AChoreographer_vsyncCallback, 渲染新帧时调用的函数原型。 它传递了不应超过回调的帧数据,以及注册回调的应用程序提供的数据指针。

那么每次Vsync信号到来时,RenderThread的choreographerCallback回调就会触发,最终将调用Choreographer的成员变量mFrameCallbacks中的每一个FrameCallback对象的回调函数。

至此,我们知道了Vsync信号到来时,保存在Choreographer的成员变量mFrameCallbacks中的回调都会得到执行,但是还有两个点不太明确,一是mFrameCallbacks中的每一个FrameCallback回调对象是如何添加的,二是FrameCallback回调的内容是什么。

6.4.2 发布回调

查看代码,发现Choreographer的成员变量mFrameCallbacks中的数据是通过各种AChoreographer_postXXXCallback函数进行填充的:

// frameworks\native\libs\nativedisplay\AChoreographer.cpp

void AChoreographer_postFrameCallback(AChoreographer* choreographer,
                                      AChoreographer_frameCallback callback, void* data) {
    AChoreographer_to_Choreographer(choreographer)
            ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, 0);
}
void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer,
                                             AChoreographer_frameCallback callback, void* data,
                                             long delayMillis) {
    AChoreographer_to_Choreographer(choreographer)
            ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, ms2ns(delayMillis));
}
void AChoreographer_postVsyncCallback(AChoreographer* choreographer,
                                      AChoreographer_vsyncCallback callback, void* data) {
    AChoreographer_to_Choreographer(choreographer)
            ->postFrameCallbackDelayed(nullptr, nullptr, callback, data, 0);
}
void AChoreographer_postFrameCallback64(AChoreographer* choreographer,
                                        AChoreographer_frameCallback64 callback, void* data) {
    AChoreographer_to_Choreographer(choreographer)
            ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, 0);
}
void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer,
                                               AChoreographer_frameCallback64 callback, void* data,
                                               uint32_t delayMillis) {
    AChoreographer_to_Choreographer(choreographer)
            ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, ms2ns(delayMillis));
}

void Choreographer::postFrameCallbackDelayed(AChoreographer_frameCallback cb,
                                             AChoreographer_frameCallback64 cb64,
                                             AChoreographer_vsyncCallback vsyncCallback, void* data,
                                             nsecs_t delay) {
    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
    FrameCallback callback{cb, cb64, vsyncCallback, data, now + delay};
    {
        std::lock_guard<std::mutex> _l{mLock};
        mFrameCallbacks.push(callback);
    }
    // ......
}

拿Vsync信号相关的AChoreographer_postVsyncCallback函数举例。

在Choreographer初始化的函数RenderThread.initializeChoreographer中,我们创建了一个ChoreographerSource类型的对象赋值给了RenderThread的成员变量mVsyncSource。

void RenderThread::initializeChoreographer() {
    LOG_ALWAYS_FATAL_IF(mVsyncSource, "Initializing a second Choreographer?");

    if (!Properties::isolatedProcess) {
        // ......
        mVsyncSource = new ChoreographerSource(this);
    } else {
        mVsyncSource = new DummyVsyncSource(this);
    }
}

ChoreographerSource类的定义为:

class ChoreographerSource : public VsyncSource {
public:
    ChoreographerSource(RenderThread* renderThread) : mRenderThread(renderThread) {}

    virtual void requestNextVsync() override {
        AChoreographer_postVsyncCallback(mRenderThread->mChoreographer,
                                         RenderThread::extendedFrameCallback, mRenderThread);
    }

    virtual void drainPendingEvents() override {
        AChoreographer_handlePendingEvents(mRenderThread->mChoreographer, mRenderThread);
    }

private:
    RenderThread* mRenderThread;
};

ChoreographerSource.requestNextVsync调用了AChoreographer_postVsyncCallback,创建了一个FrameCallback对象,添加到了Choreographer的成员变量mFrameCallbacks中,这里可以看到,FrameCallback中的回调函数是RenderThread.extendedFrameCallback,这里先记下。

接着继续看ChoreographerSource.requestNextVsync是谁调用的。

void RenderThread::requestVsync() {
    if (!mVsyncRequested) {
        mVsyncRequested = true;
        mVsyncSource->requestNextVsync();
    }
}

bool RenderThread::threadLoop() {
    // ......
    
    initThreadLocals();

    while (true) {
        waitForWork();
        processQueue();

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

        // ......
    }

    return false;
}

看到是RenderThread无限循环的那部分。

RenderThread成员变量mPendingRegistrationFrameCallbacks中保存了一个IFrameCallback类型的回调的集合,IFrameCallback定义为:

// frameworks\base\libs\hwui\renderthread\RenderThread.h

// Mimics android.view.Choreographer.FrameCallback
class IFrameCallback {
public:
    virtual void doFrame() = 0;

protected:
    virtual ~IFrameCallback() {}
};

只定义了一个接口doFrame。

这些回调通过RenderThread.postFrameCallback函数:

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

添加到了RenderThread成员变量mPendingRegistrationFrameCallbacks中,希望在下一个Vsync信号到来的时候执行doFrame函数。

1)、mPendingRegistrationFrameCallbacks不为空,说明此时App注册了对Vsync信号的监听,希望下一帧到来的时候在doFrame回调函数里做一些事情。如果该集合为空,那就没必要调用requestVsync了。

2)、调用ChoreographerSource.drainPendingEvents,这个我们上面分析过,和RenderThread.choreographerCallback流程相似,清空所有未处理的事件。

3)、将mPendingRegistrationFrameCallbacks中的数据添加到mFrameCallbacks中,然后清空mPendingRegistrationFrameCallbacks。注意这里的成员变量mFrameCallbacks和Choreographer的成员变量mFrameCallbacks可不一样。

4)、调用requestVsync函数添加下一个Vsync信号的回调。

那么当下一个Vsync信号到来时,根据之前的分析,最终我们通过RenderThread.requestVsync函数添加的回调将会得到执行,也就是RenderThread.extendedFrameCallback函数。

6.4.3 FrameCallback回调执行流程

从上面的分析我们知道了Vsync信号到来时,保存在Choreographer的成员变量mFrameCallbacks中的回调都会得到执行,而每次RenderThread唤醒后我们也通过RenderThread.requestVsync函数向Choreographer的成员变量mFrameCallbacks添加了回调,即RenderThread.extendedFrameCallback函数。

那么Vsync信号到来时,相应回调的完整调用堆栈为:

 #00 pc 00000000002986a8  /system/lib64/libhwui.so (android::uirenderer::renderthread::RenderThread::extendedFrameCallback(AChoreographerFrameCallbackData const*, void*)+72)
 #01 pc 000000000000a30c  /system/lib64/libnativedisplay.so (android::Choreographer::dispatchVsync(long, android::PhysicalDisplayId, unsigned int, android::gui::VsyncEventData)+668)
 #02 pc 00000000000bbe90  /system/lib64/libgui.so (android::DisplayEventDispatcher::handleEvent(int, int, void*)+272)
 #03 pc 0000000000298c44  /system/lib64/libhwui.so (android::uirenderer::renderthread::RenderThread::choreographerCallback(int, int, void*)+132)
 #04 pc 000000000001836c  /system/lib64/libutils.so (android::Looper::pollInner(int)+1068)
 #05 pc 0000000000017ee0  /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+112)
 #06 pc 0000000000279a94  /system/lib64/libhwui.so (android::uirenderer::ThreadBase::waitForWork()+180)
 #07 pc 00000000002997d8  /system/lib64/libhwui.so (android::uirenderer::renderthread::RenderThread::threadLoop()+552)
 #08 pc 0000000000013550  /system/lib64/libutils.so (android::Thread::_threadLoop(void*)+416)
 #09 pc 00000000000fc350  /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+208)
 #10 pc 000000000008e310  /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64)

我们继续看下Vsync信号到来的时候都做了什么。

6.4.3.1 RenderThread.extendedFrameCallback
void RenderThread::extendedFrameCallback(const AChoreographerFrameCallbackData* cbData,
                                         void* data) {
    RenderThread* rt = reinterpret_cast<RenderThread*>(data);
    
    // ......
    
    rt->frameCallback(vsyncId, frameDeadline, frameTimeNanos, frameInterval);
}

下一个Vsync信号到来,此时RenderThread.extendedFrameCallback函数触发,并且继续调用RenderThread.frameCallback。

6.4.3.2 RenderThread.frameCallback
void RenderThread::frameCallback(int64_t vsyncId, int64_t frameDeadline, int64_t frameTimeNanos,
                                 int64_t frameInterval) {
    mVsyncRequested = false;
    if (timeLord().vsyncReceived(frameTimeNanos, frameTimeNanos, vsyncId, frameDeadline,
                                 frameInterval) &&
        !mFrameCallbackTaskPending) {
        ATRACE_NAME("queue mFrameCallbackTask");
        mFrameCallbackTaskPending = true;
        nsecs_t runAt = (frameTimeNanos + mDispatchFrameDelay);
        queue().postAt(runAt, [=]() { dispatchFrameCallbacks(); });
    }
}

1)、TimeLoad.vsyncReceived用来检验Vsync是不是最新的,如果不是就会返回false。

2)、mFrameCallbackTaskPending置为true,表示WorkQueue中已经有Task正在等待处理了,等后续再次走到RenderThread.threadLoop时,就不用再继续添加Vsync回调了。

3)、这里通过WorkQueue的postAt函数向WorkQueue中添加了一个任务,当该任务的执行时间到来的时候,RenderThread.dispatchFrameCallbacks回调函数将会得到执行。

6.4.3.3 RenderThread.dispatchFrameCallbacks
void RenderThread::dispatchFrameCallbacks() {
    ATRACE_CALL();
    mFrameCallbackTaskPending = false;

    std::set<IFrameCallback*> callbacks;
    mFrameCallbacks.swap(callbacks);

    if (callbacks.size()) {
        // Assume one of them will probably animate again so preemptively
        // request the next vsync in case it occurs mid-frame
        requestVsync();
        for (std::set<IFrameCallback*>::iterator it = callbacks.begin(); it != callbacks.end();
             it++) {
            (*it)->doFrame();
        }
    }
}

1)、mFrameCallbackTaskPending置为false,表示WorkQueue中的任务已经得到处理了,后续可以继续向WorkQueue中添加。

2)、执行RenderThread.mFrameCallbacks中的每一个IFrameCallback对象的doFrame函数。

6.5 RenderThread小结

我们回过头来看前面分析的RenderThread类的成员函数threadLoop,每当Render Thread被唤醒时,它都会检查mPendingRegistrationFrameCallbacks列表是否不为空。如果不为空,那么就会将保存在里面的IFrameCallback回调接口转移至由RenderThread类的成员变量mFrameCallbacks描述的另外一个IFrameCallback回调接口列表中,并且调用RenderThread类的另外一个成员函数requestVsync请求SurfaceFlinger服务在下一个Vsync信号到来时通知Render Thread,以便Render Thread可以执行刚才被转移的IFrameCallback回调接口。

梳理一下,如果RenderThread在threadLoop函数结束之前,调用requestVsync函数请求了对Vsync信号进行监听,那么下一次进入threadLoop函数时:

1)、首先调用ThreadBase.waitForWork函数使当前线程睡眠。

2)、当Vsync信号到来时,RenderThread会被唤醒。

3)、首先choreographerCallback回调函数被调用,最终通过RenderThread.frameCallback函数向WorkQueue添加了一个任务,具体内容为RenderThread.dispatchFrameCallbacks函数。

4)、接着从ThreadBase.waitForWork函数返回,调用ThreadBase.processQueue函数,该函数会根据任务的执行时间是否到来从WorkQueue中找到当前需要处理的任务,具体就是调用这些任务的WorkItem.workItem函数,那么上述的RenderThread.dispatchFrameCallbacks函数就会得到执行,最终RenderThread.mFrameCallbacks队列中的每一个IFrameCallback对象的doFrame函数就会在Vsync信号到来的时候得到执行。

在整个过程中,有两个队列是比较关键的:

  • WorkQueue,其它线程可以把那些需要在RenderThread运行的内容封装为一个任务放入其中,这些任务会在其执行时间到来之时得到处理。这些任务一般就是由主线程发送过来的,例如,主线程将对DrawFrameTask函数的调用封装为一个任务发送给RenderThread,希望后续在RenderThread线程中进行下一帧的绘制。
  • RenderThread.mFrameCallbacks,这里的存储的iFrameCallback主要用来在Vsync信号到来的时候进行响应,比如在IFrameCallback的回调函数doFrame中做一些操作。

7 创建CanvasContext

了解了Render Thread的创建过程之后,回到RenderProxy类的构造函数中:

// frameworks\base\libs\hwui\renderthread\RenderProxy.cpp

RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode,
                         IContextFactory* contextFactory)
        : mRenderThread(RenderThread::getInstance()), mContext(nullptr) {
    mContext = mRenderThread.queue().runSync([&]() -> CanvasContext* {
        return CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory);
    });
    mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode,
                              pthread_gettid_np(pthread_self()), getRenderThreadTid());
}

接下来我们继续分析它的成员变量mContext的初始化过程,也就是画布上下文的初始化过程。

    mContext = mRenderThread.queue().runSync([&]() -> CanvasContext* {
        return CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory);
    });

这里是通过WorkQueue的runSync函数向WorkQueue添加了一个任务:

    template <class F>
    auto runSync(F&& func) -> decltype(func()) {
        std::packaged_task<decltype(func())()> task{std::forward<F>(func)};
        post([&task]() { std::invoke(task); });
        return task.get_future().get();
    };

即一个典型的Main Thread通过Render Proxy向Render Thread请求执行一个命令的流程。

任务的具体内容则是CanvasContext.create函数:

// frameworks\base\libs\hwui\renderthread\CanvasContext.cpp

CanvasContext* CanvasContext::create(RenderThread& thread, bool translucent,
                                     RenderNode* rootRenderNode, IContextFactory* contextFactory) {
    auto renderType = Properties::getRenderPipelineType();

    switch (renderType) {
        case RenderPipelineType::SkiaGL:
            return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
                                     std::make_unique<skiapipeline::SkiaOpenGLPipeline>(thread));
        case RenderPipelineType::SkiaVulkan:
            return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
                                     std::make_unique<skiapipeline::SkiaVulkanPipeline>(thread));
        default:
            LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t)renderType);
            break;
    }
    return nullptr;
}

看到这里对创建哪种SkiaPipeline进行了选择。

Skiaskia是图像渲染库,2D图形绘制自己就能完成。3D效果(依赖硬件)由OpenGLVulkanMetal支持。它不仅支持2D3D,同时支持CPU软件绘制和GPU硬件加速。Androidflutter都是使用它来完成绘制。

OpenGL: 是一种跨平台的3D图形绘制规范接口。OpenGL EL则是专门针对嵌入式设备,如手机做了优化。

Vulkan: Android引入了Vulkan支持。VulKan是用来替换OpenGL的。它不仅支持3D,也支持2D,同时更加轻量级。

Android早期通过skia库进行2d渲染,后来加入了hwui利用opengl替换skia进行大部分渲染工作,现在开始用skia opengl替换掉之前的opengl,从p的代码结构上也可以看出,p开始skia库不再作为一个单独的动态库so,而是静态库a编译到hwui的动态库里,将skia整合进hwui,hwui调用skia opengl,也为以后hwui使用skia vulkan做铺垫。

具体是SkiaGL还是SkiaVulkan,则由下面的系统属性值决定:

/**
 * Allows to set rendering pipeline mode to OpenGL (default), Skia OpenGL
 * or Vulkan.
 */
#define PROPERTY_RENDERER "debug.hwui.renderer"

我们这里是创建了一个SkiaOpenGLPipeline对象:

SkiaOpenGLPipeline::SkiaOpenGLPipeline(RenderThread& thread)
        : SkiaPipeline(thread), mEglManager(thread.eglManager()) {
    thread.renderState().registerContextCallback(this);
}

之前在RenderThread.initThreadLocals函数中,我们创建了一个EglManager对象,这里也赋值给了SkiaOpenGLPipeline的成员变量mEglManager。

最终创建了一个CanvasContext对象:

CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
                             IContextFactory* contextFactory,
                             std::unique_ptr<IRenderPipeline> renderPipeline)
        : mRenderThread(thread)
        , mGenerationID(0)
        , mOpaque(!translucent)
        , mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord()))
        , mJankTracker(&thread.globalProfileData())
        , mProfiler(mJankTracker.frames(), thread.timeLord().frameIntervalNanos())
        , mContentDrawBounds(0, 0, 0, 0)
        , mRenderPipeline(std::move(renderPipeline)) {
    rootRenderNode->makeRoot();
    mRenderNodes.emplace_back(rootRenderNode);
    mProfiler.setDensity(DeviceInfo::getDensity());
}

CanvasContext成员变量mRenderPipeline保存的是刚刚创建的SkiaOpenGLPipeline对象。

8 mDrawFrameTask初始化

回到RenderProxy类的构造函数中,接下来我们继续分析它的成员变量mDrawFrameTask的初始化过程。

RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode,
                         IContextFactory* contextFactory)
        : mRenderThread(RenderThread::getInstance()), mContext(nullptr) {
    mContext = mRenderThread.queue().runSync([&]() -> CanvasContext* {
        return CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory);
    });
    mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode,
                              pthread_gettid_np(pthread_self()), getRenderThreadTid());
}

RenderProxy类的成员变量mDrawFrameTask描述的是一个Draw Frame Task,Main Thread每次都是通过它来向Render Thread发出渲染下一帧的命令的。

//rameworks\base\libs\hwui\renderthread\DrawFrameTask.cpp

void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode,
                               int32_t uiThreadId, int32_t renderThreadId) {
    mRenderThread = thread;
    mContext = context;
    mTargetNode = targetNode;
    mUiThreadId = uiThreadId;
    mRenderThreadId = renderThreadId;
}

对Draw Frame Task的初始化很简单,主要是将前面已经获得的RenderThread对象、CanvasContext对象以及RenderNode对象保存在它内部,以便以后它可以直接使用相关的功能,这是通过调用DrawFrameTask类的成员函数setContext实现的。

9 RenderProxy小结

至此,一个RenderProxy对象的创建过程就分析完成了,从中我们也看到Render Thread的创建过程和运行模型,以及Render Proxy与Render Thread的交互模型,总结来说:

1)、RenderProxy内部有一个成员变量mRenderThread,它指向的是一个RenderThread对象,通过它可以向Render Thread线程发送命令。

2)、RenderProxy内部有一个成员变量mContext,它指向的是一个CanvasContext对象,Render Thread的渲染工作就是通过它来完成的。

3)、RenderProxy内部有一个成员变量mDrawFrameTask,它指向的是一个DrawFrameTask对象,Main Thread通过它向Render Thread线程发送渲染下一帧的命令。

10 创建EGLSurface

每一个Open GL渲染上下文都需要关联有一个EGL Surface。这个EGL Surface描述的是一个绘图表面,它封装的实际上是一个ANativeWindow。有了这个EGL Surface之后,我们在执行Open GL命令的时候,才能确定这些命令是作用在哪个窗口上。

之前分析过,当前Activity窗口对应的Surface是通过调用ViewRootImpl类的成员函数relayoutWindow向WindowManagerService服务请求创建和返回的,并且保存在ViewRootImpl类的成员变量mSurface中。如果这个Surface是新创建的,那么就会调用ViewRootImpl类的成员变量mAttachInfo指向的一个AttachInfo对象的成员变量mHardwareRenderer描述的一个HardwareRenderer对象的成员函数initialize将它绑定到Render Thread中。

                if (surfaceCreated) {
                    // ......
                    if (mAttachInfo.mThreadedRenderer != null) {
                        try {
                            hwInitialized = mAttachInfo.mThreadedRenderer.initialize(mSurface);
                            // ......
                        } catch (OutOfResourcesException e) {
                            // ......
                        }
                    }
                }

最后,如果需要绘制当前的Activity窗口,那会调用ViewRootImpl类的另外一个成员函数performDraw进行绘制。

这里我们只关注绑定窗口对应的Surface到Render Thread的过程。从前面的分析可以知道,ViewRootImpl类的成员变量mAttachInfo指向的一个AttachInfo对象的成员变量mHardwareRenderer保存的实际上是一个ThreadedRenderer对象,它的成员函数initialize的实现如下所示:

10.1 ThreadedRenderer.initialize

    /**
     * Initializes the threaded renderer for the specified surface.
     *
     * @param surface The surface to render
     *
     * @return True if the initialization was successful, false otherwise.
     */
    boolean initialize(Surface surface) throws OutOfResourcesException {
        boolean status = !mInitialized;
        mInitialized = true;
        updateEnabledState(surface);
        setSurface(surface);
        return status;
    }

    @Override
    public void setSurface(Surface surface) {
        // TODO: Do we ever pass a non-null but isValid() = false surface?
        // This is here to be super conservative for ViewRootImpl
        if (surface != null && surface.isValid()) {
            super.setSurface(surface);
        } else {
            super.setSurface(null);
        }
    }

ThreadedRenderer类的成员函数initialize首先将成员变量mInitialized的值设置为true,表明它接下来已经绑定过Surface到Render Thread了,接着再调用另外一个成员函数setSurface。

ThreadedRenderer.setSurface在验证了传入的Surface的有效性后,继续调用父类HardwareRenderer.setSurface。

10.2 HardwareRenderer.setSurface

    /**
     * <p>The surface to render into. The surface is assumed to be associated with the display and
     * as such is still driven by vsync signals such as those from
     * {@link android.view.Choreographer} and that it has a native refresh rate matching that of
     * the display's (typically 60hz).</p>
     *
     * <p>NOTE: Due to the shared, cooperative nature of the render thread it is critical that
     * any {@link Surface} used must have a prompt, reliable consuming side. System-provided
     * consumers such as {@link android.view.SurfaceView},
     * {@link android.view.Window#takeSurface(SurfaceHolder.Callback2)},
     * or {@link android.view.TextureView} all fit this requirement. However if custom consumers
     * are used such as when using {@link SurfaceTexture} or {@link android.media.ImageReader}
     * it is the app's responsibility to ensure that they consume updates promptly and rapidly.
     * Failure to do so will cause the render thread to stall on that surface, blocking all
     * HardwareRenderer instances.</p>
     *
     * @param surface The surface to render into. If null then rendering will be stopped. If
     *                non-null then {@link Surface#isValid()} must be true.
     */
    public void setSurface(@Nullable Surface surface) {
        setSurface(surface, false);
    }

    public void setSurface(@Nullable Surface surface, boolean discardBuffer) {
        if (surface != null && !surface.isValid()) {
            throw new IllegalArgumentException("Surface is invalid. surface.isValid() == false.");
        }
        nSetSurface(mNativeProxy, surface, discardBuffer);
    }

要渲染到的Surface。 假定Surface与显示相关联,因此仍然由 vsync 信号驱动,例如来自 {@link android.view.Choreographer} 的信号,并且它具有与显示刷新率匹配的native层刷新率(通常为 60hz)。

注意:由于渲染线程的共享、协作性质,使用的任何 {@link Surface} 都必须具有及时、可靠的消费端,这一点至关重要。 系统提供的消费者,例如 {@link android.view.SurfaceView}、{@link android.view.Window#takeSurface(SurfaceHolder.Callback2)} 或 {@link android.view.TextureView} 都符合此要求。 但是,如果使用自定义消费者,例如在使用 {@link SurfaceTexture} 或 {@link android.media.ImageReader} 时,应用程序有责任确保他们及时、快速地使用更新。 如果不这样做,将导致渲染线程在该Surface上停止,从而阻塞所有 HardwareRenderer 实例。

参数 surface 为要渲染到的Surface。 如果为 null,则渲染将停止。 如果非空,则 {@link Surface#isValid()} 必须为真。

接着调用JNI函数nSetSurface:

        {"nSetSurface", "(JLandroid/view/Surface;Z)V",
         (void*)android_view_ThreadedRenderer_setSurface}

10.3 android_view_ThreadedRenderer_setSurface

static void android_view_ThreadedRenderer_setSurface(JNIEnv* env, jobject clazz,
        jlong proxyPtr, jobject jsurface, jboolean discardBuffer) {
    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
    ANativeWindow* window = nullptr;
    if (jsurface) {
        window = fromSurface(env, jsurface);
    }
    
    // ......
    
    proxy->setSurface(window, enableTimeout);
    if (window) {
        ANativeWindow_release(window);
    }
}

参数proxyPtr描述的就是之前所创建的一个RenderProxy对象,而参数jsurface描述的是要绑定给Render Thread的Java层的Surface。前面提到,Java层的Surface在Native层对应的是一个ANativeWindow。我们可以通过函数fromSurface来获得一个Java层的Surface在Native层对应的ANativeWindow。

接下来,就可以通过RenderProxy类的成员函数setSurface将前面获得的ANativeWindow绑定到Render Thread中。

10.4 RenderProxy.setSurface

void RenderProxy::setSurface(ANativeWindow* window, bool enableTimeout) {
    if (window) { ANativeWindow_acquire(window); }
    mRenderThread.queue().post([this, win = window, enableTimeout]() mutable {
        mContext->setSurface(win, enableTimeout);
        if (win) { ANativeWindow_release(win); }
    });
}

从前面的分析可以知道,RenderProxy类的成员函数setSurface向Render Thread的WorkQueue发送了一个任务。当这个任务在Render Thread中执行时,以下代码段就会执行:

        mContext->setSurface(win, enableTimeout);

RenderProxy类的成员变量mContext指向的一个CanvasContext对象,而参数win指向的ANativeWindow就是要绑定到Render Thread的ANativeWindow。那么这里继续调用CanvasContext.setSurface。

10.5 CanvasContext.setSurface

void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) {
    // ......
    
    if (window) {
        mNativeSurface = std::make_unique<ReliableSurface>(window);
        mNativeSurface->init();
        // ......
    } else {
        mNativeSurface = nullptr;
    }    
    setupPipelineSurface();
}

void CanvasContext::setupPipelineSurface() {
    bool hasSurface = mRenderPipeline->setSurface(
            mNativeSurface ? mNativeSurface->getNativeWindow() : nullptr, mSwapBehavior);

    // ......
}

将ANativeWindow类型的传参window保存到CanvasContext成员变量mNativeSurface中,后续可以通过ReliableSurface.getNativeWindow函数拿到该ANativeWindow。

10.6 SkiaOpenGLPipeline.setSurface

bool SkiaOpenGLPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior) {
    // ......

    if (surface) {
        mRenderThread.requireGlContext();
        auto newSurface = mEglManager.createSurface(surface, mColorMode, mSurfaceColorSpace);
        if (!newSurface) {
            return false;
        }
        mEglSurface = newSurface.unwrap();
    }

    // ......
}

根据之前的分析,我们知道RenderThread类以及SkiaOpenGLPipeline类的成员变量mEglManager是指向前面我们分析RenderThread类的成员函数initThreadLocals时创建的一个EglManager对象。

1)、首先调用RenderThread.requireGlContext函数,对EglManager进行了初始化。

2)、通过SkiaOpenGLPipeline的成员变量mEglManager的createSurface创建了一个EGLSurface,并且保存在了SkiaOpenGLPipeline的成员变量mEglSurface中,即将参数surface描述的ANativeWindow封装成一个EGL Surface。

10.7 EglManager.initialize

void RenderThread::requireGlContext() {
    if (mEglManager->hasEglContext()) {
        return;
    }
    mEglManager->initialize();

    // ......
}

void EglManager::initialize() {
    // ......
    mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
                        eglErrorString());

    EGLint major, minor;
    LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE,
                        "Failed to initialize display %p! err=%s", mEglDisplay, eglErrorString());

    // ......
    loadConfigs();
    createContext();
    createPBufferSurface();
    makeCurrent(mPBufferSurface, nullptr, /* force */ true);

    // ......
}

RenderThread.requireGlContext继续调用了EglManager.initialize完成了EglManager的初始化,主要为:

1)、调用eglGetDisplay函数获取一个EGLDisplay对象。

2)、调用eglInitialize初始化与 EGLDisplay之间的连接。

3)、调用loadConfigs函数,最终调用eglChooseConfig函数获取一个ELGConfig对象。

4)、调用createContext函数,最终调用eglCreateContext函数获取一个EGLContext对象。

5)、调用createPBufferSurface函数,最终调用eglCreatePbufferSurface函数获取一个ELGSurface对象,该EGLSurface用于离屏渲染。

6)、调用makeCurrent函数,最终调用eglMakeCurrent函数将EGLSurface和EGLContext绑定。

这一步通过各种eglXXX函数初始化了渲染环境,实际上就是通过EGL函数建立了从Open GL到底层OS图形系统的桥梁,这一点应该怎么理解呢?Open GL是一套与OS无关的规范,不过当它在一个具体的OS实现时,仍然是需要与OS的图形系统打交道的。例如,Open GL需要从底层的OS图形系统中获得图形缓冲区来保存渲染结果,并且也需要将渲染好的图形缓冲区交给底层的OS图形系统来显示到设备屏幕去。Open GL与底层的OS图形系统的这些交互通道都是通过EGL函数来建立的。

但是注意这里EGLContext绑定了一个PbufferSurface,而非我们真正绘图的Surface。

10.8 EglManager.createSurface

Result<EGLSurface, EGLint> EglManager::createSurface(EGLNativeWindowType window,
                                                     ColorMode colorMode,
                                                     sk_sp<SkColorSpace> colorSpace) {
    // ......
        
    EGLSurface surface = eglCreateWindowSurface(mEglDisplay, config, window, attribs);
    
    // ......
}

通过eglCreateWindowSurface创建了一个EGLSurface,但是没有调用eglMakeCurrent函数将该EGLSurface和EGLContext进行绑定。真正的绑定,应该要到绘制的时候再进行,而非Surface创建的时候,后续我们分析绘制流程的时候会看到。

11 总结

在这里插入图片描述

12 补充:EGL

参考:一看就懂的 OpenGL 基础概念(2):EGL,OpenGL 与设备的桥梁丨音视频基础 - 知乎 (zhihu.com)

我们知道OpenGL是一组可以操作GPU的API,然而仅仅能够操作GPU,并不能够将图像渲染到设备的显示窗口上。那么,就需要一个中间层,连接OpenGL与设备窗口,并且最好是跨平台的。于是EGL出现了,由Khronos Group提供的一组平台无关的API,保证了OpenGL ES的平台独立性。EGL 是OpenGL ES 渲染 API 和本地窗口系统(native platform window system)之间的一个中间接口层,EGL作为OpenGL ES与显示设备的**桥梁,**让OpenGL ES绘制的内容能够在呈现当前设备上。它主要由系统制造商实现。

在这里插入图片描述

各部分分别为:

在这里插入图片描述

1)、EGLDisplay,EGL定义的一个抽象的系统显示类,用于操作设备窗口。

2)、EGLSurface,渲染区域,相当于 OpenGL ES 绘图的画布 (一块内存空间),用户想绘制的信息首先都要先绘制到 EGLSurface 上,然后通过 EGLDisplay 显示,有三种类型:

  • Surface – 可显示的 Surface,实际上就是一个FrameBuffer,用于绑定窗口后预览显示。
  • PixmapSurface – 不是可显示的 Surface,保存在系统内存中的位图。
  • PBufferSurface – 不是可显示的 Surface,保存在显存中的帧,用于离屏渲染,不需要绑定窗口。

3)、EGLContext,OpenGL上下文,保存了在这个Context下发起的GL调用指令。

初始化EGL的过程其实就是配置以上几个信息的过程。

在EGL初始化以后,即渲染环境(EGLDisplay、EGLContext、EGLSurface)准备就绪以后,需要在渲染线程(绘制图像的线程)中,明确的调用eglMakeCurrent。这时,系统底层会将OpenGL渲染环境绑定到当前线程。eglMakeCurrent这个方法,实现了设备显示窗口(EGLDisplay)、 OpenGL 上下文(EGLContext)、图像数据缓存(EGLSurface) 、当前线程的绑定。

在这之后,只要你是在渲染线程中调用任何OpenGL ES的API(比如生产纹理ID的方法GLES20.glGenTextures),OpenGL会自动根据当前线程,切换上下文(也就是切换OpenGL的渲染信息和资源)。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值