blog.csdn.net/uiop78uiop78/article/details/8954508
经过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 三角形绘制的两种模式