从零开始给 Surface 实现帧同步

背景

参与系统开发的工程师应该都比较清楚,很多时候我们用 Surface 相关功能并不是使用 SurfaceView 的,而是自己创建一个 Surface,这样 Surface 的可定制程度就可以很高。有的需求需要做到使 TransactionSurface 里面绘制的内容的位置等几何信息进行同步(例如 ViewRootImpl 本身也是直接使用 Surface 来绘制,也是自行实现了一套帧同步逻辑)。

但遗憾的是,Surface 本身只提供了绘制等基础功能,本身并不存在帧同步的实现。那么,我们可以参照 ViewRootImpl 以及 SurfaceView 的帧同步实现来给单独的 Surface 实现帧同步功能。

具体实现

要想实现帧同步,首先就要了解基础的帧同步原理。这个大体上是比较好理解的,主要分为以下几个流程。

  1. 监听帧可用回调
  2. 通过 BLASTBufferQueue 合并事务

上面两个流程已经是简化过的了,接下来通过代码来具体讲解是如何实现的。

初始化必要组件

首先,需要知道我们需要用到哪些类,SurfaceBLASTBufferQueue 是必不可少的两个,这两个是一开始创建 Surface 就创建好的,如果你不知道如何使用 BLASTBufferQueue,可以参考 SurfaceView#createBlastSurfaceControls(ViewRootImpl, String, Transaction)

与 BLAST 相关的接口,除了 BLASTBufferQueue 除了能做帧同步外,通过 setBLASTLayer 来声明创建的 Surface,可以做到背景模糊等功能。BLASTBufferQueue 也需要通过 BLAST Layer 来创建。

1
2
3
4
5
6
7
8
9
10
11
public class LayerRootImpl {

private final Surface mSurface;
private final BLASTBufferQueue mBLASTBufferQueue;

public LayerRootImpl(Surface surface, BLASTBufferQueue blastBufferQueue) {
mSurface = surface;
mBLASTBufferQueue = blastBufferQueue;
}
...
}

实现帧回调监听

如何实现帧回调监听呢?诶,框架刚好就实现了一个类 HardwareRenderer 来做这件事。

1
2
3
4
5
6
7
...
private final HardwareRenderer mRenderer;

...
mRenderer = new HardwareRenderer();
mRenderer.setSurface(surface);
...

然后可以通过 HardwareRenderer#setFrameCallback(HardwareRenderer.FrameDrawingCallback) 来注册帧回调监听了,是不是看着很简单。

但实际上,还有一个地方是需要注意的,就是什么时候去注册这个监听回调呢?先看下 BLASTBufferQueuemergeWithNextTransaction 方法,有两个参数,一个是 Transaction 本身,一个是 frameNumber,也就是需要同步的帧号。也就是说,我们是需要知道事务要具体跟哪一帧合并。但肯定不是跟过去的帧,而是跟未来要合成的帧,其实在大多数情况下都是需要下一帧去合并的(为什么是大多数呢?因为我也不知道会有哪些奇葩的需求)。

因此 setFrameCallback 这个接口肯定是需要在绘制前去调用的,这一点可以在 ThreadedRenderer#updateRootDisplayList(View, ThreadedRenderer.DrawCallbacks) 处求证,没错,我们熟知的 ViewRootImpl 的帧同步也是通过这个方法实现的。

在这里,我们可以仿照 Surface,创建一个 HwuiContext 类,来维护管理 Canvas 的生成。

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
private final class HwuiContext {
/**
* 用来绘制生成 {@link Canvas} 的 {@link RenderNode}
*/
private final RenderNode mRenderNode;

/**
* 监听帧回调需要用到
*/
private HardwareRenderer mHardwareRenderer;

/**
* 当前用于绘制的 {@link Canvas}
*/
private RecordingCanvas mCanvas;

/**
* 在绘制前需要注册的所有帧回调
*/
private List<HardwareRenderer.FrameDrawingCallback> mCallbacks;

HwuiContext() {
mRenderNode = RenderNode.create("HwuiCanvas", null);
mRenderNode.setClipToBounds(false);
mRenderNode.setForceDarkAllowed(false);

mHardwareRenderer = new HardwareRenderer();
// 关联 RenderNode
mHardwareRenderer.setContentRoot(mRenderNode);
// 关联 Surface
mHardwareRenderer.setSurface(mSurface, true);
mHardwareRenderer.setLightSourceAlpha(0.0f, 0.0f);
mHardwareRenderer.setLightSourceGeometry(0.0f, 0.0f, 0.0f, 0.0f);
}

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

void unlockAndPost(Canvas canvas) {
if (canvas != mCanvas) {
throw new IllegalArgumentException("canvas object must be the same instance that "
+ "was previously returned by lockCanvas");
}
mRenderNode.endRecording();
mCanvas = null;
// 在这里即将开始绘制前设置帧回调
setupFrameCallbacks();
// 转到 RenderThread 请求 GPU 绘制
mHardwareRenderer.createRenderRequest()
.setVsyncTime(System.nanoTime())
.syncAndDraw();
}

private void setupFrameCallbacks() {
// 收集所有之前注册好的监听回调
List<HardwareRenderer.FrameDrawingCallback> frameCallbacks = mCallbacks;
mCallbacks = null;
if (frameCallbacks == null || frameCallbacks.isEmpty()) {
return;
}
mHardwareRenderer.setFrameCallback(new HardwareRenderer.FrameDrawingCallback() {
@Override
public void onFrameDraw(long frame) {
}

@Override
public HardwareRenderer.FrameCommitCallback onFrameDraw(int syncResult,
long frame) {
ArrayList<HardwareRenderer.FrameCommitCallback>
frameCommitCallbacks = new ArrayList<>();
for (int i = 0; i < frameCallbacks.size(); ++i) {
// 这里就能拿到用于传入 BLASTBufferQueue 的 frameNumber 了,也就是下一帧送往
// SurfaceFlinger 合成的帧号。
HardwareRenderer.FrameCommitCallback frameCommitCallback =
frameCallbacks.get(i).onFrameDraw(syncResult, frame);
if (frameCommitCallback != null) {
frameCommitCallbacks.add(frameCommitCallback);
}
}

if (frameCommitCallbacks.isEmpty()) {
return null;
}

return didProduceBuffer -> {
for (int i = 0; i < frameCommitCallbacks.size(); ++i) {
frameCommitCallbacks.get(i).onFrameCommit(didProduceBuffer);
}
};
}
});
}

void destroy() {
mHardwareRenderer.destroy();
}
}

然后对外暴露注册帧回调的接口:

1
2
3
4
5
6
public void registerRtFrameCallback(HardwareRenderer.FrameDrawingCallback callback) {
if (mHwuiContext.mCallbacks == null) {
mHwuiContext.mCallbacks = new ArrayList<>();
}
mHwuiContext.mCallbacks.add(callback);
}

以及向 BLASTBufferQueue 合并事务的接口:

1
2
3
public void mergeWithNextTransaction(SurfaceControl.Transaction t, long frameNumber) {
mBLASTBufferQueue.mergeWithNextTransaction(t, frameNumber);
}

绘制以及监听

经过上面的步骤之后其实以及几乎完工了,最后就是使用示例而已了。

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
public class LayerRootImplTest {
private final SurfaceControl mSurfaceControl;
private final SurfaceControl mBlastSurfaceControl;
private final BLASTBufferQueue mBlastBufferQueue;
private final Surface mSurface;
private final LayerRootImpl mLayerRootImpl;

public LayerRootImplTest(View rootView, int width, int height, int format) {
// 创建 Surface
SurfaceSession session = new SurfaceSession();
mSurfaceControl = new SurfaceControl.Builder(session)
.setName("test-surface")
.setParent(rootView.getViewRootImpl().getSurfaceControl())
.setCallsite("SurfaceView.updateSurface")
.setContainerLayer()
.build();
mBlastSurfaceControl = new SurfaceControl.Builder(session)
.setName("test-surface(BLAST)")
.setParent(mSurfaceControl)
.setHidden(false)
.setBLASTLayer()
.setCallsite("SurfaceView.updateSurface")
.build();
mBlastBufferQueue = new BLASTBufferQueue("test-blast-queue",
false /* updateDestinationFrame */);
mBlastBufferQueue.update(mBlastSurfaceControl, width, height, format);
mSurface = new Surface();
mSurface.copyFrom(mBlastBufferQueue);
// 创建 LayerRootImpl
mLayerRootImpl = new LayerRootImpl(mSurface, mBlastBufferQueue);
}

public void testSync(SurfaceControl.Transaction t) {
mLayerRootImpl.registerRtFrameCallback(frame ->
mLayerRootImpl.mergeWithNextTransaction(t, frame));
}
}

出乎意料的简单。

完整代码

LayerRootImpl:

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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package com.example;

import android.graphics.BLASTBufferQueue;
import android.graphics.Canvas;
import android.graphics.HardwareRenderer;
import android.graphics.RecordingCanvas;
import android.graphics.RenderNode;
import android.view.Surface;
import android.view.SurfaceControl;

import java.util.ArrayList;
import java.util.List;

public class LayerRootImpl {

private final Surface mSurface;
private final BLASTBufferQueue mBLASTBufferQueue;
private final HwuiContext mHwuiContext;

public LayerRootImpl(Surface surface, BLASTBufferQueue blastBufferQueue) {
mSurface = surface;
mBLASTBufferQueue = blastBufferQueue;
mHwuiContext = new HwuiContext();
}

public void registerRtFrameCallback(HardwareRenderer.FrameDrawingCallback callback) {
if (mHwuiContext.mCallbacks == null) {
mHwuiContext.mCallbacks = new ArrayList<>();
}
mHwuiContext.mCallbacks.add(callback);
}

public void mergeWithNextTransaction(SurfaceControl.Transaction t, long frameNumber) {
mBLASTBufferQueue.mergeWithNextTransaction(t, frameNumber);
}

private final class HwuiContext {
/**
* 用来绘制生成 {@link Canvas} 的 {@link RenderNode}
*/
private final RenderNode mRenderNode;

/**
* 监听帧回调需要用到
*/
private final HardwareRenderer mHardwareRenderer;

/**
* 当前用于绘制的 {@link Canvas}
*/
private RecordingCanvas mCanvas;

/**
* 在绘制前需要注册的所有帧回调
*/
private List<HardwareRenderer.FrameDrawingCallback> mCallbacks;

HwuiContext() {
mRenderNode = RenderNode.create("HwuiCanvas", null);
mRenderNode.setClipToBounds(false);
mRenderNode.setForceDarkAllowed(false);

mHardwareRenderer = new HardwareRenderer();
// 关联 RenderNode
mHardwareRenderer.setContentRoot(mRenderNode);
// 关联 Surface
mHardwareRenderer.setSurface(mSurface, true);
mHardwareRenderer.setLightSourceAlpha(0.0f, 0.0f);
mHardwareRenderer.setLightSourceGeometry(0.0f, 0.0f, 0.0f, 0.0f);
}

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

void unlockAndPost(Canvas canvas) {
if (canvas != mCanvas) {
throw new IllegalArgumentException("canvas object must be the same instance that "
+ "was previously returned by lockCanvas");
}
mRenderNode.endRecording();
mCanvas = null;
// 在这里即将开始绘制前设置帧回调
setupFrameCallbacks();
// 转到 RenderThread 请求 GPU 绘制
mHardwareRenderer.createRenderRequest()
.setVsyncTime(System.nanoTime())
.syncAndDraw();
}

private void setupFrameCallbacks() {
// 收集所有之前注册好的监听回调
List<HardwareRenderer.FrameDrawingCallback> frameCallbacks = mCallbacks;
mCallbacks = null;
if (frameCallbacks == null || frameCallbacks.isEmpty()) {
return;
}
mHardwareRenderer.setFrameCallback(new HardwareRenderer.FrameDrawingCallback() {
@Override
public void onFrameDraw(long frame) {
}

@Override
public HardwareRenderer.FrameCommitCallback onFrameDraw(int syncResult,
long frame) {
ArrayList<HardwareRenderer.FrameCommitCallback>
frameCommitCallbacks = new ArrayList<>();
for (int i = 0; i < frameCallbacks.size(); ++i) {
// 这里就能拿到用于传入 BLASTBufferQueue 的 frameNumber 了,也就是下一帧送往
// SurfaceFlinger 合成的帧号。
HardwareRenderer.FrameCommitCallback frameCommitCallback =
frameCallbacks.get(i).onFrameDraw(syncResult, frame);
if (frameCommitCallback != null) {
frameCommitCallbacks.add(frameCommitCallback);
}
}

if (frameCommitCallbacks.isEmpty()) {
return null;
}

return didProduceBuffer -> {
for (int i = 0; i < frameCommitCallbacks.size(); ++i) {
frameCommitCallbacks.get(i).onFrameCommit(didProduceBuffer);
}
};
}
});
}

void destroy() {
mHardwareRenderer.destroy();
}
}
}

LayerRootImplTest:

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
package com.example;

import android.graphics.BLASTBufferQueue;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.View;

public class LayerRootImplTest {
private final SurfaceControl mSurfaceControl;
private final SurfaceControl mBlastSurfaceControl;
private final BLASTBufferQueue mBlastBufferQueue;
private final Surface mSurface;
private final LayerRootImpl mLayerRootImpl;

public LayerRootImplTest(View rootView, int width, int height, int format) {
// 创建 Surface
SurfaceSession session = new SurfaceSession();
mSurfaceControl = new SurfaceControl.Builder(session)
.setName("test-surface")
.setParent(rootView.getViewRootImpl().getSurfaceControl())
.setCallsite("SurfaceView.updateSurface")
.setContainerLayer()
.build();
mBlastSurfaceControl = new SurfaceControl.Builder(session)
.setName("test-surface(BLAST)")
.setParent(mSurfaceControl)
.setHidden(false)
.setBLASTLayer()
.setCallsite("SurfaceView.updateSurface")
.build();
mBlastBufferQueue = new BLASTBufferQueue("test-blast-queue",
false /* updateDestinationFrame */);
mBlastBufferQueue.update(mBlastSurfaceControl, width, height, format);
mSurface = new Surface();
mSurface.copyFrom(mBlastBufferQueue);
// 创建 LayerRootImpl
mLayerRootImpl = new LayerRootImpl(mSurface, mBlastBufferQueue);
}

public void testSync(SurfaceControl.Transaction t) {
mLayerRootImpl.registerRtFrameCallback(frame ->
mLayerRootImpl.mergeWithNextTransaction(t, frame));
}
}

作者

Hhvvg

发布于

2025-06-17

更新于

2025-06-17

许可协议