赞
踩
在上一篇文章中已经讲了setView整个流程中,最开始的addToDisplay和WMS跨进程通信的整个过程做了什么。继文章Android基础知识之Window(二),这算是另外一个分支了,接着讲分析在performTraversals的三个操作中,最后触发performDraw执行绘制的绘制原理。
SurfaceFlinger是Android操作系统中一个关键组件,负责管理和合成显示内容。你说它是显示引擎也可以,说他是Android的显示服务器也可以。
它属于一个独立的进程,在系统启动过程中,会通过init进程解析init.rc,然后再去加载SurfaceFlinger。最后加载的路径在*/frameworks/native/services/surfaceflinger/main_surfaceflinger.cpp*,执行它的main函数。
//main_surfaceflinger.cpp
int main(int, char**) {
signal(SIGPIPE, SIG_IGN);
...
// start the thread pool
sp<ProcessState> ps(ProcessState::self());
ps->startThreadPool();
...
// instantiate surfaceflinger
// 实例化SurfaceFlinger
sp<SurfaceFlinger> flinger = surfaceflinger::createSurfaceFlinger();
...
这里简单的介绍一下图形系统,应用程序可以借助图形系统在屏幕上显示画面与用户完成交互。把图形系统进行划分,可以分为UI框架、渲染系统(Skia/OpenGL)、窗口系统(X11/Wayland/SurfaceFlinger)、显示系统(DRM/显示驱动等),可以看到讲的SurfaceFlinger属于系统层级中的窗口系统。
纵向分层,从下层至上层分为
GPU -> GPU驱动 -> OpenGL -> 2D图形库(Skia等)-> UI框架(Android原生View /Flutter等)
在来说一下渲染和绘制这两个概念,很多地方经常会互用,但也没有问题,有时候我们说渲染某个画面,或者绘制某个画面也是同一个意思。但是如果需要认真区分,它们就是两个不同的概念了。
基本的概念补充了一下,就讲这次的主要内容了,performTraversals执行了测量、布局、和绘制三个操作,前面两个操作都是为最后一个绘制做的准备工作。在应用上层中,常常提到的绘制,我们知道是执行View#onDraw方法,可是怎么执行进来的,在之前文章中只是讲了一个大概,这次就详细分析一下这个流程,perfromDraw中主要的函数draw。
//ViewRootImpl.java private boolean draw(boolean fullRedrawNeeded, boolean forceDraw) { ... //DEBUG下,可以捕获当前fps值 if (DEBUG_FPS) { trackFPS(); } ... //脏视图的集合是否为空(有没有变化的视图区域) if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) { //判断是否开启了硬件加速(是否硬件支持) if (isHardwareEnabled()) { ... //硬件绘制(ThreadRenderer进行绘制) mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this); } else { ... //软件绘制 if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty, surfaceInsets)) { return false; } } } }
先看一下软件绘制drawSoftware做了什么,一般情况没有开启硬件加速,在performDraw执行进来过后,就执行这部分逻辑。
//ViewRootImpl.java private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff, boolean scalingRequired, Rect dirty, Rect surfaceInsets) { // Draw with software renderer. final Canvas canvas; try { //拿到Surface的画布 canvas = mSurface.lockCanvas(dirty); canvas.setDensity(mDensity); } catch (Surface.OutOfResourcesException e) { handleOutOfResourcesException(e); return false; } catch (IllegalArgumentException e) { Log.e(mTag, "Could not lock surface", e); mLayoutRequested = true; // ask wm for a new surface next time. return false; } try { if (!canvas.isOpaque() || yoff != 0 || xoff != 0) { canvas.drawColor(0, PorterDuff.Mode.CLEAR); } //清空脏视图缓存 dirty.setEmpty(); mIsAnimating = false; mView.mPrivateFlags |= View.PFLAG_DRAWN; canvas.translate(-xoff, -yoff); if (mTranslator != null) { mTranslator.translateCanvas(canvas); } canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0); //回调到View的onDraw方法 mView.draw(canvas); drawAccessibilityFocusedDrawableIfNeeded(canvas); } finally { try { //将后缓冲区提交到前缓冲区显示 surface.unlockCanvasAndPost(canvas); } catch (IllegalArgumentException e) { Log.e(mTag, "Could not unlock surface", e); mLayoutRequested = true; // ask wm for a new surface next time. //noinspection ReturnInsideFinallyBlock return false; } } return true; }
mSurface是ViewRootImpl创建的一个Surface对象,也就说明一个windnow对应一个Surface和SurfaceControl对象,这个在之前文章有讲过。Surface涉及的双缓冲机制,分前缓冲区和后缓冲区,前缓冲区用于显示,绘制在后缓冲区,绘制完成通过unlockCanvasAndPost和前缓冲区互换,完成显示,防止闪烁的问题。这里我们看到了mView#draw方法,回调View当中的onDraw,通过Surface拿到的canvas执行绘制代码。
补充:ViewRootImpl 和 SurfaceView 可以看作是一个层级的事物,他们都持有一个 surface,ViewRootImpl 自己把 ViewTree 渲染到 surface 上,SurfaceView 的 surface 供应用自行使用,应用可以把游戏/视频/相机/3D图形库生成数据放到 surface 上
然后继续看一下mAttachInfo.mThreadedRenderer.draw这个方法,mThreadedRenderer是我们常说的渲染线程,mAttachInfo属于View类中的一个内部类。在performTraversals中,会判断并执行enableHardwareAcceleration,然后创建renderer对象。
//ViewRootImpl.java @UnsupportedAppUsage private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) { ... if (ThreadedRenderer.sRendererEnabled || forceHwAccelerated) { if (mAttachInfo.mThreadedRenderer != null) { mAttachInfo.mThreadedRenderer.destroy(); } final Rect insets = attrs.surfaceInsets; final boolean hasSurfaceInsets = insets.left != 0 || insets.right != 0 || insets.top != 0 || insets.bottom != 0; final boolean translucent = attrs.format != PixelFormat.OPAQUE || hasSurfaceInsets; final ThreadedRenderer renderer = ThreadedRenderer.create(mContext, translucent, attrs.getTitle().toString()); mAttachInfo.mThreadedRenderer = renderer; renderer.setSurfaceControl(mSurfaceControl, mBlastBufferQueue); updateColorModeIfNeeded(attrs.getColorMode()); updateRenderHdrSdrRatio(); updateForceDarkMode(); mAttachInfo.mHardwareAccelerated = true; mAttachInfo.mHardwareAccelerationRequested = true; if (mHardwareRendererObserver != null) { renderer.addObserver(mHardwareRendererObserver); } } } }
代码我们可以看到,通过ThreadedRenderer#create的静态方法,创建renderer对象,并赋值给了mAttachInfo.mThreadedRenderer属性。继续看一下renderer#draw方法。
//ThreadedRenderer.java /** * Draws the specified view. * * @param view The view to draw. * @param attachInfo AttachInfo tied to the specified view. */ void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks) { attachInfo.mViewRootImpl.mViewFrameInfo.markDrawStart(); updateRootDisplayList(view, callbacks); // register animating rendernodes which started animating prior to renderer // creation, which is typical for animators started prior to first draw if (attachInfo.mPendingAnimatingRenderNodes != null) { final int count = attachInfo.mPendingAnimatingRenderNodes.size(); for (int i = 0; i < count; i++) { registerAnimatingRenderNode( attachInfo.mPendingAnimatingRenderNodes.get(i)); } attachInfo.mPendingAnimatingRenderNodes.clear(); // We don't need this anymore as subsequent calls to // ViewRootImpl#attachRenderNodeAnimator will go directly to us. attachInfo.mPendingAnimatingRenderNodes = null; } final FrameInfo frameInfo = attachInfo.mViewRootImpl.getUpdatedFrameInfo(); int syncResult = syncAndDrawFrame(frameInfo); if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) { Log.w("OpenGLRenderer", "Surface lost, forcing relayout"); // We lost our surface. For a relayout next frame which should give us a new // surface from WindowManager, which hopefully will work. attachInfo.mViewRootImpl.mForceNextWindowRelayout = true; attachInfo.mViewRootImpl.requestLayout(); } if ((syncResult & SYNC_REDRAW_REQUESTED) != 0) { attachInfo.mViewRootImpl.invalidate(); } }
方法注解说明是一个绘制指定View的方法,AttachInfo绑定到指定View上。syncAndDrawFrame是父类HardwareRenderer的一个方法,调用的是native方法。再看一下updateRootDisplayList。
//ThreadedRenderer.java private void updateRootDisplayList(View view, DrawCallbacks callbacks) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Record View#draw()"); //更新view的一些标志位 updateViewTreeDisplayList(view); if (mNextRtFrameCallbacks != null) { final ArrayList<FrameDrawingCallback> frameCallbacks = mNextRtFrameCallbacks; mNextRtFrameCallbacks = null; //设置每帧的绘制回调 setFrameCallback(new FrameDrawingCallback() { @Override public void onFrameDraw(long frame) { } @Override public FrameCommitCallback onFrameDraw(int syncResult, long frame) { ArrayList<FrameCommitCallback> frameCommitCallbacks = new ArrayList<>(); for (int i = 0; i < frameCallbacks.size(); ++i) { FrameCommitCallback frameCommitCallback = frameCallbacks.get(i) .onFrameDraw(syncResult, frame); if (frameCommitCallback != null) { frameCommitCallbacks.add(frameCommitCallback); } } if (frameCommitCallbacks.isEmpty()) { return null; } return didProduceBuffer -> { for (int i = 0; i < frameCommitCallbacks.size(); ++i) { frameCommitCallbacks.get(i).onFrameCommit(didProduceBuffer); } }; } }); } if (mRootNodeNeedsUpdate || !mRootNode.hasDisplayList()) { //拿到RecordingCanvas对象,通过mRootNode获取 RecordingCanvas canvas = mRootNode.beginRecording(mSurfaceWidth, mSurfaceHeight); try { final int saveCount = canvas.save(); canvas.translate(mInsetLeft, mInsetTop); callbacks.onPreDraw(canvas); canvas.enableZ(); //执行canvas的drawRenderNode,来执行mRootNode绘制 canvas.drawRenderNode(view.updateDisplayListIfDirty()); canvas.disableZ(); callbacks.onPostDraw(canvas); canvas.restoreToCount(saveCount); mRootNodeNeedsUpdate = false; } finally { mRootNode.endRecording(); } } Trace.traceEnd(Trace.TRACE_TAG_VIEW);
RecordingCanvas是Canvas的一个子类,而RecordingCanvas#drawRenderNode方法,将绘制任务传递给本地层,调用了nDrawRenderNode是一个native方法。/frameworks/base/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp
//SkiaRecordingCavas.app void SkiaRecordingCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) { // Record the child node. Drawable dtor will be invoked when mChildNodes deque is cleared. mDisplayList->mChildNodes.emplace_back(renderNode, asSkCanvas(), true, mCurrentBarrier); auto& renderNodeDrawable = mDisplayList->mChildNodes.back(); if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { // Put Vulkan WebViews with non-rectangular clips in a HW layer renderNode->mutateStagingProperties().setClipMayBeComplex(mRecorder.isClipMayBeComplex()); } drawDrawable(&renderNodeDrawable); // use staging property, since recording on UI thread if (renderNode->stagingProperties().isProjectionReceiver()) { mDisplayList->mProjectionReceiver = &renderNodeDrawable; } }
SkiaRecordingCanvas是一个用于记录绘制命令的类。它的drawRenderNode方法会将RenderNode添加到显示列表中,并记录相关的绘制命令。drawable方法会将Drawable对象绘制到当前SkCanvas上。这个方法会调用drawable#draw,会将绘制命令传递给SkCanvas。Skia图形库会将绘制命令转换为GPU指令,并通过OpenGL等图形API发送到GPU进行渲染。
SkCanvas是Skia图形库的核心类,用于执行具体的绘制操作。
软件绘制,通过Surface.unlockCanvasAndPost把提交绘制结果到SurfaceFlinger。硬件绘制,通过使用GPU进行绘制,并通过OpenGL等图形API与SurfaceFlinger通信。它们最后都实现了SurfaceFlinger的通信过程,并提交了结果,SurfaceFlinger负责合成各个窗口的内容,并将最终的显示结果提交到屏幕上。
这里给出了Activity一帧的绘制流程:
1、performDraw分两个流程软件绘制和硬件绘制
2、软件绘制直接在ViewRootImpl创建的Surface进行绘制并提交给SurfaceFlinger
3、判断启动硬件加速会创建Render对象
4、硬件绘制通过RecordingCanvas提交绘制任务给本地层
5、RenderNode会记录绘制命令并将绘制命令传递给SkCanvas上
6、Skia图形库将命令转换成GPU指令交由GPU进行渲染
之后最后一篇文章,主要围绕整个图形系统,详细讲讲SurfaceFlinger的概念。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。