写在前面
前面了解 BLASTBufferQueue
和 Surface
和 GraphicBuffer
这几个基本的组件之后,这一章主要是了解 App 是如何把 UI 绘制到图形缓冲区 GraphicBuffer
。App 的绘制流程本质上就是把 UI 绘制到一块 GraphicBuffer,然后把这一块 GraphicBuffer 插入到 BLASTBufferQueue 就结束了。后面的流程就交给 BLASTBufferQueue 和 SurfaceFlinger 来完成了。
App 绘制的起点在 ViewRootImpl.performTraversals
,当 SurfaceFlinger 收到 Vsync 信号之后,会分发给 Choreographic
,Choreographic
会执行 doFrame
最后会调用到 ViewRootImpl.doTarversal
markdown
Choreographic.doFrame
|
ViewRootImpl.doTarversal
|
ViewRootImpl.performTraversals
在 ViewRootImpl.performTraversals
中会执行 View 绘制的三大流程
scss
private void performTraversals() {
...
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
performLayout(lp, mWidth, mHeight);
...
perforDraw()
...
}
其中 UI 的绘制发生在 perforDraw
,在 perforDraw
中会调用 draw
less
private boolean draw(boolean fullRedrawNeeded, @Nullable SurfaceSyncGroup activeSyncGroup,
boolean syncBuffer) {
Surface surface = mSurface;
...
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
scalingRequired, dirty, surfaceInsets)) {
return false;
}
}
UI的绘制可以通过软件绘制和硬件绘制,虽然软件绘制和硬件绘制有所区别,但是对于 Surface
和 GraphicBuffer
来说,大致流程是一样的,我们以软件绘制为例
java
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
// 定义了一个 Canva
final Canvas canvas;
try {
// 初始化 Canvas,从 BLASTBufferQueue 中申请一块 GraphicBuffer
canvas = mSurface.lockCanvas(dirty);
canvas.setDensity(mDensity);
}
...
try {
...
// 把 canvas 提交给 View,然后在 View 的 onDraw 中通过 Canvas 绘制
mView.draw(canvas);
} finally {
try {
// 绘制完成,提交绘制结果到 BLASTBufferQueue
surface.unlockCanvasAndPost(canvas);
}
}
return true;
}
APP UI 的软件绘制,可以分为三步:
-
mSurface.lockCanvas(dirty)
申请一块内存--GraphicBuffer
。 在前面我们了解到,此时mSurface
已经初始化完成,内部持有IGraphicBufferProducer
作为BLASTBufferQueue
的生产者,所以可以通过mSurface
来申请一块GraphicBuffer
。 -
mView.draw(canvas)
通过渲染引擎OpenGL/Skia
进行绘制,把View
绘制到第一步所申请的GraphicBuffer
上。 -
surface.unlockCanvasAndPost(canvas)
把绘制的结果GraphicBuffer
插入到BLASTBufferQueue
中,再由BLASTBufferQueue
把结果提交到SurfaceFlinger
进行合成提交到屏幕显示。
我们接下来一步一步从源码来分析:
Surface.lockCanvas--申请 Buffer
arduino
// frameworks/base/core/java/android/view/Surface.java
public Canvas lockCanvas(Rect inOutDirty)
throws Surface.OutOfResourcesException, IllegalArgumentException {
synchronized (mLock) {
...
// nativeLockCanvas 是一个 native 方法
mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);
return mCanvas;
}
}
// 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));
if (!ACanvas_isSupportedPixelFormat(ANativeWindow_getFormat(surface.get()))) {
Rect dirtyRect(Rect::EMPTY_RECT);
Rect* dirtyRectPtr = NULL;
ANativeWindow_Buffer buffer;
status_t err = surface->lock(&buffer, dirtyRectPtr);
if (err < 0) {
const char* const exception = (err == NO_MEMORY) ?
OutOfResourcesException : IllegalArgumentException;
jniThrowException(env, exception, NULL);
return 0;
}
graphics::Canvas canvas(env, canvasObj);
canvas.setBuffer(&buffer, static_cast<int32_t>(surface->getBuffersDataSpace()));
sp<Surface> lockedSurface(surface);
lockedSurface->incStrong(&sRefBaseOwner);
return (jlong) lockedSurface.get();
}
在 nativeLockCanvas
中主要就是通过 surface->lock
来申请一块 buffer, 然后通过 canvas.setBuffer
把这一块 buffer
交给 canvas
,接下来看 surface->lock
是如何实现的
arduino
// frameworks/native/libs/gui/Surface.cpp
status_t Surface::lock(
ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds)
{
// out 作为返回的 Buffer
ANativeWindowBuffer* out;
int fenceFd = -1;
// 通过 dequeueBuffer 从 BLASTBufferQueue dequeue 一块 GraphicBuffer
status_t err = dequeueBuffer(&out, &fenceFd);
...
return err;
}
在 Surface::lock
中会调用 dequeueBuffer
,在之前介绍 Surface
的文章中提到, dequeueBuffer
是 Surface
的核心方法
arduino
int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { // 从 BLASTBufferQueue 中获取一块 GraphicBuffer
status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, dqInput.width,
dqInput.height, dqInput.format,
dqInput.usage, &mBufferAge,
dqInput.getTimestamps ?
&frameTimestamps : nullptr);
sp<GraphicBuffer>& gbuf(mSlots[buf].buffer);
// 返回一块 GraphicBuffer
*buffer = gbuf.get();
return OK;
}
可以看到 Surface::dequeueBuffer
主要实现就是调用 mGraphicBufferProducer->dequeueBuffer
, 前面的文章提到过,在 Surface
初始化的时候,BLASTBufferQueue
把内部的 Producer
接口赋给了 Surface
的成员变量 mGraphicBufferProducer
, 所以 Surface.mGraphicBufferProducer
可以从 BLASTBufferQueue
中 dequeue
一块 GraphicBuffer
并返回给 Canvas
。
View.draw(canvas)
Canvas
拿到了 GraphicBuffer
之后,就可以通过 Skia/OpenGL
来进行绘制了。View.draw
会调用 View 本身的 onDraw
方法,比如
scss
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(0.0f, 0.0f, getWidth(), getHeight(), mPaint);
}
canvas.drawRect
最终会调用 SkiaCanvas::drawRect
,这里的渲染就跟 Skia
相关了,关于渲染的一些第三方 API,包括 Skia和 OpenGL 后续在了解,这里先放一放。我们只需要通过渲染引擎能把 UI 绘制到 GraphicBuffer
上。
surface.unlockCanvasAndPost(canvas) 提交 Buffer
scss
// frameworks/base/core/java/android/view/Surface.java
public void unlockCanvasAndPost(Canvas canvas) {
...
unlockSwCanvasAndPost(canvas);
...
}
private void unlockSwCanvasAndPost(Canvas canvas) {
try {
nativeUnlockCanvasAndPost(mLockedObject, canvas);
} finally {
nativeRelease(mLockedObject);
mLockedObject = 0;
}
}
Java 层的 unlockSwCanvasAndPost
最终会调用 native 的 nativeUnlockCanvasAndPost
方法
arduino
// frameworks/base/core/jni/android_view_Surface.cpp
static void nativeUnlockCanvasAndPost(JNIEnv* env, jclass clazz,
jlong nativeObject, jobject canvasObj) {
sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
// detach the canvas from the surface
graphics::Canvas canvas(env, canvasObj);
canvas.setBuffer(nullptr, ADATASPACE_UNKNOWN);
// unlock surface
status_t err = surface->unlockAndPost();
}
- detach 了
Canvas
和Surface
- 执行
surface->unlockAndPost
ini
// frameworks/native/libs/gui/Surface.cpp
status_t Surface::unlockAndPost()
{
int fd = -1;
status_t err = mLockedBuffer->unlockAsync(&fd);
ALOGE_IF(err, "failed unlocking buffer (%p)", mLockedBuffer->handle);
...
err = queueBuffer(mLockedBuffer.get(), fd);
return err;
}
unlockAndPost
最终会调用 Surface
的 queueBuffer
方法,这个也是 Surface
的核心方法之一,dequeueBuffer
负责从 BLASTBufferQueue
中取 Buffer
,而 queueBuffer
是把 Buffer
插入 BLASTBufferQueue
中。从上述 dequeueBuffer
的实现来看,queueBuffer
必然也会调用 mGraphicBufferProducer
的 queueBuffer
方法。
arduino
// frameworks/native/libs/gui/Surface.cpp
int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd,
SurfaceQueueBufferOutput* surfaceOutput) {
...
// Surface::queueBuffer
// -> IConsumerListener::onFrameAvailable callback triggers automatically
// -> implementation calls IGraphicBufferConsumer::acquire/release immediately
// -> SurfaceListener::onBufferRelesed callback triggers automatically
// -> implementation calls Surface::dequeueBuffer
status_t err = mGraphicBufferProducer->queueBuffer(slot, input, &output);
}
到这里为止,App 的 View 就已经成功绘制到 Buffer, 并且通过 Surface
的 mGraphicBufferProducer->queueBuffer
成功把 Buffer 提交到了 BLASTBufferQueue
中,那么接下来的工作就是 BLASTBufferQueue
的 Comsumer
进行acquire Buffer
,然后提交给 SurfaceFlinger
做进一步的合成然后提交到 Display 进行显示,这一块的内容我们后面单独分析。