写在前面
前面了解 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 进行显示,这一块的内容我们后面单独分析。