Android 硬件加速流程和源码分析(三)

1,601 阅读8分钟

Android 硬件加速流程和源码分析(三)

Android 硬件加速流程和源码分析(一)

Android 硬件加速流程和源码分析(二)

Android 硬件加速流程和源码分析(三)

Android 硬件加速流程和源码分析(四)

Android 硬件加速流程和源码分析(五)

3 更新显示列表DisplayList

上面介绍了硬件加速过程比较重要的几个相关类,接着看硬件加速过程中显示列表(DispalyList)的更新流程,DispalyList的更新是在主线程完成的.

3.1 更新根节点绘制列表

Android硬件加速流程和源码分析(三)_01.png

一个图一行显示不下,继续

Android硬件加速流程和源码分析(三)_02.png

硬件加速 记录并更新显示列表canvas.drawRenderNode(view.updateDisplayListIfDirty())开始,view是当前窗口rootView,会从rootView向下依次遍历记录rootView下面每一个View的绘制记录到当前View对应RecordingCanvas的DisplayList中,然后更新为当前View对应的RenderNode的mStagingDisplayList暂存显示列表,等待下一次屏幕刷新时暂存显示列表mStagingDisplayList会赋值给RenderNode的mDisplayList进行绘制,当前view对应的RenderNode也会作为一个RenderNodeOp添加到上一级View的RenderNode的displayList的children集合中,对RenderNode的子RenderNode的遍历就是从children中取出遍历

3.1.1 ThreadedRenderer#updateRootDisplayList()

ThreadedRenderer#updateRootDisplayList(View view, DrawCallbacks callbacks)

public final class ThreadedRenderer extends HardwareRenderer {
  
    ...
		
    //记录并更新显示列表  (api 28 )   
    //渲染器更新根节点DisplayList
    private void updateRootDisplayList(View view, DrawCallbacks callbacks) {
       
      ...
         //1. 开始更新,生成canvas DisplayListCanvas  继承于 RecordingCanvas
        DisplayListCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight);
        try {
						...
            //实际调用nInsertReorderBarrier(mNativeCanvasWrapper, true),标记为z方向乱序,需要排序  
            canvas.insertReorderBarrier();
          
          	//最重要的一句!!!!!!,从这里开始遍历记录整个视图
          	//最重要的一句!!!!!!
          	//最重要的一句!!!!!!
						// 2. 遍历view树更新每个View的displaylist  
            //实际的动作是向RecordingCanvas.cpp 中的displayList 添加 RecordedOp
            canvas.drawRenderNode(view.updateDisplayListIfDirty());
          
          	//z方向层级排序完成,nInsertReorderBarrier(mNativeCanvasWrapper, false);
            canvas.insertInorderBarrier();
						...
            mRootNodeNeedsUpdate = false;
        } finally {
           	//3. 完成更新: 把RecordingCanvas中的 displayList设置给RenderNode.cpp中的 mStagingDisplayList
            mRootNode.end(canvas);
        }
       ...

    }


    //记录并更新显示列表  (api29 )     
    private void updateRootDisplayList(View view, DrawCallbacks callbacks) {
				...
        //1. 开始更新: 生成canvas 
        RecordingCanvas canvas = mRootNode.beginRecording(mSurfaceWidth, mSurfaceHeight);
        try {
						...
						//实际调用nInsertReorderBarrier(mNativeCanvasWrapper, true),标记为z方向乱序,需要排序
            canvas.enableZ();
          
          	//最重要的一句!!!!!!,从这里开始遍历记录整个视图
          	//最重要的一句!!!!!!
          	//最重要的一句!!!!!!
            // 2. 遍历view树更新每个View的displaylist
            //实际的动作是向RecordingCanvas.cpp 中的displayList 添加 RecordedOp
            canvas.drawRenderNode(view.updateDisplayListIfDirty());
          
          	//z方向层级排序完成,调用nInsertReorderBarrier(mNativeCanvasWrapper, false);
            canvas.disableZ(); 
						....
              
            mRootNodeNeedsUpdate = false;
        } finally {
            //3. 完成更新: 把RecordingCanvas中的 displayList设置给RenderNode.cpp中的暂存显示列表mStagingDisplayList
            mRootNode.endRecording();
        }
    }
}
            
...
  • 关于 insertReorderBarrier , 作用是做一个标记,用于绘制动作在z方向排序的处理

    android_view_DisplayListCanvas.cpp

        static void android_view_DisplayListCanvas_insertReorderBarrier(jlong canvasPtr,
    146        jboolean reorderEnable) {
    147    Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
    148    canvas->insertReorderBarrier(reorderEnable);
    149}
    

    RecordingCanvas.cpp

    57 void RecordingCanvas::insertReorderBarrier(bool enableReorder) {
    58    if (enableReorder) {
    59        mDeferredBarrierType = DeferredBarrierType::OutOfOrder; //乱序
    60        mDeferredBarrierClip = getRecordedClip();
    61    } else {
    62        mDeferredBarrierType = DeferredBarrierType::InOrder; //有序
    63        mDeferredBarrierClip = nullptr;
    64    }
    65}
    
    
    int RecordingCanvas::addOp(RecordedOp* op) {
    				...
    589    int insertIndex = mDisplayList->ops.size();
    590    mDisplayList->ops.push_back(op);//ops记录的是View自己的绘制
    591    if (mDeferredBarrierType != DeferredBarrierType::None) {
    592        // op is first in new chunk
    593        mDisplayList->chunks.emplace_back();
    594        DisplayList::Chunk& newChunk = mDisplayList->chunks.back();
    595        newChunk.beginOpIndex = insertIndex;
    596        newChunk.endOpIndex = insertIndex + 1;
      				//需要从新排序 在后续调用渲染管道的draw方法时会根据这个标志位来处理z方向排序
    597        newChunk.reorderChildren = (mDeferredBarrierType == DeferredBarrierType::OutOfOrder);
    598        newChunk.reorderClip = mDeferredBarrierClip;
    599
    600        int nextChildIndex = mDisplayList->children.size();
    601        newChunk.beginChildIndex = newChunk.endChildIndex = nextChildIndex;
    602        mDeferredBarrierType = DeferredBarrierType::None;
    603    } else {
    604        // standard case - append to existing chunk
    605        mDisplayList->chunks.back().endOpIndex = insertIndex + 1;
    606    }
    607    return insertIndex;
    608}
    

    最终渲染管道 pipeline调用draw() 方法时会调用到FrameBuilder ::buildZSortedChildList()对renderNode的绘制操作在Z方向排序. 对root RenderNode的每个子RenderNode遍历完成后, mDeferredBarrierType = DeferredBarrierType::InOrder .

3.1.2 View#updateDisplayListIfDirty( )

继续看ThreadedRenderer的绘制根布局方法updateRootDisplayList() canvas.drawRenderNode(view.updateDisplayListIfDirty()) View的方法调用,一个窗口布局的绘制是从根布局DecorView的updateDisplayListIfDirty开始的.

View#updateDisplayListIfDirty( )

public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {
      
    public View(Context context) {
				...
        //当前View的renderNode  
        mRenderNode = RenderNode.create(getClass().getName(), this);
      	...
    }

    //记录并更新显示列表
    public RenderNode updateDisplayListIfDirty() {
        final RenderNode renderNode = mRenderNode;

				...
        if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0
                || !renderNode.isValid()
                || (mRecreateDisplayList)) {
          
            // Don't need to recreate the display list, just need to tell our
            // children to restore/recreate theirs
           //1.如果当前的display不需要更新, 只需要通知子View构建displayList,所以只需要dispatchGetDisplayList()
            if (renderNode.isValid()&& !mRecreateDisplayList) {
                mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
                mPrivateFlags &= ~PFLAG_DIRTY_MASK;
                dispatchGetDisplayList();
                return renderNode; // no work needed
            }

          
            // If we got here, we're recreating it. Mark it as such to ensure that
            // we copy in child display lists into ours in drawChild()
            //2. 需要重建当前View的displaylist

            mRecreateDisplayList = true;
						...
            //根据这个layerType判断是否硬件加速  
            int layerType = getLayerType();
						//每个View对应的renderNode对应一个DisplayListCanvas/RecordingCanvas 
            final DisplayListCanvas canvas = renderNode.start(width, height);

            try {
                if (layerType == LAYER_TYPE_SOFTWARE) {
                	//软件绘制
                  
                	//自定义view避免硬件加速导致绘制失效时设置setLayerType(View.LAYER_TYPE_SOFTWARE, null)的作用就是使用软件绘制                
                    buildDrawingCache(true);
                  	//通过buildDrawingCache的到bitmap然后记录到硬件加速的DisplayListCanvas中
                    Bitmap cache = getDrawingCache(true);
                    if (cache != null) {
                        canvas.drawBitmap(cache, 0, 0, mLayerPaint);
                    }
                } else {
                    //硬件加速绘制
                  	
                  	//没有背景那么不需要绘制自己,直接绘制子View
                    // Fast path for layouts with no backgrounds
                    if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
                        //这一步会遍历View树,添加该view的childView的drawxxx()动作到RecordCavans的displayList中
                        dispatchDraw(canvas);
												...	
                    } else {
                        //绘制自己(背景)记录绘制动作到displayList
                      	//如果当前view有背景,那么会调用draw()绘制自己的背景(),draw()中会调用dispatchDraw()分发绘制子View
                        draw(canvas);
                    }
                }
            } finally {
                renderNode.end(canvas);
                setDisplayListProperties(renderNode);
            }
       	   	...
            return renderNode;
        }

上面代码可以看出RenderNoder的displayList并不是每次都需要重建的,如果上一级的RenderNode不需要重建,只需要通知下一级的RenderNoder判断是否需要重新创建或者更新displayList. 在update的过程中也会判断当前View的layerType类型,如果是软件绘制,会把当前view绘制到一个bitmap上,然后添加到上一级的RenderNode中.

  • 当前View的RendNode不需重建displayList的情况:

    当调用updateDisplayListIfDirty()时,会判断当前View的RendNode是否不需重建displayList,如果不需要,那么调用 dispatchGetDisplayList()然后遍历子View调用recreateChildDisplayList(),recreateChildDisplayList()又调用子view child.updateDisplayListIfDirty()遍历下一级子View更新子View的RecordingCanvas中的displayList.

    ViewGroup

    
        @Override
        protected void dispatchGetDisplayList() {
            final int count = mChildrenCount;
            final View[] children = mChildren;
            for (int i = 0; i < count; i++) {
                final View child = children[i];
                if (((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null)) {
                    recreateChildDisplayList(child);
                }
            }
        }
    
        private void recreateChildDisplayList(View child) {
            child.mRecreateDisplayList = (child.mPrivateFlags & PFLAG_INVALIDATED) != 0;
            child.mPrivateFlags &= ~PFLAG_INVALIDATED;
          	//调用自View更新显示列表
            child.updateDisplayListIfDirty();
            child.mRecreateDisplayList = false;
        }
    

    注意, 在recreateChildDisplayList中调用child.updateDisplayListIfDirty()返回的结果是一个RenderNode,这里没有看到这个renderNode被添加到parent view的displayList中,因为,因为..都说了啊...parent view不需要重建displayLsit,所以child view只需要updateDisplayListIfDirty()而不需要重新添加到parent的displayList中,已经在parent的displayList中了,也就是parent的canvas不需要再次调用canvas.drawRenderNode(...)

  • 当前View的RendNode需要重建displayList的情况:

    首先会根据当前View的layerType选择是否软件绘制,如果是软件绘制会把当前View的bitmap记录到硬件加速的DisplayList中, 如果当前view有背景,那么会调用draw()绘制自己的背景(),draw()中会调用dispatchDraw()分发绘制子View,如果当前View没有背景,直接dispatchDraw(),然后会调用drawChild().

        protected void dispatchDraw(Canvas canvas) {
        		...
             //遍历children,每一个child调用 
        		 drawChild(canvas, transientChild, drawingTime);
        		...
        }
        
         protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
            return child.draw(canvas, this, drawingTime);
        }
    

    接着 child.draw(canvas, this, drawingTime)

    View#draw(Canvas canvas, ViewGroup parent, long drawingTime)

    //ViewGroup的子View绘制自己,这个方法中会根据view的layerType和是否是否开启硬件加速指定绘制方式
    19842    /**
    19843     * This method is called by ViewGroup.drawChild() to have each child view draw itself.
    19844     *
    19845     * This is where the View specializes rendering behavior based on layer type,
    19846     * and hardware acceleration.
    19847     */
    19848    boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
      
      					//RecordingCanvas会复写isHardwareAccelerated()返回true,  
    19849        final boolean hardwareAcceleratedCanvas = canvas.isHardwareAccelerated();
      
                //drawingWithRenderNode 硬件加速
    19855        boolean drawingWithRenderNode = mAttachInfo != null
    19856                && mAttachInfo.mHardwareAccelerated
    19857                && hardwareAcceleratedCanvas;
    19858
    							....
      						...
    19913        if (hardwareAcceleratedCanvas) {
    								//注意这里,如果一个View 被标记 PFLAG_INVALIDATED 后需要重建displayList 	
    19916            mRecreateDisplayList = (mPrivateFlags & PFLAG_INVALIDATED) != 0;
    19918        }
      
    19920        RenderNode renderNode = null;
    19921        Bitmap cache = null;
    19922        int layerType = getLayerType(); 
      
    19923        if (layerType == LAYER_TYPE_SOFTWARE || !drawingWithRenderNode) {
    19924             if (layerType != LAYER_TYPE_NONE) {
    19925                 // If not drawing with RenderNode, treat HW layers as SW
      										//如果不使用硬件加速绘制,修改layerType为LAYER_TYPE_SOFTWARE 软件绘制
    19926                 layerType = LAYER_TYPE_SOFTWARE;
    19927                 buildDrawingCache(true);
    19928            }
    19929            cache = getDrawingCache(true);
    19930        }
    19931
      					//如果是使用硬件加速绘制,调用updateDisplayListIfDirty()更新当前View的RenderNode
    19932        if (drawingWithRenderNode) {
    19933            // Delay getting the display list until animation-driven alpha values are
    19934            // set up and possibly passed on to the view
    19935            renderNode = updateDisplayListIfDirty();
    									...
    19943        }
    						...	
    						...
      				//是否使用绘制缓存(软件绘制)	
    19953      final boolean drawingWithDrawingCache = cache != null&&!drawingWithRenderNode;
    19954   		...
    						
      					//不使用[绘制缓存]绘制	
    20067        if (!drawingWithDrawingCache) {
      
      							//硬件加速绘制	
    20068            if (drawingWithRenderNode) {
    									...
      							//drawRenderNode把当前View对应的RenderNode添加到父View的displayList的ops和children集合中去
    20070                ((DisplayListCanvas) canvas).drawRenderNode(renderNode);
    20071            } else {
      									//下面是在getDrawingCache(true)为null即软件绘制的bitmap缓存无效时使用软件绘制的逻辑	
    20072                // 不需要绘背景
    20073                if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
    20074                    ...
    20075                    dispatchDraw(canvas);
    20076                } else {
    20077                    draw(canvas);
    20078                }
    20079            }
    20080        } else if (cache != null) {
      							//cache != null表示当前View不是用硬件加速绘制,但是要把软件绘制后的bitmap添加到上一级的硬件加速绘制的RenderNode中去
                     canvas.drawBitmap(cache, 0.0f, 0.0f, ..);
    20103        }
    20104
    						//修改View为不需要重绘,再次invalidata()后才需要再次重绘	
    20123        mRecreateDisplayList = false;
    20124
    20125        return more;
    20126    }
    

    三参数的 draw(..) 方法是给ViewGroup用来绘制子View的,在ViewGroup需要更新DisplayList的情况下child调用draw(Canvas canvas, ViewGroup parent, long drawingTime)绘制自己,这个方法中会根据view的layerType和是否开启硬件加速指定 view的绘制方式,如果drawingWithRenderNode开启硬件加速,会调用updateDisplayListIfDirty()更新当前View的绘制列表然后调用drawRenderNode(..)把当前View更新后的renderNode更新到父View的RenderNode的displayList中.调用buildDrawingCache()时表示当前View使用软件绘制,,绘制结果返回一个bitmap. 可以看出 PFLAG_INVALIDATED标志会使View需要重建displayList. 上面代码中还可以看出View软件绘制的产物是bitmap,硬件加速绘制的产物是一个包含displayList的RenderNode.

3.2 向DisplayList添加记录

硬件加速过程中所有的view在调用 updateDisplayListIfDirty( ) 方法中都会记录显示列表, 最终都向RecordingCanvas添加绘制记录RecordedOp,接着看下如何添加进去的:

3.2.1 child View添加绘制记录到parent View 的RecordingCanvas

Android硬件加速流程和源码分析(三)_03.png

上面时序图中没有区分java层的RecordingCanvas和DisplayListCanvas. 一个图一行显示不下,继续

Android硬件加速流程和源码分析(三)_04.png

硬件加速情况下,在draw(Canvas canvas, ViewGroup parent, long drawingTime)中可以看到drawingWithRenderNode的情况下child View 的RenderNode作为了一个RenderNodeOp被添加到了父View的RenderNode的 RecordingCanvas/DisplayListCanvas的displayList的ops和children集合中去:

api 28中,Canvas#drawRenderNode() 会调用 DisplayListCanvas#nDrawRenderNode(long renderer, long renderNode)方法,api 29开始这里有些许差别. nDrawRenderNode通过 jni调用 native 方法android_view_DisplayListCanvas_drawRenderNode

android_view_DisplayListCanvas_drawRenderNode

156 static void android_view_DisplayListCanvas_drawRenderNode(jlong canvasPtr, jlong renderNodePtr) {
157    Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
158    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
159    canvas->drawRenderNode(renderNode);
160}
161

RecordingCanvas ::drawRenderNode(RenderNode* renderNode)

541void RecordingCanvas::drawRenderNode(RenderNode* renderNode) {
542    auto&& stagingProps = renderNode->stagingProperties();
  		//把RenderNode 封装成一个RenderNodeOp 
543    RenderNodeOp* op = alloc().create_trivial<RenderNodeOp>(
544            Rect(stagingProps.getWidth(), stagingProps.getHeight()),
545            *(mState.currentSnapshot()->transform), getRecordedClip(), renderNode);
   			//添加RenderNodeOp到DisplayList的ops集合中
546    int opIndex = addOp(op); 
547    if (CC_LIKELY(opIndex >= 0)) {
  					//drawRenderNode会向RenderNode中的DisplayList的`LsaVector<NodeOpType*> children`中添加一个op记录,遍历获取RenderNode的child就是从`children`取出
548        int childIndex = mDisplayList->addChild(op);
549
550        // update the chunk's child indices
551        DisplayList::Chunk& chunk = mDisplayList->chunks.back();
552        chunk.endChildIndex = childIndex + 1;
553
554        if (renderNode->stagingProperties().isProjectionReceiver()) {
555            // use staging property, since recording on UI thread
556            mDisplayList->projectionReceiveIndex = opIndex;
557        }
558    }
559}

可以看出drawRenderNode(..)时RecordingCanvas调用了 mDisplayList->addChild(op)addOp(op) ,前者是把子View的renderNode转化成RenderNodeOp添加到父View RecordingCanvas的DisplayList的child集合,而后者包含view的所有自己的绘制RecordedOp和子View的RenderNodeOp.

到这里实际上还没有看到View绘制自己的绘制方法是怎么添加到自己的 RecordingCanvas中的.

3.2.2 View添加绘制记录到自己的RecordingCanvas

Android硬件加速流程和源码分析(三)_05.png

ViewGroup调用child View的 draw(canvas, Viparent, long drawingTime) 最终会调用到 child View 的onDraw(),在updateDisplayListIfDirty()这一步完成的,updateDisplayListIfDirty() 方法中会获取到当前View的RecordingCanvas,然后这个RecordingCanvas会作为onDraw(canvas)的参数,系统控件和自定义控件都在onDraw()方法中完成各种canvas.drawXXX()绘制,而每一个drawXXX()方法都会调用到native层RecordingCanvas.cpp的drawXXX方法,进而向当前View的RecordingCanvas的DisplayList中添加对应的绘制记录.

下面列举几个RecordingCanvas.cpp中其他的绘制方法,比如

   ....
416 void RecordingCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
				...
419    addOp(alloc().create_trivial<PathOp>(Rect(path.getBounds()),
420                                         *(mState.currentSnapshot()->transform), getRecordedClip(),
421                                         refPaint(&paint), refPath(&path)));
422}
423
424 void RecordingCanvas::VectorDrawable(VectorDrawableRoot* tree) {
425    ...
427    addOp(alloc().create_trivial<VectorDrawableOp>(
428            tree, Rect(tree->stagingProperties()->getBounds()),
429            *(mState.currentSnapshot()->transform), getRecordedClip()));
430}
431
		...

RecordingCanvas.cpp所有的drawxxx()方法都会调用到RecordingCanvas::addOp(RecordedOp* op)

RecordingCanvas ::addOp(RecordedOp* op)

580 int RecordingCanvas::addOp(RecordedOp* op) {
				...
589    int insertIndex = mDisplayList->ops.size();

      //添加 RenderNodeOp 到集合 
  		//DisplayList 记录了将要执行的绘制命令
      //调用Canvas API 绘制UI时,实际上只是将Canvas API调用及其参数记录在DisplayList中,然后等到下一个Vsync信号到来时,记录在Display List里面的 绘制命令才会转化为Open GL或者其他渲染管道api的渲染命令由GPU执行
590    mDisplayList->ops.push_back(op);
591    if (mDeferredBarrierType != DeferredBarrierType::None) {
592        // op is first in new chunk
593        mDisplayList->chunks.emplace_back();
594        DisplayList::Chunk& newChunk = mDisplayList->chunks.back();
595        newChunk.beginOpIndex = insertIndex;
596        newChunk.endOpIndex = insertIndex + 1;
  				//需要重新排序chunk	
597        newChunk.reorderChildren = (mDeferredBarrierType == DeferredBarrierType::OutOfOrder);
598        newChunk.reorderClip = mDeferredBarrierClip;
599
600        int nextChildIndex = mDisplayList->children.size();
601        newChunk.beginChildIndex = newChunk.endChildIndex = nextChildIndex;
602        mDeferredBarrierType = DeferredBarrierType::None;
603    } else {
604        // standard case - append to existing chunk
605        mDisplayList->chunks.back().endOpIndex = insertIndex + 1;
606    }
607    return insertIndex;
608}

硬件加速是canvas.drawxxx()方法最终调用的都是 RecordingCanvas.cpp中相应的方法添加一个 BaseOpType 到 RecordingCanvas 中的 DisplayList中

drawRenderNode会向RenderNode中的DisplayList的LsaVector<NodeOpType*> children中添加一个op记录,遍历获取RenderNode的child就是从这里取出

到以上代码为止, 显示列表DisplayList的更新动作就完成了.

Android 硬件加速流程和源码分析(一)

Android 硬件加速流程和源码分析(二)

Android 硬件加速流程和源码分析(三)

Android 硬件加速流程和源码分析(四)

Android 硬件加速流程和源码分析(五)