创建流程
- 应用程序窗口对应一个Surface(Surface.java)
- Surface由每个应用进程的ViewRootImpl持有
- Surface最初初始化为一个空Surface
- Surface的初始化发生在第一次relayoutWindow
[View.invalidate]
↓
[逐级传递到 ViewRootImpl]
↓
[ViewRootImpl.scheduleTraversals()]
↓
[插入同步屏障,注册 VSYNC 回调]
↓
[VSYNC 信号到达 → 执行 doTraversal() → performTraversals() → relayoutWindow]
↓
[执行updateBlastSurfaceIfNeeded()]
↓
[创建BLASTBufferQueue → mBlastBufferQueue.createSurface() → mSurface.transferFrom(blastSurface)]
接下来分析Java层BLASTBufferQueue的createSurface创建流程
BLASTBufferQueue.createSurface
- Java层的createSurface对应到jni中的nativeGetSurface
- jni层中,通过BLASTBufferQueue::getSurface获取Surface
- 在BLASTBufferQueue::getSurface中创建并返回了一个BBQSurface
sp<Surface> BLASTBufferQueue::getSurface(bool includeSurfaceControlHandle) {
std::lock_guard _lock{mMutex};
sp<IBinder> scHandle = nullptr;
if (includeSurfaceControlHandle && mSurfaceControl) {
scHandle = mSurfaceControl->getHandle();
}
return new BBQSurface(mProducer, true, scHandle, this);
}
- BBQSurface关联了BBQ中的生产者、SurfaceControl、还有BBQ自己本身
- Java层窗口对应的Surface在native层包装的是BBQSurface
将Surface设置给hwui
- 接着上一节,在
performTraversals()中创建Surface后,接着会进行更新Surface
private void performTraversals() {
...
} else if ((surfaceReplaced || surfaceSizeChanged || updateSurfaceNeeded)
&& mSurfaceHolder == null
&& mAttachInfo.mThreadedRenderer != null
&& mSurface.isValid()) {
mFullRedrawNeeded = true;
try {
// Need to do updateSurface (which leads to CanvasContext::setSurface and
// re-create the EGLSurface) if either the Surface changed (as indicated by
// generation id), or WindowManager changed the surface size. The latter is
// because on some chips, changing the consumer side's BufferQueue size may
// not take effect immediately unless we create a new EGLSurface.
// Note that frame size change doesn't always imply surface size change (eg.
// drag resizing uses fullscreen surface), need to check surfaceSizeChanged
// flag from WindowManager.
mAttachInfo.mThreadedRenderer.updateSurface(mSurface);
} catch (OutOfResourcesException e) {
handleOutOfResourcesException(e);
mLastPerformTraversalsSkipDrawReason = "oom_update_surface";
return;
}
}
}
- 从java层一层层调用到SkiaPipeline
- 需要注意的一点是在jni层,会将Surface转成ANativeWindow,在hwui模块中只能感知到ANativeWindow的存在
- 在skiapipeline中根据opengl、vulkan后端,区分不同的流程(Android15中已经默认走vulkan)
- (opengl),在SkiaOpenGLPipeline中调用setSurface,然后是EglManager.createSurace将ANativeWindow转成EGLSurface
渲染
还有一条非常隐蔽的线,在SkiaPipeline renderFrame渲染每帧内容时,用的是SkSurface,SkSurface与EGLSurface的关联是源码中看不到的- 在SkiaPipeline中通过MakeFromBackendRenderTarget创建SkSurface时,需要传入GrBackendRenderTarget(封装了
EGLSurface的帧缓冲区对象 FBO) - 应用通过
SkSurface的SkCanvas进行绘制,生成GPU命令 - 绘制完后,Skia调用GrContext::flush,将GPU命令提交到EGLSurface的帧缓冲区
- 调用
eglSwapBuffers或eglSwapBuffersWithDamageKHR交换前后缓冲区,触发BufferQueue的queueBuffer操作,将渲染后的 Buffer 提交给 SurfaceFlinger 合成 - 小结一下:
ANativeWindow与SkSurface的关联通过以下链路实现:
ANativeWindow → EGLSurface(OpenGL ES 窗口目标) → GrBackendRenderTarget(Skia 后端渲染目标) → SkSurface(绘图表面)
整个过程依赖 EGL 的桥接作用 和 GrContext 的上下文管理,最终将 Skia 的 GPU 渲染结果提交到应用窗口的BufferQueue,实现画面显示
Buffer轮转
BufferQueue的工作流程网上很多文章都做过介绍,这里不再赘述:
ANativeWindow本身是BBQSurface,关联了BBQ,BufferQueue会有Buffer轮转- 当应用通过 OpenGL ES 完成一帧的绘制后调用
eglSwapBuffers,EGL 内部会通过绑定的ANativeWindow(如Surface)触发queueBuffer,将当前已填充数据的 Buffer 提交给消费者(如 SurfaceFlinger - 在
eglSwapBuffers内部,EGL 还会通过ANativeWindow调用dequeueBuffer,从BufferQueue中 获取一个空闲的 Buffer(状态从 FREE → DEQUEUED),用于下一帧的渲染 - 关键代码流程如下:
void CanvasContext::draw(bool solelyTextureViewUpdates) {
...
IRenderPipeline::DrawResult drawResult;
{
// FrameInfoVisualizer accesses the frame events, which cannot be mutated mid-draw
// or it can lead to memory corruption.
// This lock is overly broad, but it's the quickest fix since this mutex is otherwise
// not visible to IRenderPipeline much less FrameInfoVisualizer. And since this is
// the thread we're primarily concerned about being responsive, this being too broad
// shouldn't pose a performance issue.
std::scoped_lock lock(mFrameMetricsReporterMutex);
drawResult = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry,
&mLayerUpdateQueue, mContentDrawBounds, mOpaque,
mLightInfo, mRenderNodes, &(profiler()), mBufferParams);
}
...
bool didSwap = mRenderPipeline->swapBuffers(frame, drawResult.success, windowDirty,
mCurrentFrameInfo, &requireSwap);
}