View的绘制过程-图形缓冲

时间:2024-07-19 14:00:52

1.BufferQueue

应用程序负责生产画面,SurfaceFlinger负责消费画面,这是一个典型的生产者消费者模型,它们之间用buffer作为媒介传递,BufferQueue就是用来管理这些buffer,解决状态同步问题。
在BufferQueue中使用BufferSlot(槽)这个数据结构来表示buffer:

struct BufferSlot {
	sp<GraphicBuffer> mGraphicBuffer;
	BufferState mBufferState;
}

mGraphicBuffer表示对应的图形缓冲,mBufferState记录图形缓冲的使用状态。其中分为4种状态:
FREE: 该状态表示buffer处于空闲状态,可以被应用程序申请绘制。
DEQUEUED: 此状态表示应用程序已经通过dequeueBuffer申请到了图形缓冲,buffer的控制已经交给应用程序,此时应用程序可以往buffer绘制图形数据。
QUEUED: 此状态表示应用已经通过queueBuffer把图形缓冲提交到BufferQueue中,buffer的控制权归属BufferQueue,等待SurfaceFlinger去消费。
ACQUIRED: 此状态表示SurfaceFlinger已经通过acquireBuffer申请到了buffer的消费权,并把它交给HWC去做合成处理。
等图形消费者完成了图形合成之后,又会把buffer的状态置为FREE,又可以再次循环利用。状态迁移图如下:
在这里插入图片描述

2.buffer的申请

应用程序在绘制自己的界面前,都需要先去申请一块绘制的buffer(画布),在应用侧使用Surface表示,而在SurfaceFlinger则是用BufferLayer表示。BufferLayer包含了BufferQueue,以及对应的producer(生产者)和consumer(消费者);而应用程序中的Surface中就会包含producer的client端。
应用端在执行到performTraversals()方法的时候,在执行绘制前,会调用relayoutWindow()这个方法,在这里应用程序通过WMS作为媒介,和SurfaceFlinger关联:
frameworks/base/core/java/android/view/ViewRootImpl.java

performTraversals() {
	relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
}

private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
            boolean insetsPending) throws RemoteException {
relayoutResult = mWindowSession.relayout(mWindow, params,requestedWidth, requestedHeight,..., mSurfaceControl...);
}
...
if (mSurfaceControl.isValid()) { //通过SurfaceControl,更新Surface
	updateBlastSurfaceIfNeeded();
}
void updateBlastSurfaceIfNeeded() {
	Surface blastSurface = mBlastBufferQueue.createSurface();
	mSurface.transferFrom(blastSurface);
}

应用首先会给WMS通信,请求申请窗口;然后WMS会跟SurfaceFlinger通信,并让SurfaceFlinger创建BufferLayer:
frameworks\native\services\surfaceflinger

status_t SurfaceFlinger::createLayer(LayerCreationArgs& args, gui::CreateSurfaceResult& outResult) {
	...
    result = createBufferStateLayer(args, &outResult.handle, &layer);
    ...
}

SurfaceFlinger创建好了BufferLayer之后,会通过WMS把这个BufferLayer的控制接口传递给应用,也就是从Binder接口中的out类型参数SurfaceControl,然后创建Surface。
备注:从Android12开始,BufferQueue已经移到了应用程序端来管理,具体是BlastBufferQueue,它是对BufferQueue的进一步封装,利用事务Transaction来向SurfaceFlinger提交图形缓冲。

3.buffer的生产

应用端在接收到Choreographer的Vsync信号信号之后,会开始执行绘制的流程。在performTraversals()方法中初始化ThreadRenderer并把Surface传递进去:

private void performTraversals() {
	......
	hwInitialized = mAttachInfo.mThreadedRenderer.initialize(mSurface);
	......
}

然后在调用到draw()方法的时候,调用ThreadRenderer的draw()方法:

mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);

frameworks/base/core/java/android/view/ThreadedRenderer.java

void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks) {

	updateRootDisplayList(view, callbacks);
	......
	int syncResult = syncAndDrawFrame(frameInfo);
}

这里主要做了两件事情,首先把Decorview需要绘制的内容以displaylist这种方式记录下来,然后通知自己去异步绘制这一帧数据。

int DrawFrameTask::drawFrame() {
	......
    postAndWait();
}

void DrawFrameTask::postAndWait() {
    mRenderThread->queue().post([this]() { run(); });
}

这里可以看到,是往自己的消息队列添加了一条消息。
下一步就是在RenderThread中去执行run()方法然后开始draw():

void DrawFrameTask::run() {
	context->draw(solelyTextureViewUpdates);
}
void CanvasContext::draw(bool solelyTextureViewUpdates) {
	......
	Frame frame = getFrame(); // 这会让surface执行dequeuebuffer的操作
	......
	drawResult = mRenderPipeline->draw(frame, windowDirty, dirty......);
	......
	waitOnFences();
	bool didSwap = mRenderPipeline->swapBuffers(frame, drawResult, windowDirty, mCurrentFrameInfo, &requireSwap); // 执行surface的queuebuffer
}

执行渲染的操作,首先是通过dequeuebuffer获取到一个图形缓冲,然后调用GPU的Canvas图形库把displaylist里面的数据绘制到图形缓冲buffer中,然后执行queuebuffer提交图形缓冲到surfaceflinger中去。

4.buffer的消费

应用程序端在执行了queuebuffer后,会回调到SurfaceFlinger进程内部,并让SurfaceFlinger去监听Vsync信号:

void BufferQueueLayer::onFrameAvailable(const BufferItem& item) {
    ......
     mFlinger->signalLayerUpdate();//这里申请下一个vsync-sf信号
    ......
}

在Vsync-sf信号到来之后,SurfaceFlinger开始执行图层合成的操作。
SurfaceFlinger会检查所有的图层,查看图层是否有新的buffer提交,如果是有需要消费的图形缓冲,那么就会执行到BufferLayerComsumer的acquireBuffer的方法,把BufferQueue缓存的buffer(slot)取出,然后交给HWC Service处理,此时slot的状态是ACQUIRED。
HWC Service在执行完图形合成后,并会把图形信息给到屏幕做显示,同时BufferQueueConsumer会把对应slot的状态又改为FREE,在下一帧的绘制时,又可以拿来继续使用。

5.总体流程图理解

在这里插入图片描述

  • 大多时候显示的界面都是由多个Layer组成,如上是有4个Layer组成,每个Layer都有自己的BufferQueue,所有的Layer都是由GPU做渲染绘制。
  • SurfaceFlinger合成时,有些Layer会直接让HWC做合成,有些是自己调用GPU做合成然后再通过BufferQueue的机制给到HWC,最后由HWC送显。