脱更许久,慢慢步入咸鱼阶段。虽非我愿,但事事不如愿。一切尽在不言中,可与言者无一二。
上一节讲了合成Layer之前的准备工作,主要是就算可视化区域和初始化硬件合成环境,本节就讲讲最后的内容,合成Layer。
回顾
上一节讲合成的时候说过,合成分为两种,离线合成和在线合成:
- 先将所有图层画到一个最终层(FrameBuffer)上,再将FrameBuffer送到LCD显示。由于合成FrameBuffer与送LCD显示一般是异步的(线下生成FrameBuffer,需要时线上的LCD去取),因此叫离线合成。
- 不使用FrameBuffer,在LCD需要显示某一行的像素时,用显示控制器将所有图层与该行相关的数据取出,合成一行像素送过去。只有一个图层时,又叫Overlay技术。
由于省去合成FrameBuffer时读图层,写FrameBuffer的步骤,大幅降低了内存传输量,减少了功耗,但这个需要硬件支持。
决定Layer的合成方式是在HWC硬件部分决定的,一般上层看不到代码,不过有幸Intel、三星等等开源了一部分我们还能窥一窥源码。
如果是Overlay的图层,直接交给显示器硬件系统去在线合成;如果是FRAME_BUFFER类型的图层,需要经过OpenGL处理,然后交给FrameBuffer送给显示器。
合成流程
我们接着上一节的内容,SurfaceFlinger的handleMessageRefresh函数最后的内容。frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp,我们先来看看doComposition函数:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30void SurfaceFlinger::doComposition() {
ATRACE_CALL();
//将mRepaintEverything置为0,不用重绘所有区域
const bool repaintEverything = android_atomic_and(0, &mRepaintEverything);
//遍历所有的DisplayDevice然后调用doDisplayComposition函数
for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
const sp<DisplayDevice>& hw(mDisplays[dpy]);
if (hw->isDisplayOn()) {
// transform the dirty region into this screen's coordinate space
//获得屏幕的脏区域
const Region dirtyRegion(hw->getDirtyRegion(repaintEverything));
//合成,重绘framebuffer
// repaint the framebuffer (if needed)
doDisplayComposition(hw, dirtyRegion);
//清除屏幕脏区域
hw->dirtyRegion.clear();
//判断系统是否支持软件部分更新
hw->flip(hw->swapRegion);
//清除交换区域
hw->swapRegion.clear();
}
// inform the h/w that we're done compositing
//通知hwc硬件合成结束
hw->compositionComplete();
}
//主要是调用hwc硬件的set函数
//此方法将完成各个图层的合成与显示,等效于EGL标准里面的eglSwapBuffers,
//不过eglSwapBuffers是对OpenGL标准/GPU有效,此方法是对硬件合成器有效
postFramebuffer();
}
上述代码流程每个模块功能也很清晰,分步骤就是如下:
1)遍历所有的DisplayDevice然后调用合成相关函数;
2)获取每一个屏幕的脏区域;
3)最重要的一步:合成;
4)清除交换区域和屏幕脏区域;
5)通知hwc硬件合成结束;
6)最后主要是调用hwc硬件的set函数。此方法将完成各个图层的合成与显示,等效于EGL标准里面的eglSwapBuffers,不过eglSwapBuffers是对OpenGL标准/GPU有效,此方法是对硬件合成器有效。
其中核心的就是合成步骤。我们先看看其他几个模块:
获取每一个屏幕的脏区域。frameworks/native/services/surfaceflinger/DisplayDevice.cpp中的getDirtyRegion函数:1
2
3
4
5
6
7
8
9
10
11Region DisplayDevice::getDirtyRegion(bool repaintEverything) const {
Region dirty;
if (repaintEverything) {//0
dirty.set(getBounds());
} else {
const Transform& planeTransform(mGlobalTransform);
dirty = planeTransform.transform(this->dirtyRegion);
dirty.andSelf(getBounds());
}
return dirty;
}
如果需要重绘所有内容,就讲脏区域设为整个屏幕(但事实我们的repaintEverything是0,所以不会);如果不要重绘所有,先对脏区域作矩阵变换(如果屏幕有过几何变换的transform),然后用脏区域与上屏幕大小,求出脏区域在屏幕上大小。
获取了屏幕的脏区域之后,就开始合成了。
开始合成
我们继续查看doDisplayComposition函数:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71void SurfaceFlinger::doDisplayComposition(const sp<const DisplayDevice>& hw,
const Region& inDirtyRegion)
{
// We only need to actually compose the display if:
// 1) It is being handled by hardware composer, which may need this to
// keep its virtual display state machine in sync, or
// 2) There is work to be done (the dirty region isn't empty)
//以下两种情况我们需要去合成显示:
//1)需要报保持虚拟显示屏和物理显示屏同步显示的情况;
//2)SF需要去渲染的脏区域不为空。
bool isHwcDisplay = hw->getHwcDisplayId() >= 0;
if (!isHwcDisplay && inDirtyRegion.isEmpty()) {
return;
}
//SF需要渲染的脏区域
Region dirtyRegion(inDirtyRegion);
// compute the invalid region
//需要渲染到硬件帧缓冲区中去的脏区域的
hw->swapRegion.orSelf(dirtyRegion);
uint32_t flags = hw->getFlags();
//在这种情况下,系统在软件上支持部分区域更新功能,
//同样,这个部分被更新的区域必须要是一个矩形区域。
if (flags & DisplayDevice::SWAP_RECTANGLE) {
// we can redraw only what's dirty, but since SWAP_RECTANGLE only
// takes a rectangle, we must make sure to update that whole
// rectangle in that case
dirtyRegion.set(hw->swapRegion.bounds());
} else {
//在这种情况下,系统在硬件上直接支持部分区域更新功能,
//不过,这个部分被更新的区域必须要是一个矩形区域。
if (flags & DisplayDevice::PARTIAL_UPDATES) {
// We need to redraw the rectangle that will be updated
// (pushed to the framebuffer).
// This is needed because PARTIAL_UPDATES only takes one
// rectangle instead of a region (see DisplayDevice::flip())
dirtyRegion.set(hw->swapRegion.bounds());
} else {
//在这种情况下,系统不支持部分更新区域,这时候就需要更新整个屏幕的内容
// we need to redraw everything (the whole screen)
dirtyRegion.set(hw->bounds());
hw->swapRegion = dirtyRegion;
}
}
//如果没有开启Daltonize辅助功能(高对比性文字/色彩校正/颜色反转)
//并且没有颜色矩阵去混合
if (CC_LIKELY(!mDaltonize && !mHasColorMatrix)) {
//合成
if (!doComposeSurfaces(hw, dirtyRegion)) return;
} else {//需要处理一下高对比性文字/色彩校正/颜色反转功能
RenderEngine& engine(getRenderEngine());
mat4 colorMatrix = mColorMatrix;
if (mDaltonize) {
colorMatrix = colorMatrix * mDaltonizer();
}
engine.beginGroup(colorMatrix);
doComposeSurfaces(hw, dirtyRegion);
engine.endGroup();
}
// update the swap region and clear the dirty region
//更新与framebuffer交换的脏区域,并清除应用脏区域
hw->swapRegion.orSelf(dirtyRegion);
// swap buffers (presentation)
//与fb交换脏区域,会调用eglSwapBuffer函数
//使用egl将egl中的合成好的图像,输出到DisplayDevice的mSurface中
hw->swapBuffers(getHwComposer());
}
DisplayDevice类的成员变量swapRegion用来描述SurfaceFlinger服务需要渲染到硬件帧缓冲区中去的脏区域的。前面提到,inDirtyRegion也是用来描述SurfaceFlinger服务需要渲染的脏区域的,不过,它的作用是用来合成系统中各个应用程序窗口的图形缓冲区的,也就是说,当系统中各个应用程序窗口的图形缓冲区被合成之后,这个成员变量所描述的区域就会被清空,swapRegion会一直等到它的内容被渲染到硬件帧缓冲区中去之后,才会被清空。这样就可能会出现这种情况:上一次合成的图形缓冲区还未被渲染到硬件帧缓冲区中去,SurfaceFlinger服务又会执行新一轮的系统中各个应用程序窗口的图形缓冲区的合并操作。在这种情况下,SurfaceFlinger服务就需要将前面两次需要渲染到硬件帧缓冲区中去的区域合并在一起,以便可以正确地反映被刷新的UI。因此,函数在开头的地方,inDirtyRegion所描述的区域组合到成员变量swapRegion所描述的区域中去。
函数接下来调用用来描述系统主显示屏的一个DisplayDevice对象hw的成员函数getFlags来获得系统所支持的渲染方式,并且保存在一个uint32_t变量flags中。接下来,我们就分三种情况来讨论系统所支持的渲染方式:
- 变量flags的DisplayDevice::SWAP_RECTANGLE位等于1。在这种情况下,系统在软件上支持部分区域更新功能,同样,这个部分被更新的区域必须要是一个矩形区域。
- 变量flags的DisplayDevice::PARTIAL_UPDATES位等于1。在这种情况下,系统在硬件上直接支持部分区域更新功能,不过,这个部分被更新的区域必须要是一个矩形区域。
- 变量flags的值等于0。在这种情况下,系统不支持部分更新区域,这时候就需要更新整个屏幕的内容。
在第1种和第2种情况中,由于被更新的区域都必须是一个矩形区域,因此,函数就需要变量inDirtyRegion所描述的一个区域设置为包含了所有脏区域的一个最小矩形区域。在第3种情况中,由于需要更新的是整个屏幕的内容,因此,函数就需要inDirtyRegion所描述的一个区域设置为等于屏幕大小的一个矩形区域。
(在老的版本代码中,比如android2.3,部分更新还支持BUFFER_PRESERVED不规则区域更新。在保留后端图形缓冲区的内容的情况下,系统就可以支持仅仅渲染那些需要更新的脏区域,这些区域可以是不规则的。然而,实现不规则区域部分更新功能是有代价的,因为每次在渲染UI时,都要将后端图形缓冲区的内容拷贝回那些不在那些需要更新的区域中去,这会导致性能低下。因此,系统一般都不支持不规则区域部分更新功能。)
在安卓L版本中,辅助设置中新加了3个功能,分别是高对比性文字,颜色反转,色彩校正。
This information applies only to devices running Android 5.0 and higher.
High contrast makes text easier to read on your device. This feature fixes the text color as either black or white, depending on the original text color.
To enable or disable high contrast text, follow these steps:
Go to Settings > Accessibility.
Select High contrast text.High-contrast text is currently an experimental feature, so it might not work correctly everywhere on your device.
google说,这个功能就是让文字更容易阅读,也就是有点类似与文字高亮。最后的效果就是,把文字变成白色或黑色,把背景文字背景变成黑色或白色。也就是说,如果你之前的文字是白色,背景是黑色,就不会去应用这个功能。
如果没有开启这个辅助功能,mDaltonize和mHasColorMatrix为null,那么直接调用doComposeSurfaces函数去合成图层;如果开启了辅助功能,那么就需要OpenGL引擎去处理颜色矩阵(高对比性文字主要是修改控件,颜色反转和颜色校正主要是通过操作gpu修改显示效果),这一部分有机会我们以后会研究研究。
那么接下来两部就是合成图层和交换FrameBuffer了,我们逐步分析。
合成图层
doComposeSurfaces
合成图层的函数是doComposeSurfaces,这个函数比较长,我们分段查看:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90bool SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& hw, const Region& dirty)
{
RenderEngine& engine(getRenderEngine());
const int32_t id = hw->getHwcDisplayId();
HWComposer& hwc(getHwComposer());
HWComposer::LayerListIterator cur = hwc.begin(id);
const HWComposer::LayerListIterator end = hwc.end(id);
bool hasGlesComposition = hwc.hasGlesComposition(id);
if (hasGlesComposition) {//是否有egl合成
//DisplayDevice类的成员函数makeCurrent的实现很简单,
//它只是通过调用函数eglMakeCurrent来将前面已经创建好的绘图表面以及绘图上下文设置为当前线程的绘图表面以及绘图上下文,
//即设置为SurfaceFlinger服务的UI渲染线程的绘图表面以及绘图上下文
if (!hw->makeCurrent(mEGLDisplay, mEGLContext)) {
ALOGW("DisplayDevice::makeCurrent failed. Aborting surface composition for display %s",
hw->getDisplayName().string());
//如果绑定失败了,就要取消设置OpenGL库在当前线程的绘图表面以及绘图上下文
eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
//那么就要去设置默认屏幕创建好的绘图表面和上下文为当前线程的内容
if(!getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext)) {
ALOGE("DisplayDevice::makeCurrent on default display failed. Aborting.");
}
return false;
}
// Never touch the framebuffer if we don't have any framebuffer layers
const bool hasHwcComposition = hwc.hasHwcComposition(id);
if (hasHwcComposition) {//是否有hwc合成
// when using overlays, we assume a fully transparent framebuffer
// NOTE: we could reduce how much we need to clear, for instance
// remove where there are opaque FB layers. however, on some
// GPUs doing a "clean slate" clear might be more efficient.
// We'll revisit later if needed.
//如果使用OverLay硬件合成,就要把FrameBuffer全都清除为透明色
engine.clearWithColor(0, 0, 0, 0);
} else {//是否有OpenGL合成
//letterbox(信箱模式)
// we start with the whole screen area
const Region bounds(hw->getBounds());
// we remove the scissor part
// we're left with the letterbox region
// (common case is that letterbox ends-up being empty)
//移除内容区域,只留下大黑边(剪裁区域)
const Region letterbox(bounds.subtract(hw->getScissor()));
// compute the area to clear
//计算大黑边区域
Region region(hw->undefinedRegion.merge(letterbox));
// but limit it to the dirty region
//还要限制到更新的脏区域范围内
region.andSelf(dirty);
// screen is already cleared here
//对大黑边进行挖洞处理,使用OpenGL填成黑色
if (!region.isEmpty()) {
// can happen with SurfaceView
//SurfaceView的实现原理就是打洞覆盖:另起一个图层(即新建一个Surface),
//并把主图层的相应区域置为透明,然后渲染就发生在新图层中,
//最终显示效果自然是依赖SurfaceFlinger的叠加
drawWormhole(hw, region);
}
}
//如果不是主显屏
if (hw->getDisplayType() != DisplayDevice::DISPLAY_PRIMARY) {
// just to be on the safe side, we don't set the
// scissor on the main display. It should never be needed
// anyways (though in theory it could since the API allows it).
//为了安全起见,这个操作不会在主显屏发生
const Rect& bounds(hw->getBounds());
const Rect& scissor(hw->getScissor());
if (scissor != bounds) {
//如果剪裁区域和屏幕尺寸不匹配,就要清除所有塔之外的内容
//并且enble GL剪裁器,在我们不需要绘制的地方不绘制任何内容
// scissor doesn't match the screen's dimensions, so we
// need to clear everything outside of it and enable
// the GL scissor so we don't draw anything where we shouldn't
// enable scissor for this frame
const uint32_t height = hw->getHeight();
engine.setScissor(scissor.left, height - scissor.bottom,
scissor.getWidth(), scissor.getHeight());
}
}
}
//......
}
这部分先调用hasGlesComposition函数和hasHwcComposition函数,就是看其对应的DisplayData中是否有hasFbComp和hasOvComp,对应有无OpenGL合成或者OverLay硬件合成。1
2
3
4
5
6
7
8
9
10bool HWComposer::hasGlesComposition(int32_t id) const {
if (!mHwc || uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id))
return true;
return mDisplayData[id].hasFbComp;
}
bool HWComposer::hasHwcComposition(int32_t id) const {
if (!mHwc || uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id))
return false;
return mDisplayData[id].hasOvComp;
}
我们在Android SurfaceFlinger 学习之路(十一)—-合成Layer之准备合成中讲过,而这两个值是在prepare中调用Hwc的prepare函数之后赋值的,具体原理是在Android SurfaceFlinger 学习之路(十一)(PostView)–附:硬件HWC选取合成类型(Intel) 中实现的,如果忘记了可以翻翻上一篇的内容。
如果有OpenGL的合成,先调用DisplayDevice类的成员函数makeCurrent,通过调用函数eglMakeCurrent来将前面已经创建好的绘图表面以及绘图上下文设置为当前线程的绘图表面以及绘图上下文,即设置为SurfaceFlinger服务的UI渲染线程的绘图表面以及绘图上下文。如果绑定失败了,就要取消设置OpenGL库在当前线程的绘图表面以及绘图上下文。那么就要去设置默认屏幕创建好的绘图表面和上下文为当前线程的内容。
因为合成有可能是混合合成,所以还要判断是否也有Overlay合成方式。如果有,就要把FrameBuffer全都清除为透明黑色。
如果没有,就是OpenGL单独合成,就要使用信箱模式
。
这个只是一种比喻,信箱模式可以查一查资料,比如维基百科)中的解释。我们这里理解为图层的宽高比大于电视的宽高比的情况。从名字来看,letterbox就像邮筒一样,中间是有内容的,上下是没用的。letterbox目标就是保存原有胶片的宽高比,使内容完整的展现在屏幕上。由于内容宽高比较大,所以上下会有黑屏。采用的技术就是:抽调部分扫描线,以满足宽高比,不利的结果就是图像质量下降。
所以对于这种情况,我们需要对大黑边进行处理:
1)移除内容区域,只留下大黑边(剪裁区域);
2)计算大黑边区域;
3)还要限制到更新的脏区域范围内;
4)对大黑边进行挖洞处理,使用OpenGL填成黑色
SurfaceView的实现原理就是打洞覆盖:另起一个图层(即新建一个Surface),并把主图层的相应区域置为透明,然后渲染就发生在新图层中,最终显示效果自然是依赖SurfaceFlinger的叠加。
最后就是一个特殊处理,对于不是主显屏(为了安全起见,这个操作不会在主显屏发生),如果剪裁区域和屏幕尺寸不匹配,就要清除所有塔之外的内容,并且enble GL剪裁器,在我们不需要绘制的地方不绘制任何内容。
继续往下看:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/*
* and then, render the layers targeted at the framebuffer
*/
const Vector< sp<Layer> >& layers(hw->getVisibleLayersSortedByZ());
const size_t count = layers.size();
const Transform& tr = hw->getTransform();
if (cur != end) {//代表起码有两个以上图层
// we're using h/w composer
for (size_t i=0 ; i<count && cur!=end ; ++i, ++cur) {//遍历图层
const sp<Layer>& layer(layers[i]);
//clip用来描述要绘制的区域,而要绘制的区域即为当前需要指定的裁剪区域
//就是全部图层的总体脏区域和这个图层的可视区域的交集
const Region clip(dirty.intersect(tr.transform(layer->visibleRegion)));
if (!clip.isEmpty()) {
switch (cur->getCompositionType()) {
//overlay不做处理
case HWC_CURSOR_OVERLAY:
case HWC_OVERLAY: {
const Layer::State& state(layer->getDrawingState());
if ((cur->getHints() & HWC_HINT_CLEAR_FB)
&& i
&& layer->isOpaque(state) && (state.alpha == 0xFF)
&& hasGlesComposition) {
// never clear the very first layer since we're
// guaranteed the FB is already cleared
//上面说过,如果是Overlay合成,就要清除FrameBuffer
//这里以防万一,确保FB要被清除
layer->clearWithOpenGL(hw, clip);
}
break;
}
//如果是OpenGL合成,就调用Layer的draw函数
case HWC_FRAMEBUFFER: {
layer->draw(hw, clip);
break;
}
//HWC_FRAMEBUFFER_TARGET是OpenGL合成后使用的目标层,直接就跳了
case HWC_FRAMEBUFFER_TARGET: {
// this should not happen as the iterator shouldn't
// let us get there.
ALOGW("HWC_FRAMEBUFFER_TARGET found in hwc list (index=%zu)", i);
break;
}
}
}
layer->setAcquireFence(hw, *cur);
}
} else {//只有一个或者没有图层 就直接使用OpenGL合成,调用Layer的draw
// we're not using h/w composer
for (size_t i=0 ; i<count ; ++i) {
const sp<Layer>& layer(layers[i]);
const Region clip(dirty.intersect(
tr.transform(layer->visibleRegion)));
if (!clip.isEmpty()) {
layer->draw(hw, clip);
}
}
}
// disable scissor at the end of the frame
engine.disableScissor();
return true;
}
这一段看注释就知道合成步骤。先根据z-order拿到所有的可视图层,然后分两种情况:
1)起码有两个以上图层 :遍历图层,用全部图层的总体脏区域和这个图层的可视区域求交集,计算出clip区域。如果clip区域不为空,根据Layer的合成类型分别进行处理:
- overlay不做处理,但是上面说过,如果是Overlay合成,就要清除FrameBuffer,这里以防万一,确保FB要被清除。
- 如果是OpenGL合成,就调用Layer的draw函数。
- HWC_FRAMEBUFFER_TARGET是OpenGL合成后使用的目标层,直接就跳了。
2)只有一个或者没有图层 就直接使用OpenGL合成,调用Layer的draw。
所以合成主要是OpenGL处理,Layer的draw函数。
OpenGL处理合成
我们看看Layer的draw函数实现:1
2
3void Layer::draw(const sp<const DisplayDevice>& hw, const Region& clip) const {
onDraw(hw, clip, false);
}
draw内部又调用了onDraw函数,这个函数有一点点小长,我们依然分段查看:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43void Layer::onDraw(const sp<const DisplayDevice>& hw, const Region& clip,
bool useIdentityTransform) const
{
ATRACE_CALL();
//在纹理未创建好的情况下,一个应用程序窗口是不应该被渲染的
if (CC_UNLIKELY(mActiveBuffer == 0)) {
// the texture has not been created yet, this Layer has
// in fact never been drawn into. This happens frequently with
// SurfaceView because the WindowManager can't know when the client
// has drawn the first time.
// If there is nothing under us, we paint the screen in black, otherwise
// we just skip this update.
// figure out if there is something below us
//在纹理未创建好的情况下,一个应用程序窗口是不应该被渲染的
//这种情况频繁的发生在SurfaceView身上,因为WindowManger不知道它第一次绘制的时间
//如果没有内容在它下面,我们把整个屏幕都画成黑色
//否则就跳过这次更新
//所以我们需要计算出在它下面的内容
Region under;
const SurfaceFlinger::LayerVector& drawingLayers(
mFlinger->mDrawingState.layersSortedByZ);
const size_t count = drawingLayers.size();
for (size_t i=0 ; i<count ; ++i) {
const sp<Layer>& layer(drawingLayers[i]);
if (layer.get() == static_cast<Layer const*>(this))
break;
//这时候函数首先将位于当前正在处理的应用程序窗口下面的所有其它应用程序窗口的可见区域组合起来,
//并且保存在变量under所描述的区域中
under.orSelf( hw->getTransform().transform(layer->visibleRegion) );
}
// if not everything below us is covered, we plug the holes!
//由于这时候当前正在处理的应用程序窗口不会被绘制,
//因此,如果变量under所描述的区域小于参数clip所描述的区域,即变量holes所描述的区域不为空,
//那么SurfaceFlinger服务所要求缓制的区域clip就会留下一个洞
Region holes(clip.subtract(under));
//这个洞会被绘制成黑色,这是通过调用函数clearWithOpenGL来实现的
if (!holes.isEmpty()) {
clearWithOpenGL(hw, holes, 0, 0, 0, 1);
}
return;
}
这段代码主要是用来处应用程序窗口的纹理尚未创建好的情况。
在纹理未创建好的情况下,一个应用程序窗口是不应该被渲染的。这时候函数首先将位于当前正在处理的应用程序窗口下面的所有其它应用程序窗口的可见区域组合起来,并且保存在变量under所描述的区域中。由于这时候当前正在处理的应用程序窗口不会被绘制,因此,如果变量under所描述的区域小于参数clip所描述的区域,即变量holes所描述的区域不为空,那么SurfaceFlinger服务所要求缓制的区域clip就会留下一个洞。这个洞会被绘制成黑色,这是通过调用函数clearWithOpenGL来实现的。绘制完成之后,函数就可以直接返回了。
这里mActiveBuffer我们在Android SurfaceFlinger 学习之路(十)—-SurfaceFlinger处理Layer更新里面的更新纹理讲过,会为其赋值。
绑定纹理
接着看onDraw函数:1
2
3
4
5
6
7
8// Bind the current buffer to the GL texture, and wait for it to be
// ready for us to draw into.
status_t err = mSurfaceFlingerConsumer->bindTextureImage();
if (err != NO_ERROR) {
ALOGW("onDraw: bindTextureImage failed (err=%d)", err);
// Go ahead and draw the buffer anyway; no matter what we do the screen
// is probably going to have something visibly wrong.
}
这段代码首先,将buffer加入到GL texture,然后等待底层OpenGL调用GPU绘制完成纹理给我们上层返回。
我们看看SurfaceFlingerConsumer的bindTextureImage函数:1
2
3
4
5status_t SurfaceFlingerConsumer::bindTextureImage()
{
Mutex::Autolock lock(mMutex);
return bindTextureImageLocked();
}
又调用了父类的GLConsumer的bindTextureImageLocked函数:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53status_t GLConsumer::bindTextureImageLocked() {
if (mEglDisplay == EGL_NO_DISPLAY) {
ALOGE("bindTextureImage: invalid display");
return INVALID_OPERATION;
}
GLint error;
while ((error = glGetError()) != GL_NO_ERROR) {
ST_LOGW("bindTextureImage: clearing GL error: %#04x", error);
}
//绑定一个Texture纹理,如果没有就创建
glBindTexture(mTexTarget, mTexName);
if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT &&
mCurrentTextureImage == NULL) {
ST_LOGE("bindTextureImage: no currently-bound texture");
return NO_INIT;
}
//创建一个Image绘制
status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay,
mCurrentCrop);
if (err != NO_ERROR) {
ST_LOGW("bindTextureImage: can't create image on display=%p slot=%d",
mEglDisplay, mCurrentTexture);
return UNKNOWN_ERROR;
}
//将Image绑定到Texure
mCurrentTextureImage->bindToTextureTarget(mTexTarget);
// In the rare case that the display is terminated and then initialized
// again, we can't detect that the display changed (it didn't), but the
// image is invalid. In this case, repeat the exact same steps while
// forcing the creation of a new image.
if ((error = glGetError()) != GL_NO_ERROR) {
glBindTexture(mTexTarget, mTexName);
status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay,
mCurrentCrop,
true);
if (err != NO_ERROR) {
ST_LOGW("bindTextureImage: can't create image on display=%p slot=%d",
mEglDisplay, mCurrentTexture);
return UNKNOWN_ERROR;
}
mCurrentTextureImage->bindToTextureTarget(mTexTarget);
if ((error = glGetError()) != GL_NO_ERROR) {
ST_LOGE("bindTextureImage: error binding external image: %#04x", error);
return UNKNOWN_ERROR;
}
}
// Wait for the new buffer to be ready.
//等待OpenGL处理完成,返回fence
return doGLFenceWaitLocked();
}
OpenGL绑定纹理需要四步:
1)绑定一个Texture纹理,如果没有就创建;
2)创建一个Image用于绘制;
3)将Image绑定到Texure;
4)等待OpenGL处理完成,返回fence。
我们依然分不查看:
1)绑定纹理:glBindTexture。
这里有一个特别尴尬的地方,就是我们这里的代码也看不到,因为这和GPU硬件相关,厂商不会开放这些代码的ToT。。。之前我们能看看高通、TI、甚至Intel的硬件模块代码实属运气,Google将其也放入了AOSP部分。但是对于GPU的源代码却看不到了,想想就明白了,现在显卡卖的一点都不比CPU便宜,尤其这两年人工智能、机器学习火起来,还有比特币的高涨,挖矿越来越猛烈,这些都需要超高速密集运算硬件支持,所以GPU价值和价格也有水涨船高之势头。所以指望这些厂商开发源代码实属异想天开=。=
虽然硬件代码看不到,但是我们可以曲线救国,从软件实现去分析功能。软件实现位于/frameworks/native/opengl/libagl/texture.cpp:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53void glBindTexture(GLenum target, GLuint texture)
{
ogles_context_t* c = ogles_context_t::get();
if (target != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES) {
ogles_error(c, GL_INVALID_ENUM);
return;
}
// Bind or create a texture
//绑定或创建一个纹理
sp<EGLTextureObject> tex;
if (texture == 0) {
// 0 is our local texture object
//0表示是本地的纹理对象
tex = c->textures.defaultTexture;
} else {
tex = c->surfaceManager->texture(texture);
if (ggl_unlikely(tex == 0)) {
//创建一个纹理
tex = c->surfaceManager->createTexture(texture);
if (tex == 0) {
ogles_error(c, GL_OUT_OF_MEMORY);
return;
}
}
}
bindTextureTmu(c, c->textures.active, texture, tex);
}
void bindTextureTmu(
ogles_context_t* c, int tmu, GLuint texture, const sp<EGLTextureObject>& tex)
{
if (tex.get() == c->textures.tmu[tmu].texture)
return;
// free the reference to the previously bound object
//释放对之前纹理对象的引用
texture_unit_t& u(c->textures.tmu[tmu]);
if (u.texture)
u.texture->decStrong(c);
// bind this texture to the current active texture unit
// and add a reference to this texture object
//绑定这个纹理对象到当前活跃的纹理单元
//并且增加引用计数
u.texture = tex.get();
u.texture->incStrong(c);
u.name = texture;
invalidate_texture(c, tmu);
}
static
void invalidate_texture(ogles_context_t* c, int tmu, uint8_t flags = 0xFF) {
c->textures.tmu[tmu].dirty = flags;
}
软件实现也是先创建一个纹理texture对象,然后释放掉对之前纹理对象的引用,然后将引用指向新得纹理,再将引用计数加一,这样完成对当前纹理的绑定操作。
2)创建一个Image用于绘制:
mCurrentTextureImage是一个 EglImage,我们在Android SurfaceFlinger 学习之路(十)—-SurfaceFlinger处理Layer更新里面的更新纹理讲过,acquireBuffer时候会创建一个EglImage。所以这里调用EglImage的createIfNeeded函数:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38status_t GLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay,
const Rect& cropRect,
bool forceCreation) {
// If there's an image and it's no longer valid, destroy it.
//如果之前的image不在有效了,就要销毁它
bool haveImage = mEglImage != EGL_NO_IMAGE_KHR;
bool displayInvalid = mEglDisplay != eglDisplay;
bool cropInvalid = hasEglAndroidImageCrop() && mCropRect != cropRect;
if (haveImage && (displayInvalid || cropInvalid || forceCreation)) {
if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) {
ALOGE("createIfNeeded: eglDestroyImageKHR failed");
}
mEglImage = EGL_NO_IMAGE_KHR;
mEglDisplay = EGL_NO_DISPLAY;
}
// If there's no image, create one.
//如果没有image,就要创建一个
if (mEglImage == EGL_NO_IMAGE_KHR) {
mEglDisplay = eglDisplay;
mCropRect = cropRect;
mEglImage = createImage(mEglDisplay, mGraphicBuffer, mCropRect);
}
// Fail if we can't create a valid image.
//创建失败了,就只能GG了
if (mEglImage == EGL_NO_IMAGE_KHR) {
mEglDisplay = EGL_NO_DISPLAY;
mCropRect.makeInvalid();
const sp<GraphicBuffer>& buffer = mGraphicBuffer;
ALOGE("Failed to create image. size=%ux%u st=%u usage=0x%x fmt=%d",
buffer->getWidth(), buffer->getHeight(), buffer->getStride(),
buffer->getUsage(), buffer->getPixelFormat());
return UNKNOWN_ERROR;
}
return OK;
}
如果之前的image不在有效了,就要销毁它;如果没有image,就要创建一个;创建失败了,就只能GG了。
我们可以继续看createImage函数,如何去创建一个image:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30EGLImageKHR GLConsumer::EglImage::createImage(EGLDisplay dpy,
const sp<GraphicBuffer>& graphicBuffer, const Rect& crop) {
EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer();
EGLint attrs[] = {
EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
EGL_IMAGE_CROP_LEFT_ANDROID, crop.left,
EGL_IMAGE_CROP_TOP_ANDROID, crop.top,
EGL_IMAGE_CROP_RIGHT_ANDROID, crop.right,
EGL_IMAGE_CROP_BOTTOM_ANDROID, crop.bottom,
EGL_NONE,
};
if (!crop.isValid()) {
// No crop rect to set, so terminate the attrib array before the crop.
attrs[2] = EGL_NONE;
} else if (!isEglImageCroppable(crop)) {
// The crop rect is not at the origin, so we can't set the crop on the
// EGLImage because that's not allowed by the EGL_ANDROID_image_crop
// extension. In the future we can add a layered extension that
// removes this restriction if there is hardware that can support it.
attrs[2] = EGL_NONE;
}
/*此句为创建Image的代码*/
EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT,
EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs);
if (image == EGL_NO_IMAGE_KHR) {
EGLint error = eglGetError();
ALOGE("error creating EGLImage: %#x", error);
}
return image;
}
上面创建Image主要代码就是eglCreateImageKHR函数,不过依然很悲催,还是和GPU相关的硬件代码。我们只能继续曲线救国了,查看软件创建Image的实现,位于/frameworks/native/opengl/libagl/egl.cpp:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target,
EGLClientBuffer buffer, const EGLint* /*attrib_list*/)
{
if (egl_display_t::is_valid(dpy) == EGL_FALSE) {
return setError(EGL_BAD_DISPLAY, EGL_NO_IMAGE_KHR);
}
if (ctx != EGL_NO_CONTEXT) {
return setError(EGL_BAD_CONTEXT, EGL_NO_IMAGE_KHR);
}
if (target != EGL_NATIVE_BUFFER_ANDROID) {
return setError(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR);
}
//这个buffer还是我们上层acquireBuffer时候获取的图形缓冲区buffer
ANativeWindowBuffer* native_buffer = (ANativeWindowBuffer*)buffer;
if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC)
return setError(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR);
if (native_buffer->common.version != sizeof(ANativeWindowBuffer))
return setError(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR);
switch (native_buffer->format) {
case HAL_PIXEL_FORMAT_RGBA_8888:
case HAL_PIXEL_FORMAT_RGBX_8888:
case HAL_PIXEL_FORMAT_RGB_888:
case HAL_PIXEL_FORMAT_RGB_565:
case HAL_PIXEL_FORMAT_BGRA_8888:
break;
default:
return setError(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR);
}
native_buffer->common.incRef(&native_buffer->common);
return (EGLImageKHR)native_buffer;
}
说是创建一个EGLImageKHR,其实还是对我们上层acquireBuffer时候获取的图形缓冲区buffer,进行一些处理包装。因为这是软件实现,所以参考价值不大吧。
3)将Image绑定到Texure:1
2
3void GLConsumer::EglImage::bindToTextureTarget(uint32_t texTarget) {
glEGLImageTargetTexture2DOES(texTarget, (GLeglImageOES)mEglImage);
}
这次就真尴尬了,别说没硬件实现了,连软件都没得看了。。。。。。曲线救国没戏了,大清要亡啊。。。。。
中华民国到来,学医救不了中国。。。。。。java救不了中国,但是php能=。=
沦落到看模拟器实现绑定,看看goldfish的实现,位于device/generic/goldfish/opengl/system/GLESv2/gl2.cpp:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26//GL extensions
void glEGLImageTargetTexture2DOES(void * self, GLenum target, GLeglImageOES image)
{
DBG("glEGLImageTargetTexture2DOES v2 target=%#x img=%p\n", target, image);
//TODO: check error - we don't have a way to set gl error
android_native_buffer_t* native_buffer = (android_native_buffer_t*)image;
if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC) {
return;
}
if (native_buffer->common.version != sizeof(android_native_buffer_t)) {
return;
}
GET_CONTEXT;
DEFINE_AND_VALIDATE_HOST_CONNECTION();
//openGL的context上下文覆盖纹理坐标
ctx->override2DTextureTarget(target);
//然后用渲染引擎绑定Image的缓冲区引用
rcEnc->rcBindTexture(rcEnc, ((cb_handle_t *)(native_buffer->handle))->hostHandle);
//然后恢复纹理
ctx->restore2DTextureTarget();
return;
}
看代码核心也是三步:openGL的context上下文覆盖纹理坐标;然后用渲染引擎绑定Image的缓冲区引用;然后恢复纹理。
4)等待OpenGL处理完成,返回fence:
上一节更新纹理时候讲过,里面调用acquireBufferLocked,buffer状态迁移到acquire,获得要显示出来的Buffer。这里会创建一个Fence,目前acquire fencefd还没使用,因为还未去合成这个layer,没到用layer中数据的时候。
如果到了合成步骤,这个Fence才是使用的时机,就是接下来第四部做的操作:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65// 等待生产者的acquire fence触发
status_t GLConsumer::doGLFenceWaitLocked() const {
EGLDisplay dpy = eglGetCurrentDisplay();
EGLContext ctx = eglGetCurrentContext();
if (mEglDisplay != dpy || mEglDisplay == EGL_NO_DISPLAY) {
ST_LOGE("doGLFenceWait: invalid current EGLDisplay");
return INVALID_OPERATION;
}
if (mEglContext != ctx || mEglContext == EGL_NO_CONTEXT) {
ST_LOGE("doGLFenceWait: invalid current EGLContext");
return INVALID_OPERATION;
}
//等待生产者的acquire fence触发,
if (mCurrentFence->isValid()) {
//检查EGL库是否支持EGL_ANDROID_native_fence_sync或者EGL_KHR_fence_sync属性的fence
if (SyncFeatures::getInstance().useWaitSync()) {
// Create an EGLSyncKHR from the current fence.
//dup一份上层传来的fence
int fenceFd = mCurrentFence->dup();
if (fenceFd == -1) {
ST_LOGE("doGLFenceWait: error dup'ing fence fd: %d", errno);
return -errno;
}
EGLint attribs[] = {
EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd,
EGL_NONE
};
//创建一个GPU的fence
EGLSyncKHR sync = eglCreateSyncKHR(dpy,
EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
if (sync == EGL_NO_SYNC_KHR) {
close(fenceFd);
ST_LOGE("doGLFenceWait: error creating EGL fence: %#x",
eglGetError());
return UNKNOWN_ERROR;
}
// XXX: The spec draft is inconsistent as to whether this should
// return an EGLint or void. Ignore the return value for now, as
// it's not strictly needed.
//GPU的fence等待,绘制完成会唤醒
eglWaitSyncKHR(dpy, sync, 0);
EGLint eglErr = eglGetError();
//唤醒后移除fence围栏
eglDestroySyncKHR(dpy, sync);
if (eglErr != EGL_SUCCESS) {
ST_LOGE("doGLFenceWait: error waiting for EGL fence: %#x",
eglErr);
return UNKNOWN_ERROR;
}
} else {//不支持则上层fence去wait,一般都是-1
status_t err = mCurrentFence->waitForever(
"GLConsumer::doGLFenceWaitLocked");
if (err != NO_ERROR) {
ST_LOGE("doGLFenceWait: error waiting for fence: %d", err);
return err;
}
}
}
return NO_ERROR;
}
等待逻辑步骤如下:
1)这里会先等待生产者的acquire fence触发,检查这个Fence是否生效;
2)然后查询EGL库是否支持EGL_ANDROID_native_fence_sync或者EGL_KHR_fence_sync属性的fence,这些都是从OpenGL相关的so库中获取,我们看看实现,位于frameworks/native/libs/gui/SyncFeatures.cpp:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51SyncFeatures::SyncFeatures() : Singleton<SyncFeatures>(),
mHasNativeFenceSync(false),
mHasFenceSync(false),
mHasWaitSync(false) {
EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
// This can only be called after EGL has been initialized; otherwise the
// check below will abort.
const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS);
LOG_ALWAYS_FATAL_IF(exts == NULL, "eglQueryStringImplementationANDROID failed");
if (strstr(exts, "EGL_ANDROID_native_fence_sync")) {
// This makes GLConsumer use the EGL_ANDROID_native_fence_sync
// extension to create Android native fences to signal when all
// GLES reads for a given buffer have completed.
mHasNativeFenceSync = true;
}
if (strstr(exts, "EGL_KHR_fence_sync")) {
mHasFenceSync = true;
}
if (strstr(exts, "EGL_KHR_wait_sync")) {
mHasWaitSync = true;
}
mString.append("[using:");
if (useNativeFenceSync()) {
mString.append(" EGL_ANDROID_native_fence_sync");
}
if (useFenceSync()) {
mString.append(" EGL_KHR_fence_sync");
}
if (useWaitSync()) {
mString.append(" EGL_KHR_wait_sync");
}
mString.append("]");
}
bool SyncFeatures::useNativeFenceSync() const {
// EGL_ANDROID_native_fence_sync is not compatible with using the
// EGL_KHR_fence_sync extension for the same purpose.
return mHasNativeFenceSync;
}
bool SyncFeatures::useFenceSync() const {
// on some devices it's better to not use EGL_KHR_fence_sync
// even if they have it
return false;
// currently we shall only attempt to use EGL_KHR_fence_sync if
// USE_FENCE_SYNC is set in our makefile
return !mHasNativeFenceSync && mHasFenceSync;
}
bool SyncFeatures::useWaitSync() const {
return (useNativeFenceSync() || useFenceSync()) && mHasWaitSync;
}
查询GPU相关属性在eglQueryStringImplementationANDROID函数,不过这次我们曲线救国都没用了,中华民国也GG了=。=想想也是,Fence实现每家GPU都不同,并且也算比较重要的技术,肯定不会给我们开放的。。。
3)如果支持硬件Fence围栏,那么就用上层传入的fence去dup一份copy,然后根据这个copy创建一个GPU的fence。eglCreateSyncKHR依然GG,php也救不了中国~
4)GPU的fence等待,绘制完成会唤醒。等待操作GG。。。唤醒后就要销毁这个fence,放开栅栏;
5)fence的合法性验证为是否为-1,如果上层使用OpenGL绘制,那么fence就不为-1,下层也需要OpenGL绘制纹理;如果上层使用canvas绘制,那么fence就为-1,则gpu不等待,直接合成ok。
这就是OpenGL绑定纹理的操作,其中比较重要的是Fence在中间穿插的作用,这里只是简单分析,如果有机会,我会抽出一章专门分析一下。
drawWithOpenGL
我们继续回到Layer的onDraw函数,继续分析合成步骤。
继续看下一部分:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58//这个Layer是否是受保护的,比如截屏的Layer
bool blackOutLayer = isProtected() || (isSecure() && !hw->isSecure());
RenderEngine& engine(mFlinger->getRenderEngine());
//不是受保护的Layer
if (!blackOutLayer) {
// TODO: we could be more subtle with isFixedSize()
//是否是规则的形状,或者上层应用程序有能力确认缓冲区的有效性
//如果isFixedSize为true,那么就很容易处理图形的变换
const bool useFiltering = getFiltering() || needsFiltering(hw) || isFixedSize();
// Query the texture matrix given our current filtering mode.
float textureMatrix[16];
mSurfaceFlingerConsumer->setFilteringEnabled(useFiltering);
mSurfaceFlingerConsumer->getTransformMatrix(textureMatrix);
//如果这个图形缓冲区之前曾经被旋转过,例如,被水平翻转或者垂直翻转过,
//那么在对它进行合并之前,还需要将它的旋转方向恢复回来
if (mSurfaceFlingerConsumer->getTransformToDisplayInverse()) {
/*
* the code below applies the display's inverse transform to the texture transform
*/
// create a 4x4 transform matrix from the display transform flags
const mat4 flipH(-1,0,0,0, 0,1,0,0, 0,0,1,0, 1,0,0,1);
const mat4 flipV( 1,0,0,0, 0,-1,0,0, 0,0,1,0, 0,1,0,1);
const mat4 rot90( 0,1,0,0, -1,0,0,0, 0,0,1,0, 1,0,0,1);
mat4 tr;
uint32_t transform = hw->getOrientationTransform();
if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90)
tr = tr * rot90;
if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H)
tr = tr * flipH;
if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V)
tr = tr * flipV;
// calculate the inverse
//对它进行合并之前,还需要将它的旋转方向恢复回来
//所以需要反转变换矩阵
tr = inverse(tr);
// and finally apply it to the original texture matrix
//然后将它应用到纹理中
const mat4 texTransform(mat4(static_cast<const float*>(textureMatrix)) * tr);
memcpy(textureMatrix, texTransform.asArray(), sizeof(textureMatrix));
}
// Set things up for texturing.
//将上面对纹理的配置设置进来
mTexture.setDimensions(mActiveBuffer->getWidth(), mActiveBuffer->getHeight());
mTexture.setFiltering(useFiltering);
mTexture.setMatrix(textureMatrix);
engine.setupLayerTexturing(mTexture);
} else {
engine.setupLayerBlackedOut();
}
我们处理合成的Layer一般都是不受保护的Layer,如果是截屏或者其他被保护的Layer,blackOutLayer就为true。
这里再说一下isFixedSize,这个在讲Android SurfaceFlinger 学习之路(九)—-SurfaceFlinger事务处理的时候提到过,我们再复习一下。
Layer类的成员变量mFixedSize是一个布尔变量,它的值可以通过Layer类的成员函数isFixedSize来获得。从前面Android SurfaceFlinger 学习之路(六)—-SurfaceFlinger创建Surface可以知道,当Android应用程序请求SurfaceFlinger服务分配一块图形缓冲区时,Android应用程序会传递两个参数reqWidth和reqHeight过来,表示请求分配的图形缓冲区的宽度和高度。这两个参数是可以同时等于0的,表示使用默认的宽度和高度值来创建所请求的图形缓冲区。这两个默认的宽度和高度值即等于当前所处理的应用程序窗口的宽度和高度值,而后者的宽度和高度值是在其创建的时候指定的。
Layer类的成员函数requestBuffer的参数reqWidth和reqHeight的值等于0意味着什么呢?从前面Android SurfaceFlinger 学习之路(八)—-Surface管理图形缓冲区可以知道,Android应用程序在请求SurfaceFlinger服务分配一块图形缓冲区之前,会通过在Surface类内部来检查当前所处理的应用程序窗口的大小是否发生了变化。如果发生了变化,那么Android应用程序就会忽略掉缓存自己一侧的图形缓冲区,而去SurfaceFlinger服务请求新的图形缓冲区,因为那些缓存的图形缓冲区由于与它们所关联的应用程序窗口大小发生了变化而变为无效了。但是有一种特殊情况,在Android应用程序这一侧,用来描述应用程序窗口的Surface类可以不维护应用程序窗口的大小值。在这种情况下,Surface类就会将与它所关联的应用程序窗口的大小值设置为0,这意味着Android应用程序每次为这个应用程序窗口向SurfaceFlinger服务请求分配图形缓冲区之前,都认为这个应用程序窗口的大小值没有发生变化,同时传递给Layer类的参数reqWidth和reqHeight的值会等于0。事实上,一个应用程序窗口的大小是随时都可以发生变化的,比如,我们可以通过调用用来在Android应用程序和SurfaceFlinger服务建立连接的一个类型为Client的Binder对象的成员函数setState来改变一个应用程序窗口的大小,而一旦一个应用程序窗口的大小发生了变化,Layer类的成员函数doTransaction就会被调用。
Layer类的成员函数doTransaction在处理应用程序窗口大小变化时,需要考虑Android应用程序每次在为该应用程序窗口向SurfaceFlinger服务请求分配图形缓冲区之前,是否有能力去判断之前为该应用程序窗口缓存的图形缓冲区的有效性。如果没有的话,那么Layer类的成员函数doTransaction就需要将为该应用程序窗口缓存的图形缓冲区设置为无效,以便以后Android应用程序可以请求SurfaceFlinger服务分配新的、大小正确的图形缓冲区。从前面的分析还可以知道,当Android应用程序没有能力去判断之前为一个应用程序窗口所缓存的图形缓冲区的有效性时,那么之前在请求分配这些图形缓冲区时,传递给Layer类的的参数reqWidth和reqHeight的值就会等于0,这时候Layer类就会将Layer类的成员变量mFixedSize的值设置为false。
当Layer类的成员变量mFixedSize的值等于false时,由于Android应用程序没有能力去判断之前为一个应用程序窗口所缓存的图形缓冲区的有效性,因此,Layer类的成员函数doTransaction除了会调用外一个成员函数setBuffers来将新的应用程序窗口大小记录下来之外,还会通知BufferQueueProducer将当前正在处理的应用程序窗口所缓存的图形缓冲区设置为无效。
当Layer类的成员变量mFixedSize的值等于false时,Layer类的成员函数doTransaction还会提前将成员变量mCurrentState所描述的一个State对象的成员变量requested_w和requested_h的值保存到成员变量mDrawingState所描述的一个State对象的成员变量requested_w和requested_h中去,这是为了避免后面调用父类Layer的成员函数doTransaction时,会返回一个Layer::eVisibleRegion位不等于0的标志值给前面,而这将会导致SurfaceFlinger服务马上重新计算各个应用程序窗口的可见区域。现在不返回一个Layer::eVisibleRegion位不等于0的标志值给前面,就会等到下次渲染当前正在处理的应用程序窗口时再重新计算各个应用程序窗口的可见区域。
太长了,跳过~
GLConsumer的构造函数中mFilteringEnabled(true),所以filter为true。filter参数是OpenGL中使用纹理坐标映射到纹素数组,比如我们在doTransatin时候提到的线性滤波方法(linear filtering)。要理解这个也是需要OpenGL基础啊,奈何我也是小白=。=不过我们知道这里就是打开了滤波设置。
接下来代码用来检查当前正在处理的应用程序窗口的图形缓冲区是否是一个可以跳过合成阶段的图形缓冲区。本来这种图形缓冲区是可以直接渲染到硬件帧缓冲区中去的,但是由于它不是全屏显示的,因此就需要与其它应用程序窗口的图形缓冲区进行合并操作。如果这个图形缓冲区之前曾经被旋转过,例如,被水平翻转或者垂直翻转过,那么在对它进行合并之前,还需要将它的旋转方向恢复回来。
当用来描述一个图形缓冲区的一个GraphicBuffer对象的成员变量transform的值不等于0时,那么就说明这个图形缓冲区是被旋转过的,这时候函数就会对这个成员变量的值的NATIVE_WINDOW_TRANSFORM_FLIP_H位或者NATIVE_WINDOW_TRANSFORM_FLIP_V位进行取反,目的就是为了恢复它之前的旋转方向。
然后进入最后一部分,drawWithOpenGL:1
2drawWithOpenGL(hw, clip, useIdentityTransform);
engine.disableTexturing();
drawWithOpenGL来将这个图形缓冲区的内容绘制在系统的主显示屏的指定区域上来,这个图形缓冲区的内容是使用mesh对象来描述的,包括顶点缓冲,索引缓冲,纹理对象等,而指定的主显示屏区域是由参数clip来描述的。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
void Layer::drawWithOpenGL(const sp<const DisplayDevice>& hw,
const Region& /* clip */, bool useIdentityTransform) const {
const uint32_t fbHeight = hw->getHeight();
const State& s(getDrawingState());
computeGeometry(hw, mMesh, useIdentityTransform);
/*
* NOTE: the way we compute the texture coordinates here produces
* different results than when we take the HWC path -- in the later case
* the "source crop" is rounded to texel boundaries.
* This can produce significantly different results when the texture
* is scaled by a large amount.
*
* The GL code below is more logical (imho), and the difference with
* HWC is due to a limitation of the HWC API to integers -- a question
* is suspend is whether we should ignore this problem or revert to
* GL composition when a buffer scaling is applied (maybe with some
* minimal value)? Or, we could make GL behave like HWC -- but this feel
* like more of a hack.
*/
const Rect win(computeBounds());
float left = float(win.left) / float(s.active.w);
float top = float(win.top) / float(s.active.h);
float right = float(win.right) / float(s.active.w);
float bottom = float(win.bottom) / float(s.active.h);
// TODO: we probably want to generate the texture coords with the mesh
// here we assume that we only have 4 vertices
Mesh::VertexArray<vec2> texCoords(mMesh.getTexCoordArray<vec2>());
texCoords[0] = vec2(left, 1.0f - top);
texCoords[1] = vec2(left, 1.0f - bottom);
texCoords[2] = vec2(right, 1.0f - bottom);
texCoords[3] = vec2(right, 1.0f - top);
RenderEngine& engine(mFlinger->getRenderEngine());
engine.setupLayerBlending(mPremultipliedAlpha, isOpaque(s), s.alpha);
engine.drawMesh(mMesh);
engine.disableBlending();
}
这个我们也是分步查看:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 //主显示屏高度
const uint32_t fbHeight = hw->getHeight();
//当前正在处理的应用程序窗口状态的一个State对象s
const State& s(getDrawingState());
computeGeometry(hw, mMesh, useIdentityTransform);
/*-----------------*/
void Layer::computeGeometry(const sp<const DisplayDevice>& hw, Mesh& mesh,
bool useIdentityTransform) const
{
const Layer::State& s(getDrawingState());
//上一步反转后的变换transform
const Transform tr(useIdentityTransform ?
hw->getTransform() : hw->getTransform() * s.transform);
const uint32_t hw_h = hw->getHeight();
Rect win(s.active.w, s.active.h);
if (!s.active.crop.isEmpty()) {
win.intersect(s.active.crop, &win);
}
// subtract the transparent region and snap to the bounds
//应用程序窗口减去透明区域
win = reduce(win, s.activeTransparentRegion);
//获取纹理的位置坐标
Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
position[0] = tr.transform(win.left, win.top);
position[1] = tr.transform(win.left, win.bottom);
position[2] = tr.transform(win.right, win.bottom);
position[3] = tr.transform(win.right, win.top);
for (size_t i=0 ; i<4 ; i++) {
position[i].y = hw_h - position[i].y;
}
}
这段代码首先得到主显示屏的高度fbHeight、要绘制的区域mesh的位置坐标,以及用来描述当前正在处理的应用程序窗口状态的一个State对象s。
接着往下看:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 const Rect win(computeBounds());
float left = float(win.left) / float(s.active.w);
float top = float(win.top) / float(s.active.h);
float right = float(win.right) / float(s.active.w);
float bottom = float(win.bottom) / float(s.active.h);
// TODO: we probably want to generate the texture coords with the mesh
// here we assume that we only have 4 vertices
Mesh::VertexArray<vec2> texCoords(mMesh.getTexCoordArray<vec2>());
texCoords[0] = vec2(left, 1.0f - top);
texCoords[1] = vec2(left, 1.0f - bottom);
texCoords[2] = vec2(right, 1.0f - bottom);
texCoords[3] = vec2(right, 1.0f - top);
/*-----------------*/
Rect Layer::computeBounds() const {
const Layer::State& s(getDrawingState());
Rect win(s.active.w, s.active.h);
if (!s.active.crop.isEmpty()) {
win.intersect(s.active.crop, &win);
}
// subtract the transparent region and snap to the bounds
return reduce(win, s.activeTransparentRegion);
}
上面是根据mesh的位置计算出纹理顶点的坐标。
如果Android应用程序没有指定一个窗口的纹理坐标,那么这个窗口的纹理坐标的默认值就使用要绘制的纹理的四个角的坐标来描述。注意,在计算纹理坐标的时候,还要考虑纹理的大小,以及纹理本身所设置的缩放因子,以便可以正确地将纹理绘制在应用程序窗口中。
继续往下,先看看这一步:1
2RenderEngine& engine(mFlinger->getRenderEngine());
engine.setupLayerBlending(mPremultipliedAlpha, isOpaque(s), s.alpha);
Android在此新增一个RenderEngine类,用来屏蔽OpenGL ES1.0、1.1和2.0的用法差异。基本用法和opengl是一样的,没有简化太多。
这一步是对颜色的处理,我们看看OpenGL E2.0的实现,位于frameworks/native/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp:1
2
3
4
5
6
7
8
9
10
11
12
13
14void GLES20RenderEngine::setupDimLayerBlending(int alpha) {
mState.setPlaneAlpha(1.0f);
mState.setPremultipliedAlpha(true);
mState.setOpaque(false);
mState.setColor(0, 0, 0, alpha/255.0f);
mState.disableTexture();
if (alpha == 0xFF) {
glDisable(GL_BLEND);
} else {
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
}
}
这里是设定图层混合的模式(mPremultipliedAlpha表示该图层是否已经做过预乘处理,Opaque表示该图层像素是否无视本图层的透明度,s.alpha表示该图层的整体透明度)。是否需要以混合模式来绘制,这是通过调用函数glEnable(GL_BLEND)来实现的。在需要混合模式来绘制纹理texture的情况下,还需要调用函数glBlendFunc来指定混合函数。
当前正在处理的应用程序窗口的Alpha通道的值小于0xFF,即State对象s的成员变量alpha的值小于0xFF,这表明该窗口的背景是半透明的。此时就需要使用混合模式来绘制纹理。
我们看最后一部分:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 engine.drawMesh(mMesh);
engine.disableBlending();
/*---------GLES20RenderEngine.cpp---------*/
void GLES20RenderEngine::drawMesh(const Mesh& mesh) {
ProgramCache::getInstance().useProgram(mState);
//指定要绘制的顶点数组
if (mesh.getTexCoordsSize()) {
glEnableVertexAttribArray(Program::texCoords);
glVertexAttribPointer(Program::texCoords,
mesh.getTexCoordsSize(),
GL_FLOAT, GL_FALSE,
mesh.getByteStride(),
mesh.getTexCoords());
}
//指定要绘制的纹理坐标
glVertexAttribPointer(Program::position,
mesh.getVertexSize(),
GL_FLOAT, GL_FALSE,
mesh.getByteStride(),
mesh.getPositions());
//绘制前面指定的顶点数组以及纹理
glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
if (mesh.getTexCoordsSize()) {
glDisableVertexAttribArray(Program::texCoords);
}
}
void GLES20RenderEngine::disableBlending() {
glDisable(GL_BLEND);
}
分别调用函数glVertexPointer和glTexCoordPointer来指定要绘制的顶点数组以及纹理坐标。设置好要绘制的顶点数组以及纹理坐标之后,最后调用函数glDrawArrays来绘制前面指定的顶点数组以及纹理。
其中这三个函数依然让我们曲线救国失败啊,大清彻底亡了=。=
doComposeSurfaces流程就分析完了,我们回到SF的doDisplayComposition函数继续。
到此合成流程就完了,接下来到了和FB交换buffer。
交换合成缓冲区
沿着上面到了doDisplayComposition的最后一步。 与fb交换脏区域,会调用eglSwapBuffer函数,使用egl将egl中的合成好的图像,输出到DisplayDevice的mSurface中。
继续查看,调用DisplayDevice的swapBuffers函数:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36void DisplayDevice::swapBuffers(HWComposer& hwc) const {
// We need to call eglSwapBuffers() if:
// (1) we don't have a hardware composer, or
// (2) we did GLES composition this frame, and either
// (a) we have framebuffer target support (not present on legacy
// devices, where HWComposer::commit() handles things); or
// (b) this is a virtual display
//当以下几种情况我们会调用eglSwapBuffers:
//1.没有硬件合成器
//2.使用OpenGL合成这一帧时候,以下两种情况之一
//a.有FRAME_BUFFER_TARGET支持(overlay合成时候还有遗留的设备)
//b.这是一个虚拟显屏
if (hwc.initCheck() != NO_ERROR ||
(hwc.hasGlesComposition(mHwcDisplayId) &&
(hwc.supportsFramebufferTarget() || mType >= DISPLAY_VIRTUAL))) {
//调用OpenGL库中的函数eglSwapBuffers来将系统的UI渲染到系统的主绘图表面上去的
EGLBoolean success = eglSwapBuffers(mDisplay, mSurface);
if (!success) {
EGLint error = eglGetError();
if (error == EGL_CONTEXT_LOST ||
mType == DisplayDevice::DISPLAY_PRIMARY) {
LOG_ALWAYS_FATAL("eglSwapBuffers(%p, %p) failed with 0x%08x",
mDisplay, mSurface, error);
} else {
ALOGE("eglSwapBuffers(%p, %p) failed with 0x%08x",
mDisplay, mSurface, error);
}
}
}
status_t result = mDisplaySurface->advanceFrame();
if (result != NO_ERROR) {
ALOGE("[%s] failed pushing new frame to HWC: %d",
mDisplayName.string(), result);
}
}
调用OpenGL库中的函数eglSwapBuffers来将系统的UI渲染到系统的主绘图表面上去的,即渲染到系统的硬件帧缓冲区上去的。
这里天不亡大清~~继续曲线救国,我们看看软件的实现,位于frameworks/native/opengl/libagl/egl.cpp:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw)
{
if (egl_display_t::is_valid(dpy) == EGL_FALSE)
return setError(EGL_BAD_DISPLAY, EGL_FALSE);
//将EGLSurface 强转为egl_surface_t结构体指针
egl_surface_t* d = static_cast<egl_surface_t*>(draw);
if (!d->isValid())
return setError(EGL_BAD_SURFACE, EGL_FALSE);
if (d->dpy != dpy)
return setError(EGL_BAD_DISPLAY, EGL_FALSE);
// post the surface
//然后调用swapBuffers完成交换操作
d->swapBuffers();
// if it's bound to a context, update the buffer
if (d->ctx != EGL_NO_CONTEXT) {
d->bindDrawSurface((ogles_context_t*)d->ctx);
// if this surface is also the read surface of the context
// it is bound to, make sure to update the read buffer as well.
// The EGL spec is a little unclear about this.
egl_context_t* c = egl_context_t::context(d->ctx);
if (c->read == draw) {
d->bindReadSurface((ogles_context_t*)d->ctx);
}
}
return EGL_TRUE;
}
先将将EGLSurface 强转为egl_surface_t结构体指针,这里为什么能强转,我们下面一个小节会讲到。接着调用swapBuffers无参函数完成交换。同样位于frameworks/native/opengl/libagl/egl.cpp下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106EGLBoolean egl_window_surface_v2_t::swapBuffers()
{
if (!buffer) {
return setError(EGL_BAD_ACCESS, EGL_FALSE);
}
/*
* Handle eglSetSwapRectangleANDROID()
* We copyback from the front buffer
*/
//如果合成区域的脏区域不为空
if (!dirtyRegion.isEmpty()) {
//首先通过andSelf()函数,算出在buffer中的dirtyRegion的区域
dirtyRegion.andSelf(Rect(buffer->width, buffer->height));
//如果上一次buffer不为空
if (previousBuffer) {
// This was const Region copyBack, but that causes an
// internal compile error on simulator builds
//然后调用subtract将oldDirtyRegion中去掉了dirtyRegion区域,
//然后见这块区域从previousBuffer拷贝到当前的buffer中。
/*const*/ Region copyBack(Region::subtract(oldDirtyRegion, dirtyRegion));
if (!copyBack.isEmpty()) {
void* prevBits;
if (lock(previousBuffer,
GRALLOC_USAGE_SW_READ_OFTEN, &prevBits) == NO_ERROR) {
// copy from previousBuffer to buffer
copyBlt(buffer, bits, previousBuffer, prevBits, copyBack);
unlock(previousBuffer);
}
}
}
//将新的脏区域赋值给oldDirtyRegion
oldDirtyRegion = dirtyRegion;
}
//将少之前buffer的对gralloc的引用
if (previousBuffer) {
previousBuffer->common.decRef(&previousBuffer->common);
previousBuffer = 0;
}
unlock(buffer);
//完成buffer内容的填充,然后将previousBuffer指向buffer
previousBuffer = buffer;
//这里的nativeWindow是egl_window_surface_v2_t结构体的成员变量
//是一个ANativeWindow*类型
//在DisplayDevice构造函数里调用eglCreateWindowSurface创建egl_window_surface_v2_t实例时候,
//new了一个Surface传入的
//这里还调用了Surface的queueBuffer函数
//从之前将Surface管理缓冲区得知,这会回调consumer的onFrameAvailable函数
//调用surface的queueBuffer,agl的实现fencefd输入为-1
//硬件平台不为-1,比如抓高通的log
//肯定在前面先创建了fence同步对象,经过merge后肯定不再为-1了
nativeWindow->queueBuffer(nativeWindow, buffer, -1);
buffer = 0;
// dequeue a new buffer
int fenceFd = -1;
//然后dequeue一个新的buffer,并等待fence
///第一次被申请的buffer slot。返回-1
//假设不是。有release fence,则会dup该fencefd
if (nativeWindow->dequeueBuffer(nativeWindow, &buffer, &fenceFd) == NO_ERROR) {
sp<Fence> fence(new Fence(fenceFd));
//wait,等待release fence触发
//如果fence超时,就把buffer cancel掉。
if (fence->wait(Fence::TIMEOUT_NEVER)) {
nativeWindow->cancelBuffer(nativeWindow, buffer, fenceFd);
return setError(EGL_BAD_ALLOC, EGL_FALSE);
}
// reallocate the depth-buffer if needed
//按需重新计算buffer
if ((width != buffer->width) || (height != buffer->height)) {
// TODO: we probably should reset the swap rect here
// if the window size has changed
width = buffer->width;
height = buffer->height;
if (depth.data) {
free(depth.data);
depth.width = width;
depth.height = height;
depth.stride = buffer->stride;
depth.data = (GGLubyte*)malloc(depth.stride*depth.height*2);
if (depth.data == 0) {
setError(EGL_BAD_ALLOC, EGL_FALSE);
return EGL_FALSE;
}
}
}
// keep a reference on the buffer
buffer->common.incRef(&buffer->common);
// finally pin the buffer down
if (lock(buffer, GRALLOC_USAGE_SW_READ_OFTEN |
GRALLOC_USAGE_SW_WRITE_OFTEN, &bits) != NO_ERROR) {
ALOGE("eglSwapBuffers() failed to lock buffer %p (%ux%u)",
buffer, buffer->width, buffer->height);
return setError(EGL_BAD_ACCESS, EGL_FALSE);
// FIXME: we should make sure we're not accessing the buffer anymore
}
} else {
return setError(EGL_BAD_CURRENT_SURFACE, EGL_FALSE);
}
return EGL_TRUE;
}
软件实现大致如下:
1)如果合成区域的脏区域不为空,首先通过andSelf()函数,算出在buffer中的dirtyRegion的区域,然后调用subtract将oldDirtyRegion中去掉了dirtyRegion区域,然后见这块区域从previousBuffer拷贝到当前的buffer中。这一部分和我门之前讲Android SurfaceFlinger 学习之路(八)—-Surface管理图形缓冲区中Surface申请图形缓冲区过程类似,也是没有变化的区域就用copyBlt拷贝像素。
2)将新的脏区域赋值给oldDirtyRegion,将少之前buffer的对gralloc的引用(这个我们在讲Android SurfaceFlinger 学习之路(一)—-Android图形显示之HAL层Gralloc模块实现中提到过,这是C语言中的一种继承方式),完成buffer内容的填充,然后将previousBuffer指向buffer。
3)接着调用调用nativeBuffer变量的queueBuffer。这里的nativeWindow是egl_window_surface_v2_t结构体的成员变量,是一个ANativeWindow*类型。在DisplayDevice构造函数里调用eglCreateWindowSurface创建egl_window_surface_v2_t实例时候,new了一个Surface传入的。之前Android SurfaceFlinger 学习之路(六)—-SurfaceFlinger创建Surface讲过,Surface间接继承与ANativeWindow。
从之前将Surface管理缓冲区得知,这会回调consumer的onFrameAvailable函数,后面会讲到,这会触发FrameBufferSurface的onFrameAvailable函数回调。
调用surface的queueBuffer,agl的实现fencefd输入为-1。硬件平台不为-1,比如抓高通的log,肯定在前面先创建了fence同步对象,经过merge后肯定不再为-1了。
4)Dequeue一块新的buffer,并wait,等待release fence触发。如果等待超时,就将buffer cancel掉。第一次被申请的buffer slot,返回-1。假设不是。有release fence,则会dup该fencefd。
5)按需重新计算buffer。
既然提到交换缓冲区,就是讲生产者的内容post到消费者去,所以我们应该关注nativeWindow的queueBuffer过程。在讲这一部分之前,我们要看看之前讲到的DisplayDevice和FrameBufferSurface关系,还要引入一点OpenGL相关的内容。
OpenGL环境创建
使用OpenGL标准,用GPU把图层画到统一的FrameBuffer上,然后送显。毫无疑问这是离线合成的一种。EGL标准下,OpenGL环境创建的一般流程如下图所示:
这部分工作在SurfaceFlinger::init函数完成,也即服务初起之时。我们在讲Android SurfaceFlinger 学习之路(五)—-VSync 工作原理时候提到过这一部分:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54// initialize EGL for the default display
mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(mEGLDisplay, NULL, NULL);
//初始化硬件合成器(这个和3D合成无关)
mHwc = new HWComposer(this,
*static_cast<HWComposer::EventHandler *>(this));
//创建渲染引擎,主要是选择EGL配置,选择OpenGL版本,创建OpenGL上下文
mRenderEngine = RenderEngine::create(mEGLDisplay, mHwc->getVisualID());
// retrieve the EGL context that was selected/created
mEGLContext = mRenderEngine->getEGLContext();
LOG_ALWAYS_FATAL_IF(mEGLContext == EGL_NO_CONTEXT,
"couldn't create EGLContext");
//创建OpenGL的渲染目标Surface
for (size_t i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {
DisplayDevice::DisplayType type((DisplayDevice::DisplayType)i);
// set-up the displays that are already connected
if (mHwc->isConnected(i) || type==DisplayDevice::DISPLAY_PRIMARY) {
// All non-virtual displays are currently considered secure.
bool isSecure = true;
createBuiltinDisplayLocked(type);
wp<IBinder> token = mBuiltinDisplays[i];
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer,
new GraphicBufferAlloc());
/*创建窗口Surface所需要的window句柄,注意这里面window句柄是FramebufferSurface*/
sp<FramebufferSurface> fbs = new FramebufferSurface(*mHwc, i,
consumer);
int32_t hwcId = allocateHwcDisplayId(type);
/*在构造函数中,调用 eglCreateSurface 创建了OpenGL渲染的目标Surface*/
sp<DisplayDevice> hw = new DisplayDevice(this,
type, hwcId, mHwc->getFormat(hwcId), isSecure, token,
fbs, producer,
mRenderEngine->getEGLConfig());
if (i > DisplayDevice::DISPLAY_PRIMARY) {
// FIXME: currently we don't get blank/unblank requests
// for displays other than the main display, so we always
// assume a connected display is unblanked.
ALOGD("marking display %zu as acquired/unblanked", i);
hw->setPowerMode(HWC_POWER_MODE_NORMAL);
}
mDisplays.add(token, hw);
}
}
// make the GLContext current so that we can create textures when creating Layers
// (which may happens before we render something)
/*绑定上下文和Surface,以便绘制,这一步在调用OpenGL的drawcall之前就可以,这里调一次貌似是没必要的*/
getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext);
对使用opengl合成的layer将合成结果放置到HWC_FRAMEBUFFER_TARGET layer中,然后再交给HWComposer处理。在surfaceflinger的init函数中,定义了HWC_FRAMEBUFFER_TARGET layer合成时相应的生成者和消费者,每一个display相应有一个DisplayDevice作为生产者(opengl合成数据),而FramebufferSurface是相应的消费者(注意这个消费者仅仅是处理opengl合成相关的,overlay全然由HAL层的hwcomposer处理)。
注意到,创建的窗口是FramebufferSurface。为了更直观的查看Surface创建流程,我们再把DisplayDevice构造函数贴出来:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77DisplayDevice::DisplayDevice(
const sp<SurfaceFlinger>& flinger,
DisplayType type,
int32_t hwcId,
bool isSecure,
const wp<IBinder>& displayToken,
const sp<DisplaySurface>& displaySurface,
const sp<IGraphicBufferProducer>& producer,
EGLConfig config)
: mFlinger(flinger),
mType(type), mHwcDisplayId(hwcId),
mDisplayToken(displayToken),
mDisplaySurface(displaySurface),
mDisplay(EGL_NO_DISPLAY),
mSurface(EGL_NO_SURFACE),
mDisplayWidth(), mDisplayHeight(), mFormat(),
mFlags(),
mPageFlipCount(),
mIsSecure(isSecure),
mSecureLayerVisible(false),
mScreenAcquired(false),
mLayerStack(NO_LAYER_STACK),
mOrientation()
{
//利用bq创建surface
mNativeWindow = new Surface(producer, false);
ANativeWindow* const window = mNativeWindow.get();
int format;
window->query(window, NATIVE_WINDOW_FORMAT, &format);
// Make sure that composition can never be stalled by a virtual display
// consumer that isn't processing buffers fast enough. We have to do this
// in two places:
// * Here, in case the display is composed entirely by HWC.
// * In makeCurrent(), using eglSwapInterval. Some EGL drivers set the
// window's swap interval in eglMakeCurrent, so they'll override the
// interval we set here.
if (mType >= DisplayDevice::DISPLAY_VIRTUAL)
window->setSwapInterval(window, 0);
/*
* Create our display's surface
*/
// 利用EGL创建本地opengl环境,要用opengl 合成layer
EGLSurface surface;
EGLint w, h;
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
//调用eglCreateWindowSurface创建EGLSurface
surface = eglCreateWindowSurface(display, config, window, NULL);
eglQuerySurface(display, surface, EGL_WIDTH, &mDisplayWidth);
eglQuerySurface(display, surface, EGL_HEIGHT, &mDisplayHeight);
mDisplay = display;
mSurface = surface;
mFormat = format;
mPageFlipCount = 0;
mViewport.makeInvalid();
mFrame.makeInvalid();
// virtual displays are always considered enabled
mScreenAcquired = (mType >= DisplayDevice::DISPLAY_VIRTUAL);
// Name the display. The name will be replaced shortly if the display
// was created with createDisplay().
switch (mType) {
case DISPLAY_PRIMARY:
mDisplayName = "Built-in Screen";
break;
case DISPLAY_EXTERNAL:
mDisplayName = "HDMI Screen";
break;
default:
mDisplayName = "Virtual Screen"; // e.g. Overlay #n
break;
}
}
在DisplayDevice构造函数里,利用从SF的init 函数里传入的BufferQueueProducer构造了一个Surface,然后调用eglCreateWindowSurface创建EGLSurface。
我们知道surface继承了RefBase,所以get()实际上RefBase提供的函数。返回了surface的对象引用。而surface继承了ANativeObjectBase模版,通过ANativeObjectBase模版,可以理解成surface类也继承了AnativeWindow和RefBase。那么surface.get()作为AnativeWindow的类型参数传递给CreateWindowSurface也就好理解咯。也就是window本质上就是surface。
为了解决上面留下的迷惑,我们看看eglCreateWindowSurface的实现。依然需要曲线救国,看看软件实现,位于frameworks/native/opengl/libagl/egl.cpp:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config,
NativeWindowType window,
const EGLint *attrib_list)
{
return createWindowSurface(dpy, config, window, attrib_list);
}
static EGLSurface createWindowSurface(EGLDisplay dpy, EGLConfig config,
NativeWindowType window, const EGLint* /*attrib_list*/)
{
/*------前面都是一些错误检查------*/
if (egl_display_t::is_valid(dpy) == EGL_FALSE)
return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE);
if (window == 0)
return setError(EGL_BAD_MATCH, EGL_NO_SURFACE);
EGLint surfaceType;
if (getConfigAttrib(dpy, config, EGL_SURFACE_TYPE, &surfaceType) == EGL_FALSE)
return EGL_FALSE;
if (!(surfaceType & EGL_WINDOW_BIT))
return setError(EGL_BAD_MATCH, EGL_NO_SURFACE);
if (static_cast<ANativeWindow*>(window)->common.magic !=
ANDROID_NATIVE_WINDOW_MAGIC) {
return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
}
EGLint configID;
if (getConfigAttrib(dpy, config, EGL_CONFIG_ID, &configID) == EGL_FALSE)
return EGL_FALSE;
int32_t depthFormat;
int32_t pixelFormat;
if (getConfigFormatInfo(configID, pixelFormat, depthFormat) != NO_ERROR) {
return setError(EGL_BAD_MATCH, EGL_NO_SURFACE);
}
// FIXME: we don't have access to the pixelFormat here just yet.
// (it's possible that the surface is not fully initialized)
// maybe this should be done after the page-flip
//if (EGLint(info.format) != pixelFormat)
// return setError(EGL_BAD_MATCH, EGL_NO_SURFACE);
egl_surface_t* surface;
//egl_surface_t的详细实现为egl_window_surface_v2_t
surface = new egl_window_surface_v2_t(dpy, config, depthFormat,
static_cast<ANativeWindow*>(window));
if (!surface->initCheck()) {
// there was a problem in the ctor, the error
// flag has been set.
delete surface;
surface = 0;
}
return surface;
}
前面都是一些错误检测,最后面new了一个egl_window_surface_v2_t结构体实现,egl_surface_t的详细实现为egl_window_surface_v2_t。
我们再看看这个结构体部分内容:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
struct egl_window_surface_v2_t : public egl_surface_t
{
egl_window_surface_v2_t(
EGLDisplay dpy, EGLConfig config,
int32_t depthFormat,
ANativeWindow* window);
~egl_window_surface_v2_t();
private:
status_t lock(ANativeWindowBuffer* buf, int usage, void** vaddr);
status_t unlock(ANativeWindowBuffer* buf);
ANativeWindow* nativeWindow;
ANativeWindowBuffer* buffer;
ANativeWindowBuffer* previousBuffer;
gralloc_module_t const* module;
int width;
int height;
void* bits;
GGLFormat const* pixelFormatTable;
};
egl_window_surface_v2_t::egl_window_surface_v2_t(EGLDisplay dpy,
EGLConfig config,
int32_t depthFormat,
ANativeWindow* window)
: egl_surface_t(dpy, config, depthFormat),
nativeWindow(window), buffer(0), previousBuffer(0), module(0),
bits(NULL)
{
hw_module_t const* pModule;
hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &pModule);
module = reinterpret_cast<gralloc_module_t const*>(pModule);
pixelFormatTable = gglGetPixelFormatTable();
// keep a reference on the window
nativeWindow->common.incRef(&nativeWindow->common);
nativeWindow->query(nativeWindow, NATIVE_WINDOW_WIDTH, &width);
nativeWindow->query(nativeWindow, NATIVE_WINDOW_HEIGHT, &height);
}
上面提到了,这里的nativeWindow是egl_window_surface_v2_t结构体的成员变量,是一个ANativeWindow*类型。在DisplayDevice构造函数里调用eglCreateWindowSurface创建egl_window_surface_v2_t实例时候,new了一个Surface传入的。
然后就是上面调用Surface的dequeueBuffer,忘了的话可以往上翻=。=
前面提到过。eglSwapBuffers会触发DisplayDevice这个producer去dequeue buffer和queue buffer。这里的opengl合成和上层的opengl画图相似,在queuebuffer中就会为该buffer设置一个acquire buffer,传递给消费者consumer。从SF的init函数中得知,创建的窗口是FramebufferSurface,这个FrameBufferSurface包了一个BufferQueueConsumer,就像我们在Android SurfaceFlinger 学习之路(八)—-Surface管理图形缓冲区中讲到上层绘制通知SF消费时候的SurfaceFlingerConsumer类的功能一样。所以最后queue buffer会触发FramebufferSurface::onFrameAvailable()。
FrameBufrerSurface的类图如下:
我们再看看FrameBufferSurface的构造函数,位于frameworks/native/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
FramebufferSurface::FramebufferSurface(HWComposer& hwc, int disp,
const sp<IGraphicBufferConsumer>& consumer) :
ConsumerBase(consumer),
mDisplayType(disp),
mCurrentBufferSlot(-1),
mCurrentBuffer(0),
mHwc(hwc)
{
mName = "FramebufferSurface";
mConsumer->setConsumerName(mName);
mConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_FB |
GRALLOC_USAGE_HW_RENDER |
GRALLOC_USAGE_HW_COMPOSER);
mConsumer->setDefaultBufferFormat(mHwc.getFormat(disp));
mConsumer->setDefaultBufferSize(mHwc.getWidth(disp), mHwc.getHeight(disp));
mConsumer->setDefaultMaxBufferCount(NUM_FRAMEBUFFER_SURFACE_BUFFERS);
}
我们注意到mConsumer的setConsumerUsageBits函数中设置的标志位:GRALLOC_USAGE_HW_FB | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER。从之前的Android SurfaceFlinger 学习之路(八)—-Surface管理图形缓冲区讲过,如果是上层应用程序申请图形缓冲区,应用程序进程使用的图形缓冲区一般都是在匿名共享内存里面分配的,这个图形缓冲区填好数据之后,就会再交给SurfaceFlinger服务来合成到硬件帧缓冲区上去渲染。因此,从前面传过来给函数gralloc_alloc的参数usage的GRALLOC_USAGE_HW_FB位会被设置为0,以便可以在匿名共享内存中分配一个图形缓冲区。
而这里使用的标志位不同,GRALLOC_USAGE_HW_FB | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER,从之前Android SurfaceFlinger 学习之路(一)—-Android图形显示之HAL层Gralloc模块实现可以得知,FrameBufferSurface类使用的图形缓冲区是直接在硬件帧缓冲区分配的,并且它可以直接将这些图形缓冲区渲染到硬件帧缓冲区中去。要从硬件帧缓冲区中分配和渲染图形缓冲区,就必须要将HAL层中的Gralloc模块加载到当前的进程空间来,并且打开里面的gralloc设备和fb设备,其中,gralloc设备用来分配图形缓冲区,而fb设备用来渲染图形缓冲区。
还有一个注意点就是consumer的setDefaultMaxBufferCount函数,设置大小为NUM_FRAMEBUFFER_SURFACE_BUFFERS,为2。硬件帧缓冲区能够提供的图形缓冲区的个数等于2,这意味着Android系统可以使用双缓冲区技术来渲染系统的UI。
这里有个误区,上面说的双缓冲并不是指4.1之后的Triple Buffer。三缓冲是对于Producer这一侧的生产者来说,我们在Layer的onFirstRef函数中有设置过,在Android SurfaceFlinger 学习之路(六)—-SurfaceFlinger创建Surface中也分析过,回顾一下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27void Layer::onFirstRef() {
// Creates a custom BufferQueue for SurfaceFlingerConsumer to use
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
//BufferQueue创建图形缓冲区管理成员,我们以后分析图形缓冲区管理会讲到
BufferQueue::createBufferQueue(&producer, &consumer);
//mProducer 不为空了,赋值
mProducer = new MonitoredProducer(producer, mFlinger);
//mSurfaceFlingerConsumer 不为空了,赋值
mSurfaceFlingerConsumer = new SurfaceFlingerConsumer(consumer, mTextureName);
//设置消费者相关设置
mSurfaceFlingerConsumer->setConsumerUsageBits(getEffectiveUsage(0));
mSurfaceFlingerConsumer->setContentsChangedListener(this);
mSurfaceFlingerConsumer->setName(mName);
// TARGET_DISABLE_TRIPLE_BUFFERING为false,所以使用了三缓冲,project butter计划嘛
mSurfaceFlingerConsumer->setDefaultMaxBufferCount(2);
mSurfaceFlingerConsumer->setDefaultMaxBufferCount(3);
//获取默认显示器
const sp<const DisplayDevice> hw(mFlinger->getDefaultDisplayDevice());
//更新显示图像方向
updateTransformHint(hw);
}
根据“黄油计划”定义了三缓冲。因为Producer定义了三缓冲,为了避免junk现象,让屏幕始终有一块buffer拉去离屏渲染。所以在producer这一侧,一块给cpu调度,一块给gpu绘制,看看谁有空闲的送给显示屏。如果默认的cpu调度和gpu性能都没有问题,在一个vsync周期内都能完成各自工作,那么以前仅有两块buffer情况下都不会出问题,因为,等他们用完后,总会空出一块buffer送给显示屏。但是如果其中任何一个出了问题,占用时间周期超过了一个vsync,或者更严重,两个都拖堂了,那么显示屏就没有空闲buffer拉取了,就会有很严重的掉帧。所以从4.1之后又加了一块buffer在producer这一侧,保证出问题时候还有一块buffer送显屏幕,即使从时间周期的pipeline来看,就这一个出问题的帧多显示了一下,但是后续的都显示正常,虽然仅仅延时了一帧,但是整体不会像以前那样严重的连续掉下帧。
这就是triple buffer的简单概述,如果有机会,后面专门讲讲。如果我理解有问题,也欢迎大家指正。
因为对于屏幕显示器来说,上层app和SF都是属于producer生产者,而自己display才是消费者。屏幕是双缓冲显示,一块back buffer作为离屏渲染,一块front buffer作为在线显示,然后用完了又回到后台拉去producer送显的内容作为离屏渲染,同时之前的back buffer交换到前台作为front buffer在线显示,这样周而复始不挺循环。
这里扯的有点多,如果有误请轻喷,欢迎指出,我会在第一时间修改。
触发渲染
继续回到上面内容,由eglSwapBuffers触发queueBuffer,进而触发FramebufferSurface中的onFrameAvailable方法:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16void FramebufferSurface::onFrameAvailable() {
sp<GraphicBuffer> buf;
sp<Fence> acquireFence;
/*acquireBuffer,取得一块生产完成(OpenGL合成好)的Buffer*/
status_t err = nextBuffer(buf, acquireFence);
if (err != NO_ERROR) {
ALOGE("error latching nnext FramebufferSurface buffer: %s (%d)",
strerror(-err), err);
return;
}
/*最终调用 gralloc 模块中的 post方法,该此Buffer送显*/
err = mHwc.fbPost(mDisplayType, acquireFence, buf);
if (err != NO_ERROR) {
ALOGE("error posting framebuffer: %d", err);
}
}
我们先看看nextBuffer函数,取得一块生产完成(OpenGL合成好)的Buffer。(由于surfaceflinger是用opengl合成HWC_FRAMEBUFFER_TARGET layer的,所以有可能“合成”这个生产还未完毕):1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
status_t FramebufferSurface::nextBuffer(sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence) {
Mutex::Autolock lock(mMutex);
BufferQueue::BufferItem item;
//通过acquireBufferLocked获取BufferItem,其中的mBuf就是buffer了
//acquireBufferLocked由之前父类传入的BufferQueueConsumer调用
status_t err = acquireBufferLocked(&item, 0);
if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
outBuffer = mCurrentBuffer;
return NO_ERROR;
} else if (err != NO_ERROR) {
ALOGE("error acquiring buffer: %s (%d)", strerror(-err), err);
return err;
}
// If the BufferQueue has freed and reallocated a buffer in mCurrentSlot
// then we may have acquired the slot we already own. If we had released
// our current buffer before we call acquireBuffer then that release call
// would have returned STALE_BUFFER_SLOT, and we would have called
// freeBufferLocked on that slot. Because the buffer slot has already
// been overwritten with the new buffer all we have to do is skip the
// releaseBuffer call and we should be in the same state we'd be in if we
// had released the old buffer first.
//把老的buffer先release掉,还给BufferQueue,release时肯定得加入个release fence
//如果在acquireBuffer之前调用releaseBuffer,就会返回一个STALE_BUFFER_SLOT状态,就不得不调用freeBufferLocked释放掉那个过时的buffer回到slot中。
//为了避免获老的buffer被新的buffer内容覆盖,就要先acquire新的buffer然后release旧的buffer
if (mCurrentBufferSlot != BufferQueue::INVALID_BUFFER_SLOT &&
item.mBuf != mCurrentBufferSlot) {
// Release the previous buffer.
err = releaseBufferLocked(mCurrentBufferSlot, mCurrentBuffer,
EGL_NO_DISPLAY, EGL_NO_SYNC_KHR);
if (err < NO_ERROR) {
ALOGE("error releasing buffer: %s (%d)", strerror(-err), err);
return err;
}
}
mCurrentBufferSlot = item.mBuf;
mCurrentBuffer = mSlots[mCurrentBufferSlot].mGraphicBuffer;
outFence = item.mFence;
outBuffer = mCurrentBuffer;
return NO_ERROR;
}
获取一块合成好的buffer,就是之前HWC_FRAMEBUFFER标志的所有layer合成到HWC_FRAMEBUFFER_TARGET这个图层(有可能还没有合成好)。通过acquireBufferLocked获取BufferItem,其中的mBuf就是buffer了,acquireBufferLocked由之前父类传入的BufferQueueConsumer调用。
然后把老的buffer先release掉,还给BufferQueue,release时肯定得加入个release fence。如果在acquireBuffer之前调用releaseBuffer,就会返回一个STALE_BUFFER_SLOT状态,就不得不调用freeBufferLocked释放掉那个过时的buffer回到slot中。为了避免获老的buffer被新的buffer内容覆盖,就要先acquire新的buffer然后release旧的buffer。这个我们在Android SurfaceFlinger 学习之路(十)—-SurfaceFlinger处理Layer更新讲过。
继续回到剩下的内容,最终调用 gralloc 模块中的 post方法,该此Buffer送显。这一部分可以参考Android SurfaceFlinger 学习之路(一)—-Android图形显示之HAL层Gralloc模块实现,其中的图形缓冲区的渲染过程模块。
到这里万里长征终于快到尽头了,doDisplayComposition函数流程分析完了。合成和送显示差不多完成了,还剩下最后一点杂物。
硬件模块渲染过程
我们回到SF的doComposition函数,查看剩下内容:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 //清除屏幕脏区域
hw->dirtyRegion.clear();
//判断系统是否支持软件部分更新
hw->flip(hw->swapRegion);
//清除交换区域
hw->swapRegion.clear();
}
// inform the h/w that we're done compositing
//通知hwc硬件合成结束
hw->compositionComplete();
}
//主要是调用hwc硬件的set函数
//此方法将完成各个图层的合成与显示,等效于EGL标准里面的eglSwapBuffers,
//不过eglSwapBuffers是对OpenGL标准/GPU有效,此方法是对硬件合成器有效
postFramebuffer();
}
主要就三个功能:
1)判断系统是否支持软件部分更新;
2)通知hwc硬件合成结束;
3)调用hwc硬件的set函数,各个overlay图层的合成与显示。
老规矩,分步骤分析:
1)判断系统是否支持软件部分更新。调用了DisplayDevice的flip函数,参数时要交换的合成区域:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20void DisplayDevice::flip(const Region& dirty) const
{
mFlinger->getRenderEngine().checkErrors();
EGLDisplay dpy = mDisplay;
EGLSurface surface = mSurface;
#ifdef EGL_ANDROID_swap_rectangle
if (mFlags & SWAP_RECTANGLE) {
const Region newDirty(dirty.intersect(bounds()));
const Rect b(newDirty.getBounds());
eglSetSwapRectangleANDROID(dpy, surface,
b.left, b.top, b.width(), b.height());
}
#else
(void) dirty; // Eliminate unused parameter warning
#endif
mPageFlipCount++;
}
这段代码主要用来检查系统的主绘图表面是否支持EGL_ANDROID_swap_rectangle扩展属性。如果支持的话,那么每次在调用函数eglSwapBuffers来渲染UI时,都会使用软件的方式来支持部分更新区域功能,即:先得到不在新脏区域里面的那部分旧脏区域的内容,然后再将得到的这部分旧脏区域的内容拷贝回到要渲染的新图形缓冲区中去,这要求每次在渲染UI时,都要将被渲染的图形缓冲区以及对应的脏区域保存下来。
函数会首先判断系统的主绘图表面是否支持EGL_ANDROID_swap_rectangle扩展属性。如果支持EGL_ANDROID_swap_rectangle扩展属性,即DisplayDevice类的成员变量mFlags的SWAP_RECTANGLE位等于1,那么就需要调用函数eglSetSwapRectangleANDROID来设置要渲染的区域,以便在渲染UI时,可以通过软件的方式来支持部分更新。
不过从上一段选择部分更新的内容来看,是直接在硬件上支持部分更新,因而性能会更好。这一段软件的eglSetSwapRectangleANDROID就不看了曲线救国了,大清亡就亡了=。=
2)通知hwc硬件合成结束。go on:1
2
3status_t DisplayDevice::compositionComplete() const {
return mDisplaySurface->compositionComplete();
}
之前从SF的init函数和DisplayDevice的构造函数得知,mDisplaySurface是一个FrameBufferSurface对象,所以我们继续看看它的compositionComplete函数:1
2
3
4status_t FramebufferSurface::compositionComplete()
{
return mHwc.fbCompositionComplete();
}
它又调用了HWComposer的fbCompositionComplete函数:1
2
3
4
5
6
7
8
9
10
11int HWComposer::fbCompositionComplete() {
//如果支持HWC硬件1.1版本,就不用通知任何消息
if (mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1))
return NO_ERROR;
//否则通知FrameBuffer驱动FrameBufferTarget合成结束
if (mFbDev->compositionComplete) {
return mFbDev->compositionComplete(mFbDev);
} else {
return INVALID_OPERATION;
}
}
如果支持HWC硬件1.1版本,就不用通知任何消息,否则通知FrameBuffer驱动FrameBufferTarget合成结束。
3)调用hwc硬件的set函数,各个overlay图层的合成与显示。这一步是postFramebuffer函数内完成的,我们看看实现:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55void SurfaceFlinger::postFramebuffer()
{
ATRACE_CALL();
const nsecs_t now = systemTime();
mDebugInSwapBuffers = now;
HWComposer& hwc(getHwComposer());
if (hwc.initCheck() == NO_ERROR) {
if (!hwc.supportsFramebufferTarget()) {
// EGL spec says:
// "surface must be bound to the calling thread's current context,
// for the current rendering API."
getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext);
}
//调用HWComposer的commit函数
hwc.commit();
}
// make the default display current because the VirtualDisplayDevice code cannot
// deal with dequeueBuffer() being called outside of the composition loop; however
// the code below can call glFlush() which is allowed (and does in some case) call
// dequeueBuffer().
//为了虚拟显示屏做的规避
getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext);
for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
sp<const DisplayDevice> hw(mDisplays[dpy]);
const Vector< sp<Layer> >& currentLayers(hw->getVisibleLayersSortedByZ());
//完毕了framebuffertarget layer的swapbuffers。
hw->onSwapBuffersCompleted(hwc);
const size_t count = currentLayers.size();
int32_t id = hw->getHwcDisplayId();
if (id >=0 && hwc.initCheck() == NO_ERROR) {
HWComposer::LayerListIterator cur = hwc.begin(id);
const HWComposer::LayerListIterator end = hwc.end(id);
for (size_t i = 0; cur != end && i < count; ++i, ++cur) {
//最后回调每个Layer的onLayerDisplayed函数
currentLayers[i]->onLayerDisplayed(hw, &*cur);
}
} else {
for (size_t i = 0; i < count; i++) {
currentLayers[i]->onLayerDisplayed(hw, NULL);
}
}
}
mLastSwapBufferTime = systemTime() - now;
mDebugInSwapBuffers = 0;
uint32_t flipCount = getDefaultDisplayDevice()->getPageFlipCount();
if (flipCount % LOG_FRAME_STATS_PERIOD == 0) {
logFrameStats();
}
}
也是三部操作:1)调用调用HWComposer的commit函数完成OverLay合成;2)完毕了framebuffertarget layer的swapbuffers;3)最后回调每个Layer的onLayerDisplayed函数。
我们先看第一步:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39status_t HWComposer::commit() {
int err = NO_ERROR;
if (mHwc) {
if (!hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
// On version 1.0, the OpenGL ES target surface is communicated
// by the (dpy, sur) fields and we are guaranteed to have only
// a single display.
mLists[0]->dpy = eglGetCurrentDisplay();
mLists[0]->sur = eglGetCurrentSurface(EGL_DRAW);
}
for (size_t i=VIRTUAL_DISPLAY_ID_BASE; i<mNumDisplays; i++) {
DisplayData& disp(mDisplayData[i]);
if (disp.outbufHandle) {
mLists[i]->outbuf = disp.outbufHandle;
mLists[i]->outbufAcquireFenceFd =
disp.outbufAcquireFence->dup();
}
}
//主要是调用hwc硬件的set函数
//此方法将完成各个图层的合成与显示,等效于EGL标准里面的eglSwapBuffers,
//不过eglSwapBuffers是对OpenGL标准/GPU有效,此方法是对硬件合成器有效
err = mHwc->set(mHwc, mNumDisplays, mLists);
for (size_t i=0 ; i<mNumDisplays ; i++) {
DisplayData& disp(mDisplayData[i]);
disp.lastDisplayFence = disp.lastRetireFence;
disp.lastRetireFence = Fence::NO_FENCE;
if (disp.list) {
if (disp.list->retireFenceFd != -1) {
disp.lastRetireFence = new Fence(disp.list->retireFenceFd);
disp.list->retireFenceFd = -1;
}
disp.list->flags &= ~HWC_GEOMETRY_CHANGED;
}
}
}
return (status_t)err;
}
主要是调用hwc硬件的set函数,此方法将完成各个图层的合成与显示,等效于EGL标准里面的eglSwapBuffers,不过eglSwapBuffers是对OpenGL标准/GPU有效,此方法是对硬件合成器有效。
不过这是hwc硬件的实现,我们也看不到代码。感兴趣的同学可以搜一搜AOSP里qcom、boardcom、ti、intel、samsung等公司开源出来的实现。
后两部都是和Fence相关,这里不是重点:
第二步是完毕了framebuffertarget layer的swapbuffers,调用DisplayDevice的onSwapBuffersCompleted函数:1
2
3
4
5void DisplayDevice::onSwapBuffersCompleted(HWComposer& hwc) const {
if (hwc.initCheck() == NO_ERROR) {
mDisplaySurface->onFrameCommitted();
}
}
依然调用FrameBufferSurface的onFrameCommitted函数:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26void FramebufferSurface::onFrameCommitted() {
sp<Fence> fence = mHwc.getAndResetReleaseFence(mDisplayType);
if (fence->isValid() &&
mCurrentBufferSlot != BufferQueue::INVALID_BUFFER_SLOT) {
status_t err = addReleaseFence(mCurrentBufferSlot,
mCurrentBuffer, fence);
ALOGE_IF(err, "setReleaseFenceFd: failed to add the fence: %s (%d)",
strerror(-err), err);
}
}
sp<Fence> HWComposer::getAndResetReleaseFence(int32_t id) {
if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id))
return Fence::NO_FENCE;
int fd = INVALID_OPERATION;
if (mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
const DisplayData& disp(mDisplayData[id]);
// 这里的disp.framebufferTarget->releaseFenceFd应该就是底层hwcomposer设置的
if (disp.framebufferTarget) {
fd = disp.framebufferTarget->releaseFenceFd;
disp.framebufferTarget->acquireFenceFd = -1;
disp.framebufferTarget->releaseFenceFd = -1;
}
}
return fd >= 0 ? new Fence(fd) : Fence::NO_FENCE;
}
onFrameCommitted主要就是获取hwcomposer设置的release fence,然后设置到slot中。
第三部回调Layer的onLayerDisplayed函数:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29void Layer::onLayerDisplayed(const sp<const DisplayDevice>& hw,
HWComposer::HWCLayerInterface* layer) {
if (layer) {
//HWCLayerVersion1的onDisplayed函数,位于HWComposer.cpp中
layer->onDisplayed();
//将fence设置到slot中
mSurfaceFlingerConsumer->setReleaseFence(layer->getAndResetReleaseFence());
}
}
/*----------- HWCLayerVersion1 ----------*/
virtual sp<Fence> getAndResetReleaseFence() {
//获取layer的releaseFenceFd
int fd = getLayer()->releaseFenceFd;
getLayer()->releaseFenceFd = -1;
//new 一个Fence
return fd >= 0 ? new Fence(fd) : Fence::NO_FENCE;
}
virtual void onDisplayed() {
hwc_region_t& visibleRegion = getLayer()->visibleRegionScreen;
SharedBuffer const* sb = SharedBuffer::bufferFromData(visibleRegion.rects);
if (sb) {
sb->release();
// not technically needed but safer
visibleRegion.numRects = 0;
visibleRegion.rects = NULL;
}
getLayer()->acquireFenceFd = -1;
}
而对overlay相应的layer而言,前面仅仅设置了acquire fence,在hwcomposer HAL处理后肯定会给加入一个release fence,而这一部分代码我们看不到实现。
到此硬件模块渲染过程就分析完了。SF的doComposition函数的流程就结束了。
更新SW Vsync误差
最后一部分就是SF中handleMessageRefresh最后一部,postComposition。主要用于调试,调Layer的onPostComposition方法。我们看看实现:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72void SurfaceFlinger::postComposition()
{
const LayerVector& layers(mDrawingState.layersSortedByZ);
const size_t count = layers.size();
for (size_t i=0 ; i<count ; i++) {
//调Layer的onPostComposition方法
layers[i]->onPostComposition();
}
// 通过 HWComposer 获得 Fence
const HWComposer& hwc = getHwComposer();
sp<Fence> presentFence = hwc.getDisplayFence(HWC_DISPLAY_PRIMARY);
//注意,如果硬件vsync已经被打开了,那么fence是无效了,只有它在关闭的情况下,它才有效
//矫正更新Vsync,是否打开或关闭vsync信号
if (presentFence->isValid()) {
if (mPrimaryDispSync.addPresentFence(presentFence)) {
enableHardwareVsync();
} else {
disableHardwareVsync(false);
}
}
if (kIgnorePresentFences) {
const sp<const DisplayDevice> hw(getDefaultDisplayDevice());
if (hw->isDisplayOn()) {
enableHardwareVsync();
}
}
//动画相关
if (mAnimCompositionPending) {
mAnimCompositionPending = false;
if (presentFence->isValid()) {
mAnimFrameTracker.setActualPresentFence(presentFence);
} else {
// The HWC doesn't support present fences, so use the refresh
// timestamp instead.
nsecs_t presentTime = hwc.getRefreshTimestamp(HWC_DISPLAY_PRIMARY);
mAnimFrameTracker.setActualPresentTime(presentTime);
}
mAnimFrameTracker.advanceFrame();
}
}
/*--------Layer.cpp--------*/
void Layer::onPostComposition() {
if (mFrameLatencyNeeded) {
nsecs_t desiredPresentTime = mSurfaceFlingerConsumer->getTimestamp();
mFrameTracker.setDesiredPresentTime(desiredPresentTime);
sp<Fence> frameReadyFence = mSurfaceFlingerConsumer->getCurrentFence();
if (frameReadyFence->isValid()) {
mFrameTracker.setFrameReadyFence(frameReadyFence);
} else {
// There was no fence for this frame, so assume that it was ready
// to be presented at the desired present time.
mFrameTracker.setFrameReadyTime(desiredPresentTime);
}
const HWComposer& hwc = mFlinger->getHwComposer();
sp<Fence> presentFence = hwc.getDisplayFence(HWC_DISPLAY_PRIMARY);
if (presentFence->isValid()) {
mFrameTracker.setActualPresentFence(presentFence);
} else {
// The HWC doesn't support present fences, so use the refresh
// timestamp instead.
nsecs_t presentTime = hwc.getRefreshTimestamp(HWC_DISPLAY_PRIMARY);
mFrameTracker.setActualPresentTime(presentTime);
}
mFrameTracker.advanceFrame();
mFrameLatencyNeeded = false;
}
}
postComposition函数中比较重要的是更新SW Vsync的误差值。注意,如果硬件vsync已经被打开了,那么fence是无效了,只有它在关闭的情况下,它才有效。当更新SW Vsync模型后,就会关闭硬件Vsync信号,这时候Fence就有效了。所以我们进入第一个if条件内部,查看DispSync的addPresentFence函数:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29bool DispSync::addPresentFence(const sp<Fence>& fence) {
Mutex::Autolock lock(mMutex);
// 将当前硬件vsync的fence保存在 mPresentFences里, 目的是为了计算偏移
// mPresentFences 最多保存8个硬件 偏移
mPresentFences[mPresentSampleOffset] = fence;
mPresentTimes[mPresentSampleOffset] = 0;
mPresentSampleOffset = (mPresentSampleOffset + 1) % NUM_PRESENT_SAMPLES;
mNumResyncSamplesSincePresent = 0; // 将 mNumResyncSamplesSincePresent 置为0,
for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
const sp<Fence>& f(mPresentFences[i]);
if (f != NULL) { //这里 f 是有可能为NULL, 即只有一个 硬件 vsync 偏移时
nsecs_t t = f->getSignalTime(); //猜测这个就是硬件 vsync的时间
if (t < INT64_MAX) {
mPresentFences[i].clear();
//将每个vsync时间戳记录在 mPresentTimes 里,这里 kPresentTimeOffset是可以配置的,即可调的
mPresentTimes[i] = t + kPresentTimeOffset;
}
}
}
//更新错误信息
updateErrorLocked();
// 这里,一般的情况是 mModelUpdated 已经被更新了,然后硬件vsync被disable了,
// 所以这里只需要看SW vsync的真实的硬件vsync的误差是否在可
// 允许的范围内即可
return !mModelUpdated || mError > kErrorThreshold;
}
ddPresentFence最后的返回, mError是方差,见下面分析,当方差大于 kErrorThreshold后就返回true:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40void DispSync::updateErrorLocked() {
if (!mModelUpdated) {
return;
}
// Need to compare present fences against the un-adjusted refresh period,
// since they might arrive between two events.
//得到真实的 period, 具体见 5.2.4 updateModelLocked 里的分析
nsecs_t period = mPeriod / (1 + mRefreshSkipCount);
int numErrSamples = 0;
nsecs_t sqErrSum = 0;
//这里的 mReferenceTime 是第一个硬件vsync的时间戳 见 addResyncSample里的 mReferenceTime
for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
nsecs_t sample = mPresentTimes[i] - mReferenceTime;
// 这里 sample 一般来说是大于偏移的
if (sample > mPhase) {
nsecs_t sampleErr = (sample - mPhase) % period;
if (sampleErr > period / 2) {
sampleErr -= period;
}
//记录 偏移差的平方和
sqErrSum += sampleErr * sampleErr;
numErrSamples++;
}
}
// 说到底mError就是方差
if (numErrSamples > 0) {
mError = sqErrSum / numErrSamples;
} else {
mError = 0;
}
if (kTraceDetailedInfo) {
ATRACE_INT64("DispSync:Error", mError);
}
}
如果 addPresentFence返回true, 那么就说明SW vsync和硬件Vsync的误差已经无法接受了,那么这时就得重新打开硬件Vsync,来重新调节SW vsync模型了。
合成全部流程到此结束,打完收工~~
小结
合成流程中比较重要的就是Layer与纹理,我们贴两幅图,一张是GraphicBuffer上传为纹理,一张为更新纹理:
GraphicBuffer上传为纹理
更新纹理
合成流程是整个SurfaceFlinger中最重要的环节,这个大山我们推到之后,对于Android显示系统的理解豁然开朗。后面我们有机会继续研究研究其他内容,比如Fence相关的东西(可能没有时间=。=)
细数2017年每一件事,真是事事不如意。这一年,也是我整个人生中最动荡的一年,尝遍酸甜苦辣,受尽世态炎凉ToT~
希望2018年能对我好一点。。。。。。