GUI系统之SurfaceFlinger(17)handleRepaint

文章都是通过阅读源码分析出来的,还在不断完善与改进中,其中难免有些地方理解得不对,欢迎大家批评指正。
转载请注明:From LXS. http://blog.csdn.net/uiop78uiop78/

GUI系统之SurfaceFlinger章节目录:

blog.csdn.net/uiop78uiop78/article/details/8954508



1.1.1 handleRepaint

经过handleTransaction和handlePageFlip等步骤的准备工作后,现在可以合成各图层数据了。

void SurfaceFlinger::handleRepaint()

{…

mSwapRegion.orSelf(mDirtyRegion);

    const DisplayHardware&hw(graphicPlane(0).displayHardware());

    …

    uint32_t flags =hw.getFlags();//系统支持的渲染方式

    if (flags &DisplayHardware::SWAP_RECTANGLE) {       

       mDirtyRegion.set(mSwapRegion.bounds());

    } else {

        if (flags & DisplayHardware::PARTIAL_UPDATES) {          

           mDirtyRegion.set(mSwapRegion.bounds());

        } else {

           mDirtyRegion.set(hw.bounds());

            mSwapRegion =mDirtyRegion;

        }

    }

    setupHardwareComposer();

    composeSurfaces(mDirtyRegion);//合成各图层数据

   mSwapRegion.orSelf(mDirtyRegion);

    mDirtyRegion.clear();

}

系统支持多种类型的渲染方式,可以从DisplayHardware获取到:

l  SWAP_RECTANGLE

我们只需要渲染“脏”区域,或者说系统在软件层面上支持部分区域更新,但更新区域必须是长方形规则的。由于这个限制,mDirtyRegion应该是覆盖所有“脏”区域的最小矩形

 

l  PARTIAL_UPDATES

系统支持硬件层面部分区域更新,同样也需要是矩形区域

 

l  其它

其它情况下,我们需要重绘整个屏幕,即hw.bounds()

 

接着setupHardwareComposer()对所有mVisibleLayersSortedByZ中的layers进行数据初始化,比如合成类型(compositionType)。包括HWC_FRAMEBUFFER和HWC_OVERLAY两种。如果是前者的话,那么图层合成将由SurfaceFlinger通过OpenGL ES来完成;否则就由一种叫overlay的硬件来执行。Overlay通常被用于处理视频回放和录制时的预览,这种硬件合成可以加快处理,减小CPU的负担。不过从Android 4.0开始,源码中关于Overlay部分的实现被移除了。

HWComposer内部维持着一个各layer属性的list:

hwc_layer_list_t*      mList;

这个结构体定义如下:

typedef struct hwc_layer_list {

    uint32_t flags;//各标志

    size_tnumHwLayers;//图层数量

    hwc_layer_t  hwLayers[0];//各图层属性详细描述

} hwc_layer_list_t;

 

当SurfaceFlinger进行handleWorkList时,它根据当前可见图层的数量调用HWComposer::createWorkList(size_tnumLayers)来创建这一mList。SurfaceFlinger::setupHardwareComposer()中,首先将mList里所有的hwc_layer对象的compositionType都设置为HWC_FRAMEBUFFER,然后调用HWComposer::prepare。这个函数会通过hwc设备(即前面章节中讲过的HWC_HARDWARE_MODULE_ID)提供的prepare接口来为每个layer判断HWC支持的合成类型,大家可以自己挑一个具体的硬件平台来分析prepare细节。另外,它还统计使用不同合成类型的layer的数量,分别由mNumOVLayers (HWC_OVERLAY)和mNumFBLayers (HWC_FRAMEBUFFER)来记录。

图层合成最关键的一步就是composeSurfaces(),我们来详细分析它的实现:

void SurfaceFlinger::composeSurfaces(const Region& dirty)

{

    const DisplayHardware&hw(graphicPlane(0).displayHardware());

    HWComposer&hwc(hw.getHwComposer());//注意hw,hwc和graphicPlane的关系

    hwc_layer_t* constcur(hwc.getLayers());//前面讲到的mList

 

    const size_t fbLayerCount= hwc.getLayerCount(HWC_FRAMEBUFFER);// mNumFBLayers

    if (!cur || fbLayerCount){// HWC_FRAMEBUFFER类型数量必须要大于0才处理

        if(hwc.getLayerCount(HWC_OVERLAY)) {// HWC_OVERLAY数量大于0的情况

            //我们不考虑这种情况

        } else {

            if(!mWormholeRegion.isEmpty()) {

                drawWormhole();//特殊区域需要特别处理

            }

        }

        /*以下开始真正的渲染工作*/

        const Vector<sp<LayerBase> >& layers(mVisibleLayersSortedByZ);//所有可见图层

        const size_t count =layers.size();

        for (size_t i=0 ;i<count ; i++) {

            constsp<LayerBase>& layer(layers[i]);

            const Regionclip(dirty.intersect(layer->visibleRegionScreen));//判断各个layer是否需要渲染

            if(!clip.isEmpty()) {

                …

                layer->draw(clip);//调用各layer进行绘图

            }

        }

    }

}

在SurfaceFlinger中,显示屏用GraphicPlane表示。它内部的成员数组变量mGraphicPlanes[1]用于存储支持的屏幕,目前只实现了第0号Display。GraphicPlane内部包含了DisplayHardware,用于封装宽、高、格式等一系列帧缓冲区信息;OpenGL的本地窗口FramebufferNativeWindow;EGL相关结构,如配置、Context、Surface;以及HWComposer,即mHwc成员变量。

HWComposer简单来说,它管理一个名称为HWC_HARDWARE_MODULE_ID的设备,比如硬件是否支持产生VSYNC信号就需要由它来判断。大家可以理一下这几个对象的关系。

得到hwc后,进一步获取worklist,也就是我们前面讲过的mList。这时列表中所有的layer属性都已经更新完毕,包括compositionType。而且也分别统计出了HWC_FRAMEBUFFER和HWC_OVERLAY两种类型的layer数量。所以fbLayerCount实际上就是mNumFBLayers。假如这个数小于0,就什么都不做。

这个函数中的“脏”区域是由handleRepaint传下来的,也就是mDirtyRegion。对于每个layer,我们都使用dirty.intersect(layer->visibleRegionScreen)来判断它的可见区域与整个“脏”区域是否有交集。有的话才需要进行渲染,否则就不用多此一举了。

最后,通过各Layer自身的draw()来执行具体的绘图工作。

LayerBase::draw()直接调用了onDraw(),这个接口在LayerBase中虚函数,由它的子类Layer来实现。

void Layer::onDraw(const Region& clip) const

{…

    if(CC_UNLIKELY(mActiveBuffer == 0)) {//当前还没有活跃的Buffer,有可能发生      

        Region under;//此layer下面被遮盖的区域。如果没有的话,就把它“涂黑”

        const SurfaceFlinger::LayerVector&drawingLayers(mFlinger->mDrawingState.layersSortedByZ);

        const size_t count =drawingLayers.size();

        for (size_t i=0 ;i<count ; ++i) {

            constsp<LayerBase>& layer(drawingLayers[i]);

            if (layer.get() ==static_cast<LayerBase const*>(this))//layer自己

                break;

           under.orSelf(layer->visibleRegionScreen);

        }

        Regionholes(clip.subtract(under));//哪些区域不会被遮盖

        if (!holes.isEmpty()){

            clearWithOpenGL(holes, 0, 0, 0, 1);

        }

        return;

    }

    …/*配置opengl 参数,源码省略*/

    drawWithOpenGL(clip);

    …

}

变量mActiveBuffer在前面lockPageFlip中已经设置成最新的缓冲区了,但它有可能是空的。比如应用程序还没有开始做绘制工作,在此之前都可能发生这种情况。按照目前版本的实现,先找出所有在这个layer之下的可见区域,即利用mFlinger->mDrawingState.layersSortedByZ不断累加visibleRegionScreen;再由此计算出不会被遮盖的区域:

Region holes(clip.subtract(under));

除holes之外的所有区域,都会被“涂黑”,这是由clearWithOpenGL(holes,0, 0, 0, 1)完成的。

接下来根据当前情况调用opengl的各API接口进行必要配置,为drawWithOpenGL做最后准备工作:

void LayerBase::drawWithOpenGL(const Region& clip) const

{

    const DisplayHardware&hw(graphicPlane(0).displayHardware());//这句代码我们见过几次了

    const uint32_t fbHeight =hw.getHeight();//framebuffer高度

    const State&s(drawingState());//即mDrawingState

 

    GLenum src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA;

    if (CC_UNLIKELY(s.alpha< 0xFF)) {//非完全不透明

        const GLfloat alpha =s.alpha * (1.0f/255.0f);//归一化

        if(mPremultipliedAlpha) {//预乘alpha打开

            glColor4f(alpha,alpha, alpha, alpha);//alpha表达方式不同

        } else {

            glColor4f(1, 1, 1,alpha);

        }

        glEnable(GL_BLEND);//需要混合

        glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA);//制定混合算法

        glTexEnvx(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE, GL_MODULATE);

    } else {

        glColor4f(1, 1, 1, 1);//完全不透明,alpha为1

        glTexEnvx(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE, GL_REPLACE);

        if (!isOpaque()) {//不是opaque,需要blend

            glEnable(GL_BLEND);

            glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA);//同前面s.alpha < 0xFF情况一样

        } else {

            glDisable(GL_BLEND);//不需要blend

        }

    }

   /*顶点结构体*/

    struct TexCoords {

        GLfloat u;

        GLfloat v;

    };

 

    Rect crop(s.active.w,s.active.h);

    if(!s.active.crop.isEmpty()) {

        crop = s.active.crop;

    }

    GLfloat left = GLfloat(crop.left)/ GLfloat(s.active.w);//左边缘

    GLfloat top =GLfloat(crop.top) / GLfloat(s.active.h);//上边缘

    GLfloat right =GLfloat(crop.right) / GLfloat(s.active.w);//右边缘

    GLfloat bottom =GLfloat(crop.bottom) / GLfloat(s.active.h);//下边缘

 

    TexCoords texCoords[4];//定义四个点

    texCoords[0].u = left;/*texCoords[4],分别表示*/

    texCoords[0].v = top;/*crop框的左上、左下、右下、和右上的4个顶点*/

    texCoords[1].u = left;

    texCoords[1].v = bottom;

    texCoords[2].u = right;

    texCoords[2].v = bottom;

    texCoords[3].u = right;

    texCoords[3].v = top;

    for (int i = 0; i < 4;i++) {

        texCoords[i].v = 1.0f- texCoords[i].v;

    }

 

    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    glVertexPointer(2,GL_FLOAT, 0, mVertices);

    glTexCoordPointer(2,GL_FLOAT, 0, texCoords);

    glDrawArrays(GL_TRIANGLE_FAN,0, mNumVertices);

   glDisableClientState(GL_TEXTURE_COORD_ARRAY);

    glDisable(GL_BLEND);

}

对DisplayHardware的获取,我们已经讲过了,不再赘述。变量mPremultipliedAlpha表示“预乘alpha通道”,它是RGBA的另一种表达方式。比如说传统的RGBA就是(r,g,b,a),而premultiplied alpha就是(ra,ga,ba,a)。在某些场景下采用后一种方式能达到更好的效果,大家可以自行查阅相关资料了解详情。

因而当mPremultipliedAlpha为true时(这个变量的初始值就是true,当LayerBase:: initStates时,再根据eNonPremultiplied标志来判断是否启用)设置颜色:

glColor4f(alpha, alpha, alpha, alpha);

否则就是:

glColor4f(1, 1, 1, alpha);

假如当前不是完全不透明的,或者isOpaque()返回为false,那么需要开启BLEND功能。OpenglES中对应的API是glEnable(GLenum cap),参数为GL_BLEND。BLEND的目的就是通过源色和目标色的混合计算来产生透明特效,有多种混合算法可供选择,由glBlendFunc来配置。

glBlendFunc(GLenum sfactor, GLenum dfactor);

第一个参数就是源因子,后一个为目标因子。

可选值如下表所示:

表格 18glBlendFunc可选因子

VALUE

Description

GL_ZERO

使用0.0作为混合因子

GL_ONE

使用1.0作为混合因子

GL_SRC_COLOR

根据源颜色的各分量计算出混合因子

GL_ONE_MINUS_SRC_COLOR

根据(1-源颜色各分量)计算出混合因子

GL_SRC_ALPHA

根据源颜色的alpha计算出混合因子

GL_ONE_MINUS_SRC_ALPHA

根据(1.0-源颜色alpha)计算出混合因子

GL_DST_ALPHA

根据目标颜色的alpha计算出混合因子

GL_ONE_MINUS_DST_ALPHA

根据(1.0-目标颜色alpha)计算出混合因子

GL_DST_COLOR

根据目标颜色的各分量计算出混合因子

GL_ONE_MINUS_DST_COLOR

根据(1-目标颜色各分量)计算出混合因子

 

每种情况下混合因子的具体计算方法,以及blend的计算公式,可以参考官方文档描述。这里我们只要明白它的使用方法就行了。

接下来计算纹理区域的各坐标点,结果以texCoords[4]数组来表示。这样才能保证绘图结果体现在正确的区域中。一切准备就绪,最后就可以调用openglapi进行绘制了:

其中glEnableClientState(GL_TEXTURE_COORD_ARRAY)说明要处理的是纹理坐标数组;glVertexPointer(2, GL_FLOAT, 0, mVertices)指明是二维坐标,float数据类型,紧凑方式排列,顶点数组描述是mVertices(这个数组值在validateVisibility中计算,个数为4); glTexCoordPointer(2,GL_FLOAT, 0, texCoords)也是二维坐标系,float数据类型,紧凑排列,纹理的顶点数组由前面计算得到的texCoords表示。

最后,glDrawArrays(GL_TRIANGLE_FAN,0, mNumVertices)来绘制三角形。OpenGLES取消了对QUAD的支持,有GL_TRIANGLE_FAN与GL_TRIANGLE_STRIP两种三角形绘制方式。

GL_TRIANGLE_FAN:假设有N个顶点。那么第n个三角形的顶点就是(1,n+1,n+2),总共有N-2个三角形。

GL_TRIANGLE_STRIP:假设有N个顶点,也是有N-2个三角形

Ø  N为奇数(odd)。那么第n个三角形的顶点是(n,n+1,n+2)

Ø  N为偶数(even)。那么第n个三解形的顶点就是(n+1,n,n+2)

以这个场景为例,因为mVertices[0]-mVertices[3]分别表示左上、左下、右下、右上四个顶点(可以参见validateVisibility中的实现),那么采用这两种模式的结果如下:

 


图 11‑42 三角形绘制的两种模式


©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页