Android硬件渲染环境初始化

其实本不想写这个的,因为老罗的 Android应用程序UI硬件加速渲染环境初始化过程分析 写得太好了,而且非常详细,所以根本不必重复造轮子。原本只是将它作为即将要写的DisplayList绘制里一个小部分的,结果DisplayList绘制越写越多,篇幅太大了,所以就单独拎了出来

一、硬件渲染环境

 public void setView(View view, ...) {
    ...
    if (mSurfaceHolder == null) {
        enableHardwareAcceleration(attrs);
    }
    ...
}

在ViewRootImpl.java的setView里,会去enable硬件加速功能, 这里先不去深究mSurfaceHolder.

mAttachInfo.mHardwareRenderer = ThreadedRenderer.create(mContext, translucent);

renderer = new ThreadedRenderer(context, translucent);

ThreadedRenderer(Context context, boolean translucent) {
    ...
    long rootNodePtr = nCreateRootRenderNode();
    mRootNode = RenderNode.adopt(rootNodePtr);
    mRootNode.setClipToBounds(false);
    mNativeProxy = nCreateProxy(translucent, rootNodePtr);
    ...
}

而在enableHardwareAcceleration中会去生成ThreadedRender这个类, ThreadedRender类在初始化时会将native的RenderThread环境准备好. 具体是在生成RenderProxy对象时,会launch RenderThread

RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode,  IContextFactory* contextFactory)
        : mRenderThread(RenderThread::getInstance())

RenderThread::getInstance()是一个单例函数, 在RenderThread的构造函数中会直接调用run函数, 这样RenderThread线程就运行起来了。RenderThread线程一旦运行起来了会一直循环调用threadLoop函数.

bool RenderThread::threadLoop() {
    initThreadLocals();
    int timeoutMillis = -1;
    for (;;) {
        int result = mLooper->pollOnce(timeoutMillis);
        ...
    }
    return false;
}

threadLoop函数首先初始化RenderThread环境, 然后进入死循环等着task的唤醒然后执行。这些线程模型都大同小异,就不细讲了。

Android Surface创建这篇文章讲了关于Android Surface的创建过程, 而RenderThread也要建立与Surface之间的联系.

当App的ViewImplRoot通过relayout向WMS申请创建Surface相关信息后,最后在App端创建了Surface类。 接着RenderThread线程就开始初始化Surface了,
performTraversals

    if (mAttachInfo.mHardwareRenderer != null) {
    try {
        hwInitialized = mAttachInfo.mHardwareRenderer.initialize(mSurface);
            if (...) {
                mSurface.allocateBuffers()
            }
    } catch (...) {}

mSurface.allocateBuffers()会提前去分配GraphicBuffer, 以避免在渲染时分配产生的延时,这个后续会讲

    boolean initialize(Surface surface) throws OutOfResourcesException {
        ...
        nInitialize(mNativeProxy, surface);
        ...
    }

在JNI层通过RenderProxy去初始化

最终的硬件渲染UML图如下所示

硬件渲染UML图
  • ThreadedRender
    这个是Java的类,它主要是将UI线程的渲染操作传递给RenderThread线程, 大部分的调用是同步操作

  • RootRenderNode
    这个是Native的类, 它保存着整个UI的DisplayList tree

  • RenderProxy
    Native的类,由ThreadedRender创建,它的任务就是传递UI线程的渲染操作给RenderThread线程。

  • CanvasContext
    这个类由RenderThread线程创建

  • EglSurface
    系统窗口或 frame buffer 句柄 EGL接口介绍 , 那它和Surface是啥关系?

  • RenderState

  • DisplayInfo
    这个是从SurfaceFlinger里拿到的显示器的信息

  • Surface
    native window

二、Open GL ES环境初始化

在讲绘制之前,先来看下Open GL ES环境的准备, 代码入口是 nInitialize(mNativeProxy, surface); 见第一节。而最终又会通过RenderProxy在RenderThread线程中调用到 CanvasContext::setSurface. 也就是通过EglManager去创建EGL surface, 调用函数mEglSurface = mEglManager.createSurface(surface);
最终的调用过程如下, 参考EGL接口介绍

createSurface的初始化顺序如下:

// 获得一个与native windowing系统的 Display connection的, 比如Linux下的X window系统
mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);

// EGLDisplay初始化, 同时获得 Open GL ES版本号
eglInitialize(mEglDisplay, &major, &minor)

// 通过eglQueryString查询EGLDisplay的extensions信息
initExtensions()

//eglChooseConfig配置frame buffer参数, 并且获得匹配attribs的frame buffer的配置信息,
eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_configs, &num_configs)

//创建Open GL ES渲染需要的上下文, 
// OpenGL的pipeline从程序的角度看就是一个状态机,有当前的颜色、纹理坐标、变换矩阵、绚染模式等一大堆状态,
//这些状态作用于程序提交的顶点 坐标等图元从而形成帧缓冲内的像素。
// 在OpenGL的编程接口中,Context就代表这个状态机,
//程序的主要工作就是向Context提供图元、设置状态,偶尔也从Context里获取一些信息。
//它的目的是保存输入的渲染数据
mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs)

//创建EGL surface, 然后就可以用gl API往这个surface里绘制了, 
//目的是保存渲染的输出数据 
EGLSurface surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, window, nullptr)

其中eglCreateWindowSurface是创建EGL surface, 参数里面的 window就是native window, 也就是Surface概念.

最后,在绘制之前还需要绑定上下文, 也就是 eglMakeCurrent

bool EglManager::makeCurrent(EGLSurface surface, EGLint* errOut) {
    if (isCurrent(surface)) return false;

    if (surface == EGL_NO_SURFACE) {
        // Ensure we always have a valid surface & context
        surface = mPBufferSurface;
    }   
    if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) { 
        ...
    }   
    mCurrentSurface = surface;
    return true;
}

Let’s talk about eglMakeCurrent, eglSwapBuffers, glFlush, glFinish这篇文章对elgMakeCurrent解释得很到位,

eglMakeCurrent binds context to the current rendering thread AND TO the draw and read surfaces. draw is used for all GL operations except for any pixel data read back (glReadPixels, glCopyTexImage2D, and glCopyTexSubImage2D), which is taken from the frame buffer values of read.
eglMakeCurrent绑定mEGLContext到RenderThread, 这样RenderThread就可以绘制和读取Surface了,绘制操作基本上适用于所有的GL操作,这里要除去从frame buffer读取的操作(glReadPixels, glCopyTextImag2D ...)

Therefore, when you call the GL command on a thread T, OpenGL ES will detect which context C was bound to T, and which surface S[draw] and S[read] were bound to C.
因此,当你在线程T中调用GL API时,OpengGL ES会检测到哪个 Context绑定到了线程中, 并且哪个(绘制Surface)、(读Surface)绑定给了Context.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容