8.5.2 SF工作线程分析
最后更新于:2022-04-02 05:52:47
SF中的工作线程就是来做图像混合的,比起AudioFlinger来,它相当简单,下面是它的代码:
**SurfaceFlinger.cpp**
~~~
bool SurfaceFlinger::threadLoop()
{
waitForEvent();//① 等待什么事件呢?
if (UNLIKELY(mConsoleSignals)) {
handleConsoleEvents();
}
if(LIKELY(mTransactionCount == 0)) {
const uint32_t mask = eTransactionNeeded | eTraversalNeeded;
uint32_t transactionFlags = getTransactionFlags(mask);
if(LIKELY(transactionFlags)) {
//Transaction(事务)处理,放到本节最后来讨论
handleTransaction(transactionFlags);
}
}
//②处理PageFlipping工作
handlePageFlip();
constDisplayHardware& hw(graphicPlane(0).displayHardware());
if (LIKELY(hw.canDraw() && !isFrozen())) {
//③处理重绘
handleRepaint();
hw.compositionComplete();
//④投递BackBuffer
unlockClients();
postFramebuffer();
} else{
unlockClients();
usleep(16667);
}
returntrue;
}
~~~
ThreadLoop一共有四个关键点,这里,分析除Transaction外的三个关键点。
1. waitForEvent
SF工作线程一上来就等待事件,它会是什么事件呢?来看代码:
**SurfaceFlinger.cpp**
~~~
void SurfaceFlinger::waitForEvent()
{
while(true) {
nsecs_t timeout = -1;
const nsecs_t freezeDisplayTimeout = ms2ns(5000);
......
MessageList::value_type msg = mEventQueue.waitMessage(timeout);
......//还有一些和冻屏相关的内容
if(msg != 0) {
switch (msg->what) {
//千辛万苦就等这一个重绘消息
case MessageQueue::INVALIDATE:
return;
}
}
}
}
~~~
SF收到重绘消息后,将退出等待。那么,是谁发送的这个重绘消息呢?还记得在unlockCanvasAndPost函数中调用的signal吗?它在SF端的实现代码如下:
**SurfaceFlinger**
~~~
void SurfaceFlinger::signal() const {
const_cast(this)->signalEvent();
}
void SurfaceFlinger::signalEvent() {
mEventQueue.invalidate(); //往消息队列中加入INVALIDATE消息
}
~~~
2. 分析handlePageFlip
SF工作线程从waitForEvent中返回后,下一步要做的就是处理事务和handlePageFlip了。先看handlePageFlip,从名字上可知,它和PageFlipping工作有关。
* * * * *
**注意**:事务处理将在8.5.3节中介绍。
* * * * *
代码如下所示:
**SurfaceFlinger.cpp**
~~~
void SurfaceFlinger::handlePageFlip()
{
bool visibleRegions = mVisibleRegionsDirty;
/*
还记得前面所说的mCurrentState吗?它保存了所有显示层的信息,而绘制的时候使用的
mDrawingState则保存了当前需要显示的显示层信息。
*/
LayerVector& currentLayers =
const_cast(mDrawingState.layersSortedByZ);
//①调用lockPageFlip
visibleRegions |= lockPageFlip(currentLayers);
const DisplayHardware& hw =graphicPlane(0).displayHardware();
//取得屏幕的区域
const Region screenRegion(hw.bounds());
if (visibleRegions) {
Region opaqueRegion;
computeVisibleRegions(currentLayers, mDirtyRegion,opaqueRegion);
mWormholeRegion = screenRegion.subtract(opaqueRegion);
mVisibleRegionsDirty = false;
}
//② 调用unlockPageFlip
unlockPageFlip(currentLayers);
mDirtyRegion.andSelf(screenRegion);
}
~~~
hanldePageFlip调用了两个看起来是一对的函数:lockPageFlip和unlockPageFlip。这两个函数会干些什么呢?
(1)lockPageFlip的分析
先看lockPageFlip函数,代码如下所示:
**SurfaceFlinger.cpp**
~~~
bool SurfaceFlinger::lockPageFlip(constLayerVector& currentLayers)
{
boolrecomputeVisibleRegions = false;
size_tcount = currentLayers.size();
sp const* layers = currentLayers.array();
for(size_t i=0 ; i& layer = layers[i];
//调用每个显示层的lockPageFlip
layer->lockPageFlip(recomputeVisibleRegions);
}
returnrecomputeVisibleRegions;
}
~~~
假设当前的显示层是Layer类型,那么得转到Layer类去看它的lockPageFlip函数,代码如下所示:
**Layer.cpp**
~~~
void Layer::lockPageFlip(bool&recomputeVisibleRegions)
{
//lcblk是SharedBufferServer类型,调用retireAndLock函数将返回FrontBuffer的
//索引号
ssize_tbuf = lcblk->retireAndLock();
......
mFrontBufferIndex = buf;
//得到FrontBuffer对应的GraphicBuffer
sp newFrontBuffer(getBuffer(buf));
if (newFrontBuffer != NULL) {
//取出脏区域
const Region dirty(lcblk->getDirtyRegion(buf));
//和GraphicBuffer所表示的区域进行裁剪,得到一个脏区域
mPostedDirtyRegion = dirty.intersect( newFrontBuffer->getBounds() );
const Layer::State& front(drawingState());
if(newFrontBuffer->getWidth() ==front.requested_w &&
newFrontBuffer->getHeight() ==front.requested_h)
{
if ((front.w != front.requested_w) ||
(front.h != front.requested_h))
{
...... //需要重新计算可见区域
recomputeVisibleRegions = true;
}
mFreezeLock.clear();
}
} else{
mPostedDirtyRegion.clear();
}
if(lcblk->getQueuedCount()) {
mFlinger->signalEvent();
}
/*
如果脏区域不为空,则需要绘制成纹理,reloadTexture将绘制一张纹理保存在
mTextures数组中,里边涉及很多OpenGL的操作,读者有兴趣可以自己研究。
*/
if(!mPostedDirtyRegion.isEmpty()) {
reloadTexture( mPostedDirtyRegion );
}
}
~~~
我们知道,Layer的lockPageFlip将根据FrontBuffer的内容生成一张纹理。那么,unlockPageFlip会做些什么呢?
(2)unlockPageFlip的分析
unlockPageFlip的代码如下所示:
**SurfaceFlinger.cpp**
~~~
void SurfaceFlinger::unlockPageFlip(constLayerVector& currentLayers)
{
constGraphicPlane& plane(graphicPlane(0));
constTransform& planeTransform(plane.transform());
size_tcount = currentLayers.size();
sp const* layers = currentLayers.array();
for(size_t i=0 ; i& layer = layers[i];
//调用每个显示层的unlockPageFlip,Layer的unlockPageFlip主要做一些
//区域的清理工作,读者可以自己看看。
layer->unlockPageFlip(planeTransform, mDirtyRegion);
}
}
~~~
(3)handlePageFlip的总结
handlePageFlip的工作其实很简单,以Layer类型为例来总结一下:
各个Layer需要从FrontBuffer中取得新数据,并生成一张OpenGL中的纹理。纹理可以看做是一个图片,这个图片的内容就是FrontBuffer中的图像。
现在每一个Layer都准备好了新数据,下一步的工作当然就是绘制了。来看handleRepaint函数。
3. 分析handleRepaint函数
handleRepaint函数的代码如下所示:
**SurfaceFlinger.cpp**
~~~
void SurfaceFlinger::handleRepaint()
{
mInvalidRegion.orSelf(mDirtyRegion);
if(mInvalidRegion.isEmpty()) {
return;
}
......
constDisplayHardware& hw(graphicPlane(0).displayHardware());
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
uint32_t flags = hw.getFlags();
if((flags & DisplayHardware::SWAP_RECTANGLE) ||
(flags & DisplayHardware::BUFFER_PRESERVED))
{
......//计算mDirtyRegion
}
// 在脏区域上进行绘制
composeSurfaces(mDirtyRegion);
mDirtyRegion.clear();
}
~~~
其中,composeSurfaces将不同的显示层内容进行混合,其实就是按Z轴的顺序由里到外依次绘制。当然,最后绘制的数据有可能遮盖前面绘制的数据,代码如下所示:
**SurfaceFlinger.cpp**
~~~
void SurfaceFlinger::composeSurfaces(constRegion& dirty)
{
constSurfaceFlinger& flinger(*this);
constLayerVector& drawingLayers(mDrawingState.layersSortedByZ);
constsize_t count = drawingLayers.size();
sp const* const layers = drawingLayers.array();
for(size_t i=0 ; i& layer = layers[i];
const Region&visibleRegion(layer->visibleRegionScreen);
if(!visibleRegion.isEmpty()) {
const Region clip(dirty.intersect(visibleRegion));
if (!clip.isEmpty()) {
layer->draw(clip); //调用各个显示层的layer函数
}
}
}
}
~~~
draw函数在LayerBase类中实现,代码如下所示:
**LayerBase.cpp**
~~~
void LayerBase::draw(const Region& inClip)const
{
......
glEnable(GL_SCISSOR_TEST);
onDraw(clip);//调用子类的onDraw函数
}
~~~
至于Layer是怎么实现这个onDraw函数的,代码如下所示:
**Layer.cpp**
~~~
void Layer::onDraw(const Region& clip) const
{
intindex = mFrontBufferIndex;
if(mTextures[index].image == EGL_NO_IMAGE_KHR)
index = 0;
GLuint textureName = mTextures[index].name;
....
Region holes(clip.subtract(under));
if(!holes.isEmpty()) {
clearWithOpenGL(holes);
}
return;
}
//index是FrontBuffer对应生成的纹理,在lockPageFlip函数中就已经生成了。
drawWithOpenGL(clip,mTextures[index]);//将纹理画上去,里面有很多和OpenGL相关内容
}
~~~
drawWithOpenGL函数由LayerBase实现,看它是不是使用了这张纹理,代码如下所示:
**LayerBase.cpp**
~~~
void LayerBase::drawWithOpenGL(const Region&clip, const Texture& texture) const
{
constDisplayHardware& hw(graphicPlane(0).displayHardware());
constuint32_t fbHeight = hw.getHeight();
constState& s(drawingState());
//validateTexture函数内部将绑定指定的纹理
validateTexture(texture.name);
//下面就是OpenGL操作函数了
glEnable(GL_TEXTURE_2D);
......
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
//坐标旋转
switch(texture.transform) {
case HAL_TRANSFORM_ROT_90:
glTranslatef(0, 1, 0);
glRotatef(-90, 0, 0, 1);
break;
case HAL_TRANSFORM_ROT_180:
glTranslatef(1, 1, 0);
glRotatef(-180, 0, 0, 1);
break;
case HAL_TRANSFORM_ROT_270:
glTranslatef(1, 0, 0);
glRotatef(-270, 0, 0, 1);
break;
}
if (texture.NPOTAdjust) {
//缩放处理
glScalef(texture.wScale, texture.hScale, 1.0f);
}
//使能纹理坐标
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
//设置顶点坐标
glVertexPointer(2, GL_FIXED, 0, mVertices);
//设置纹理坐标
glTexCoordPointer(2, GL_FIXED, 0, texCoords);
while(it != end) {
const Rect& r = *it++;
const GLint sy = fbHeight - (r.top + r.height());
//裁剪
glScissor(r.left, sy, r.width(), r.height());
//画矩形
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}
//禁止纹理坐标
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
~~~
纹理绑定是OpenGL的常用函数,其代码如下所示。
**LayerBase.cpp**
~~~
void LayerBase::validateTexture(GLinttextureName) const
{
//下面这个函数将绑定纹理
glBindTexture(GL_TEXTURE_2D, textureName);
......//其他一些设置
}
~~~
handleRepaint这个函数基本上就是按Z轴的顺序对每一层进行重绘,重绘的方法就是使用OpenGL。
我在Android平台上有几个月的OpenGL开发经历,还谈不上很深刻,其中的一些资料,希望能够给感兴趣的读者提供参考。
1)OpenGL的入门教材当选NeHe的资料,大略看前几章即可。
2) Android平台上关于OpenGL ES的开发,有一篇很详细的Word文档叫《OpenGL ESTutorial for Android》。该文详细描述了在Android平台上进行OpenGL开发的流程。大家可跟着这篇教材,在模拟器上做一些练习。那里面涉及到的一些基础知识,从前面介绍的入门教材中可以学到。
3)有了前面两点的基础后,就需要对整个OpenGL有比较完整深入的了解了。我在那时所看的书是《OpenGL Programming Guide (7th Edition)》。该书很厚,有1000多页。里面有一些内容可能与工作无涉,只要大概知道有那回事就行了,暂时不必深入学习,等需要时再进一步学习并运用。我在开发的项目中曾用到的光照、雾化等效果,都是之前先知道有这个东西,后来在项目中才逐渐学习运用的。
4)嵌入式平台上用的其实是OpenGL ES。这里,还有一本书叫《OpenGL ES 2.0 Programming Guide》,它介绍了OpenGL ES的开发,读者可认真修习。
5)在Android SDK文档中,对OpenGL API的描述只寥寥数语。怎么办?由于它使用了J2ME中的javax.microedition.khronos.opengles包,所以J2ME的SDK文档中对OpenGL的API有着非常详细的描述,读者手头应该要有一个J2ME的文档。
6)如果想做深入开发,就不得不学习计算机图形学了。我后来买了书,可惜没时间学了。
4. unlockClients和postFrameBuffer的分析
在绘制完图后,还有两项工作需要做,一个涉及unlockClients函数,另外一个涉及postFrameBuffer函数,这两个函数分别干了什么呢?unlockClients的代码如下所示:
**SurfaceFlinger.cpp**
~~~
void SurfaceFlinger::unlockClients()
{
constLayerVector& drawingLayers(mDrawingState.layersSortedByZ);
constsize_t count = drawingLayers.size();
sp const* const layers = drawingLayers.array();
for (size_t i=0 ; i& layer = layers[i];
layer->finishPageFlip();
}
}
~~~
再看Layer的finishPageFlip函数,代码如下所示:
**Layer.cpp**
~~~
void Layer::finishPageFlip()
{
//释放FrontBufferIndex
status_t err = lcblk->unlock( mFrontBufferIndex );
}
~~~
原来,unlockClients会释放之前占着的FrontBuffer的索引号。下面看最后一个函数postFrameBuffer,代码如下所示:
**SurfaceFlinger.cpp**
~~~
void SurfaceFlinger::postFramebuffer()
{
if(!mInvalidRegion.isEmpty()) {
const DisplayHardware& hw(graphicPlane(0).displayHardware());
const nsecs_t now = systemTime();
mDebugInSwapBuffers = now;
//调用这个函数后,混合后的图像就会传递到屏幕中显示了
hw.flip(mInvalidRegion);
mLastSwapBufferTime = systemTime() - now;
mDebugInSwapBuffers = 0;
mInvalidRegion.clear();
}
}
~~~
flip将调用在DisplayHardware一节中提到的eglSwapBuffer函数,来完成FrameBuffer的PageFlip操作,代码如下所示:
**DisplayHardware.cpp**
~~~
void DisplayHardware::flip(const Region&dirty) const
{
checkGLErrors();
EGLDisplay dpy = mDisplay;
EGLSurface surface = mSurface;
......
if(mFlags & PARTIAL_UPDATES) {
mNativeWindow->setUpdateRectangle(dirty.getBounds());
}
mPageFlipCount++;
eglSwapBuffers(dpy, surface);//PageFlipping,此后图像终于显示在屏幕上了!
}
~~~
';