0%

Android-Surface原理解析

概述

注:本文基于Android 10源码,为了文章的简洁性,引用源码的地方可能有所删减。

在解析了 Android-SurfaceFlinger启动与工作流程Android-Choreographer工作原理 后,明白了 Vsync 信号是怎么控制 SurfaceFlinger 进行合成 Layer 数据以及 Choreographer 是怎么控制开始绘制流程的,另外 Android-View绘制流程 中贴出了从 Choreographer.postCallback 接收到 Vsync 信号后调用 ViewRootImpl.performTraversals 开始 View 的 measure, layout, draw 流程的代码。接下来还有一个问题就是在 View 开始绘制后,即 View.draw 方法中绘制的数据是怎么流入 SurfaceFlinger 进程进行合成的。这里涉及到 Surface 的工作流程以及 BufferQueue 处理图形缓存区的逻辑,关于 BufferQueue 在后面会分析到与它相关的几个重要方法,不影响 Surface 的流程解析。

Surface创建

WMS.addWindow

Android-Window机制原理 知道当调用 WM.addView 方法时会调用到 ViewRootImpl.setView 方法,然后通过 Binder 跨进程调用到 WMS.addWindow 方法,在该方法里创建了一个 WindowState 对象,进而调用其 attach 方法,最终调用到 Session.windowAddedLocked 方法,Surface 的创建就是从这里开始的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void windowAddedLocked(String packageName) {
if (mSurfaceSession == null) {
mSurfaceSession = new SurfaceSession();
}
}

// SurfaceSession
private long mNativeClient; // SurfaceComposerClient*

/** Create a new connection with the surface flinger. */
public SurfaceSession() {
mNativeClient = nativeCreate();
}

// frameworks/base/core/jni/android_view_SurfaceSession.cpp
static jlong nativeCreate(JNIEnv* env, jclass clazz) {
SurfaceComposerClient* client = new SurfaceComposerClient();
client->incStrong((void*)nativeCreate);
return reinterpret_cast<jlong>(client);
}

可以看到 SurfaceSession 构造方法中通过 nativeCreate 方法返回了一个 SurfaceComposerClient 指针,它表示一个跟 SurfaceFlinger 的连接。

当其第一次被使用时会调用如下函数:

1
2
3
4
5
6
7
8
9
10
11
void SurfaceComposerClient::onFirstRef() {
sp<ISurfaceComposerClient> conn = (rootProducer != nullptr) ? sf->createScopedConnection(rootProducer) : sf->createConnection();
if (conn != 0) {
mClient = conn;
}
// ...
}

sp<ISurfaceComposerClient> SurfaceFlinger::createConnection() {
return initClient(new Client(this)); // initClient方法只是调用initCheck检查了一下
}

即创建了一个实现 ISurfaceComposerClient 接口的 Client 对象。

总结一下上面 WMS 中的相关操作:WMS 创建了一个 WindowState 对象表示客户端的一个 Window, 接着调用 WindowState.attach 方法创建了一个 SurfaceSession 对象,SurfaceSession 表示一个跟 SurfaceFlinger 的连接,它创建了一个 SurfaceComposerClient 对象,然后 SurfaceComposerClient 又创建了一个 Client 对象。

创建Surface

一个 ViewRootImpl 对象对应了一个 Surface 对象,在其源码中有如下代码:

1
2
// ViewRootImpl.java
public final Surface mSurface = new Surface();

但此时这个 Surface 对象啥东西都没有,构造方法也是空的。根据 Android-Choreographer工作流程 中的分析,ViewRootImpl.setView 方法会调用到 Choreographer 的逻辑,进而在 Vsync 信号到来后执行 ViewRootImpl.performTraversals 方法:

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
private void performTraversals() {
relayoutWindow(params, viewVisibility, insetsPending)
// measure, layout, draw
}

private int relayoutWindow(...) throws RemoteException {
// 注意最后一个参数 mSurface 便是之前创建的 Surface 对象
// 会调用 WMS.relayoutWindow 方法
mWindowSession.relayout(mWindow, ..., mSurface);
// ...
}

// WMS
public int relayoutWindow(Session session, ..., Surface outSurface) {
result = createSurfaceControl(outSurface, result, win, winAnimator);
// ...
}

private int createSurfaceControl(Surface outSurface, int result, WindowState win, WindowStateAnimator winAnimator) {
WindowSurfaceController surfaceController = winAnimator.createSurfaceLocked(win.mAttrs.type, win.mOwnerUid);
if (surfaceController != null) {
surfaceController.getSurface(outSurface);
} else {
outSurface.release();
}
return result;
}

上面调用 createSurfaceLocked 方法创建了一个 WindowSurfaceController 对象,然后在 WindowSurfaceController 的构造方法中会创建一个 SurfaceControl 对象(mSurfaceControl, 通过构造方法实例化,代码不贴了),顾名思义这个对象是用来维护 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
27
28
private SurfaceControl(...) {
// 返回 native SurfaceControl 指针
mNativeObject = nativeCreate(session, name, w, h, format, flags, parent != null ? parent.mNativeObject : 0, windowType, ownerUid);
}

// frameworks/base/core/jni/android_view_SurfaceControl.cpp
static jlong nativeCreate(...) {
// client 即上面创建的 SurfaceComposerClient 对象
sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj));
sp<SurfaceControl> surface;
client->createSurfaceChecked(String8(name.c_str()), w, h, format, &surface, flags, parent, windowType, ownerUid);
return reinterpret_cast<jlong>(surface.get());
}

status_t SurfaceComposerClient::createSurfaceChecked(..., sp<SurfaceControl>* outSurface, ...) {
// ...
sp<IGraphicBufferProducer> gbp;
err = mClient->createSurface(name, w, h, format, flags, parentHandle, windowType, ownerUid, &handle, &gbp);
if (err == NO_ERROR) {
*outSurface = new SurfaceControl(this, handle, gbp, true /* owned */);
}
return err;
}

status_t Client::createSurface(...) {
// ...
flinger->createLayer(name, client, w, h, format, flags, windowType, ownerUid, handle, gbp, parent);
}

Surface 在 SurfaceFlinger 中对应的实体是 Layer 对象,在 createLayer 方法中会创建好几种 Layer。因此可以知道在创建 SurfaceControl 对象时通过 SurfaceFlinger 创建了一个对应 Surface 的 Layer 对象。

接下来就到了 WindowSurfaceController.getSurface 方法:

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
void getSurface(Surface outSurface) {
outSurface.copyFrom(mSurfaceControl);
}

// Surface.java
public void copyFrom(SurfaceControl other) {
// 即上面返回的 native SurfaceControl 指针
long surfaceControlPtr = other.mNativeObject;
long newNativeObject = nativeGetFromSurfaceControl(surfaceControlPtr);

synchronized (mLock) {
if (mNativeObject != 0) {
nativeRelease(mNativeObject);
}
setNativeObjectLocked(newNativeObject);
}
}

private void setNativeObjectLocked(long ptr) {
if (mNativeObject != ptr) {
mNativeObject = ptr;
if (mHwuiContext != null) {
mHwuiContext.updateSurface();
}
}
}

// frameworks/base/core/jni/android_view_Surface.cpp
static jlong nativeGetFromSurfaceControl(JNIEnv* env, jclass clazz, jlong surfaceControlNativeObj) {
// ctrl 是前面创建的 SurfaceControl 对象
sp<SurfaceControl> ctrl(reinterpret_cast<SurfaceControl *>(surfaceControlNativeObj));
sp<Surface> surface(ctrl->getSurface());
if (surface != NULL) {
surface->incStrong(&sRefBaseOwner);
}
return reinterpret_cast<jlong>(surface.get());
}

sp<Surface> SurfaceControl::getSurface() const
{
Mutex::Autolock _l(mLock);
if (mSurfaceData == 0) {
return generateSurfaceLocked();
}
return mSurfaceData;
}

sp<Surface> SurfaceControl::generateSurfaceLocked() const
{
// mGraphicBufferProducer 是上面创建的 gbp 对象
mSurfaceData = new Surface(mGraphicBufferProducer, false);
return mSurfaceData;
}

上面通过 nativeGetFromSurfaceControl 方法返回了一个 native 层创建的 Surface 指针,并赋值给了 Java 层 Surface 对象的 mNativeObject 属性。

IGraphicBufferProducer

上面看到了一个 IGraphicBufferProducer gbp 对象,这是一个很重要的对象,我们看一下它是怎么被创建的:

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
status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& client, ..., sp<IGraphicBufferProducer>* gbp, ...) {
switch (flags & ISurfaceComposerClient::eFXSurfaceMask) {
case ISurfaceComposerClient::eFXSurfaceNormal:
// 只看这一种 Layer
result = createBufferLayer(client, uniqueName, w, h, flags, format, handle, gbp, &layer);
break;
// ...
}
}

status_t SurfaceFlinger::createBufferLayer(const sp<Client>& client,
const String8& name, uint32_t w, uint32_t h, uint32_t flags, PixelFormat& format,
sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* outLayer)
{
sp<BufferLayer> layer = new BufferLayer(this, client, name, w, h, flags);
status_t err = layer->setBuffers(w, h, format, flags);
if (err == NO_ERROR) {
*handle = layer->getHandle();
*gbp = layer->getProducer(); // 获取 gbp
*outLayer = layer;
}
return err;
}

sp<IGraphicBufferProducer> BufferLayer::getProducer() const {
return mProducer;
}

void BufferLayer::onFirstRef() {
// Creates a custom BufferQueue for SurfaceFlingerConsumer to use
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer, true);
mProducer = new MonitoredProducer(producer, mFlinger, this);
mConsumer = new BufferLayerConsumer(consumer, mFlinger->getRenderEngine(), mTextureName, this);
// ...
}

mProducer 是 MonitoredProducer 实例,它是一个装饰类,实际功能都委托给了其 producer 属性:

1
2
3
4
5
6
7
8
void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
sp<IGraphicBufferConsumer>* outConsumer, bool consumerIsSurfaceFlinger) {
sp<BufferQueueCore> core(new BufferQueueCore());
sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core, consumerIsSurfaceFlinger));
sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core));
*outProducer = producer;
*outConsumer = consumer;
}

可以看出 producer 是 BufferQueueProducer 对象, consumer 是 BufferQueueConsumer 对象。

小结

在 Java 层中 ViewRootImpl 实例中持有一个 Surface 对象,该 Surface 对象中的 mNativeObject 属性指向 native 层中创建的 Surface 对象,native 层的 Surface 对应 SurfaceFlinger 中的 Layer 对象,它持有 Layer 中的 BufferQueueProducer 生产者指针,在 Surface 上绘制的内容最终会交由 SurfaceFlinger 来合成渲染送到显示器显示,这点在后面会讲到。

硬件加速&软件绘制

概述

Resterization: 栅格化(光栅化)。栅格化把 Button, TextView 等组件拆分到不同的像素上进行显示,这是一个很费时的操作,GPU可以加快栅格化的操作。

Android 系统的 UI 从绘制到显示在屏幕上可分为两个步骤:

  1. Android APP 进程: 将 UI 绘制到一个图形缓冲区 GraphicBuffer 中,然后通知 SurfaceFlinger 进行合成。
  2. SurfaceFlinger 进程: 将 GraphicBuffer 数据合成并交给屏幕缓存区去显示,这一步本身就是通过硬件(OpenGL 和 HardWare Composer)去完成的。

因此我们说的硬件加速一般是指在 APP 进程将图形通过 GPU 加速渲染到 GraphicBuffer 的过程。GPU 作为一个硬件,用户空间不能直接使用,GPU 厂商会按照 OpenGL 的规范实现一套 API 驱动来调用它的相关功能。

软件渲染

  • 当 App 更新部分 UI 时,CPU 会遍历 View Tree 计算出需要重绘的脏区,接着在 View 层次结构中绘制所有跟脏区相交的区域,因此软件绘制会绘制到不需要重绘的视图。
  • 软件绘制的绘制过程是在主线程进行的,可能会造成卡顿等情况。
  • 软件绘制把要绘制的内容写进一个 Bitmap 位图,在之后的渲染过程中,这个 Bitmap 的像素内容会填充到 Surface 的缓存区里。
  • 软件绘制使用 Skia 库。

硬件渲染

  • 当 App 更新部分 UI 时,CPU 会计算出脏区,但是不会立即执行绘制命令,而是将 drawXXX 函数作为绘制指令(DrawOp)记录在一个列表(DisplayList)中,然后交给单独的 Render 线程使用 GPU 进行硬件加速渲染。
  • 只需要针对需要更新的 View 对象的脏区进行记录或更新,无需更新的 View 对象则能重用先前 DisplayList 中记录的指令。
  • 硬件加速是在单独的 Render 线程中完成绘制的,分担了主线程的压力,提高了响应速度。
  • 硬件绘制使用 OpenGL 在 GPU 上完成,OpenGL 是跨平台的图形 API,为 2D/3D 图形处理硬件制定了标准的软件接口。听说在 Android 新版本中,Google 开始逐渐让 Skia 接手 OpenGL,实现间接统一调用。
  • 硬件加速有几个缺陷:兼容性(部分绘制函数不支持速),内存消耗,电量消耗(GPU耗电)等。
  • 从 Android 3.0(API 11)开始支持硬件加速,Android 4.0(API 14)默认开启硬件加速。

关于 OpenGl ES 的简单使用可以参考:Android-OpenGL-ES笔记官方文档

配置硬件加速

  • Application: 在 Manifest 文件的 application 标签添加 android:hardwareAccelerated="boolean"
  • Activity: 在 Manifest 文件的 activity 标签添加 android:hardwareAccelerated="boolean"
  • Window: getWindow().setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED)
  • View: setLayerType(View.LAYER_TYPE_HARDWARE/*View.LAYER_TYPE_SOFTWARE*/, mPaint)

判断是否支持硬件加速:

1
2
3
4
5
6
// 如果Application、Activity配置了不开启硬件加速,则返回false
view.isHardwareAccelerated()

// 假如没有设置setLayerType,则受到Application、Activity的影响
// 假如设置了setLayerType,其返回值则受到setLayerType参数的影响
canvas.isHardwareAccelerated()

在绘制过程中会通过 VRImpl.enableHardwareAcceleration 方法去判断是否需要开启硬件加速:

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
private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {
mAttachInfo.mHardwareAccelerated = false;
mAttachInfo.mHardwareAccelerationRequested = false;

// Don't enable hardware acceleration when the application is in compatibility mode
if (mTranslator != null) return;

// Try to enable hardware acceleration if requested
final boolean hardwareAccelerated = (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;

if (hardwareAccelerated) {
if (!ThreadedRenderer.isAvailable()) {
return;
}

// Persistent processes (including the system) should not do accelerated rendering on low-end devices.
// In that case, sRendererDisabled will be set. In addition, the system process itself should never do accelerated rendering.
// In that case, both sRendererDisabled and sSystemRendererDisabled are set. When sSystemRendererDisabled is set,
// PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED can be used by code on the system process to escape that and enable
// HW accelerated drawing. (This is basically for the lock screen.)
final boolean fakeHwAccelerated = (attrs.privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED) != 0;
final boolean forceHwAccelerated = (attrs.privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED) != 0;

if (fakeHwAccelerated) {
// This is exclusively for the preview windows the window manager shows for launching applications,
// so they will look more like the app being launched.
mAttachInfo.mHardwareAccelerationRequested = true;
} else if (!ThreadedRenderer.sRendererDisabled || (ThreadedRenderer.sSystemRendererDisabled && forceHwAccelerated)) {
// 创建硬件加速的渲染器
mAttachInfo.mThreadedRenderer = ThreadedRenderer.create(mContext, translucent, attrs.getTitle().toString());
mAttachInfo.mThreadedRenderer.setWideGamut(wideGamut);
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mHardwareAccelerated = mAttachInfo.mHardwareAccelerationRequested = true;
}
}
}
}

public static boolean isAvailable() {
if (sSupportsOpenGL != null) {
return sSupportsOpenGL.booleanValue();
}
if (SystemProperties.getInt("ro.kernel.qemu", 0) == 0) {
// Device is not an emulator.
sSupportsOpenGL = true;
return true;
}
int qemu_gles = SystemProperties.getInt("qemu.gles", -1);
if (qemu_gles == -1) {
// In this case, the value of the qemu.gles property is not ready
// because the SurfaceFlinger service may not start at this point.
return false;
}
// In the emulator this property will be set > 0 when OpenGL ES 2.0 is
// enabled, 0 otherwise. On old emulator versions it will be undefined.
sSupportsOpenGL = qemu_gles > 0;
return sSupportsOpenGL.booleanValue();
}

软件绘制

Android-View绘制流程 知道软件绘制从 VRImpl.drawSoftware 方法开始。

VRImpl.drawSoftware

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty, Rect surfaceInsets) {

// Draw with software renderer.
final Canvas canvas;
canvas = mSurface.lockCanvas(dirty);
canvas.setDensity(mDensity);

try {
dirty.setEmpty();
mView.draw(canvas);
} finally {
surface.unlockCanvasAndPost(canvas);
}
return true;
}

上面的软件绘制可以分成三个步骤:

  1. 通过 Surface.lockCanvas 方法向 SurfaceFlinger Layer 申请一段共享内存
  2. 调用 View.draw 方法将绘制数据写入缓存区
  3. 通过 Surface.unlockCanvasAndPost 方法将图形缓存区数据入 Layer 的缓存队列并通知 SurfaceFlinger 进行合成

Surface.lockCanvas

1
2
3
4
5
6
7
8
9
10
public Canvas lockCanvas(Rect inOutDirty) throws Surface.OutOfResourcesException, IllegalArgumentException {
synchronized (mLock) {
if (mLockedObject != 0) {
// refuse to re-lock the Surface.
throw new IllegalArgumentException("Surface was already locked");
}
mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);
return mCanvas;
}
}

这里调用 native 方法 nativeLockCanvas 来获取 mLockedObject 指针,上面说过 mNativeObject 指向 native 层创建的 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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// frameworks/base/core/jni/android_view_Surface.cpp
static jlong nativeLockCanvas(JNIEnv* env, jclass clazz, jlong nativeObject, jobject canvasObj, jobject dirtyRectObj) {
sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));

// 根据 Dirty 区域创建 native Rect 对象
Rect dirtyRect(Rect::EMPTY_RECT);
Rect* dirtyRectPtr = NULL;

if (dirtyRectObj) {
// 根据 Java 层 dirtyRectObj 初始化 dirtyRect 和 dirtyRectPtr
}

ANativeWindow_Buffer outBuffer;
// 调用 lock 方法
status_t err = surface->lock(&outBuffer, dirtyRectPtr);
// 根据 outBuffer 创建 SkImageInfo
SkImageInfo info = SkImageInfo::Make(outBuffer.width, outBuffer.height, convertPixelFormat(outBuffer.format),
outBuffer.format == PIXEL_FORMAT_RGBX_8888 ? kOpaque_SkAlphaType : kPremul_SkAlphaType,
GraphicsJNI::defaultColorSpace());
// 设置 bitmap
SkBitmap bitmap;
ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
bitmap.setInfo(info, bpr);
if (outBuffer.width > 0 && outBuffer.height > 0) {
bitmap.setPixels(outBuffer.bits);
} else {
// be safe with an empty bitmap.
bitmap.setPixels(NULL);
}
// 将 native bitmap 设置给 native Canvas
Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);
nativeCanvas->setBitmap(bitmap);
if (dirtyRectPtr) {
nativeCanvas->clipRect(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom, SkClipOp::kIntersect);
}
// 返回
sp<Surface> lockedSurface(surface);
lockedSurface->incStrong(&sRefBaseOwner);
return (jlong) lockedSurface.get();
}

上面 nativeLockCanvas 方法根据脏区创建了一个 Rect 对象,然后调用 surface->lock:

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
status_t Surface::lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds) {
ANativeWindowBuffer* out;
int fenceFd = -1;
status_t err = dequeueBuffer(&out, &fenceFd); // 取出一个图形缓存区 GraphicBuffer
sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out));
// 锁定 GraphicBuffer
status_t res = backBuffer->lockAsync(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, newDirtyRegion.bounds(), &vaddr, fenceFd);

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;
}

int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
// ...
status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, reqWidth, reqHeight,
reqFormat, reqUsage, &mBufferAge, enableFrameTimestamps ? &frameTimestamps : nullptr);
// ...
*buffer = gbuf.get();
// ...
}

mGraphicBufferProducer 是 Layer 中的 BufferQueueProducer – graph buffer 的生产者,在绘制时通过 BufferQueueProducer 生产者从 BufferQueue 中取出一个 GraphicBuffer 缓存区用来绘制。

小结: Surface.lockCanvas 方法的作用是通过 BufferQueueProducer 生产者从 BufferQueue 队列中取出一个图形缓存区 GraphicBuffer(用来创建 Canvas 中的 Bitmap 对象) 并锁定该 Surface,然后将 Surface 的地址返回给 Java 层 Surface 中的 mLockedObject 属性

View.draw

Android-View绘制原理, 以 drawLines 为例:

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
// BaseCanvas
public void drawLines(@Size(multiple = 4) @NonNull float[] pts, int offset, int count, @NonNull Paint paint) {
nDrawLines(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance());
}

// {"nDrawLines", "(J[FIIJ)V", (void*) CanvasJNI::drawLines}
// nDrawLines 方法对应 SkiaCanvas::drawLines
void SkiaCanvas::drawLines(const float* points, int count, const SkPaint& paint) {
this->drawPoints(points, count, paint, SkCanvas::kLines_PointMode);
}

void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint, SkCanvas::PointMode mode) {
// ...
// mCanvas 是 SkCanvas 类型指针
mCanvas->drawPoints(mode, count, pts.get(), paint);
}

void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
this->onDrawPoints(mode, count, pts, paint);
}

void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
const SkPaint& paint) {
// ...
while (iter.next()) {
iter.fDevice->drawPoints(mode, count, pts, looper.paint());
}
}

在 Android 新版本中,Google 开始逐渐让 Skia 接手 OpenGL,实现间接统一调用,因此无论是软件绘制还是硬件加速采用的都是 native 层 SkiaCanvas 对象,然后通过 fDevice->drawPoints 来真正实现绘制(构建)。

1
2
3
4
5
// 这里应该是软件绘制的 Device 类...吧,大概就是这么个意思
void SkBitmapDevice::drawPoints(SkCanvas::PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
// 绘制在 Bitmap 中
BDDraw(this).drawPoints(mode, count, pts, paint, nullptr);
}

Surface.unlockCanvasAndPost

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public void unlockCanvasAndPost(Canvas canvas) {
synchronized (mLock) {
checkNotReleasedLocked();
if (mHwuiContext != null) { // 硬件绘制走这里
mHwuiContext.unlockAndPost(canvas);
} else {
unlockSwCanvasAndPost(canvas);
}
}
}

private void unlockSwCanvasAndPost(Canvas canvas) {
// ...
try {
nativeUnlockCanvasAndPost(mLockedObject, canvas);
} finally {
nativeRelease(mLockedObject);
mLockedObject = 0;
}
}

这里通过之前锁定的 Surface 的地址调用了 native 层的 nativeUnlockCanvasAndPost 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static void nativeUnlockCanvasAndPost(JNIEnv* env, jclass clazz, jlong nativeObject, jobject canvasObj) {
sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));

// detach the canvas from the surface
Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);
nativeCanvas->setBitmap(SkBitmap());

// unlock surface
status_t err = surface->unlockAndPost();
}

status_t Surface::unlockAndPost() {
int fd = -1;
status_t err = mLockedBuffer->unlockAsync(&fd);
err = queueBuffer(mLockedBuffer.get(), fd);
mPostedBuffer = mLockedBuffer;
mLockedBuffer = 0;
return err;
}

mLockedBuffer 就是前面的 backBuffer, mLockedBuffer->unlockAsync 方法用来解除 GraphicBuffer 的锁定状态,然后看一下 queueBuffer 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
// ...
int i = getSlotFromBufferLocked(buffer);
// ...
IGraphicBufferProducer::QueueBufferOutput output;
IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp,
static_cast<android_dataspace>(mDataSpace), crop, mScalingMode,
mTransform ^ mStickyTransform, fence, mStickyTransform, mEnableFrameTimestamps);
// ...
status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);
// ...
return err;
}

上面 mGraphicBufferProducer->queueBuffer 的具体代码就不看了,其逻辑是通过 mGraphicBufferProducer 生产者将填充了绘制数据的图形缓存区入 BufferQueue 队列,在 queueBuffer 调用后,会调用到 SF.signalLayerUpdate 方法:

1
2
3
4
5
6
7
void SurfaceFlinger::signalLayerUpdate() {
mEventQueue->invalidate();
}

void MessageQueue::invalidate() {
mEvents->requestNextVsync();
}

到这里就回到了 Android-SurfaceFlinger启动与绘图原理 的流程,它用来请求下一次 Vsync 信号。

小结

软件绘制可以简单分成以下三个步骤:

  1. Surface.lockCanvas 方法通过 BufferQueueProducer.dequeueBuffer 函数从 BufferQueue 中取出一个图形缓存区 GraphicBuffer(用来创建 Canvas 中的 Bitmap 对象) 并锁定该 Surface,然后将 Surface 的地址返回给 Java 层 Surface 中的 mLockedObject 属性。在这个方法中还会涉及到 Surface 的双缓冲逻辑,后面会具体讲解。
  2. 调用 View.draw 方法将内容绘制到 Canvas 对应的 Bitmap 中,其实就是往上面的图形缓存区 GraphicBuffer 填充绘制数据。
  3. Surface.unlockCanvasAndPost 方法通过调用被锁定的 surface->unlockAndPost 方法解锁 Surface 且通过 queueBuffer 函数将填充了数据的图形缓存区 GraphicBuffer 存入 BufferQueue 队列中,然后通知给 SurfaceFlinger 进行合成(请求 Vsync 信号)。

BufferQueueProducer 中的两个重要函数:

  • dequeueBuffer: BufferQueueProducer 生产者通过 BufferQueue 请求一块空闲的缓存区(GraphicBuffer)
  • queueBuffer: BufferQueueProducer 生产者将填充了数据的缓存区(GraphicBuffer)入 BufferQueue 队列

与 BufferQueue 以及生产者/消费者相关的几个重要方法在后续文章里会讲到,有上面的生产者就肯定有消费者的工作,大概猜一下可以知道消费者应该就是在 SurfaceFlinger 进程中通过 BufferQueueConsumer 去从 BufferQueue 中取出 GraphicBuffer 中的数据进行合成的,具体逻辑有兴趣的话在参考了 Android-SurfaceFlinger启动与工作流程 后可以自行阅读源码。

对于软件绘制中的 Canvas 而言其绘制目标是一个 Bitmap 对象,绘制的内容会填充到 Surface 持有的缓存区(GraphicBuffer)里。

硬件绘制

参考: https://www.jianshu.com/p/40f660e17a73

Android-View绘制流程 知道硬件绘制从 ThreadedRenderer.draw 方法开始。

ThreadedRenderer.draw

1
2
3
4
5
6
7
8
9
void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks, FrameDrawingCallback frameDrawingCallback) {
final Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer;
choreographer.mFrameInfo.markDrawStart();
// 构建 View 的 DrawOp 树
updateRootDisplayList(view, callbacks);
// 通知 RenderThread 线程进行绘制
int syncResult = nSyncAndDrawFrame(mNativeProxy, frameInfo, frameInfo.length);
// ...
}

可以将硬件绘制分为两个阶段:构建阶段 和 渲染阶段。

构建阶段

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
private RenderNode mRootNode;

private void updateRootDisplayList(View view, DrawCallbacks callbacks) {
// 如有必要则更新 View 的 DisplayList
updateViewTreeDisplayList(view);

if (mRootNodeNeedsUpdate || !mRootNode.isValid()) {
// 通过 RenderNode 获取 DisplayListCanvas
DisplayListCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight);
try {
final int saveCount = canvas.save();
canvas.translate(mInsetLeft, mInsetTop);
callbacks.onPreDraw(canvas);

canvas.insertReorderBarrier();
// 利用 DisplayListCanvas 构建并缓存所有的 DrawOp
canvas.drawRenderNode(view.updateDisplayListIfDirty());
canvas.insertInorderBarrier();

callbacks.onPostDraw(canvas);
canvas.restoreToCount(saveCount);
mRootNodeNeedsUpdate = false;
} finally {
// 将 View 构建的 DrawOp 存入 RenderNode 中,完成构建
mRootNode.end(canvas);
}
}
}

// View.java
public RenderNode updateDisplayListIfDirty() {
final RenderNode renderNode = mRenderNode;
if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || !renderNode.isValid() || (mRecreateDisplayList)) {
// ...
final DisplayListCanvas canvas = renderNode.start(width, height);
try {
if (layerType == LAYER_TYPE_SOFTWARE) {// 强制使用软件绘制
buildDrawingCache(true);
Bitmap cache = getDrawingCache(true);
if (cache != null) {
canvas.drawBitmap(cache, 0, 0, mLayerPaint);
}
} else {
// 如果自身不用绘制则直接递归子View
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
dispatchDraw(canvas);
} else {// 调用 draw 方法,如果是 ViewGroup 会递归子View
draw(canvas);
}
}
} finally {
// 缓存构建Op
renderNode.end(canvas);
}
}
return renderNode;
}

这里我们创建的 Canvas 是 DisplayListCanvas 类型实例,在调用 View.draw 方法后,使用 DisplayListCanvas 来绘图,以 drawLines 为例:

1
2
3
4
5
6
7
8
9
10
11
// DisplayListCanvas
public final void drawLines(@Size(multiple = 4) @NonNull float[] pts, int offset, int count, @NonNull Paint paint) {
nDrawLines(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance());
}

// 由上面软件绘制 Canvas.draw 可知实现可能是在这里...
void SkGpuDevice::drawPoints(SkCanvas::PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
// ...
// 从 fRenderTargetContext 中的源码可以看出来这里面会构建 DrawOp
fRenderTargetContext->drawPath(this->clip(), std::move(grPaint), GrAA(paint.isAntiAlias()), this->ctm(), path, style);
}

上面在 View.updateDisplayListIfDirty 方法中会遍历所有子 View 并通过 DisplayListCanvas 构建出一个 DrawOp 树,在递归完成 DrawOp 的构建后,会调用 RenderNode.end 方法:

1
2
3
4
5
6
public void end(DisplayListCanvas canvas) {
long displayList = canvas.finishRecording();
// 将 displayList 缓存到 native 层的 RenderNode 中
nSetDisplayList(mNativeRenderNode, displayList);
canvas.recycle();
}

RenderNode.end 方法用来将 displayList 缓存到 native 层的 RenderNode 中。在 updateDisplayListIfDirty 方法遍历了子 View 并将缓存了 displayList 的 RenderNode 返回后,ThreadedRenderer 通过 DisplayListCanvas.drawRenderNode 方法将之前返回的 RenderNode 合入 ThreadedRenderer 内部的 RenderNode 中,然后也通过 RenderNode.end 方法将 displayList 缓存到 native 层的 RenderNode 里。

渲染阶段

申请内存

软件绘制申请内存是通过 Surface.lockCanvas 方法借由 BufferQueueProducer 取出一个图形缓存区 GraphicBuffer。至于硬件加速的内存是怎么申请的可以看看这部分代码(performTraversals 方法应该很熟悉了):

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
private void performTraversals() {
// ...
if (mAttachInfo.mThreadedRenderer != null) {
try {
hwInitialized = mAttachInfo.mThreadedRenderer.initialize(mSurface);
if (hwInitialized && (host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0) {
mSurface.allocateBuffers();
}
} catch (OutOfResourcesException e) {
handleOutOfResourcesException(e);
return;
}
}
// ...
}

// Surface.java
public void allocateBuffers() {
synchronized (mLock) {
checkNotReleasedLocked();
nativeAllocateBuffers(mNativeObject);
}
}

// frameworks/base/core/jni/android_view_Surface.cpp
static void nativeAllocateBuffers(JNIEnv* /* env */ , jclass /* clazz */, jlong nativeObject) {
sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
surface->allocateBuffers();
}

void Surface::allocateBuffers() {
// 依旧是调用 BufferQueueProducer 生产者分配 buffer
mGraphicBufferProducer->allocateBuffers(reqWidth, reqHeight, mReqFormat, mReqUsage);
}

虽然硬件加速申请内存调用的方法不一样,但看上去也是借助 Layer 中的 BufferQueueProducer 生产者从 BufferQueue 中出队列了一块空闲缓存区。

可以看到硬件加速请求 SurfaceFlinger 内存分配的时机会比软件绘制更前,硬件加速这么设计可以预先分配内存,避免在渲染的时候再申请,防止分配内存失败时浪费了 CPU 之前的构建等工作,另外也可以将渲染线程的工作简化。

渲染线程绑定Surface

接着看一下 Render 线程是怎么跟目标 Surface 绘图界面绑定的(因为同一时刻可能有多个 Surface 绘图界面,它需要绑定一个渲染的上下文),从上面看到申请内存前调用了 ThreadedRenderer.initialize 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// ThreadedRenderer
boolean initialize(Surface surface) throws OutOfResourcesException {
boolean status = !mInitialized;
mInitialized = true;
updateEnabledState(surface);
nInitialize(mNativeProxy, surface);
return status;
}

// frameworks/base/core/jni/android_view_ThreadedRenderer.cpp
static void android_view_ThreadedRenderer_initialize(JNIEnv* env, jobject clazz, jlong proxyPtr, jobject jsurface) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
sp<Surface> surface = android_view_Surface_getSurface(env, jsurface);
proxy->initialize(surface);
}

void RenderProxy::initialize(const sp<Surface>& surface) {
// 向 Render 线程发送消息,执行 CanvasContext->setSurface 方法
mRenderThread.queue().post([ this, surf = surface ]() mutable { mContext->setSurface(std::move(surf)); });
}

上面初始化时 CanvasContext 上下文通过 setSurface 方法将当前要渲染的 Surface 绑定到了 Render 线程中。

渲染

当渲染线程绑定了 Surface,且 Surface 内存分配以及 DrawOp 树构建完成后,便可以看一下渲染流程,从上面的 nSyncAndDrawFrame 方法开始,其实现在 Native 层:

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
// frameworks/base/core/jni/android_view_ThreadedRenderer.cpp
static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
jlong proxyPtr, jlongArray frameInfo, jint frameInfoSize) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
env->GetLongArrayRegion(frameInfo, 0, frameInfoSize, proxy->frameInfo());
return proxy->syncAndDrawFrame();
}

int RenderProxy::syncAndDrawFrame() {
return mDrawFrameTask.drawFrame();
}

int DrawFrameTask::drawFrame() {
postAndWait();
return mSyncResult;
}

void DrawFrameTask::postAndWait() {
AutoMutex _lock(mLock);
mRenderThread->queue().post([this]() { run(); });
mSignal.wait(mLock);
}

void DrawFrameTask::run() {
// ...
if (CC_LIKELY(canDrawThisFrame)) { // CanvasContext
context->draw();
} else {
// wait on fences so tasks don't overlap next frame
context->waitOnFences();
}
}

接下来所有的 DrawOp 都会通过 OpenGL 被绘制到 GraphicBuffer 中,然后通知 SurfaceFlinger 进行合成,具体源码不贴了,因为看不大懂。

小结

硬件加速可以从两个阶段来看:

  1. 构建阶段:将 View 抽象成 RenderNode 节点,其每个绘制操作(drawLine…)都会抽象成 DrawOp 操作,它存在对应的 OpenGL 绘制命令并保存了绘图需要的数据。这个阶段会递归遍历所有 View 并通过 Canvas.drawXXX 将绘制操作转化成 DrawOp 存入 DisplayList 中,根据 ViewTree 模型,这个 DisplayList 虽然命名为 List,但其实更像一棵树。
  2. 绘制阶段:通过单独的 Render 线程,依赖 GPU 绘制上面的 DrawOp 数据。

其中硬件加速的内存申请跟软件绘制一样都是借助 Layer 中的 BufferQueueProducer 生产者从 BufferQueue 中出队列一块空闲缓存区 GraphicBuffer 用来渲染数据的,之后也都会通知 SurfaceFlinger 进行合成。不一样的地方在于硬件加速相比软件绘制而言算法可能更加合理,同时采用了一个单独的 Render 线程,减轻了主线程的负担。

双缓冲(View绘制过程)

一般来说将双缓冲用到的两块缓冲区称为 – 前缓冲区(front buffer) 和 后缓冲区(back buffer)。显示器显示的数据来源于 front buffer 前缓存区,而每一帧的数据都绘制到 back buffer 后缓存区,在 Vsync 信号到来后会交互缓存区的数据(指针指向),这时 front buffer 和 back buffer 的称呼及功能倒转。

双缓冲的使用范围非常广泛,比如说在屏幕图像显示的时候就应用到了双缓冲 – 分为屏幕前缓冲区和屏幕后缓冲区,此外还有三缓冲…这里主要看看 View 在绘制的过程中是怎么使用双缓冲的。

软件绘制中的双缓冲

通过之前的解析可以知道软件绘制可分为三个步骤:

  1. Surface.lockCanvas: 会调用到 Native 层的 Surface.lock 方法
  2. View.draw: 将绘制数据写入缓存区
  3. Surface.unlockCanvasAndPost: 会调用到 Surface.unlockAndPost 方法

双缓冲的解析可以从 Native 层的 Surface.lock 方法开始看起:

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
status_t Surface::lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds) {
ANativeWindowBuffer* out;
// 通过生产者从 QueueBuffer 队列中取出一块空闲的图形缓存区--GraphicBuffer
status_t err = dequeueBuffer(&out, &fenceFd);
// 将 GraphicBuffer 赋值给后缓存区 backBuffer
sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out));
const Rect bounds(backBuffer->width, backBuffer->height);
// 计算新的脏区--1处图
Region newDirtyRegion;
if (inOutDirtyBounds) {
// App 通过调用 lockCanvas(Rect inOutDirty) 传递了一个脏区
// 则将传入的 inOutDirty 作为新的脏区
newDirtyRegion.set(static_cast<Rect const&>(*inOutDirtyBounds));
newDirtyRegion.andSelf(bounds);
} else {// 否则将后缓存区大小作为脏区
newDirtyRegion.set(bounds);
}
// 将正在显示的 mPostedBuffer 缓存区赋值给 frontBuffer 前缓存区
const sp<GraphicBuffer>& frontBuffer(mPostedBuffer);
// 是否需要将前缓存区拷贝到后缓存区
// 前缓存区有内容 && 前后缓存区的长宽及格式一样
// 第一次绘制时 frontBuffer 是没内容的
const bool canCopyBack = (frontBuffer != 0 &&
backBuffer->width == frontBuffer->width &&
backBuffer->height == frontBuffer->height &&
backBuffer->format == frontBuffer->format);

if (canCopyBack) {
// 可以拷贝时--2处图
// copy the area that is invalid and not repainted this round
const Region copyback(mDirtyRegion.subtract(newDirtyRegion));
if (!copyback.isEmpty()) {
copyBlt(backBuffer, frontBuffer, copyback, &fenceFd);
}
} else {
// 不能拷贝时,后缓存区直接取新脏区的区域,确保重绘整个区域
newDirtyRegion.set(bounds);
mDirtyRegion.clear();
Mutex::Autolock lock(mMutex);
for (size_t i=0 ; i<NUM_BUFFER_SLOTS ; i++) {
mSlots[i].dirtyRegion.clear();
}
}
// 锁定 backBuffer
status_t res = backBuffer->lockAsync(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
newDirtyRegion.bounds(), &vaddr, fenceFd);
if (res != 0) {
err = INVALID_OPERATION;
} else {
// 将后缓存区赋值给 mLockedBuffer
mLockedBuffer = backBuffer;
outBuffer->width = backBuffer->width;
outBuffer->height = backBuffer->height;
outBuffer->stride = backBuffer->stride;
outBuffer->format = backBuffer->format;
outBuffer->bits = vaddr;
}
return err;
}

上图的注释都比较清晰了,看一下上面标数字的 1 和 2 处逻辑:

软件绘制双缓冲

在拷贝后,后缓存区浅绿色的部分就是要重绘的区域,而绿色区域是之前前缓存区显示的内容,与脏区相交的部分要重绘,未相交的区域则不需要重绘,下次显示时接着使用该区域即可。

接下来看看 Surface.unlockAndPost 方法,其中 mLockedBuffer 在 lock 方法中被赋值为 backBuffer:

1
2
3
4
5
6
7
8
9
10
11
status_t Surface::unlockAndPost() {
int fd = -1;
// 解锁 mLockedBuffer
status_t err = mLockedBuffer->unlockAsync(&fd);
// 将绘制后的缓存区入队列,等待被合成显示
err = queueBuffer(mLockedBuffer.get(), fd);
// 赋值给 mPostedBuffer,代表要被显示的数据
mPostedBuffer = mLockedBuffer;
mLockedBuffer = 0;
return err;
}

小结

Surface.lock:

  1. 将出队列的空闲缓存区 GraphicBuffer 赋给后缓存区 backBuffer,将正在显示的 mPostedBuffer 赋给前缓存区。
  2. 计算新的脏区,并确定是否需要将前缓存区拷贝到后缓存区,依此计算出后缓存区 backBuffer 的最终数据。然后将 backBuffer 与应用层的 Canvas 关联,当操作 Canvas 绘图时会将数据绘制到 backBuffer 上。
  3. 锁定 backBuffer 且将 backBuffer 指针赋值给 mLockedBuffer。

Surface.unlockAndPost:

  1. 将存有绘制数据的 mLockedBuffer 解锁并将其赋值给 mPostedBuffer。
  2. 将 mLockedBuffer 入 BufferQueue 队列,等待被合成显示,在这里便相当于交换了前后缓冲区的指针,等到下次绘制时,接着重复上面的步骤。

硬件绘制中的双缓冲

由之前的解析可以知道硬件绘制最终会调用 CanvasContext.draw 方法来绘制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void CanvasContext::draw() {
SkRect dirty;
mDamageAccumulator.finish(&dirty);
Frame frame = mRenderPipeline->getFrame();
// 计算脏区
SkRect windowDirty = computeDirtyRect(frame, &dirty);
// 渲染
bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue,
mContentDrawBounds, mOpaque, mWideColorGamut, mLightInfo,
mRenderNodes, &(profiler()));
// 交换缓存区
bool didSwap = mRenderPipeline->swapBuffers(frame, drew, windowDirty, mCurrentFrameInfo, &requireSwap);
// ...
}

具体的代码比较复杂(看不太懂了),大概可以看出硬件绘制时也是存在双缓冲的。

SurfaceView

概述

SurfaceView 是一种较之 TextView, Button 等更为特殊的 View, 它不与其宿主的 Window 共享一个 Surface, 而是有自己的独立 Surface。并且它可以在一个独立的线程中绘制 UI。因此 SurfaceView 一般用来实现比较复杂的图像或动画/视频的显示。

这里插入一个内容,关于 View 为啥不能在子线程中操作 UI 的话可以看看 ViewRootImpl.checkThread 这个方法,参考 ViewRootImpl.checkThread 方法 其实就是因为每次操作 UI 都会去 check 线程,当然前提是 ViewRootImpl 已经被实例化了~

  • SurfaceView 在绘图时实现了双缓冲机制(独立的 Surface)
  • 普通 View 在绘图时会绘制到 Bitmap 中,然后通过其所在 Window 的 Surface 对象实现双缓冲。

一般来说,每个 Window 都有对应的 Surface 绘制表面,它在 SurfaceFlinger 服务中对应一个 Layer。而对于存在 SurfaceView 的 Window 来说,它除了自己的 Surface 以外还会有另一个 SurfaceView 独有的 Surface 绘制表面,在 SurfaceFlinger 服务中也会存在着两个 Layer 分别对应它们。查看 SurfaceView 的源码可以看到类似于 ViewRootImpl 一样其内部都有一个单独的 Surface 实例,于是结合之前的解析,就能理解刚才对 SurfaceView 的描述了。

SurfaceView 官方注释有一段: The surface is Z ordered so that it is behind the window holding its SurfaceView; the SurfaceView punches a hole in its window to allow its surface to be displayed.

翻译一下大体意思是:Surface 是按照 Z 轴顺序排列的,SurfaceView 的 Surface 位于其宿主窗口的 Surface 后面;SurfaceView 在其窗口上打一个孔,以显示其 Surface。这个孔实际上是 SurfaceView 在其宿主窗口上设置了一块透明区域。

示例

接下来看一个使用 SurfaceView 的示例:

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
class MySurfaceView(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) :
SurfaceView(context, attrs, defStyleAttr), SurfaceHolder.Callback, Runnable {

companion object {
private const val TAG = "MySurfaceView"
}

private val surfaceHolder: SurfaceHolder = holder
private var canvas: Canvas? = null

@Volatile
private var canDoDraw = false
private val drawThread = Thread(this)
private val lock = Object()

private var xx = 0f
private var yy = 400f
private val path = Path()
private val paint = Paint()

constructor(context: Context?) : this(context, null, 0)
constructor(context: Context?, attrs: AttributeSet?) : this(context, attrs, 0)

init {
surfaceHolder.addCallback(this)
isFocusable = true
isFocusableInTouchMode = true
keepScreenOn = true
path.moveTo(xx, yy)
paint.style = Paint.Style.STROKE
paint.strokeWidth = 10f
paint.color = Color.RED
drawThread.start()
}

override fun surfaceCreated(holder: SurfaceHolder?) {
Log.d(TAG, "surfaceCreated")
setCanDraw(true)
}

override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) {
Log.d(TAG, "surfaceChanged")
}

override fun surfaceDestroyed(holder: SurfaceHolder?) {
Log.d(TAG, "surfaceDestroyed")
canDoDraw = false
// 注意当APP到后台时会 destroy Surface, 回到前台会重新调用 surfaceCreated
// 因此这里不能移除回调,否则会黑屏
// surfaceHolder.removeCallback(this)
}

override fun onTouchEvent(event: MotionEvent?): Boolean {
Log.d(TAG, "onTouchEvent")
setCanDraw(!canDoDraw)
return super.onTouchEvent(event)
}

private fun setCanDraw(canDraw: Boolean) {
if (canDraw) {
synchronized(lock) {
try {
lock.notifyAll()
} catch (e: Exception) {
}
}
}
canDoDraw = canDraw
}

override fun run() {
while (true) {
synchronized(lock) {
if (!canDoDraw) {
try {
lock.wait()
} catch (e: Exception) {
e.printStackTrace()
}
}
}
draw()
xx += 1
yy = (100 * sin(xx * 2 * Math.PI / 180) + 400).toFloat()
path.lineTo(xx, yy)
}
}

private fun draw() {
try {
canvas = surfaceHolder.lockCanvas()
canvas?.drawColor(Color.WHITE)
canvas?.drawPath(path, paint)
} catch (e: Exception) {
e.printStackTrace()
} finally {
surfaceHolder.unlockCanvasAndPost(canvas ?: return)
}
}
}

源码解析

先看一下 SurfaceHolder 这个接口:

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
public interface SurfaceHolder {
public interface Callback {
// 首次创建 Surface 后会调用此方法,在这个回调里应该开始绘制任务
// 只有一个线程可以绘制到 Surface 中,如果渲染任务将在另一个线程中进行则不能在此处绘制 Surface
public void surfaceCreated(SurfaceHolder holder);

// 当 Surface 结构更改(format or size)后会调用此方法,在这个回调里应该更新 Surface 的图像
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height);

// 在 Surface destroy 之前会调用此方法,在这个回调以后不应该尝试访问此 Surface
// 如果具有直接访问*曲面的渲染线程,则必须确保该线程不再接触*曲面,然后才能从此函数返回。
public void surfaceDestroyed(SurfaceHolder holder);
}

public void addCallback(Callback callback);
public void removeCallback(Callback callback);
// 是否正在通过 Callback 方法创建 Surface
public boolean isCreating();
public void setType(int type); // Sets the surface's type.
public void setFixedSize(int width, int height);
public void setSizeFromLayout();
public void setFormat(int format);
// Enable or disable option to keep the screen turned on while this surface is displayed.
public void setKeepScreenOn(boolean screenOn); // 默认 false
public Canvas lockCanvas();
public Canvas lockCanvas(Rect dirty);
default Canvas lockHardwareCanvas() {
throw new IllegalStateException("This SurfaceHolder doesn't support lockHardwareCanvas");
}
public void unlockCanvasAndPost(Canvas canvas);
public Rect getSurfaceFrame();
public Surface getSurface();
}

可以看出 SurfaceHolder 是用来管理 Surface 的类。接下来看下 SurfaceView 的 draw 源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Override
public void draw(Canvas canvas) {
if (mDrawFinished && !isAboveParent()) {
// draw() is not called when SKIP_DRAW is set
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) {
// punch a whole in the view-hierarchy below us
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
}
}
super.draw(canvas);
}

@Override
protected void dispatchDraw(Canvas canvas) {
if (mDrawFinished && !isAboveParent()) {
// draw() is not called when SKIP_DRAW is set
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
// punch a whole in the view-hierarchy below us
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
}
}
super.dispatchDraw(canvas);
}

SurfaceView 方法中 draw 和 dispatchDraw 的参数 canvas 是从宿主的 Surface 中获取的,因此在该 canvas 上绘制的内容都会出现在宿主的 Surface 上。

所以可以看到 SurfaceView.draw 和 SurfaceView.dispatchDraw 方法的逻辑是:如果当前 SurfaceView 不是用作宿主窗口面板,则 SurfaceView 在其宿主窗口 Surface 上的操作只是清空 Canvas 区域,因为 SurfaceView 的内容是需要展现在自己单独的 Surface 上的(像上面的示例一样,通过其 Surface 拿到一个 Canvas 并在一个独立线程在其上进行绘制)。

接着看一下 SurfaceView 中 SurfaceHolder 的实现:

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
public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallback {
final ArrayList<SurfaceHolder.Callback> mCallbacks = new ArrayList<>();
final ReentrantLock mSurfaceLock = new ReentrantLock();
final Surface mSurface = new Surface();

public SurfaceHolder getHolder() {
return mSurfaceHolder;
}

private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
@Override
public void addCallback(Callback callback) {
synchronized (mCallbacks) { // 添加回调
if (mCallbacks.contains(callback) == false) {
mCallbacks.add(callback);
}
}
}

@Override
public void setFixedSize(int width, int height) {
if (mRequestedWidth != width || mRequestedHeight != height) {
mRequestedWidth = width;
mRequestedHeight = height;
requestLayout(); // 重新 layout
}
}

@Override
public void setKeepScreenOn(boolean screenOn) {
runOnUiThread(() -> SurfaceView.this.setKeepScreenOn(screenOn));
}

@Override
public Canvas lockCanvas() {
return internalLockCanvas(null, false);
}

@Override
public Canvas lockCanvas(Rect inOutDirty) {
return internalLockCanvas(inOutDirty, false);
}

@Override
public Canvas lockHardwareCanvas() {
return internalLockCanvas(null, true);
}

// 锁定 Surface
private Canvas internalLockCanvas(Rect dirty, boolean hardware) {
mSurfaceLock.lock();

Canvas c = null;
if (!mDrawingStopped && mSurfaceControl != null) {
try {
if (hardware) { // 硬件渲染
c = mSurface.lockHardwareCanvas();
} else { // 软件渲染
c = mSurface.lockCanvas(dirty);
}
} catch (Exception e) {
Log.e(LOG_TAG, "Exception locking surface", e);
}
}

if (c != null) {
mLastLockTime = SystemClock.uptimeMillis();
return c;
}

// 当返回 null 时使 internalLockCanvas 被调用的间隔超过100ms
long now = SystemClock.uptimeMillis();
long nextTime = mLastLockTime + 100;
if (nextTime > now) {
try {
Thread.sleep(nextTime-now);
} catch (InterruptedException e) {
}
now = SystemClock.uptimeMillis();
}
mLastLockTime = now;
mSurfaceLock.unlock();

return null;
}

@Override
public void unlockCanvasAndPost(Canvas canvas) {
mSurface.unlockCanvasAndPost(canvas);
mSurfaceLock.unlock();
}

@Override
public Surface getSurface() {
return mSurface;
}

// ...
}
}

可以看出调用 SurfaceHolder 的 lock 和 unlock 系列方法都是调用到了 Surface 中的方法。除了 lockHardwareCanvas 方法,其他的都已经看过了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Surface
public Canvas lockHardwareCanvas() {
synchronized (mLock) {
checkNotReleasedLocked();
if (mHwuiContext == null) {
mHwuiContext = new HwuiContext(false);
}
return mHwuiContext.lockCanvas(nativeGetWidth(mNativeObject), nativeGetHeight(mNativeObject));
}
}

// HwuiContext
Canvas lockCanvas(int width, int height) {
if (mCanvas != null) {
throw new IllegalStateException("Surface was already locked!");
}
mCanvas = mRenderNode.start(width, height);
return mCanvas;
}

可以看到 HwuiContext.lockCanvas 是使用硬件加速的方式,其调用的 RenderNode.start 之前已经看过了,与之对应的 RenderNode.end 方法是在这里调用的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Surface
public void unlockCanvasAndPost(Canvas canvas) {
synchronized (mLock) {
checkNotReleasedLocked();

if (mHwuiContext != null) { // 不为空时
mHwuiContext.unlockAndPost(canvas);
} else {
unlockSwCanvasAndPost(canvas);
}
}
}

// HwuiContext
void unlockAndPost(Canvas canvas) {
if (canvas != mCanvas) {
throw new IllegalArgumentException("canvas object must be the same instance that "
+ "was previously returned by lockCanvas");
}
mRenderNode.end(mCanvas);
mCanvas = null;
nHwuiDraw(mHwuiRenderer);
}

即 SurfaceView 的绘制兼顾了软件绘制和硬件加速绘制。另外在 SurfaceView.updateSurface 方法中会更新 Surface 的状态并将其回调给 SurfaceHolder.Callback 相关方法,具体逻辑便不给出了。

总结

  • Java 层的 Surface 对象中 mNativeObject 属性指向 native 层中创建的 Surface 对象。
  • Surface 对应 SurfaceFlinger 中的 Layer 对象,它持有 Layer 中的 BufferQueueProducer 指针(生产者),通过这个生产者对象可以在绘制时向 BufferQueue 申请一块空闲的图形缓存区 GraphicBuffer,在 Surface 上绘制的内容会存入该缓存区内。
  • SurfaceFlinger 通过 BufferQueueConsumer 消费者从 BufferQueue 中取出 GraphicBuffer 中的数据进行合成渲染并送到显示器显示。

软件绘制

软件绘制可能会绘制到不需要重绘的视图,且其绘制过程在主线程进行的,可能会造成卡顿等情况。它把要绘制的内容写进一个 Bitmap 位图,其实就是填充到了 Surface 申请的图形缓存区里。

软件绘制可分为三个步骤:

  1. Surface.lockCanvas – dequeueBuffer 从 BufferQueue 中出队列一块缓存区。
  2. View.draw – 绘制内容。
  3. Surface.unlockCanvasAndPost – queueBuffer 将填充了数据的缓存区存入 BufferQueue 队列中,然后通知给 SurfaceFlinger 进行合成(请求 Vsync 信号)。

硬件绘制

硬件绘制会将绘制函数作为绘制指令(DrawOp)记录在一个列表(DisplayList)中,然后交给单独的 Render 线程使用 GPU 进行硬件加速渲染。它只需要针对需要更新的 View 对象的脏区进行记录或更新,无需更新的 View 对象则能重用先前 DisplayList 中记录的指令。

硬件绘制可分为两个阶段:

  1. 构建阶段:将 View 的绘制操作(drawLine…)抽象成 DrawOp 操作并存入 DisplayList 中。
  2. 绘制阶段:首先分配缓存区(同软件绘制),然后将 Surface 绑定到 Render 线程,最后通过 GPU 渲染 DrawOp 数据。

硬件加速的内存申请跟软件绘制一样都是借助 Layer 中的 BufferQueueProducer 生产者从 BufferQueue 中出队列一块空闲缓存区 GraphicBuffer 用来渲染数据的,之后也都会通知 SurfaceFlinger 进行合成。不一样的地方在于硬件加速相比软件绘制而言算法可能更加合理,同时采用了一个单独的 Render 线程,减轻了主线程的负担。

双缓冲

一般来说将双缓冲用到的两块缓冲区称为 – 前缓冲区(front buffer) 和 后缓冲区(back buffer)。显示器显示的数据来源于 front buffer 前缓存区,而每一帧的数据都绘制到 back buffer 后缓存区,在 Vsync 信号到来后会交互缓存区的数据(指针指向),这时 front buffer 和 back buffer 的称呼及功能倒转。

在 View 的绘制过程中 Surface 使用了双缓冲技术。

SurfaceView

SurfaceView 就是一块拥有自己独立 Surface 的特殊 View 视图,由于这个特性,它一般用来实现比较复杂的图像或动画/视频的显示。

用一张图总结一下 Android 软硬件绘制的流程:

Android-软硬件绘制流程