写在前面
前面了解 BLASTBufferQueue 和 Surface 和 GraphicBuffer 这几个基本的组件之后,这一章主要是了解 App 是如何把 UI 绘制到图形缓冲区 GraphicBuffer 。App 的绘制流程本质上就是把 UI 绘制到一块 GraphicBuffer,然后把这一块 GraphicBuffer 插入到 BLASTBufferQueue 就结束了。后面的流程就交给 BLASTBufferQueue 和 SurfaceFlinger 来完成了。
App 绘制的起点在 ViewRootImpl.performTraversals,当 SurfaceFlinger 收到 Vsync 信号之后,会分发给 Choreographic,Choreographic 会执行 doFrame 最后会调用到 ViewRootImpl.doTarversal
Choreographic.doFrame
|
ViewRootImpl.doTarversal
|
ViewRootImpl.performTraversals
在 ViewRootImpl.performTraversals 中会执行 View 绘制的三大流程
private void performTraversals() {
...
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
performLayout(lp, mWidth, mHeight);
...
perforDraw()
...
}
其中 UI 的绘制发生在 perforDraw,在 perforDraw 中会调用 draw
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 来说,大致流程是一样的,我们以软件绘制为例
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
// 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 是如何实现的
// 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 的核心方法
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。
mGraphicBufferProducer->dequeueBuffer 的实现如下:
status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* outFence,
uint32_t width, uint32_t height, PixelFormat format,
uint64_t usage, uint64_t* outBufferAge,
FrameEventHistoryDelta* outTimestamps) {
...
{ // Autolock scope
std::unique_lock<std::mutex> lock(mCore->mMutex);
// 如果没有空闲的 buffer, 但是目前又正在 allocate buffer,等待 allocate 的结果
if (mCore->mFreeBuffers.empty() && mCore->mIsAllocating) {
mDequeueWaitingForAllocation = true;
mCore->waitWhileAllocatingLocked(lock);
mDequeueWaitingForAllocation = false;
mDequeueWaitingForAllocationCondition.notify_all();
}
int found = BufferItem::INVALID_BUFFER_SLOT;
// 1. 找到一块空闲的 buffer slot
while (found == BufferItem::INVALID_BUFFER_SLOT) {
status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue, lock, &found);
const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
}
// 2. 判断这个 buffer slot 里面的 GraphicBuffer 是否已经被 release 或需要重新申请
const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
bool needsReallocation = buffer == nullptr ||
buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage);
*outSlot = found;
// 如果需要重新申请一块 GraphicBuffer
if (needsReallocation) {
mSlots[found].mAcquireCalled = false;
mSlots[found].mGraphicBuffer = nullptr;
mSlots[found].mRequestBufferCalled = false;
mSlots[found].mFence = Fence::NO_FENCE;
mCore->mBufferAge = 0;
mCore->mIsAllocating = true;
returnFlags |= BUFFER_NEEDS_REALLOCATION;
} else {
// We add 1 because that will be the frame number when this buffer
// is queued
mCore->mBufferAge = mCore->mFrameCounter + 1 - mSlots[found].mFrameNumber;
}
listener = mCore->mConsumerListener;
} // Autolock scope
if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot);
// 3. 创建一块 GraphicBuffer
sp<GraphicBuffer> graphicBuffer =
sp<GraphicBuffer>::make(width, height, format, BQ_LAYER_COUNT, usage,
std::string{mConsumerName.c_str(), mConsumerName.size()});
status_t error = graphicBuffer->initCheck();
{ // Autolock scope
std::lock_guard<std::mutex> lock(mCore->mMutex);
if (error == NO_ERROR && !mCore->mIsAbandoned) {
graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);
// 4. 把创建好的 graphicBuffer 返回给 *outSlot
mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
callOnFrameDequeued = true;
bufferId = mSlots[*outSlot].mGraphicBuffer->getId();
}
mCore->mIsAllocating = false;
mCore->mIsAllocatingCondition.notify_all();
} // Autolock scope
}
...
}
BufferQueueProducer::dequeueBuffer 的主要逻辑
- 找到一块 buffer slot
- 判断该 buffer slot 里面的
GraphicBuffer是否有效,如GraphicBuffer已经被释放(nullptr) 或者尺寸等不满足当前的需求,则需要重新创建GraphicBuffer - 如果已经失效,重新创建
GraphicBuffer, 在GraphicBuffer的构造函数中会通过 Binder IPC 通知 HAL 调用Gralloc来真正分配实际物理内存,并这块内存的"句柄"给 GraphicBuffer - 把新创建的
GraphicBuffer绑定到该 outslot
View.draw(canvas)
Canvas 拿到了 GraphicBuffer 之后,就可以通过 Skia/OpenGL 来进行绘制了。View.draw 会调用 View 本身的 onDraw 方法,比如
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
// 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 方法
// 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
// 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 方法。
// 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 进行显示,这一块的内容我们后面单独分析。