本节我们学习一下上层app创建Surface到底层Surface管理图形缓冲区的简要过程,详细的从Activity到WMS最后至底层SurfaceFlinger消费surface我们以后再分析。
上层概述
Surface概述
Surface也是个很复杂的系统,通过之前的学习,我们知道Surface相关的内容主要有两点:
- 应用程序和Surface的关系
- Surface和SurfaceFlinger之间的关系
通过之前对SurfaceFlinger系统的概述,大概用如下图表示:
(右图合成不止有OpenGL,还有HWC硬件,这个我们图中没有画出)
先看左图。可以发现,不论是使用Skia绘制二维图像,还是用OpenGL绘制三维图像,最终Application都要和Surface交互。Surface就像是UI的画布,而App则像是在Surface上作画。
再看右图。Surface和SurfaceFlinger的关系,Surface向SurfaceFlinger提供数据,而SurfaceFlinger则混合数据。其实就是调用OpenGL ES库中的EGL去合成(或者HWC硬件),然后在FrameBuffer上渲染。
简述Activity显示
(这一部分我们以后会详细分析,这里因为后面分析surface需要用到,所以简单提一下)
一般来说,应用程序的外表是通过Activity来展示的。那么,Activity是如何完成界面绘制工作的呢?根据前面所讲的知识,应用程序的显示和Surface有关,那么具体到Activity上,它和Surface又是什么关系呢?因为我们只是提一下,所以用两幅巨图来表示:
Android 应用程序是怎么创建出来的,大概的流程是 ActivityManagerService -> Zygote -> Fork App,。一个新的应用被fork完后,第一个调用的方法就是 ActivityThread的main(),这个函数主要做的事情就是创建一个ActivityThread线程,然后调用loop()开始等待。当收到来自 ActivityManager 的 LAUNCH_ACTIVITY 消息后,Activity开始了他的显示之旅。下图描绘的是Activity在显示前的准备流程:
我们可以说,一个应用可以有多个Activity,每个 Activity 一个Window(PhoneWindow), 每个Window 有一个DecorView, 一个ViewRootImpl, 对应在WindowManagerService 里有一个Window(WindowState)。我们再看另一幅巨图:
ViewRootImpl 在整个Android的GUI系统中占据非常重要的位置,如果把Activity和View 看作 ‘MVC’ 中的V, 把各种后台服务看作Modal,ViewRootImpl 则是’MVC’ 中的’C’ - Controller. Controller在MVC架构里承担着承上启下的作用,一般来说它的逻辑最为复杂。从下图可以看到,ViewRootImpl 与 用户输入系统(接收用户按键,触摸屏输入), 窗口系统(复杂窗口的布局,刷新,动画),显示合成系统(包括定时器Choreograph, SurfaceFlinger), 乃至Audio系统(音效输出)等均有密切的关联。
关于创建一Surface的简要时序图可以如下表示:
应用层创建Surface上面时序图已经列出了,即:ViewRootImpl通过Session向WindowMangerService简历通信回话,然后请求WindowManagerService调用relayoutWindow方法,在方法内部通过WindowStateAnimator创建一个SurfaceControl,然后将ViewRootImpl中的无参构造的Surface通过调用copyFrom在JNI层创建一个Surface,并返回句柄给java层。(只是简述,后面章节在分析)相关方法如下: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/*WindowManagerService的relayoutWindow方法*/
public int relayoutWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int requestedWidth,
int requestedHeight, int viewVisibility, int flags,
Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
Rect outVisibleInsets, Rect outStableInsets, Configuration outConfig,
Surface outSurface) {
//这个最后的参数outSurface就是ViewRootImpl的无参构造方法创建的对象
//private final Surface mSurface = new Surface();
......
//调用WindowStateAnimator的createSurfaceLocked方法创建一个SurfaceControl
SurfaceControl surfaceControl = winAnimator.createSurfaceLocked();
if (surfaceControl != null) {
//调用Surface的copyFrom方法,在JNI层构造一个Surface
outSurface.copyFrom(surfaceControl);
if (SHOW_TRANSACTIONS) Slog.i(TAG,
" OUT SURFACE " + outSurface + ": copied");
} else {
// For some reason there isn't a surface. Clear the
// caller's object so they see the same state.
outSurface.release();
}
......
}
SurfaceControl createSurfaceLocked() {
...省略其他步骤...
//这个构造方法第一个参数SurfaceSession就是SurfaceComposerClient在java层的代表
mSurfaceControl = new SurfaceControl(
mSession.mSurfaceSession,
attrs.getTitle().toString(),
width, height, format, flags);
...省略其他步骤...
return mSurfaceControl;
}
我们从Android SurfaceFlinger 学习之路(六)—-SurfaceFlinger创建Surface得知应用层创建Surface对应在SurfaceFlinger这边就是一个Layer,而这个Layer是由SurfaceFlinger创建的。所以我们上面创建java层Surface也需要拿到SurfaceFlinger服务的客户端代理才行。这个代理就是SurfaceSession类对象,mSession.mSurfaceSession。
它的创建是构造ViewRootImpl时候,调用WindowManagerGlobal的getWindowSession方法,通过WMS的代理创建openSession创建一个Session回话。然后ViewRootImpl的setView方法,方法内通过WMS代理调用WMS的addWindow方法,里面利用Session对象调用windowAddedLocked方法创建一个SurfaceSession。这就是它的流程,我们仅仅简单描述。
上面就是Activity创建Surface的java层简要过程。
关于绘制这个流程很复杂,我们后续章节在分析。这里我们因为要分析Surface机制,所以只分析ViewRootImpl的draw流程。(如果开启了硬件加速功能,则会使用hwui硬件绘制功能,这里我们忽略这个,使用默认的软件绘制流程drawSoftware)位于frameworks/base/core/java/android/view/ViewRootImpl.java:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private final Surface mSurface = new Surface();
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty) {
// Draw with software renderer.
final Canvas canvas;
try {
final int left = dirty.left;
final int top = dirty.top;
final int right = dirty.right;
final int bottom = dirty.bottom;
canvas = mSurface.lockCanvas(dirty);
......
mView.draw(canvas);
......
surface.unlockCanvasAndPost(canvas);
}
return true;
}
我们核心就是这三行代码,这里中间的mView.draw(canvas);这个是调用skia图形库的绘制流程。我们暂时不分析,因为我们不关注这个绘制流程。
我们主要分析canvas = mSurface.lockCanvas(dirty);和surface.unlockCanvasAndPost(canvas);的流程。这两个关系到我们下面的图形缓冲区的管理。
Surface的JNI调用
我们分别分析Surface的构建还有lockCanvas还有unlockCanvasAndPost方法。
Surface的构造
其实看到上面代码,让我们想起来java层构建Surface和在Android SurfaceFlinger 学习之路(六)—-SurfaceFlinger创建Surface分析的native层创建Surface流程基本一样,也是先创建一个SurfaceControl,然后利用SurfaceControl创建Surface。我们分部查看这个过程。
1)因为创建SurfaceControl,在native层需要SurfaceFlinger服务,所以我们看看java层如何获取SurfaceFlinger服务。所以我们看看上面讲到的创建SurfaceSession,位于frameworks/base/core/java/android/view/SurfaceSession.java:1
2
3
4
5
6
7
8
// Note: This field is accessed by native code.
private long mNativeClient; // SurfaceComposerClient*
private static native long nativeCreate();
/** Create a new connection with the surface flinger. */
public SurfaceSession() {
mNativeClient = nativeCreate();
}
对于JNI层位于frameworks/base/core/jni/android_view_SurfaceSession.cpp:1
2
3
4
5
6static jlong nativeCreate(JNIEnv* env, jclass clazz) {
//SurfaceComposerClient专门用来和surfaceflinger建立connection(ISurfaceComposerClient)
SurfaceComposerClient* client = new SurfaceComposerClient();
client->incStrong((void*)nativeCreate);
return reinterpret_cast<jlong>(client);
}
SurfaceComposerClient专门用来和surfaceflinger建立connection(ISurfaceComposerClient) ,SurfaceComposerClient是client,而surface flinger中的server为Client,SurfaceSession就是SurfaceComposerClient在java层的代表,其mNativeClient 成员就是native层的SurfaceComposerClient对象的指针。这一部分可以查看Android SurfaceFlinger 学习之路(四)—-SurfaceFlinger服务的启动与连接过程。
2)然后我们该看看java层SurfaceControl的创建了,位于frameworks/native/libs/gui/SurfaceControl.java:1
2
3
4
5
6
7
8
9
10
11
12
13
14public SurfaceControl(SurfaceSession session,
String name, int w, int h, int format, int flags)
throws OutOfResourcesException {
......
mName = name;
//调用nativeCreate本地方法
mNativeObject = nativeCreate(session, name, w, h, format, flags);
......
}
private final String mName;
long mNativeObject; // package visibility only for Surface.java access
private static native long nativeCreate(SurfaceSession session, String name,
int w, int h, int format, int flags)
throws OutOfResourcesException;
nativeCreate方法对应于JNI层位于frameworks/base/core/jni/android_view_SurfaceControl.cpp:1
2
3
4
5
6
7
8
9
10
11
12
13static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj,
jstring nameStr, jint w, jint h, jint format, jint flags) {
ScopedUtfChars name(env, nameStr);
sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj));
sp<SurfaceControl> surface = client->createSurface(
String8(name.c_str()), w, h, format, flags);
if (surface == NULL) {
jniThrowException(env, OutOfResourcesException, NULL);
return 0;
}
surface->incStrong((void *)nativeCreate);
return reinterpret_cast<jlong>(surface.get());
}
这里在JNI层调用上面创建的SurfaceComposerClient对象的createSurface函数,请求SurfaceFlinger去创建一个Layer,然后将这些信息封装成一个SurfaceControl。这一部分我们之前讲过,可以参考Android SurfaceFlinger 学习之路(六)—-SurfaceFlinger创建Surface。
3)最后就是Surface的copyFrom方法,从SurfaceControl获取Surface信息。位于frameworks/base/core/java/android/view/Surface.java:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public void copyFrom(SurfaceControl other) {
if (other == null) {
throw new IllegalArgumentException("other must not be null");
}
long surfaceControlPtr = other.mNativeObject;
if (surfaceControlPtr == 0) {
throw new NullPointerException(
"SurfaceControl native object is null. Are you using a released SurfaceControl?");
}
long newNativeObject = nativeCreateFromSurfaceControl(surfaceControlPtr);
synchronized (mLock) {
if (mNativeObject != 0) {
nativeRelease(mNativeObject);
}
setNativeObjectLocked(newNativeObject);
}
}
对应的JNI层调用位于frameworks/base/core/jni/android_view_Surface.cpp:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16static jlong nativeCreateFromSurfaceControl(JNIEnv* env, jclass clazz,
jlong surfaceControlNativeObj) {
/*
* This is used by the WindowManagerService just after constructing
* a Surface and is necessary for returning the Surface reference to
* the caller. At this point, we should only have a SurfaceControl.
*/
sp<SurfaceControl> ctrl(reinterpret_cast<SurfaceControl *>(surfaceControlNativeObj));
//调用SurfaceControl的getSurface函数
sp<Surface> surface(ctrl->getSurface());
if (surface != NULL) {
surface->incStrong(&sRefBaseOwner);
}
return reinterpret_cast<jlong>(surface.get());
}
先从java层保存c++层SurfaceControl的long型句柄,获取C++层SurfaceControl,然后调用其getSurface函数创建一个Surface,最后将这个Surface的句柄通过JNI回传给java层保存。这个getSurface函数我们以前讲过,可以查看Android SurfaceFlinger 学习之路(六)—-SurfaceFlinger创建Surface。
至此我们的Surface的创建就完成了。
lockCanvas
我们接着回到ViewRootImpl的drawSoftware方法,按步骤来,先卡看Surface的lockCanvas方法,位于frameworks/base/core/java/android/view/Surface.java:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18//mCanvas 变量直接赋值
private final Canvas mCanvas = new CompatibleCanvas();
public Canvas lockCanvas(Rect inOutDirty)
throws Surface.OutOfResourcesException, IllegalArgumentException {
synchronized (mLock) {
checkNotReleasedLocked();
if (mLockedObject != 0) {
// Ideally, nativeLockCanvas() would throw in this situation and prevent the
// double-lock, but that won't happen if mNativeObject was updated. We can't
// abandon the old mLockedObject because it might still be in use, so instead
// we just refuse to re-lock the Surface.
throw new IllegalArgumentException("Surface was already locked");
}
mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);
return mCanvas;
}
}
老路子,继续查看JNI代码,位于frameworks/base/core/jni/android_view_Surface.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
81static jlong nativeLockCanvas(JNIEnv* env, jclass clazz,
jlong nativeObject, jobject canvasObj, jobject dirtyRectObj) {
//获取java层的Surface保存的long型句柄
sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
if (!isSurfaceValid(surface)) {
doThrowIAE(env);
return 0;
}
Rect dirtyRect;
Rect* dirtyRectPtr = NULL;
//获取java层dirty Rect的位置大小信息
if (dirtyRectObj) {
dirtyRect.left = env->GetIntField(dirtyRectObj, gRectClassInfo.left);
dirtyRect.top = env->GetIntField(dirtyRectObj, gRectClassInfo.top);
dirtyRect.right = env->GetIntField(dirtyRectObj, gRectClassInfo.right);
dirtyRect.bottom = env->GetIntField(dirtyRectObj, gRectClassInfo.bottom);
dirtyRectPtr = &dirtyRect;
}
ANativeWindow_Buffer outBuffer;
//调用Surface的lock方法,将申请的图形缓冲区赋给outBuffer
status_t err = surface->lock(&outBuffer, dirtyRectPtr);
if (err < 0) {
const char* const exception = (err == NO_MEMORY) ?
OutOfResourcesException :
"java/lang/IllegalArgumentException";
jniThrowException(env, exception, NULL);
return 0;
}
// Associate a SkCanvas object to this surface
//给java层的Canvas对象的mSurfaceFormat变量赋值。
//Java的Surface对象构造的时候会创建一个CompatibleCanvas
env->SetIntField(canvasObj, gCanvasClassInfo.mSurfaceFormat, outBuffer.format);
//创建一个SkImageInfo ,用于下面创建SkBitmap
SkImageInfo info = SkImageInfo::Make(outBuffer.width, outBuffer.height,
convertPixelFormat(outBuffer.format),
kPremul_SkAlphaType);
if (outBuffer.format == PIXEL_FORMAT_RGBX_8888) {
info.fAlphaType = kOpaque_SkAlphaType;
}
SkBitmap bitmap;//创建一个SkBitmap
//图形缓冲区每一行像素大小
ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
然后填充SkBitmap相关变量
bitmap.setInfo(info, bpr);
if (outBuffer.width > 0 && outBuffer.height > 0) {
//outBuffer.bits指向一块存储区域
bitmap.setPixels(outBuffer.bits);
} else {
// be safe with an empty bitmap.
bitmap.setPixels(NULL);
}
//调用Java层Canvas的setNativeBitmap方法,保存JNI层构建的SkBitmap
env->CallVoidMethod(canvasObj, gCanvasClassInfo.setNativeBitmap,
reinterpret_cast<jlong>(&bitmap));
if (dirtyRectPtr) {
//剪裁出dirty区域大小
SkCanvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);
nativeCanvas->clipRect( SkRect::Make(reinterpret_cast<const SkIRect&>(dirtyRect)) );
}
if (dirtyRectObj) {
//将剪裁位置大小信息赋给java层Canvas对象
env->SetIntField(dirtyRectObj, gRectClassInfo.left, dirtyRect.left);
env->SetIntField(dirtyRectObj, gRectClassInfo.top, dirtyRect.top);
env->SetIntField(dirtyRectObj, gRectClassInfo.right, dirtyRect.right);
env->SetIntField(dirtyRectObj, gRectClassInfo.bottom, dirtyRect.bottom);
}
// Create another reference to the surface and return it. This reference
// should be passed to nativeUnlockCanvasAndPost in place of mNativeObject,
// because the latter could be replaced while the surface is locked.
sp<Surface> lockedSurface(surface);
lockedSurface->incStrong(&sRefBaseOwner);
return (jlong) lockedSurface.get();
}
这段代码逻辑主要如下:
1)获取java层dirty 的Rect大小和位置信息;
2)调用Surface的lock方法,将申请的图形缓冲区赋给outBuffer;
3)创建一个Skbitmap,填充它用来保存申请的图形缓冲区,并赋值给Java层的Canvas对象;
4)将剪裁位置大小信息赋给java层Canvas对象。
我们关注的核心就是在第二步申请图形缓冲区,这个我们下面会分析到。
unlockCanvasAndPost
我们跳过draw过程,不分析这个,所以到了最后一步,Surface绘制完毕后,unlockCanvasAndPost操作。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24public void unlockCanvasAndPost(Canvas canvas) {
if (canvas != mCanvas) {
throw new IllegalArgumentException("canvas object must be the same instance that "
+ "was previously returned by lockCanvas");
}
synchronized (mLock) {
checkNotReleasedLocked();
if (mNativeObject != mLockedObject) {
Log.w(TAG, "WARNING: Surface's mNativeObject (0x" +
Long.toHexString(mNativeObject) + ") != mLockedObject (0x" +
Long.toHexString(mLockedObject) +")");
}
if (mLockedObject == 0) {
throw new IllegalStateException("Surface was not locked");
}
try {
nativeUnlockCanvasAndPost(mLockedObject, canvas);
} finally {
nativeRelease(mLockedObject);
mLockedObject = 0;
}
}
}
依然查看JNI层代码,位于frameworks/base/core/jni/android_view_Surface.cpp:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19static void nativeUnlockCanvasAndPost(JNIEnv* env, jclass clazz,
jlong nativeObject, jobject canvasObj) {
//获取java层的Surface保存的long型句柄
sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
if (!isSurfaceValid(surface)) {
return;
}
// detach the canvas from the surface
//接触java层对native层SkBitmap的引用
env->CallVoidMethod(canvasObj, gCanvasClassInfo.setNativeBitmap, (jlong)0);
// unlock surface
//调用native层Surface的unlockAndPost函数
status_t err = surface->unlockAndPost();
if (err < 0) {
doThrowIAE(env);
}
}
这个代码依然很简单,核心就是调用native层Surface的unlockAndPost函数。我们接下来会分析。
综上这个流程大概是如下图:
Surface管理图形缓冲区
Surface申请图形缓冲区
我们上边分析到了申请图形缓冲区,用到了Surface的lock函数,我们继续查看,位于frameworks/native/libs/gui/Surface.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
106
107
108
109
110
111
112
113
114
115status_t Surface::lock(
ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds)
{
if (mLockedBuffer != 0) {//还没有lock
ALOGE("Surface::lock failed, already locked");
return INVALID_OPERATION;
}
if (!mConnectedToCpu) {//false
//调用connect函数完成一些初始化
int err = Surface::connect(NATIVE_WINDOW_API_CPU);
if (err) {
return err;
}
// we're intending to do software rendering from this point
//设置GRALLOC_USAGE_SW_READ_OFTEN 和GRALLOC_USAGE_SW_WRITE_OFTEN标志位,
//表示打算使用软件渲染
setUsage(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
}
ANativeWindowBuffer* out;
int fenceFd = -1;
//调用dequeueBuffer函数,申请图形缓冲区
status_t err = dequeueBuffer(&out, &fenceFd);
ALOGE_IF(err, "dequeueBuffer failed (%s)", strerror(-err));
if (err == NO_ERROR) {
//获取图形缓冲区区域大小,赋给后备缓冲区变量backBuffer
sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out));
const Rect bounds(backBuffer->width, backBuffer->height);
Region newDirtyRegion;
if (inOutDirtyBounds) {
//如果上层指定乐刷新脏矩形区域,则用这个区域和缓冲区区域求交集,
//然后将交集的结果设给需要去刷新的新区域
newDirtyRegion.set(static_cast<Rect const&>(*inOutDirtyBounds));
//与运算,求交集
newDirtyRegion.andSelf(bounds);
} else {
//如果上层没有指定脏矩形区域,所以刷新整个图形缓冲区
newDirtyRegion.set(bounds);
}
// figure out if we can copy the frontbuffer back
//上一次绘制的信息保存在mPostedBuffer中,而这个mPostedBuffer则要在unLockAndPost函数中设置
const sp<GraphicBuffer>& frontBuffer(mPostedBuffer);
const bool canCopyBack = (frontBuffer != 0 &&
backBuffer->width == frontBuffer->width &&
backBuffer->height == frontBuffer->height &&
backBuffer->format == frontBuffer->format);
if (canCopyBack) {//如果后备缓冲区大小和上次绘制缓冲区大小一样,则不用更新整个buffer
// copy the area that is invalid and not repainted this round
//将需要更新的dirty区域剪裁出来,剩余的不需要更新的区域我们不用重绘
//附注:mDirtyRegion变量命名在老版本Android是mOldDirtyRegion,指的是上一次绘制的dirty区域
const Region copyback(mDirtyRegion.subtract(newDirtyRegion));
if (!copyback.isEmpty())
//这里把mPostedBuffer中的旧数据拷贝到BackBuffer中。
//后续的绘画只要更新脏区域就可以了,这会节约不少资源
copyBlt(backBuffer, frontBuffer, copyback);
} else {
// if we can't copy-back anything, modify the user's dirty
// region to make sure they redraw the whole buffer
//如果两次图形缓冲区大小不一致,我们就要修改用户指定的dirty区域大小为整个缓冲区大小,
//然后去更新整个缓冲区
newDirtyRegion.set(bounds);
mDirtyRegion.clear();
Mutex::Autolock lock(mMutex);
for (size_t i=0 ; i<NUM_BUFFER_SLOTS ; i++) {
mSlots[i].dirtyRegion.clear();
}
}
{ // scope for the lock
Mutex::Autolock lock(mMutex);
//得到此次后备缓冲器的bufferSlot在mSlots里面的index
int backBufferSlot(getSlotFromBufferLocked(backBuffer.get()));
if (backBufferSlot >= 0) {
//将新的dirty赋给这个bufferslot
Region& dirtyRegion(mSlots[backBufferSlot].dirtyRegion);
mDirtyRegion.subtract(dirtyRegion);
dirtyRegion = newDirtyRegion;
}
}
//然后再加上新的dirty区域,或运算,相叠加
mDirtyRegion.orSelf(newDirtyRegion);
if (inOutDirtyBounds) {
*inOutDirtyBounds = newDirtyRegion.getBounds();
}
void* vaddr;
//lock和unlock分别用来锁定和解锁一个指定的图形缓冲区,在访问一块图形缓冲区的时候,
//例如,向一块图形缓冲写入内容的时候,需要将该图形缓冲区锁定,用来避免访问冲突,
//锁定之后,就可以获得由参数参数l、t、w和h所圈定的一块缓冲区的起始地址,保存在输出参数vaddr中
status_t res = backBuffer->lockAsync(
GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
newDirtyRegion.bounds(), &vaddr, fenceFd);
ALOGW_IF(res, "failed locking buffer (handle = %p)",
backBuffer->handle);
if (res != 0) {
err = INVALID_OPERATION;
} else {
//相关赋值
mLockedBuffer = backBuffer;
outBuffer->width = backBuffer->width;
outBuffer->height = backBuffer->height;
outBuffer->stride = backBuffer->stride;
outBuffer->format = backBuffer->format;
outBuffer->bits = vaddr;
}
}
return err;
}
Surface的lock函数用来申请图形缓冲区和一些操作,方法不长,大概工作有:
1)调用connect函数完成一些初始化;
2)调用dequeueBuffer函数,申请图形缓冲区;
3)计算需要绘制的新的dirty区域,旧的区域原样copy数据。
在大部分情况下,UI只有一小部分会发生变化(例如一个按钮被按下去,导致颜色发生变化),这一小部分UI只对应整个GraphicBuffer中的一小块存储(就是在前面代码中见到的dirtyRegion),如果整块存储都更新,则会极大地浪费资源。怎么办?
这就需要将变化的图像和没有发生变化的图像进行叠加。上一次绘制的信息保存在mPostedBuffer中,而这个mPostedBuffer则要在unLockAndPost函数中设置。这里将根据需要,把mPostedBuffer中的旧数据拷贝到BackBuffer中。后续的绘画只要更新脏区域就可以了,这会节约不少资源。
4)lock和unlock分别用来锁定和解锁一个指定的图形缓冲区,在访问一块图形缓冲区的时候,例如,向一块图形缓冲写入内容的时候,需要将该图形缓冲区锁定,用来避免访问冲突,锁定之后,就可以获得由参数参数l、t、w和h所圈定的一块缓冲区的起始地址,保存在输出参数vaddr中。另一方面,在访问完成一块图形缓冲区之后,需要解除这块图形缓冲区的锁定。
其他步骤都已经在注释里说明了,我们主要看看那第二步,dequeueBuffer函数: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
72int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
ATRACE_CALL();
ALOGV("Surface::dequeueBuffer");
int reqW;
int reqH;
bool swapIntervalZero;
uint32_t reqFormat;
uint32_t reqUsage;
{
Mutex::Autolock lock(mMutex);
reqW = mReqWidth ? mReqWidth : mUserWidth;
reqH = mReqHeight ? mReqHeight : mUserHeight;
swapIntervalZero = mSwapIntervalZero;
reqFormat = mReqFormat;
reqUsage = mReqUsage;
} // Drop the lock so that we can still touch the Surface while blocking in IGBP::dequeueBuffer
int buf = -1;
sp<Fence> fence;
//申请图形缓冲区,我们上一节分析过这个过程
status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, swapIntervalZero,
reqW, reqH, reqFormat, reqUsage);
if (result < 0) {
ALOGV("dequeueBuffer: IGraphicBufferProducer::dequeueBuffer(%d, %d, %d, %d, %d)"
"failed: %d", swapIntervalZero, reqW, reqH, reqFormat, reqUsage,
result);
return result;
}
Mutex::Autolock lock(mMutex);
//根据index获取缓冲区
sp<GraphicBuffer>& gbuf(mSlots[buf].buffer);
// this should never happen
ALOGE_IF(fence == NULL, "Surface::dequeueBuffer: received null Fence! buf=%d", buf);
//上一篇分析过,如果所有状态buffer的个数大于64,就要释放所有超过64的buffer,并贴上RELEASE_ALL_BUFFERS标志位
if (result & IGraphicBufferProducer::RELEASE_ALL_BUFFERS) {
freeAllBuffers();
}
//增加BUFFER_NEEDS_REALLOCATION标志,已重新分配GraphicBuffer
if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) {
//由于申请的内存是在surfaceflinger进程中,
//BufferQueue中的图形缓冲区也是通过匿名共享内存和binder传递描述符映射过去的,
//Surface通过调用requestBuffer将图形缓冲区映射到Surface所在进程
result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);
if (result != NO_ERROR) {
ALOGE("dequeueBuffer: IGraphicBufferProducer::requestBuffer failed: %d", result);
mGraphicBufferProducer->cancelBuffer(buf, fence);
return result;
}
}
if (fence->isValid()) {
*fenceFd = fence->dup();
if (*fenceFd == -1) {
ALOGE("dequeueBuffer: error duping fence: %d", errno);
// dup() should never fail; something is badly wrong. Soldier on
// and hope for the best; the worst that should happen is some
// visible corruption that lasts until the next frame.
}
} else {
*fenceFd = -1;
}
//获取这个这个buffer对象的指针内容
*buffer = gbuf.get();
return OK;
}
首先调用BufferQueueProducer的dequeueBuffer函数申请一块图形缓冲区,这个我们上一节讲过,Android SurfaceFlinger 学习之路(七)—-创建图形缓冲区GraphicBuffer。
先在mSlot数组中查找FREE状态的slot,如果找到了就返回这个slot中的index。这个操作是调用了waitForFreeSlotThenRelock函数。如果没有找到,就要从匿名共享内存中重新分配。重新分配返回的flag会加上BUFFER_NEEDS_REALLOCATION标志位。
如果是在匿名共享内存中重新分配的,就要根据index调用BufferQueueProducer的requestBuffer去获取mSlots数组中的bufferSlot,然后取出slot中的buffer。
最后就是将取到的Buffer返回。
我们上一篇没有分析BufferQueueProducer的requestBuffer,也是通过IPC Binder调用远程接口,省去这个过程,直接看实现,位于frameworks/native/libs/gui/BufferQueueProducer.cpp:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
ATRACE_CALL();
BQ_LOGV("requestBuffer: slot %d", slot);
Mutex::Autolock lock(mCore->mMutex);
if (mCore->mIsAbandoned) {
BQ_LOGE("requestBuffer: BufferQueue has been abandoned");
return NO_INIT;
}
//check index and state
if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
BQ_LOGE("requestBuffer: slot index %d out of range [0, %d)",
slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
return BAD_VALUE;
} else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) {
BQ_LOGE("requestBuffer: slot %d is not owned by the producer "
"(state = %d)", slot, mSlots[slot].mBufferState);
return BAD_VALUE;
}
//将slot的mRequestBufferCalled 标志位置为true,并返回指定index的slot的Graphicbuffer
mSlots[slot].mRequestBufferCalled = true;
*buf = mSlots[slot].mGraphicBuffer;
return NO_ERROR;
}
这个比较简单,还是很好理解的额,就是根据指定index取出mSlots中的slot中的buffer。
匿名共享内存 OR 帧缓冲区
我们上边在注释中说了,申请的图形缓冲区是在匿名共享内存中申请的,不是直接在帧缓冲区。这个是有原因,我们下面分析一下。
之前在Android SurfaceFlinger 学习之路(一)—-Android图形显示之HAL层Gralloc模块实现中我们讲过:
图形缓冲区可以从系统帧缓冲区分配也可以从内存中分配,分配一个图形缓冲区后还需要将该图形缓冲区映射到分配该buffer的进程地址空间来,在Android系统中,图形缓冲区的管理由SurfaceFlinger服务来负责。在系统帧缓冲区中分配的图形缓冲区是在SurfaceFlinger服务中使用,而在内存中分配的图形缓冲区既可以在SurfaceFlinger服务中使用,也可以在其它的应用程序中使用。当其它的应用程序需要使用图形缓冲区的时候,它们就会请求SurfaceFlinger服务为它们分配并将SurfaceFlinger服务返回来的图形缓冲区映射到应用程序进程地址空间。在从内存中分配buffer时,已经将分配的buffer映射到了SurfaceFlinger服务进程地址空间,如果该buffer是应用程序请求SurfaceFlinger服务为它们分配的,那么还需要将SurfaceFlinger服务返回来的图形缓冲区映射到应用程序进程地址空间。
我们这里是应用请求SurfaceFlinger去分配图形缓冲,在匿名共享内存当中。我们要去验证一下。
首先在Android SurfaceFlinger 学习之路(一)—-Android图形显示之HAL层Gralloc模块实现中讲到,用户空间的应用程序用到的图形缓冲区是由Gralloc模块中的函数gralloc_alloc来分配的,这个函数实现在文件hardware/libhardware/modules/gralloc/gralloc.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
45static int gralloc_alloc(alloc_device_t* dev,
int w, int h, int format, int usage,
buffer_handle_t* pHandle, int* pStride)
{
if (!pHandle || !pStride)
return -EINVAL;
size_t size, stride;
int align = 4;
int bpp = 0;
switch (format) {
case HAL_PIXEL_FORMAT_RGBA_8888:
case HAL_PIXEL_FORMAT_RGBX_8888:
case HAL_PIXEL_FORMAT_BGRA_8888:
bpp = 4;
break;
case HAL_PIXEL_FORMAT_RGB_888:
bpp = 3;
break;
case HAL_PIXEL_FORMAT_RGB_565:
case HAL_PIXEL_FORMAT_RAW_SENSOR:
bpp = 2;
break;
default:
return -EINVAL;
}
size_t bpr = (w*bpp + (align-1)) & ~(align-1);
size = bpr * h;
stride = bpr / bpp;
int err;
if (usage & GRALLOC_USAGE_HW_FB) {
err = gralloc_alloc_framebuffer(dev, size, usage, pHandle);
} else {
err = gralloc_alloc_buffer(dev, size, usage, pHandle);
}
if (err < 0) {
return err;
}
*pStride = stride;
return 0;
}
参数usage用来描述要分配的图形缓冲区的用途。如果是用来在系统帧缓冲区中渲染的,即参数usage的GRALLOC_USAGE_HW_FB位等于1,那么就必须要系统帧缓冲区中分配,否则的话,就在内存中分配。注意,在内存中分配的图形缓冲区,最终是需要拷贝到系统帧缓冲区去的,以便可以将它所描述的图形渲染出来。
而这个usage是由上层app然后到SurfaceFlinger一系列过程增减标志位的,所以我们从下往上,看看标志是怎么设置的。
同事我们回顾Android SurfaceFlinger 学习之路(七)—-创建图形缓冲区GraphicBuffer的内容。标志位设置在一下几个地方:
1)创建Layer时候,在onFirstRef函数中:1
2
3
4
5void Layer::onFirstRef() {
//这里getEffectiveUsage获取一些标志位
mSurfaceFlingerConsumer->setConsumerUsageBits(getEffectiveUsage(0));
}
这里getEffectiveUsage获取一些标志位,我们继续查看:1
2
3
4
5
6
7
8
9
10
11
12
13uint32_t Layer::getEffectiveUsage(uint32_t usage) const
{
// TODO: should we do something special if mSecure is set?
if (mProtectedByApp) {//// application requires protected path to external sink
// need a hardware-protected path to external video sink
usage |= GraphicBuffer::USAGE_PROTECTED;
}
if (mPotentialCursor) {//// This layer can be a cursor on some displays.
usage |= GraphicBuffer::USAGE_CURSOR;
}
usage |= GraphicBuffer::USAGE_HW_COMPOSER;
return usage;
}
如果mProtectedByApp为true,则设置GraphicBuffer::USAGE_PROTECTED标志位,默认为false;如果mPotentialCursor为true,设置USAGE_CURSOR标志位,默认为false。这两个注释已经说明了。
最后设置GraphicBuffer::USAGE_HW_COMPOSER,表示使用HWC硬件合成。
2)在上面Surface的lock方法中,设置了setUsage(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);we’re intending to do software rendering from this point,打算使用软件绘制。
3)Surface的lock方法中,锁定图形缓冲区的函数调用处:status_t res = backBuffer->lockAsync(
GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
newDirtyRegion.bounds(), &vaddr, fenceFd);函数就会将参数usage的值修改为(GraphicBuffer::USAGE_SW_READ_OFTEN | GraphicBuffer::USAGE_SW_WRITE_OFTEN),目的是防止该Surface的图形缓冲区直接在硬件帧缓冲区上分配。
4)上层app在WindowStateAnimator的createSurfaceLocked构建java层SurfaceControl时也仅仅设置了几个变量: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
29SurfaceControl createSurfaceLocked() {
......
int flags = SurfaceControl.HIDDEN;
final WindowManager.LayoutParams attrs = w.mAttrs;
if ((attrs.flags&WindowManager.LayoutParams.FLAG_SECURE) != 0) {
flags |= SurfaceControl.SECURE;
}
if (mService.isScreenCaptureDisabledLocked(UserHandle.getUserId(mWin.mOwnerUid))) {
flags |= SurfaceControl.SECURE;
}
......
if (!PixelFormat.formatHasAlpha(attrs.format)
&& attrs.surfaceInsets.left == 0
&& attrs.surfaceInsets.top == 0
&& attrs.surfaceInsets.right == 0
&& attrs.surfaceInsets.bottom == 0) {
flags |= SurfaceControl.OPAQUE;
}
......
mSurfaceControl = new SurfaceControl(
mSession.mSurfaceSession,
attrs.getTitle().toString(),
width, height, format, flags);
......
}
也是简单的flag,影响不大。
我们找了这么多,也没有GRALLOC_USAGE_HW_FB标志位。我们再看看这些标志位的定义,位于hardware/libhardware/include/hardware/Gralloc.h: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
68enum {
/* buffer is never read in software */
GRALLOC_USAGE_SW_READ_NEVER = 0x00000000,
/* buffer is rarely read in software */
GRALLOC_USAGE_SW_READ_RARELY = 0x00000002,
/* buffer is often read in software */
GRALLOC_USAGE_SW_READ_OFTEN = 0x00000003,
/* mask for the software read values */
GRALLOC_USAGE_SW_READ_MASK = 0x0000000F,
/* buffer is never written in software */
GRALLOC_USAGE_SW_WRITE_NEVER = 0x00000000,
/* buffer is rarely written in software */
GRALLOC_USAGE_SW_WRITE_RARELY = 0x00000020,
/* buffer is often written in software */
GRALLOC_USAGE_SW_WRITE_OFTEN = 0x00000030,
/* mask for the software write values */
GRALLOC_USAGE_SW_WRITE_MASK = 0x000000F0,
/* buffer will be used as an OpenGL ES texture */
GRALLOC_USAGE_HW_TEXTURE = 0x00000100,
/* buffer will be used as an OpenGL ES render target */
GRALLOC_USAGE_HW_RENDER = 0x00000200,
/* buffer will be used by the 2D hardware blitter */
GRALLOC_USAGE_HW_2D = 0x00000400,
/* buffer will be used by the HWComposer HAL module */
GRALLOC_USAGE_HW_COMPOSER = 0x00000800,
/* buffer will be used with the framebuffer device */
GRALLOC_USAGE_HW_FB = 0x00001000,
/* buffer will be used with the HW video encoder */
GRALLOC_USAGE_HW_VIDEO_ENCODER = 0x00010000,
/* buffer will be written by the HW camera pipeline */
GRALLOC_USAGE_HW_CAMERA_WRITE = 0x00020000,
/* buffer will be read by the HW camera pipeline */
GRALLOC_USAGE_HW_CAMERA_READ = 0x00040000,
/* buffer will be used as part of zero-shutter-lag queue */
GRALLOC_USAGE_HW_CAMERA_ZSL = 0x00060000,
/* mask for the camera access values */
GRALLOC_USAGE_HW_CAMERA_MASK = 0x00060000,
/* mask for the software usage bit-mask */
GRALLOC_USAGE_HW_MASK = 0x00071F00,
/* buffer will be used as a RenderScript Allocation */
GRALLOC_USAGE_RENDERSCRIPT = 0x00100000,
/* buffer should be displayed full-screen on an external display when
* possible
*/
GRALLOC_USAGE_EXTERNAL_DISP = 0x00002000,
/* Must have a hardware-protected path to external display sink for
* this buffer. If a hardware-protected path is not available, then
* either don't composite only this buffer (preferred) to the
* external sink, or (less desirable) do not route the entire
* composition to the external sink.
*/
GRALLOC_USAGE_PROTECTED = 0x00004000,
/* buffer may be used as a cursor */
GRALLOC_USAGE_CURSOR = 0x00008000,
/* implementation-specific private usage flags */
GRALLOC_USAGE_PRIVATE_0 = 0x10000000,
GRALLOC_USAGE_PRIVATE_1 = 0x20000000,
GRALLOC_USAGE_PRIVATE_2 = 0x40000000,
GRALLOC_USAGE_PRIVATE_3 = 0x80000000,
GRALLOC_USAGE_PRIVATE_MASK = 0xF0000000,
};
由于整个系统在硬件上就只有一个帧缓冲区,它是由SurfaceFlinger服务来统一管理的,即只有SurfaceFlinger服务使用的图形缓冲区才可以在上面分配,否则的话,随便一个应用程序进程都可以在上面分配图形缓冲区来使用,这个帧缓冲区的管理就乱套了。应用程序进程使用的图形缓冲区一般都是在匿名共享内存里面分配的,这个图形缓冲区填好数据之后,就会再交给SurfaceFlinger服务来合成到硬件帧缓冲区上去渲染。因此,从前面传过来给函数gralloc_alloc的参数usage的GRALLOC_USAGE_HW_FB位会被设置为0,以便可以在匿名共享内存中分配一个图形缓冲区。
图形缓冲区入队
我们前面讲了,省略了第二步绘制流程,因此我们这里分析第三部,绘制完毕后再queueBuffer。
同样,调用了Surface的unlockCanvasAndPost函数,我们查看它的实现:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21status_t Surface::unlockAndPost()
{
if (mLockedBuffer == 0) {
ALOGE("Surface::unlockAndPost failed, no locked buffer");
return INVALID_OPERATION;
}
int fd = -1;
//解锁图形缓冲区,和前面的lockAsync成对出现
status_t err = mLockedBuffer->unlockAsync(&fd);
ALOGE_IF(err, "failed unlocking buffer (%p)", mLockedBuffer->handle);
//queueBuffer去归还图形缓冲区
err = queueBuffer(mLockedBuffer.get(), fd);
ALOGE_IF(err, "queueBuffer (handle=%p) failed (%s)",
mLockedBuffer->handle, strerror(-err));
//将绘制buffer赋值给mPostedBuffer ,在lock函数里提到了
mPostedBuffer = mLockedBuffer;
//最后将mLockedBuffer 置为0
mLockedBuffer = 0;
return err;
}
这里也比较简单,核心也是分两步:
1)解锁图形缓冲区,和前面的lockAsync成对出现;
2)queueBuffer去归还图形缓冲区;
所以我们还是重点分析第二步,查看queueBuffer的实现: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
52int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
ATRACE_CALL();
ALOGV("Surface::queueBuffer");
Mutex::Autolock lock(mMutex);
int64_t timestamp;
bool isAutoTimestamp = false;
if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) {
timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
isAutoTimestamp = true;
ALOGV("Surface::queueBuffer making up timestamp: %.2f ms",
timestamp / 1000000.f);
} else {
timestamp = mTimestamp;
}
//获取图形缓冲区的slot在mSlots中的index
int i = getSlotFromBufferLocked(buffer);
if (i < 0) {
return i;
}
// Make sure the crop rectangle is entirely inside the buffer.
确保剪裁的区域完全位于图形缓冲区内部
Rect crop;
mCrop.intersect(Rect(buffer->width, buffer->height), &crop);
sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
//创建in、out两个栈对象,用于填充数据
IGraphicBufferProducer::QueueBufferOutput output;
IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp,
crop, mScalingMode, mTransform ^ mStickyTransform, mSwapIntervalZero,
fence, mStickyTransform);
//调用BufferQueueProducer的queueBuffer归还缓冲区
status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);
if (err != OK) {
ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
}
uint32_t numPendingBuffers = 0;
uint32_t hint = 0;
//解析赋值给全局变量
output.deflate(&mDefaultWidth, &mDefaultHeight, &hint,
&numPendingBuffers);
// Disable transform hint if sticky transform is set.
if (mStickyTransform == 0) {
mTransformHint = hint;
}
mConsumerRunningBehind = (numPendingBuffers >= 2);
return err;
}
这里创建了QueueBufferOutput、QueueBufferInput 两个栈对象用来传输数据,这两个我们在下面的附件内容会讲到,有一些关于结构体的知识。
上述的核心就是调用BufferQueueProducer的queueBuffer归还缓冲区,j将绘制后的图形缓冲区queue回去。我们依然省略IPC Binder过程,直接看实现: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
106
107
108
109
110
status_t BufferQueueProducer::queueBuffer(int slot,
const QueueBufferInput &input, QueueBufferOutput *output) {
ATRACE_CALL();
ATRACE_BUFFER_INDEX(slot);
int64_t timestamp;
bool isAutoTimestamp;
Rect crop;
int scalingMode;
uint32_t transform;
uint32_t stickyTransform;
bool async;
sp<Fence> fence;
//从传入的QueueBufferInput ,解析填充一些变量
input.deflate(×tamp, &isAutoTimestamp, &crop, &scalingMode, &transform,
&async, &fence, &stickyTransform);
......
sp<IConsumerListener> listener;
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
...省略一些错误判断逻辑...
//获取入队的GraphicBuffer的区域大小
const sp<GraphicBuffer>& graphicBuffer(mSlots[slot].mGraphicBuffer);
Rect bufferRect(graphicBuffer->getWidth(), graphicBuffer->getHeight());
Rect croppedRect;
crop.intersect(bufferRect, &croppedRect);
if (croppedRect != crop) {
BQ_LOGE("queueBuffer: crop rect is not contained within the "
"buffer in slot %d", slot);
return BAD_VALUE;
}
//改变入队的slot的状态为QUEUED
mSlots[slot].mFence = fence;
mSlots[slot].mBufferState = BufferSlot::QUEUED;
//每次推进来,mFrameCounter都加1,
//上一篇讲分配缓冲区返回最老的FREE状态buffer,就是用这个mFrameCounter最小值判断
++mCore->mFrameCounter;
mSlots[slot].mFrameNumber = mCore->mFrameCounter;
//创建一个BufferItem来描述GraphicBuffer
BufferItem item;
item.mAcquireCalled = mSlots[slot].mAcquireCalled;
item.mGraphicBuffer = mSlots[slot].mGraphicBuffer;
item.mCrop = crop;
item.mTransform = transform & ~NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
item.mTransformToDisplayInverse =
bool(transform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY);
item.mScalingMode = scalingMode;
item.mTimestamp = timestamp;
item.mIsAutoTimestamp = isAutoTimestamp;
item.mFrameNumber = mCore->mFrameCounter;//每次加1,最小的肯定是最早推进去的
item.mSlot = slot;
item.mFence = fence;
item.mIsDroppable = mCore->mDequeueBufferCannotBlock || async;
mStickyTransform = stickyTransform;
if (mCore->mQueue.empty()) {//如果mQueue队列为空,则直接push进入这个mQueue,不用考虑阻塞
// When the queue is empty, we can ignore mDequeueBufferCannotBlock
// and simply queue this buffer
mCore->mQueue.push_back(item);
listener = mCore->mConsumerListener;
} else {
// When the queue is not empty, we need to look at the front buffer
// state to see if we need to replace it
//如果这个mQueue队列不为空,我们要看看它的队头的状态,然后判断是否替换它
BufferQueueCore::Fifo::iterator front(mCore->mQueue.begin());
if (front->mIsDroppable) {//如果队头可以丢弃
// If the front queued buffer is still being tracked, we first
// mark it as freed
//如果已经出队的buffer对应的slot依然保留在mSlots数组中,就要释放他,变为FREE状态
if (mCore->stillTracking(front)) {
mSlots[front->mSlot].mBufferState = BufferSlot::FREE;
// Reset the frame number of the freed buffer so that it is
// the first in line to be dequeued again
mSlots[front->mSlot].mFrameNumber = 0;
}
// Overwrite the droppable buffer with the incoming one
//然后队头插入新入队的BufferItem
*front = item;
} else {
//如果队头不能丢弃,则直接将新的Item入队
mCore->mQueue.push_back(item);
//将BufferQueueCore的mConsumerListener赋给listener,这个我们上一节讲过
listener = mCore->mConsumerListener;
}
}
mCore->mBufferHasBeenQueued = true;//入队后mBufferHasBeenQueued 置为true
//上一篇讲了,没有空闲buffer去dequeue,则要等待,这里解除等待
mCore->mDequeueCondition.broadcast();
output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight,
mCore->mTransformHint, mCore->mQueue.size());
ATRACE_INT(mCore->mConsumerName.string(), mCore->mQueue.size());
} // Autolock scope
// Call back without lock held
//然后通知SurfaceFlinger去消费,这个我们上一篇讲过,下面会在分析
if (listener != NULL) {
listener->onFrameAvailable();
}
return NO_ERROR;
}
queueBuffer方法也挺长的,但是我们分部查看,每一步也不难:
1)从传入的QueueBufferInput ,解析填充一些变量;
2)改变入队Slot的状态为QUEUED,每次推进来,mFrameCounter都加1。这里的slot,上一篇讲分配缓冲区返回最老的FREE状态buffer,就是用这个mFrameCounter最小值判断,就是上一篇LRU算法的判断;
3)创建一个BufferItem来描述GraphicBuffer,用mSlots[slot]中的slot填充BufferItem;
4)将BufferItem塞进mCore的mQueue队列,依照指定规则;
5)然后通知SurfaceFlinger去消费,这个我们上一篇讲过,这个我们下面会讲到。
上述lockCanvas和unlockCanvasAndPost可以用下图来总结一下:
通知SF消费合成
上面讲到,当绘制完毕的GraphicBuffer入队之后,会通知SurfaceFlinger去消费,就是BufferQueueProducer的queueBuffer函数的最后几行,listener->onFrameAvailable()。
这个listener赋值为mCore->mConsumerListener,我们上一篇Android SurfaceFlinger 学习之路(七)—-创建图形缓冲区GraphicBuffer在讲到Consumer时候,得知这个listener最终通过回调,会回到Layer当中,所以最终调用Layer的onFrameAvailable接口,我们看看它的实现:1
2
3
4void Layer::onFrameAvailable() {
android_atomic_inc(&mQueuedFrames);
mFlinger->signalLayerUpdate();
}
这里又调用SurfaceFlinger的signalLayerUpdate函数,继续查看:1
2
3void SurfaceFlinger::signalLayerUpdate() {
mEventQueue.invalidate();
}
这里又调用MessageQueue的invalidate函数:1
2
3
4
5
6
7void MessageQueue::invalidate() {
mEvents->requestNextVsync();
mHandler->dispatchInvalidate();
}
到这里就比较熟悉了,就是们讲Vsync信号的时候内容了,可以查看Android SurfaceFlinger 学习之路(五)—-VSync 工作原理。
其实上面和下面执行的逻辑都一样,最种结果都会走到SurfaceFlinger的vsync信号接收逻辑,即SurfaceFlinger的onMessageReceived函数,走的case是INVALIDATE:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16void SurfaceFlinger::onMessageReceived(int32_t what) {
ATRACE_CALL();
switch (what) {
case MessageQueue::TRANSACTION:
handleMessageTransaction();
break;
case MessageQueue::INVALIDATE:
handleMessageTransaction();
handleMessageInvalidate();
signalRefresh();
break;
case MessageQueue::REFRESH:
handleMessageRefresh();
break;
}
}
SurfaceFlinger就去消费这块图形缓冲,然后合成图像,送给FrameBuffer,显示设备读取FrameBuffer内容去显示。这个我们以后再讲。
附:结构体对齐
我们上面讲Surface的queueBuffer时候,会创建QueueBufferOutput、QueueBufferInput 两个栈对象用来传输数据。他们定义位于frameworks/native/include/gui/IGraphicBufferProducer.h: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
82struct QueueBufferInput : public Flattenable<QueueBufferInput> {
friend class Flattenable<QueueBufferInput>;
inline QueueBufferInput(const Parcel& parcel);
// timestamp - a monotonically increasing value in nanoseconds
// isAutoTimestamp - if the timestamp was synthesized at queue time
// crop - a crop rectangle that's used as a hint to the consumer
// scalingMode - a set of flags from NATIVE_WINDOW_SCALING_* in <window.h>
// transform - a set of flags from NATIVE_WINDOW_TRANSFORM_* in <window.h>
// async - if the buffer is queued in asynchronous mode
// fence - a fence that the consumer must wait on before reading the buffer,
// set this to Fence::NO_FENCE if the buffer is ready immediately
// sticky - the sticky transform set in Surface (only used by the LEGACY
// camera mode).
inline QueueBufferInput(int64_t timestamp, bool isAutoTimestamp,
const Rect& crop, int scalingMode, uint32_t transform, bool async,
const sp<Fence>& fence, uint32_t sticky = 0)
: timestamp(timestamp), isAutoTimestamp(isAutoTimestamp), crop(crop),
scalingMode(scalingMode), transform(transform), stickyTransform(sticky),
async(async), fence(fence) { }
inline void deflate(int64_t* outTimestamp, bool* outIsAutoTimestamp,
Rect* outCrop, int* outScalingMode, uint32_t* outTransform,
bool* outAsync, sp<Fence>* outFence,
uint32_t* outStickyTransform = NULL) const {
*outTimestamp = timestamp;
*outIsAutoTimestamp = bool(isAutoTimestamp);
*outCrop = crop;
*outScalingMode = scalingMode;
*outTransform = transform;
*outAsync = bool(async);
*outFence = fence;
if (outStickyTransform != NULL) {
*outStickyTransform = stickyTransform;
}
}
// Flattenable protocol
size_t getFlattenedSize() const;
size_t getFdCount() const;
status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
private:
int64_t timestamp;
int isAutoTimestamp;
Rect crop;
int scalingMode;
uint32_t transform;
uint32_t stickyTransform;
int async;
sp<Fence> fence;
};
// QueueBufferOutput must be a POD structure
struct __attribute__ ((__packed__)) QueueBufferOutput {
inline QueueBufferOutput() { }
// outWidth - filled with default width applied to the buffer
// outHeight - filled with default height applied to the buffer
// outTransformHint - filled with default transform applied to the buffer
// outNumPendingBuffers - num buffers queued that haven't yet been acquired
// (counting the currently queued buffer)
inline void deflate(uint32_t* outWidth,
uint32_t* outHeight,
uint32_t* outTransformHint,
uint32_t* outNumPendingBuffers) const {
*outWidth = width;
*outHeight = height;
*outTransformHint = transformHint;
*outNumPendingBuffers = numPendingBuffers;
}
inline void inflate(uint32_t inWidth, uint32_t inHeight,
uint32_t inTransformHint, uint32_t inNumPendingBuffers) {
width = inWidth;
height = inHeight;
transformHint = inTransformHint;
numPendingBuffers = inNumPendingBuffers;
}
private:
uint32_t width;
uint32_t height;
uint32_t transformHint;
uint32_t numPendingBuffers;
};
这两个结构体,一个用来填充数据,一个用来解析数据。
我们先看看结构字节对齐。
结构体字节对齐
在用sizeof运算符求算某结构体所占空间时,并不是简单地将结构体中所有元素各自占的空间相加,这里涉及到内存字节对齐的问题。从理论上讲,对于任何 变量的访问都可以从任何地址开始访问,但是事实上不是如此,实际上访问特定类型的变量只能在特定的地址访问,这就需要各个变量在空间上按一定的规则排列, 而不是简单地顺序排列,这就是内存对齐。
内存对齐的原因:
- 某些平台只能在特定的地址处访问特定类型的数据;
- 提高存取数据的速度。比如有的平台每次都是从偶地址处读取数据,对于一个int型的变量,若从偶地址单元处存放,则只需一个读取周期即可读取该变量;但是若从奇地址单元处存放,则需要2个读取周期读取该变量。
对齐策略:
- 结构体变量的首地址能够被其最宽数据类型成员的大小整除。编译器在为结构体变量开辟空间时,首先找到结构体中最宽的数据类型,然后寻找内存地址能被该数据类型大小整除的位置,这个位置作为结构体变量的首地址。而将最宽数据类型的大小作为对齐标准。
- 结构体每个成员相对结构体首地址的偏移量(offset)都是每个成员本身大小的整数倍,如有需要会在成员之间填充字节。编译器在为结构体成员开辟空 间时,首先检查预开辟空间的地址相对于结构体首地址的偏移量是否为该成员大小的整数倍,若是,则存放该成员;若不是,则填充若干字节,以达到整数倍的要求。
- 结构体变量所占空间的大小必定是最宽数据类型大小的整数倍。如有需要会在最后一个成员末尾填充若干字节使得所占空间大小是最宽数据类型大小的整数倍。
下面看一下sizeof在计算结构体大小的时候具体是怎样计算的:
1)test1 空结构体1
2
3
4
5
typedef struct node
{
}S;
则sizeof(S)=1;或sizeof(S)=0;
在C++中占1字节,而在C中占0字节。
2)test21
2
3
4
5
6typedef struct node1
{
int a;
char b;
short c;
}S1;
则sizeof(S1)=8。这是因为结构体node1中最长的数据类型是int,占4个字节,因此以4字节对齐,则该结构体在内存中存放方式为:|--------int--------| 4字节
|char|----|--short--| 4字节
总共占8字节
3)test31
2
3
4
5
6
7
typedef struct node2
{
char a;
int b;
short c;
}S2;
则siezof(S3)=12.最长数据类型为int,占4个字节。因此以4字节对齐,其在内存空间存放方式如下:|char|----|----|----| 4字节
|--------int--------| 4字节
|--short--|----|----| 4字节
总共占12个字节
4)test4 含有静态数据成员1
2
3
4
5
6typedef struct node3
{
int a;
short b;
static int c;
}S3;
则sizeof(S3)=8。这里结构体中包含静态数据成员,而静态数据成员的存放位置与结构体实例的存储地址无关(注意只有在C++中结构体中才能含有静态数据成员,而C中结构体中是不允许含有静态数据成员的)。其在内存中存储方式如下:|--------int--------| 4字节
|--short--|----|----| 4字节
而变量c是单独存放在静态数据区的,因此用siezof计算其大小时没有将c所占的空间计算进来。
5)test5 结构体中含有结构体1
2
3
4
5
6typedef struct node4
{
bool a;
S1 s1;
short b;
}S4;
数据成员包含另一个结构体变量s1的话,则取s1中最 长数据类型与其他数据成员比较,取最长的作为对齐标准,但是s1存放时看做一个单位存放,只需看其他成员即可。
则sizeof(S4)=16。是因为s1占8字节,而s1中最长数据类型为int,占4个字节,bool类型1个字节,short占2字节,因此以4字节对齐,则存储方式为:|-------bool--------| 4字节
|-------s1----------| 8字节
|-------short-------| 4字节
6)test61
2
3
4
5
6
7typedef struct node5
{
bool a;
S1 s1;
double b;
int c;
}S5;
则sizeof(S5)=32。是因为s1占8字节,而s1中最长数据类型为int,占4字节,而double占8字节,因此以8字节对齐,则存放方式为:|--------bool--------| 8字节
|---------s1---------| 8字节
|--------double------| 8字节
|----int----|--------| 8字节
总结一下,在计算sizeof时主要注意一下几点:
- 若为空结构体,则只占1个字节的单元;
- 若结构体中所有数据类型都相同,则其所占空间为 成员数据类型长度×成员个数;若结构体中数据类型不同,则取最长数据类型成员所占的空间为对齐标准,数据成员包含另一个结构体变量t的话,则取t中最 长数据类型与其他数据成员比较,取最长的作为对齐标准,但是t存放时看做一个单位存放,只需看其他成员即可。
attribute 关键字(双下划线相连并紧贴attribute)
上面的结构体对齐只是普通情况,但是如果对于指定了 attribute (由于MarkDown文本原因,打出来双下划线相连并紧贴attribute有问题,因此先这么表示
)关键字,就不痛了。例如我们上面定义 QueueBufferOutput结构体也用了( attribute (( packed )))。
1) attribute 关键字主要是用来在函数或数据声明中设置其属性。给函数赋给属性的主要目的在于让编译器进行优化。函数声明中的 attribute ((noreturn)),就是告诉编译器这个函数不会返回给调用者,以便编译器在优化时去掉不必要的函数返回代码。
GNU C的一大特色就是 attribute 机制。 attribute 可以设置函数属性(Function Attribute)、变量属性(Variable Attribute)和类型属性(Type Attribute)。
- attribute 书写特征是: attribute 前后都有两个下划线,并且后面会紧跟一对括弧,括弧里面是相应的 attribute 参数。
- attribute 语法格式为: attribute ((attribute-list)) 其位置约束:放于声明的尾部“;”之前。
- 函数属性(Function Attribute):函数属性可以帮助开发者把一些特性添加到函数声明中,从而可以使编译器在错误检查方面的功能更强大。 attribute 机制也很容易同非GNU应用程序做到兼容之功效。
GNU CC需要使用 –Wall编译器来击活该功能,这是控制警告信息的一个很好的方式。
2) attribute ((packed)) 的作用就是告诉编译器取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐,是GCC特有的语法。这个功能是跟操作系统没关系,跟编译器有关,gcc编译器不是紧凑模式的,我在windows下,用vc的编译器也不是紧凑的,用tc的编译器就是紧凑的。例如:
- 在TC下:struct my{ char ch; int a;} sizeof(int)=2;sizeof(my)=3;(紧凑模式)
- 在GCC下:struct my{ char ch; int a;} sizeof(int)=4;sizeof(my)=8;(非紧凑模式)
- 在GCC下:struct my{ char ch; int a;} attrubte ((packed)) sizeof(int)=4;sizeof(my)=5
packed属性:使用该属性可以使得变量或者结构体成员使用最小的对齐方式,即对变量是一字节对齐,对域(field)是位对齐。
3)若使用了 attribute ((aligned(n)))命令强制对齐标准,则取n和结构体中最长数据类型占的字节数两者之中的小者作为对齐标准。内存对齐,往往是由编译器来做的,如果你使用的是gcc,可以在定义变量时,添加 attribute ,来决定是否使用内存对齐,或是内存对齐到几个字节,以上面面的结构体为例:1
2
3
4
5
6
7
8
typedef struct __attribute__ ((aligned(4))) node5
{
bool a;
S1 s1;
double b;
int c;
}S5;
则sizeof(S5)=24。因为强制以4字节对齐,而S5中最长数据类型为double,占8字节,因此以4字节对齐。在内存中存放方式为:|-----------a--------| 4字节
|--------s1----------| 4字节
|--------s1----------| 4字节
|--------b-----------| 4字节
|--------b-----------| 4字节
|---------c----------| 4字节
参考
http://www.unixwiz.net/techtips/gnu-c-attributes.html
http://www.cnblogs.com/dolphin0520/archive/2011/09/17/2179466.html
小结
本篇我们讲了Surface对图形缓冲区的管理,前面从Activity到Surface创建过程我们只大概提了下,这个放在以后有空会分析的。下一篇我们讲讲SurfaceFlinger消费图形缓冲区,还有合成等等操作。
唉,有一件特别悲催的事情,就是我失业了ToT,所以估计有一段时间不能更新了,望大家见谅。