Android HWUI绘制流程

Android中绘图的API有很多,比如2D的绘图skia;3D的绘图OpenGLES,Vulkan等。Android在后来完善3D API支持的同时,也在更新View Widget渲染机制,提出了硬件加速机制。

HWUI绘制的大致流程是先初始化绘制环境(创建rendernode、渲染线程RenderThread、Context上下文、RenderProxy代理对象),之后是创建DisplayList显示列表,然后开始视图绘制,视图绘制结束后开始同步帧数据。


硬件加速
作用:是将2D的绘图操纵转换为对应的3D绘图操纵。需要显示的时候,再用OpenGLES通过GPU渲染。过程:界面创建时,第一次全部渲染,后续界面如果只有部分区域的widget更新,只需要重新渲染更新的widget。渲染好的绘图保存在一个显示列表
DisplayList
中,需要真正显示到界面的时候,直接显示DisplayList中的绘图。好处:一方面利用GPU去渲染,比Skia要快;另一方面,采用DisplayList,再次渲染只更新部分区域,最大程度利用上一帧的数据,提高效率。

使用Android Q AOSP源码梳理流程。

1. GPU渲染(硬件加速)介绍

在Android应用程序中是通过Canvas API来绘制UI元素的。在硬件加速渲染环境中,这些Canvas API调用最终会转化为OpenGL API调用(转化过程对应用程序来说是透明的)。由于OpenGL API调用要求发生在Open GL环境中,因此在每当有新的Activity窗口启动时,系统都会为其初始化好OpenGL环境。

这里的渲染,主要是Android硬件加速,即GPU渲染。android上就是通过libhwui调用OpenGL api来渲染, Android P上libhwui 会调用skia,再调用GLES相关的API进行渲染。

GPU作为一个硬件 , 用户空间是不可以直接使用的, 它是由GPU厂商按照Open GL规范实现的驱动间接进行使用的。也就是说 , 如果一个设备支持GPU硬件加速渲染, 那么当Android应用程序调用OpenGL接口来绘制UI时 ,Android应用程序的UI就是通过硬件加速技术进行渲染的。

名词介绍:

GPU:一个类似于CPU的专门用来处理Graphics的处理器, 作用用来帮助加快栅格化操作, 当然, 也有相应的缓存数据(例如缓存已经光栅化过的bitmap等)机制。OpenGL ES:是手持嵌入式设备的3DAPI, 跨平台的、功能完善的2D和3D图形应用程序接口API, 有一套固定渲染管线流程DisplayList:在Android把XML布局文件转换成GPU能够识别并绘制的对象。这个操作是在DisplayList的帮助下完成的。DisplayList持有所有将要交给GPU绘制到屏幕上的数据信息。栅格化:是将图片等矢量资源, 转化为一格格像素点的像素图, 显示到屏幕上。垂直同步VSYNC:让显卡的运算和显示器刷新率一致以稳定输出的画面质量。它告知GPU在载入新帧之前,要等待屏幕绘制完成前一帧。RefreshRate:屏幕一秒内刷新屏幕的次数, 由硬件决定, 例如60HzFrame Rate:GPU一秒绘制操作的帧数, 单位是fps

2. Android 5.0 之后的渲染框架

在Android应用程序窗口中, 每一个View都抽象为一个Render Node, 而且如果一个View设置有Background, 这个background 也被抽象为一个Render Node 。

这是由于在OpenGLRenderer库中, 并没有View的概念, 所有的一切可绘制的元素都抽象为一个Render Node。

每一个Render Node都关联有一个
DisplayList Renderer
, Display List是一个绘制命令缓冲区。当View的成员函数onDraw被调用时, 我们调用通过参数传递进来的Canvas的
drawXXX
成员函数绘制图形时, 我们实际上只是将对应的绘制命令以及参数保存在一个Display List中。接下来再通过DisplayList Renderer执行这个Display List的命令, 这个过程称为Display List Replay。

Android应用程序窗口的View是通过树形结构来组织的。这些View不管是通过硬件加速渲染还是软件渲染, 或者是一个特殊的TextureView,在它们的成员函数onDraw被调用期间, 它们都是将自己的UI绘制在ParentView的DisplayList中。

其中, 最顶层的Parent View是一个Root View, 它关联的RootNode称为
Root Render Node
。也就是说, 最终Root Render Node的DisplayList将会包含一个窗口的所有绘制命令。

在绘制窗口的下一帧时, RootRender Node的Display List都会通过一个OpenGL Renderer真正地通过Open GL命令绘制在一个
Graphic Buffer
中。

最后这个 Graphic Buffer 被交给 SurfaceFlinger 服务进行合成和显示。


3. Android原生硬件绘制案例

这个案例是用的SurfaceView.java的流程。这个流程和实际上从ViewRootImpl.java中通过performDraw的流程类似。可以互相借鉴参考。

Android原生的硬件绘制案例,在
frameworks/base/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasSurfaceViewActivity.java



//HardwareCanvasSurfaceViewActivity.java
private static class RenderingThread extends Thread {
        private final SurfaceHolder mSurface;
        private volatile boolean mRunning = true;
        private int mWidth, mHeight;
        //应用拿到一个Surface
        public RenderingThread(SurfaceHolder surface) {
            mSurface = surface;
        }
 
        void setSize(int width, int height) {
            mWidth = width;
            mHeight = height;
        }
 
        @Override
        public void run() {
            float x = 0.0f;
            float y = 0.0f;
            float speedX = 5.0f;
            float speedY = 3.0f;
 
            Paint paint = new Paint();
            paint.setColor(0xff00ff00);
 
            while (mRunning && !Thread.interrupted()) {
                //先调用Surface的lockHardwareCanvas函数
                final Canvas canvas = mSurface.lockHardwareCanvas();
                try {
                    //绘制
                    canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR);
                    canvas.drawRect(x, y, x + 20.0f, y + 20.0f, paint);
                } finally {
                    //绘制完成后
                    mSurface.unlockCanvasAndPost(canvas);
                }
                if (x + 20.0f + speedX >= mWidth || x + speedX <= 0.0f) {
                    speedX = -speedX;
                }
                if (y + 20.0f + speedY >= mHeight || y + speedY <= 0.0f) {
                    speedY = -speedY;
                }
 
                x += speedX;
                y += speedY;
 
                try {
                    //每个15s循环一次
                    Thread.sleep(15);
                } catch (InterruptedException e) {
                    // Interrupted
                }
            }
        }
 
        void stopRendering() {
            interrupt();
            mRunning = false;
        }
    }
 

3.1. Java层相关流程(frameworks/base的View模块和graphics模块)

首先调用关键函数
lockHardwareCanvas
,在
frameworks/base/core/java/android/view/SurfaceView.java



//frameworks/base/core/java/android/view/SurfaceView.java
    @Override
    public Surface getSurface() {
        return mSurface;
    }
 
    public Canvas lockHardwareCanvas() {
            return internalLockCanvas(null, true);
        }
 
        private Canvas internalLockCanvas(Rect dirty, boolean hardware) {
            mSurfaceLock.lock();
            Canvas c = null;
            if (!mDrawingStopped && mSurfaceControl != null) {
                try {
                    if (hardware) {
                        //hardware传递的是true,执行lockHardwareCanvas
                        c = mSurface.lockHardwareCanvas();
                    } else {
                        c = mSurface.lockCanvas(dirty);
                    }
                } catch (Exception e) {
                    Log.e(LOG_TAG, "Exception locking surface", e);
                }
            }
 
            if (c != null) {
                mLastLockTime = SystemClock.uptimeMillis();
                return c;
            }
            ......
            mLastLockTime = now;
            mSurfaceLock.unlock();
 
            return null;
        }

然后就调用Surface.java的lockHardwareCanvas函数,此处封装了一个
HwuiContext
对象,构造函数如下:



//frameworks/base/core/java/android/view/Surface.java
    public Canvas lockHardwareCanvas() {
        Log.d(TAG, "lockHardwareCanvas");
        synchronized (mLock) {
            checkNotReleasedLocked();
            if (mHwuiContext == null) {
                //Step 1 创建HwuiContext,调用构造函数
                mHwuiContext = new HwuiContext(false);
            }
            //Step 2 调用他的lockCanvas函数
            return mHwuiContext.lockCanvas(
                    nativeGetWidth(mNativeObject),
                    nativeGetHeight(mNativeObject));
        }
    }
    //从上面调用到
    Canvas lockCanvas(int width, int height) {
            if (mCanvas != null) {
                throw new IllegalStateException("Surface was already locked!");
            }
            //调用RenderNode的beginRecording函数
            mCanvas = mRenderNode.beginRecording(width, height);
            return mCanvas;
        }
    .....
  }

3.2. 创建RenderNode

RenderNode用以绘图操纵的批处理,当绘制的时候,可以store和apply。java层的代码如下:其实RenderNode就对应前面我们所说的ViewGroup,有一个RootView,同样也有一个RootNode。

在上面Surface.java调用HwuiContext构造函数的时候,会创建RenderNode对象:



//Surface.java
    private final class HwuiContext {
        //创建RenderNode和HwuiRender
        private final RenderNode mRenderNode;
        private long mHwuiRenderer;
        private RecordingCanvas mCanvas;
        private final boolean mIsWideColorGamut;
        //构造函数
        HwuiContext(boolean isWideColorGamut) {
            //创建一个RenderNode
            mRenderNode = RenderNode.create("HwuiCanvas", null);
            ......

创建RenderNode对象:



//frameworks/base/graphics/java/android/graphics/RenderNode.java
    public static RenderNode create(String name, @Nullable AnimationHost animationHost) {
        return new RenderNode(name, animationHost);
    }
 
    private RenderNode(String name, AnimationHost animationHost) {
        mNativeRenderNode = nCreate(name);
        NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativeRenderNode);
        mAnimationHost = animationHost;
    }

JNI层:



//frameworks/base/core/jni/android_view_RenderNode.cpp
static const JNINativeMethod gMethods[] = {
// ----------------------------------------------------------------------------
// Regular JNI
// ----------------------------------------------------------------------------
    { "nCreate",               "(Ljava/lang/String;)J", (void*) android_view_RenderNode_create },
    { "nGetNativeFinalizer",   "()J",    (void*) android_view_RenderNode_getNativeFinalizer },
    { "nOutput",               "(J)V",    (void*) android_view_RenderNode_output },
    ...
 
static jlong android_view_RenderNode_create(JNIEnv* env, jobject, jstring name) {
    //创建一个native层的rendernode对象
    RenderNode* renderNode = new RenderNode();
    renderNode->incStrong(0);
    if (name != NULL) {
        const char* textArray = env->GetStringUTFChars(name, NULL);
        renderNode->setName(textArray);
        env->ReleaseStringUTFChars(name, textArray);
    }
    return reinterpret_cast<jlong>(renderNode);
}

Native层,创建好RenderNode是提供给DisplayListCanvas。



//frameworks/base/libs/hwui/RenderNode.cpp
RenderNode::RenderNode()
        : mUniqueId(generateId())
        , mDirtyPropertyFields(0)
        , mNeedsDisplayListSync(false)
        , mDisplayList(nullptr)
        , mStagingDisplayList(nullptr)
        , mAnimatorManager(*this)
        , mParentCount(0) {}

3.3. beginRecording初始化DisplayList

在Surface.java中通过lockCanvas调用RenderNode对象的
beginRecording
函数。



//frameworks/base/graphics/java/android/graphics/RenderNode.java
   public @NonNull RecordingCanvas beginRecording(int width, int height) {
        if (mCurrentRecordingCanvas != null) {
            throw new IllegalStateException(
                    "Recording currently in progress - missing #endRecording() call?");
        }
        mCurrentRecordingCanvas = RecordingCanvas.obtain(this, width, height);
        return mCurrentRecordingCanvas;
    }

接着调用RecordingCanvas的obtain函数:

类的继承关系: RecordingCanvas类继承DisplayListCanvas,而DisplayListCanvas继承BaseRecordingCanvas, BaseRecordingCanvas继承Canvas(继承BaseCanvas)。



//frameworks/base/graphics/java/android/graphics/RecordingCanvas.java
public final class RecordingCanvas extends DisplayListCanvas {
    //构造函数
    /** @hide */
    protected RecordingCanvas(@NonNull RenderNode node, int width, int height) {
        super(nCreateDisplayListCanvas(node.mNativeRenderNode, width, height));
        mDensity = 0; // disable bitmap density scaling
    }
 
    static RecordingCanvas obtain(@NonNull RenderNode node, int width, int height) {
        if (node == null) throw new IllegalArgumentException("node cannot be null");
        RecordingCanvas canvas = sPool.acquire();
        if (canvas == null) {
            canvas = new RecordingCanvas(node, width, height);
        } else {
            //创建一个native的DisplayListCanvas对象(即显示列表的Canvas)
            //JNI通过mNativeCanvasWrapper(BaseCanvas.java创建)找对应的Native的Canvas
            nResetDisplayListCanvas(canvas.mNativeCanvasWrapper, node.mNativeRenderNode,
                    width, height);
        }
        canvas.mNode = node;
        canvas.mWidth = width;
        canvas.mHeight = height;
        return canvas;
    }
    ...
}

查看测试案例代码中的
canvas.drawColor

canvas.drawRect
函数,是调用了其父类
BaseCanvas
的对应方法。



//frameworks/base/graphics/java/android/graphics/Canvas.java
    public void drawColor(@ColorLong long color) {
        super.drawColor(color, BlendMode.SRC_OVER);
    }
 
     public void drawRect(@NonNull RectF rect, @NonNull Paint paint) {
        super.drawRect(rect, paint);
    }

父类
BaseCanvas.java



//frameworks/base/graphics/java/android/graphics/BaseCanvas.java
   public void drawColor(@ColorInt int color, @NonNull PorterDuff.Mode mode) {
        nDrawColor(mNativeCanvasWrapper, color, mode.nativeInt);
    }
 
  public void drawRect(@NonNull Rect r, @NonNull Paint paint) {
        throwIfHasHwBitmapInSwMode(paint);
        drawRect(r.left, r.top, r.right, r.bottom, paint);
    }

Native层Canvas创建(JNI和HWUI模块)

1.在上面RecordingCanvas.java的构造函数中调用了
nCreateDisplayListCanvas
函数,对饮的JNI实现:



//frameworks/base/core/jni/android_view_DisplayListCanvas.cpp
 
const char* const kClassPathName = "android/graphics/RecordingCanvas";
 
static JNINativeMethod gMethods[] = {
 
    // ------------ @FastNative ------------------
 
    { "nCallDrawGLFunction", "(JJLjava/lang/Runnable;)V",
            (void*) android_view_DisplayListCanvas_callDrawGLFunction },
 
    // ------------ @CriticalNative --------------
    { "nCreateDisplayListCanvas", "(JII)J",     (void*) android_view_DisplayListCanvas_createDisplayListCanvas },
    { "nResetDisplayListCanvas",  "(JJII)V",    (void*) android_view_DisplayListCanvas_resetDisplayListCanvas },
    { "nGetMaximumTextureWidth",  "()I",        (void*) android_view_DisplayListCanvas_getMaxTextureSize },
    { "nGetMaximumTextureHeight", "()I",        (void*) android_view_DisplayListCanvas_getMaxTextureSize },
    { "nInsertReorderBarrier",    "(JZ)V",      (void*) android_view_DisplayListCanvas_insertReorderBarrier },
    { "nFinishRecording",         "(J)J",       (void*) android_view_DisplayListCanvas_finishRecording },
    { "nDrawRenderNode",          "(JJ)V",      (void*) android_view_DisplayListCanvas_drawRenderNode },
    { "nDrawTextureLayer",        "(JJ)V",      (void*) android_view_DisplayListCanvas_drawTextureLayer },
    { "nDrawCircle",              "(JJJJJ)V",   (void*) android_view_DisplayListCanvas_drawCircleProps },
    { "nDrawRoundRect",           "(JJJJJJJJ)V",(void*) android_view_DisplayListCanvas_drawRoundRectProps },
    { "nDrawWebViewFunctor",      "(JI)V",      (void*) android_view_DisplayListCanvas_drawWebViewFunctor },
};
 
//具体实现,此处的renderNodePtr变量是RenderNode在native层的对象(地址)
static jlong android_view_DisplayListCanvas_createDisplayListCanvas(jlong renderNodePtr,
        jint width, jint height) {
    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
    return reinterpret_cast<jlong>(Canvas::create_recording_canvas(width, height, renderNode));
}

调用到frameworks/base/libs/hwui/hwui/Canvas.cpp,在Andorid Q中,此处只调用了
SkiaRecordingCanvas
函数。使用skia进行绘制。



//frameworks/base/libs/hwui/hwui/Canvas.cpp
Canvas* Canvas::create_recording_canvas(int width, int height, uirenderer::RenderNode* renderNode) {
    return new uirenderer::skiapipeline::SkiaRecordingCanvas(renderNode, width, height);
}

然后初始化DisplayList:



//frameworks/base/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
    explicit SkiaRecordingCanvas(uirenderer::RenderNode* renderNode, int width, int height) {
        initDisplayList(renderNode, width, height);
    }


//frameworks/base/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
void SkiaRecordingCanvas::initDisplayList(uirenderer::RenderNode* renderNode, int width,
                                          int height) {
    mCurrentBarrier = nullptr;
    SkASSERT(mDisplayList.get() == nullptr);
 
    if (renderNode) {
        mDisplayList = renderNode->detachAvailableList();
    }
    if (!mDisplayList) {
        mDisplayList.reset(new SkiaDisplayList());
    }
 
    mDisplayList->attachRecorder(&mRecorder, SkIRect::MakeWH(width, height));
    SkiaCanvas::reset(&mRecorder);
}

3.4. Draw绘制操纵

正常流程的绘制是在frameworks/base/core/java/android/view/ThreadedRenderer.java的
updateRootDisplayList
函数中。 调用到drawRnderNode函数绘制。

Java层:上面案例中的
drawColor

drawRect
实际调用的是在frameworks/base/graphics/java/android/graphics/BaseCanvas.java中:



//frameworks/base/graphics/java/android/graphics/BaseCanvas.java
   public void drawColor(@ColorInt int color, @NonNull PorterDuff.Mode mode) {
        nDrawColor(mNativeCanvasWrapper, color, mode.nativeInt);
    }
 
  public void drawRect(@NonNull Rect r, @NonNull Paint paint) {
        throwIfHasHwBitmapInSwMode(paint);
        drawRect(r.left, r.top, r.right, r.bottom, paint);
    }

调用JNI层在:



//frameworks/base/core/jni/android_graphics_Canvas.cpp
static const JNINativeMethod gDrawMethods[] = {
    {"nDrawColor","(JII)V", (void*) CanvasJNI::drawColor},
    {"nDrawColor","(JJJI)V", (void*) CanvasJNI::drawColorLong},
    {"nDrawPaint","(JJ)V", (void*) CanvasJNI::drawPaint},
    {"nDrawPoint", "(JFFJ)V", (void*) CanvasJNI::drawPoint},
    {"nDrawPoints", "(J[FIIJ)V", (void*) CanvasJNI::drawPoints},
    {"nDrawLine", "(JFFFFJ)V", (void*) CanvasJNI::drawLine},
    {"nDrawLines", "(J[FIIJ)V", (void*) CanvasJNI::drawLines},
    {"nDrawRect","(JFFFFJ)V", (void*) CanvasJNI::drawRect},
    ......
}
 
static void drawColor(JNIEnv* env, jobject, jlong canvasHandle, jint color, jint modeHandle) {
    SkBlendMode mode = static_cast<SkBlendMode>(modeHandle);
    get_canvas(canvasHandle)->drawColor(color, mode);
}
 
static void drawRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
                     jfloat right, jfloat bottom, jlong paintHandle) {
    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    get_canvas(canvasHandle)->drawRect(left, top, right, bottom, *paint);
}

Native层:因为
class SkiaCanvas : public Canvas
,所以调用到SkiaCanvas类中的具体实现:

调用



//frameworks/base/libs/hwui/SkiaCanvas.cpp
void SkiaCanvas::drawColor(int color, SkBlendMode mode) {
    mCanvas->drawColor(color, mode);
}
 
void SkiaCanvas::drawRect(float left, float top, float right, float bottom, const SkPaint& paint) {
    if (CC_UNLIKELY(paint.nothingToDraw())) return;
    mCanvas->drawRect({left, top, right, bottom}, *filterPaint(paint));
}

调用到
external/skia
图形库模块:

Skia是Google一个底层的图形、图像、动画、SVG、文本等多方面的图形库,是Android中图形系统的引擎。Skia作为第三方软件放在目录:
external/skia/

主要包含三个库:

libcorecg.so: 包含
/skia/src/core
的部分内容,比如其中的
Region、Rect
是在SurfaceFlinger里面用来计算可视区域的;libsgl.so: 包含
/skia/src/core|effects|images|ports|utils
的部分和全部内容,这个实现了skia大部分的图形效果,以及图形格式的编解码;libskiagl.so: 包含
/skia/src/gl
里面的内容,主要用来调用opengl实现部分效果。



//external/skia/src/core/SkCanvas.cpp
void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
    SkPaint paint;
    paint.setColor(c);
    paint.setBlendMode(mode);
    this->drawPaint(paint);
}
 
void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
    TRACE_EVENT0("skia", TRACE_FUNC);
    // To avoid redundant logic in our culling code and various backends, we always sort rects
    // before passing them along.
    this->onDrawRect(r.makeSorted(), paint);
}

3.5. HwuiContext和HwuiRenderer

3.5.1. Java层

从上面的Surface.java中看到,nHwuiCreate创建HwuiRenderer。



//Surface.java
            //创建一个native的HwuiRender对象
            mHwuiRenderer = nHwuiCreate(mRenderNode.mNativeRenderNode, mNativeObject,
                    isWideColorGamut);
                    ......

3.5.2. JNI层



//frameworks/base/core/jni/android_view_Surface.cpp
static const JNINativeMethod gSurfaceMethods[] = {
    ......
    // HWUI context
    {"nHwuiCreate", "(JJZ)J", (void*) hwui::create },
    {"nHwuiSetSurface", "(JJ)V", (void*) hwui::setSurface },
    {"nHwuiDraw", "(J)V", (void*) hwui::draw },
    {"nHwuiDestroy", "(J)V", (void*) hwui::destroy },
};
 
//=========================================具体实现
namespace uirenderer {
 
using namespace android::uirenderer::renderthread;
 
class ContextFactory : public IContextFactory {
......
static jlong create(JNIEnv* env, jclass clazz, jlong rootNodePtr, jlong surfacePtr,
        jboolean isWideColorGamut) {
    RenderNode* rootNode = reinterpret_cast<RenderNode*>(rootNodePtr);
    sp<Surface> surface(reinterpret_cast<Surface*>(surfacePtr));
    ContextFactory factory;
    //创建一个RenderProxy对象,并作为返回对象
    RenderProxy* proxy = new RenderProxy(false, rootNode, &factory);
    proxy->loadSystemProperties();
    if (isWideColorGamut) {
        proxy->setWideGamut(true);
    }
    proxy->setSwapBehavior(SwapBehavior::kSwap_discardBuffer);
    proxy->setSurface(surface);
    // Shadows can't be used via this interface, so just set the light source
    // to all 0s.
    proxy->setLightAlpha(0, 0);
    proxy->setLightGeometry((Vector3){0, 0, 0}, 0);
    return (jlong) proxy;
}

以下流程部分是和通常的HWUI环境初始化流程相同的。

3.5.3. *native层:调用RenderProxy.cpp构造函数

该构造函数的几个重要成员变量:

RenderProxy是一个代理者,严格的单线程。所有的方法都必须在自己的线程中调用。
MainThread通过这个代理对象想Task Queue发送drawFrame命令
;RenderThread(即构造函数中的mRenderThread):渲染线程,是一个单例,也就是说,一个进程中只有一个,所有的绘制操纵都必须在这个线程中完成。应用端很多操纵,都以RenderTask的形式post到RenderThread线程中完成。(在Android 5.0之后独立出来的应用程序的OpenGL线程)CanvasContext(即构造函数中的mContext):上下文,由于OpenGL是单线程的,所以,我们给到GPU的绘图命令都封装在各自的上下文中。这个和上层的HwuiRenderer是对应的。(将窗口绑定到Open GL渲染上下文中,从而使后面的渲染操作都是针对被绑定窗口的)DrawFrameTask(即构造函数中的mDrawFrameTask):一个用来执行渲染任务的task,MainThread通过他向RenderThread线程发送渲染下一帧的命令。(比较特殊的一个RenderTask,可重复使用的绘制Task。)



//frameworks/base/libs/hwui/renderthread/RenderProxy.cpp
 
namespace android {
namespace uirenderer {
namespace renderthread {
 
RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode,
                         IContextFactory* contextFactory)
        : mRenderThread(RenderThread::getInstance()), mContext(nullptr) {  //Step1 创建RenderThread渲染线程
    mContext = mRenderThread.queue().runSync([&]() -> CanvasContext* {
        return CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory); //Step2 创建CanvasContext渲染上下文
    });
    mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode);
}
......

3.6. 创建RenderThread渲染线程

从上面构造函数中的
RenderThread::getInstance()
调用下去。创建一个RenderThread线程。

而该类的父类是ThreadBase.h,父类的父类是Thread.h



//frameworks/base/libs/hwui/renderthread/RenderThread.cpp
RenderThread& RenderThread::getInstance() {
    // This is a pointer because otherwise __cxa_finalize
    // will try to delete it like a Good Citizen but that causes us to crash
    // because we don't want to delete the RenderThread normally.
    static RenderThread* sInstance = new RenderThread();
    gHasRenderThreadInstance = true;
    return *sInstance;
}
 
RenderThread::RenderThread()
        : ThreadBase()
        , mVsyncSource(nullptr)
        , mVsyncRequested(false)
        , mFrameCallbackTaskPending(false)
        , mRenderState(nullptr)
        , mEglManager(nullptr)
        , mFunctorManager(WebViewFunctorManager::instance())
        , mVkManager(nullptr) {
    Properties::load();
    //实现父类的函数,调用run
    start("RenderThread");
}

父类创建looper循环和start函数实现,然后开始线程循环:



//frameworks/base/libs/hwui/thread/ThreadBase.h
    ThreadBase()
            : Thread(false)
            , mLooper(new Looper(false))
            , mQueue([this]() { mLooper->wake(); }, mLock) {}
 
    void start(const char* name = "ThreadBase") { Thread::run(name); }

开始线程循环,调用ThreadLoop开始工作。



//frameworks/base/libs/hwui/renderthread/RenderThread.cpp
bool RenderThread::threadLoop() {
    ...
    initThreadLocals();
 
    while (true) {
        waitForWork();
        processQueue();
 
        if (mPendingRegistrationFrameCallbacks.size() && !mFrameCallbackTaskPending) {
            drainDisplayEventQueue();
            mFrameCallbacks.insert(mPendingRegistrationFrameCallbacks.begin(),
                                   mPendingRegistrationFrameCallbacks.end());
            mPendingRegistrationFrameCallbacks.clear();
            requestVsync();
        }
 
        if (!mFrameCallbackTaskPending && !mVsyncRequested && mFrameCallbacks.size()) {
            requestVsync();
        }
    }
 
    return false;
}
 
//以下流程会处理vsync信号
void RenderThread::initThreadLocals() {
    setupFrameInterval();
    initializeDisplayEventReceiver();
    mEglManager = new EglManager();
    mRenderState = new RenderState(*this);
    mVkManager = new VulkanManager();
    mCacheManager = new CacheManager(DeviceInfo::get()->displayInfo());
}
 
void RenderThread::initializeDisplayEventReceiver() {
    if (!Properties::isolatedProcess) {
        ...
        // Register the FD
        mLooper->addFd(receiver->getFd(), 0, Looper::EVENT_INPUT,
                       RenderThread::displayEventReceiverCallback, this);
        mVsyncSource = new DisplayEventReceiverWrapper(std::move(receiver), [this] {
            DeviceInfo::get()->onDisplayConfigChanged();
            setupFrameInterval();
        });
    } else {
        mVsyncSource = new DummyVsyncSource(this);
    }
}
 
int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) {
    ...
    reinterpret_cast<RenderThread*>(data)->drainDisplayEventQueue();
 
    return 1;  // keep the callback
}
 
void RenderThread::drainDisplayEventQueue() {
    ATRACE_CALL();
    nsecs_t vsyncEvent = mVsyncSource->latestVsyncEvent();
    if (vsyncEvent > 0) {
        mVsyncRequested = false;
        if (mTimeLord.vsyncReceived(vsyncEvent) && !mFrameCallbackTaskPending) {
            ATRACE_NAME("queue mFrameCallbackTask");
            mFrameCallbackTaskPending = true;
            nsecs_t runAt = (vsyncEvent + mDispatchFrameDelay);
            queue().postAt(runAt, [this]() { dispatchFrameCallbacks(); });
        }
    }
}

3.7. CanvasContext渲染上下文

该函数中会选择渲染绘制方式(渲染管线)。在Android Q中取消了OpenGL渲染。

Android P之前:
enum class RenderPipelineType { OpenGL = 0, SkiaGL, SkiaVulkan, NotInitialized = 128 };
Android Q:
enum class RenderPipelineType { SkiaGL, SkiaVulkan, NotInitialized = 128 };



//frameworks/base/libs/hwui/renderthread/CanvasContext.cpp
CanvasContext* CanvasContext::create(RenderThread& thread, bool translucent,
                                     RenderNode* rootRenderNode, IContextFactory* contextFactory) {
    //获取渲染方式getRenderPipelineType
    auto renderType = Properties::getRenderPipelineType();
 
    switch (renderType) {
        case RenderPipelineType::SkiaGL:
            return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
                                     std::make_unique<skiapipeline::SkiaOpenGLPipeline>(thread));
        case RenderPipelineType::SkiaVulkan:
            return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
                                     std::make_unique<skiapipeline::SkiaVulkanPipeline>(thread));
        default:
            LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t)renderType);
            break;
    }
    return nullptr;
}
 
RenderPipelineType Properties::getRenderPipelineType() {
    sRenderPipelineType = peekRenderPipelineType();
    return sRenderPipelineType;
}
 
RenderPipelineType Properties::peekRenderPipelineType() {
    // If sRenderPipelineType has been locked, just return the locked type immediately.
    if (sRenderPipelineType != RenderPipelineType::NotInitialized) {
        return sRenderPipelineType;
    }
    bool useVulkan = use_vulkan().value_or(false);
    char prop[PROPERTY_VALUE_MAX];
    //PROPERTY_RENDERER "debug.hwui.renderer"
    //enum class RenderPipelineType { SkiaGL, SkiaVulkan, NotInitialized = 128 };
    property_get(PROPERTY_RENDERER, prop, useVulkan ? "skiavk" : "skiagl");
    if (!strcmp(prop, "skiavk")) {
        return RenderPipelineType::SkiaVulkan;
    }
    return RenderPipelineType::SkiaGL;
}

mRenderPipeline有几种类型,创建CanvasContext时(
create函数
),会根据pipeline的类型,创建对应的Pipeline。(即调用
getRenderPipelineType函数

渲染类型:
enum class RenderPipelineType { SkiaGL, SkiaVulkan, NotInitialized = 128 };




//frameworks/base/libs/hwui/renderthread/CanvasContext.cpp
//构造函数
CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
                             IContextFactory* contextFactory,
                             std::unique_ptr<IRenderPipeline> renderPipeline)
        : mRenderThread(thread)
        , mGenerationID(0)
        , mOpaque(!translucent)
        , mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord()))
        , mJankTracker(&thread.globalProfileData(), DeviceInfo::get()->displayInfo())
        , mProfiler(mJankTracker.frames(), thread.timeLord().frameIntervalNanos())
        , mContentDrawBounds(0, 0, 0, 0)
        , mRenderPipeline(std::move(renderPipeline)) {
    rootRenderNode->makeRoot();
    mRenderNodes.emplace_back(rootRenderNode);  //是前面创建的RenderNode
    mProfiler.setDensity(DeviceInfo::get()->displayInfo().density);
    setRenderAheadDepth(Properties::defaultRenderAhead);
}

在上面调用nHwuiCreate的JNI层,会创建ContextFactory,然后在此处的构造函数中会使用到。主要用来创建AnimationContext,即
mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord()))

AnimationContext主要用来处理动画Animation。



//frameworks/base/core/jni/android_view_Surface.cpp
class ContextFactory : public IContextFactory {
public:
    virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) {
        return new AnimationContext(clock);
    }
};

CanvasContext实现了IFrameCallback接口,所以,CanvasContext能接收编舞者Choreographer的callback,处理实时动画。
class CanvasContext : public IFrameCallback {...}


3.7.1. RenderThread渲染线程

RenderThread渲染运行模型:空闲的时候, RenderThread睡眠在成员变量mLooper指向的一个Looper对象的成员函数pollOnceh中。当其他线程需要调度RenderThread, 会向他的任务队列添加一个任务, 然后唤醒RenderThread进行处理。RenderThread通过
processQueue
方法处理任务。



//frameworks/base/libs/hwui/renderthread/RenderThread.h
//继承ThreadBase,而ThreadBase是继承基类Thread
class RenderThread : private ThreadBase {
    //组织拷贝构造函数和重载
    PREVENT_COPY_AND_ASSIGN(RenderThread);

调用构造函数中,同时启动了渲染线程RenderThread:



//frameworks/base/libs/hwui/renderthread/RenderThread.cpp
RenderThread::RenderThread()
        : ThreadBase()
        , mVsyncSource(nullptr)
        , mVsyncRequested(false)
        , mFrameCallbackTaskPending(false)
        , mRenderState(nullptr)
        , mEglManager(nullptr)
        , mFunctorManager(WebViewFunctorManager::instance())
        , mVkManager(nullptr) {
    Properties::load();
    start("RenderThread"); //线程启动
}

查看父类的构造函数:



//frameworks/base/libs/hwui/thread/ThreadBase.h
    ThreadBase()
            : Thread(false)
            , mLooper(new Looper(false))
            , mQueue([this]() {
                 mLooper->wake();  //此处调用是唤醒mLooper,线程开始工作
            }, mLock) {}

在渲染线程启动后,会调用RenderThread.cpp的threadLoop函数。



bool RenderThread::threadLoop() {
    setpriority(PRIO_PROCESS, 0, PRIORITY_DISPLAY);
    Looper::setForThread(mLooper);
    if (gOnStartHook) {
        gOnStartHook("RenderThread");
    }
    //初始化Thread的本地变量
    initThreadLocals();
 
    while (true) {
        //没有任务就等在此处
        waitForWork();
        processQueue();
 
        if (mPendingRegistrationFrameCallbacks.size() && !mFrameCallbackTaskPending) {
            drainDisplayEventQueue();
            mFrameCallbacks.insert(mPendingRegistrationFrameCallbacks.begin(),
                                   mPendingRegistrationFrameCallbacks.end());
            mPendingRegistrationFrameCallbacks.clear();
            requestVsync();
        }
 
        if (!mFrameCallbackTaskPending && !mVsyncRequested && mFrameCallbacks.size()) {
            // TODO: Clean this up. This is working around an issue where a combination
            // of bad timing and slow drawing can result in dropping a stale vsync
            // on the floor (correct!) but fails to schedule to listen for the
            // next vsync (oops), so none of the callbacks are run.
            requestVsync();
        }
    }
 
    return false;
}

3.7.2. DrawFrameTask构造函数


RenderProxy
调用构造函数时,会创建DrawFrameTask,同时调用其
setContext
函数:



//frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp
DrawFrameTask::DrawFrameTask()
        : mRenderThread(nullptr)
        , mContext(nullptr)
        , mContentDrawBounds(0, 0, 0, 0)
        , mSyncResult(SyncResult::OK) {}
 
DrawFrameTask::~DrawFrameTask() {}
 
void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context,
                               RenderNode* targetNode) {
    mRenderThread = thread;
    mContext = context;
    mTargetNode = targetNode;
}

3.8. syncAndDrawFrame绘制帧

从上面的分析看,DisplayList和RenderThread都创建好了,正常绘制的时候会调用到
syncAndDrawFrame

从ViewRootImpl的performDraw函数调用到draw,在调用到 ThreadedRenderer.java的draw函数。开始绘制:



//ThreadedRenderer.java
  void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks) {
        final Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer;
        choreographer.mFrameInfo.markDrawStart();
        //绘制每个视图的内容(在文章下面会梳理)
        updateRootDisplayList(view, callbacks);
        //绘制一帧的内容
        int syncResult = syncAndDrawFrame(choreographer.mFrameInfo);
        ...
    }


//frameworks/base/libs/hwui/renderthread/RenderProxy.cpp
int RenderProxy::syncAndDrawFrame() {
    return mDrawFrameTask.drawFrame();
}


//frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp
int DrawFrameTask::drawFrame() {
    LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!");
 
    mSyncResult = SyncResult::OK;
    mSyncQueued = systemTime(CLOCK_MONOTONIC);
    postAndWait();
 
    return mSyncResult;
}
 
void DrawFrameTask::postAndWait() {
    AutoMutex _lock(mLock);
    mRenderThread->queue().post([this]() { run(); }); //执行此处的run函数
    mSignal.wait(mLock);
}

此时,drawFrame,也就通过RenderThread,post一个WorkItem到RenderThread的队列里面,在RenderThread线程中执行的。

然后RenderThread处理Queue时,执行的会是DrawFrameTask的run函数。



//frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp
void DrawFrameTask::run() {
    ATRACE_NAME("DrawFrame");
 
    bool canUnblockUiThread;
    bool canDrawThisFrame;
    {
        //info,即描述Viewtree的,也就是RenderNode tree
        //此处的mode是MODE_FULL,即只有primary的node是FULL,其他都是实时 **
        TreeInfo info(TreeInfo::MODE_FULL, *mContext);
        //同步Frame帧状态
        canUnblockUiThread = syncFrameState(info);
        //判断是否可以绘制这一帧
        canDrawThisFrame = info.out.canDrawThisFrame;
 
        if (mFrameCompleteCallback) {
            mContext->addFrameCompleteListener(std::move(mFrameCompleteCallback));
            mFrameCompleteCallback = nullptr;
        }
    }
 
    // Grab a copy of everything we need
    CanvasContext* context = mContext;
    std::function<void(int64_t)> callback = std::move(mFrameCallback);
    mFrameCallback = nullptr;
 
    ......
 
    if (CC_LIKELY(canDrawThisFrame)) {
        //**绘制
        context->draw();
    } else {
        // wait on fences so tasks don't overlap next frame
        context->waitOnFences();
    }
 
    if (!canUnblockUiThread) {
        unblockUiThread();
    }
}
 
//同步Frame的State
bool DrawFrameTask::syncFrameState(TreeInfo& info) {
    ATRACE_CALL();
    int64_t vsync = mFrameInfo[static_cast<int>(FrameInfoIndex::Vsync)];
    mRenderThread->timeLord().vsyncReceived(vsync);
    //通知GPU处理当前的Context上下文
    bool canDraw = mContext->makeCurrent();
    //hwui为了提高速度,对各种object都做了cache,此处unpin,就是让cache去做unpin,其他都不要了
    mContext->unpinImages();
 
    for (size_t i = 0; i < mLayers.size(); i++) {
        mLayers[i]->apply();
    }
    mLayers.clear();
    //设置绘制的区域大小
    mContext->setContentDrawBounds(mContentDrawBounds);
    //**Android View是树型结构的,这就是在绘制之前,去准备这些Tree节点的绘图操作
    //准备绘制一帧的数据
    mContext->prepareTree(info, mFrameInfo, mSyncQueued, mTargetNode);
 
    // This is after the prepareTree so that any pending operations
    // (RenderNode tree state, prefetched layers, etc...) will be flushed.
    if (CC_UNLIKELY(!mContext->hasSurface() || !canDraw)) {
        if (!mContext->hasSurface()) {
            mSyncResult |= SyncResult::LostSurfaceRewardIfFound;
        } else {
            // If we have a surface but can't draw we must be stopped
            mSyncResult |= SyncResult::ContextIsStopped;
        }
        info.out.canDrawThisFrame = false;
    }
 
    if (info.out.hasAnimations) {
        if (info.out.requiresUiRedraw) {
            mSyncResult |= SyncResult::UIRedrawRequired;
        }
    }
    if (!info.out.canDrawThisFrame) {
        mSyncResult |= SyncResult::FrameDropped;
    }
    // If prepareTextures is false, we ran out of texture cache space
    return info.prepareTextures;
}

3.8.1. prepareTree

调用函数prepareTree



//frameworks/base/libs/hwui/renderthread/CanvasContext.cpp
void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued,
                                RenderNode* target) {
    mRenderThread.removeFrameCallback(this);
......
    mAnimationContext->startFrame(info.mode);
    mRenderPipeline->onPrepareTree();
    //Context可能会有多个Node
    for (const sp<RenderNode>& node : mRenderNodes) {
        //即只有Primary的node是 FULL,其他都是实时
        info.mode = (node.get() == target ? TreeInfo::MODE_FULL : TreeInfo::MODE_RT_ONLY);
        //此处遍历,对每个RenderNode都进行prepare
        node->prepareTree(info);
        GL_CHECKPOINT(MODERATE);
    }
    mAnimationContext->runRemainingAnimations(info);
    GL_CHECKPOINT(MODERATE);
 
    freePrefetchedLayers();
    GL_CHECKPOINT(MODERATE);
 
    mIsDirty = true;
    //如果窗口已经没有Native Surface,这一帧就丢掉!!
    if (CC_UNLIKELY(!hasSurface())) {
        mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
        info.out.canDrawThisFrame = false;
        return;
    }
......
                                }

遍历RenderNode的prepareTree方法:



//frameworks/base/libs/hwui/RenderNode.cpp
void RenderNode::prepareTree(TreeInfo& info) {
    ATRACE_CALL();
    LOG_ALWAYS_FATAL_IF(!info.damageAccumulator, "DamageAccumulator missing");
    MarkAndSweepRemoved observer(&info);
 
    const int before = info.disableForceDark;
    //具体实现
    prepareTreeImpl(observer, info, false);
    LOG_ALWAYS_FATAL_IF(before != info.disableForceDark, "Mis-matched force dark");
}

prepareTreeImpl是RenderNode真正进行Prepare的地方。

damageAccumulator是从CanvasContext中传过来的,是CanvasContext的成员,damage的叠加器。主要是用来标记,屏幕的那些区域被破坏了,需要重新绘制,所有的RenderNode累加起来,就是总的。



//frameworks/base/libs/hwui/RenderNode.cpp
void RenderNode::prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer) {
    if (mDamageGenerationId == info.damageGenerationId) {
        info.damageAccumulator->dirty(DIRTY_MIN, DIRTY_MIN, DIRTY_MAX, DIRTY_MAX);
    }
    //Step 1
    info.damageAccumulator->pushTransform(this);
 
    if (info.mode == TreeInfo::MODE_FULL) {
        //property是对RenderNode的描述,也就是对View的描述,比如大小,位置等。
        //有两个状态,正在使用的syncProperties和待处理的mStagingProperties。
        //syncProperties时,将mStagingProperties赋值给syncProperties
        pushStagingPropertiesChanges(info);
    }
......
    pushLayerUpdate(info);
 
    if (!mProperties.getAllowForceDark()) {
        info.disableForceDark--;
    }
    info.damageAccumulator->popTransform();
}

调用pushTransform函数,damage累加器中,每一个元素由DirtyStack描述,分两种类型:TransformMatrix4和TransformRenderNode。采用一个双向链表mHead进行管理。



//frameworks/base/libs/hwui/DamageAccumulator.cpp
void DamageAccumulator::pushCommon() {
    if (!mHead->next) {
        DirtyStack* nextFrame = mAllocator.create_trivial<DirtyStack>();
        nextFrame->next = nullptr;
        nextFrame->prev = mHead;
        mHead->next = nextFrame;
    }
    mHead = mHead->next;
    mHead->pendingDirty.setEmpty();
}
 
void DamageAccumulator::pushTransform(const RenderNode* transform) {
    pushCommon();
    mHead->type = TransformRenderNode;
    mHead->renderNode = transform;
}

此时prepare完成后,绘制一帧的数据就准备好了。


3.9. 绘制帧数据


syncAndDrawFrame绘制帧
上面的章节,此处会调用到HardwareRenderer.java父类函数;然后JNI到Native层,调用到RenderProxy.cpp的该函数,再到
frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp
的drawFrame函数,开始绘制一帧数据,此时启动RenderThread线程的run函数,调用到关键函数
CanvasContext::draw()

Android Q中,具体绘制是在各自的pipeline中进行的。(在
frameworks/base/libs/hwui/pipeline/

在Android P中,一般是执行的
frameworks/base/libs/hwui/renderthread/OpenGLPipeline.cpp

在Android Q中,可以看出Google在慢慢用Vulkan替代OpenGL。



//frameworks/base/libs/hwui/renderthread/CanvasContext.cpp
void CanvasContext::draw() {
    SkRect dirty;
    mDamageAccumulator.finish(&dirty);
    //跳过绘制条件:脏区域为空、空帧数据、不需要重绘
    if (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw()) {
        mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
        return;
    }
......
    mCurrentFrameInfo->markIssueDrawCommandsStart();
    //获取frame(一帧数据信息,主要是ufferAge、Surface等)
    Frame frame = mRenderPipeline->getFrame();
    setPresentTime();
 
    SkRect windowDirty = computeDirtyRect(frame, &dirty);
    //绘制
    bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue,
                                      mContentDrawBounds, mOpaque, mLightInfo, mRenderNodes,
                                      &(profiler()));
 
    int64_t frameCompleteNr = mFrameCompleteCallbacks.size() ? getFrameNumber() : -1;
 
    waitOnFences();
 
    bool requireSwap = false;
    //绘制完成后调用
    bool didSwap =
            mRenderPipeline->swapBuffers(frame, drew, windowDirty, mCurrentFrameInfo, &requireSwap);
......
}

3.10. 绘制完成后unlockCanvasAndPost流程Permalink

回到绘制案例,此时,RenderThread,DrawFrameTask,CanvasContext等已经就绪,绘制操纵已经被添加到了DisplayList中。

绘制完成,然后会在
frameworks/base/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasSurfaceViewActivity.java
中调用到
方法unlockCanvasAndPost

SurfaceHolder直接调的Surface的unlockCanvasAndPost。



//frameworks/base/core/java/android/view/Surface.java
  public void unlockCanvasAndPost(Canvas canvas) {
        synchronized (mLock) {
            checkNotReleasedLocked();
 
            if (mHwuiContext != null) {
                //硬件加速执行此处
                mHwuiContext.unlockAndPost(canvas);
            } else {
                //软件绘制
                unlockSwCanvasAndPost(canvas);
            }
        }
    }
 
private final class HwuiContext {
    ......
        //HwuiContext的unlockAndPost函数
        void unlockAndPost(Canvas canvas) {
            if (canvas != mCanvas) {
                throw new IllegalArgumentException("canvas object must be the same instance that "
                        + "was previously returned by lockCanvas");
            }
            mRenderNode.endRecording();
            mCanvas = null;
            nHwuiDraw(mHwuiRenderer);
        }
}

上面代码看到,在lockHardwareCanvas的时候有相同流程,会调用
mRenderNode.beginRecording

此处就对应的调用到
mRenderNode.endRecording();
,结束RenderNode,保存数据。



//frameworks/base/graphics/java/android/graphics/RenderNode.java
 public void endRecording() {
        if (mCurrentRecordingCanvas == null) {
            throw new IllegalStateException(
                    "No recording in progress, forgot to call #beginRecording()?");
        }
        RecordingCanvas canvas = mCurrentRecordingCanvas;
        mCurrentRecordingCanvas = null;
        //Step1 先结束Canvas的录制
        long displayList = canvas.finishRecording();
        //Step2 然后将录制的list给mNativeRenderNode
        nSetDisplayList(mNativeRenderNode, displayList);
        canvas.recycle();
    }

首先finishRecording函数通过JIN层
android_view_DisplayListCanvas.cpp
调用到Native层。

返回录制好的mDisplayList。



//frameworks/base/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
uirenderer::DisplayList* SkiaRecordingCanvas::finishRecording() {
    // close any existing chunks if necessary
    insertReorderBarrier(false);
    mRecorder.restoreToCount(1);
    return mDisplayList.release();
}

第二步的nSetDisplayList通过JNI层
android_view_RenderNode.cpp
调用到Native层。

将displayList给到RenderNode的mStagingDisplayList。



//frameworks/base/libs/hwui/RenderNode.cpp
void RenderNode::setStagingDisplayList(DisplayList* displayList) {
    mValid = (displayList != nullptr);
    mNeedsDisplayListSync = true;
    delete mStagingDisplayList;
    mStagingDisplayList = displayList;
}

4. Activity HWUI渲染环境初始化流程(RenderThreadc创建)

主要是通过setView创建rendernode,渲染线程RenderThread,Context上下文,RenderProxy代理对象等。

4.1. 附序列图

4.2. Java层

Activity.java开始设置view:



//frameworks/base/core/java/android/app/Activity.java
public Window getWindow() {
        return mWindow;
    }
 
public void setContentView(View view) {
        getWindow().setContentView(view);
        initWindowDecorActionBar();
    }

PhoneWindow继承Window抽象类调用setContentView函数:



//frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java
public class PhoneWindow extends Window implements MenuBuilder.Callback {
...
@Override
    public void setContentView(View view) {
        //新对象ViewGroup
        setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    }
 
    @Override
    public void setContentView(View view, ViewGroup.LayoutParams params) {
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }
 
        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            view.setLayoutParams(params);
            final Scene newScene = new Scene(mContentParent, view);
            transitionTo(newScene);
        } else {
            //调用此处添加一个view到Parent View中
            mContentParent.addView(view, params);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }
...
}

此处的
mContentParent.addView(view, params);
,mContentParent是ViewGroup类型对象。

ViewGroup实现接口ViewManager,而
interface WindowManager extends ViewManager
,WindowManagerImpl.java又是接口WindowManager的实现类。所以会同时调用WindowManagerImpl类的addView函数。

WindowManagerImpl.java调用addView函数。



//frameworks/base/core/java/android/view/WindowManagerImpl.java
@Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

调用windowManagerGlobal的addView函数。



//frameworks/base/core/java/android/view/WindowManagerGlobal.java
    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
                ...
        ViewRootImpl root;
        View panelParentView = null;
        synchronized (mLock) {
            ......
            // do this last because it fires off messages to start doing things
            try {
                //调用ViewRootImpl的setView函数
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }

ViewRootImpl.java中调用流程:



//frameworks/base/core/java/android/view/ViewRootImpl.java
    /**
     * We have one child
     */
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                //设置根节点视图(顶层视图)
                mView = view;
                ...
                // If the application owns the surface, don't enable hardware acceleration
                if (mSurfaceHolder == null) {
                    // While this is supposed to enable only, it can effectively disable
                    // the acceleration too.
                    //启动硬件加速!
                    enableHardwareAcceleration(attrs);
                    final boolean useMTRenderer = MT_RENDERER_AVAILABLE
                            && mAttachInfo.mThreadedRenderer != null;
                    if (mUseMTRenderer != useMTRenderer) {
                        // Shouldn't be resizing, as it's done only in window setup,
                        // but end just in case.
                        endDragResizing();
                        mUseMTRenderer = useMTRenderer;
                ...
                    }
                }
            }
        }
    }
 
    @UnsupportedAppUsage
    private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {
            ...
            mAttachInfo.mThreadedRenderer = ThreadedRenderer.create(mContext, translucent,
                        attrs.getTitle().toString());
            ...
    }

在ThreadedRenderer的create函数中new了一个该对象,从而调用其构造函数。构造函数的
super()
调用基类HardwareRenderer的构造函数。



//frameworks/base/graphics/java/android/graphics/HardwareRenderer.java
    /**
     * Creates a new instance of a HardwareRenderer. The HardwareRenderer will default
     * to opaque with no light source configured.
     */
     //创建硬件渲染rendernode对象
    public HardwareRenderer() {
        //创建一个窗口的Root Render Node,并用Java层的RenderNode封装起来,即adopt通过new对象返回一个java层的RenderNode对象
        mRootNode = RenderNode.adopt(nCreateRootRenderNode());
        mRootNode.setClipToBounds(false);
        //调用到JNI层,创建一个RenderProxy(即MainThread的代理对象)
        mNativeProxy = nCreateProxy(!mOpaque, mRootNode.mNativeRenderNode);
        if (mNativeProxy == 0) {
            throw new OutOfMemoryError("Unable to create hardware renderer");
        }
        Cleaner.create(this, new DestroyContextRunnable(mNativeProxy));
        ProcessInitializer.sInstance.init(mNativeProxy);
    }

4.3. Native层

从上面的nCreateRootRenderNode函数调用到JNI层的android_view_ThreadedRenderer.cpp文件中。

创建一个窗口的Root Render Node。

之后创建RenderProxy对象。



static jlong android_view_ThreadedRenderer_createRootRenderNode(JNIEnv* env, jobject clazz) {
    //new对象
    RootRenderNode* node = new RootRenderNode(env);
    node->incStrong(0);
    node->setName("RootRenderNode");
    return reinterpret_cast<jlong>(node);
}
 
static jlong android_view_ThreadedRenderer_createProxy(JNIEnv* env, jobject clazz,
        jboolean translucent, jlong rootRenderNodePtr) {
    RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootRenderNodePtr);
    ContextFactoryImpl factory(rootRenderNode);
    //new对象
    return (jlong) new RenderProxy(translucent, rootRenderNode, &factory);
}

之后调用RenderProxy构造函数,就同上面Google原生绘制案例的流程分析相同
RenderProxy::RenderProxy(){...}
,负责从MainThread向RenderThread发送命令。

参考上面的
native层:调用RenderProxy.cpp构造函数

CanvasContext渲染上下文
章节


4.4. ThreadedRenderer架构图


5. Activity 窗口绘制流程

由ViewRootImpl的成员函数
performTraversals
发起。

在绘制之前,首先需要创建一个Surface,即描述一个窗口(创建Surface的流程后续单独整理)一旦获得对应的Surface, 就需要将其绑定到RenderThread中。Activity窗口对应的Surface是通过ViewRootImpl类的成员函数
relayoutWindow
向WindowManagerService服务请求创建和返回的,并且保存在ViewRootImpl类的成员变量mSurface中。如果Surface是新创建的,将调用ViewRootImpl类的成员变量mAttachInfo指向的AttachInfo函数。对象的成员变量mHardwareRenderer描述的一个HardwareRenderer对象的成员函数
initialize
将它绑定到RenderThread中。最后, 如果需要绘制当前的Activity窗口, 那会调用 iewRootImpl类的另外一个成员函数
performDraw

此处在performTraversals函数中,先是用relayoutWindow创建mSurface,将Native层之前通过构造函数创建的Surface copy过来。 而后是三个主要流程:performMeasure测量、performLayout布局、performDraw绘制

5.1. 绘制序列图Permalink

5.1.1. ViewRootImpl中的软/硬件绘制区分

在ViewRootImpl.java的draw函数中:



//frameworks/base/core/java/android/view/ViewRootImpl.java
private boolean draw(boolean fullRedrawNeeded) {
        Surface surface = mSurface;
        if (!surface.isValid()) {
            return false;
        }
 
        // Draw with hardware renderer.
        mIsAnimating = false;
        ...
        if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
            if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {
        ...
        //硬件加速
        mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
        } else {
        ...
                //软件绘制
                if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
                        scalingRequired, dirty, surfaceInsets)) {
                    if (DEBUG_DRAW) {
                        Log.v(mTag, "drawSoftware return: this = " + this);
                    }
                    return false;
                }
            }
        }

drawSoftware中会调用到Surface.java的
lockCanvas

unlockCanvasAndPost
函数。



//frameworks/base/core/java/android/view/Surface.java
public Canvas lockCanvas(Rect inOutDirty)
            throws Surface.OutOfResourcesException, IllegalArgumentException {
        synchronized (mLock) {
            /// M: add for white list @{
            //如果在白名单,则返回,仍使用硬件绘制
            if (mSurfaceExt.isInWhiteList()) {
                return lockHardwareCanvas();
            }
            /// @}
            ...
            mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);
            return mCanvas;
        }
    }
 
private void unlockSwCanvasAndPost(Canvas canvas) {
        ......
        try {
            nativeUnlockCanvasAndPost(mLockedObject, canvas);
        } finally {
            nativeRelease(mLockedObject);
            mLockedObject = 0;
        }
    }

5.2. 初始化DisplayList

updateRootDisplayList方法的作用是先初始化DIsplayList(即调用
beginRecording
,流程同上面案例中的分析),然后绘制整个树型视图结构,从顶层视图开始,每个视图节点逐一绘制,最终目的是触发每个视图的
Canvas#draw***
方法。
syncAndDrawFrame
查看上面章节
绘制准备,同步帧

updateRootDisplayList方法分成两步:

先顶层视图结构遍历绘制,更新DisplayList数据,第二步是ThreadedRenderer的根RenderNode绘制,同样,通过根RenderNode创建DisplayListCanvas,通过它的drawRenderNode方法,负责绘制顶层视图DecorView的RenderNode节点

5.3. 绘制view视图

参考上面目录
beginRecording初始化DisplayList
小节

updateViewTreeDisplayList方法,从顶层视图DecorView开始,遍历树形视图结构的每一个节点,利用视图内的RenderNode创建Canvas,绘制。利用ThreadedRenderer的根RootRenderNode创建Canvas,绘制顶层RenderNode节点



//frameworks/base/core/java/android/view/ThreadedRenderer.java
   private void updateViewTreeDisplayList(View view) {
        view.mPrivateFlags |= View.PFLAG_DRAWN;
        //判断视图的PFLAG_INVALIDATED标志
        //有这个标志,在调用每个View的updateDisplayListIfDirty()时,才会创建Canvas
        //当一个视图需要绘制时,上层肯定会设置该标志
        //最后会将重建标志还原
        view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
                == View.PFLAG_INVALIDATED;
        view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
        //此处会用到mRecreateDisplayList进行判断是否创建DisplayListCanvas
        view.updateDisplayListIfDirty();
        view.mRecreateDisplayList = false;
    }

每个视图的流程是一样的,都有三个步骤,第一次绘制时,每个视图都要建立Canvas。

通过视图RenderNode节点start方法,创建DisplayListCanvas画布对象(调用
beginRecording
,流程同上面案例中的分析)通过View的draw(canvas)方法,实现具体记录绘制操作,(绘制自身与派发),draw方法包括很多步骤,包括递归到子视图的updateDisplayListIfDirty方法。最后,RenderNode结束记录
endRecording
方法。


5.3.1. draw实现视图绘制六个步骤

第二步的draw实现视图绘制。参数就是上面创建的DisplayListConvas画布,视图有一些公用绘制,例如背景,滚定条,修饰等。



//frameworks/base/core/java/android/view/View.java
public void draw(Canvas canvas) {
    ......
}

具体视图绘制六个步骤:

绘制背景:
drawBackground(canvas)

这里会先得到一个mBGDrawable对象然后根据layout过程确定的视图位置来设置背景的绘制区域之后再调用Drawable的draw()方法来完成背景的绘制工作
必要时保存canvas的layers,绘制边缘fadeonDraw方法:绘制视图内容,调用Canvas API,此处是空方法,子类会实现。例如TextView、ImageView等类的源码,它们都有重写onDraw()这个方法,并且在里面执行了相当不少的绘制逻辑。绘制的方式主要是借助Canvas这个类,它会作为参数传入到onDraw()方法中,供给每个视图使用,可以将其当成一块画布
下面有个APP代码案例
dispatchDraw派发绘制子视图,空方法,容器类视图会重写。如果有跳过标志,将不会来到draw方法,直接去dispatchDraw。



//frameworks/base/core/java/android/view/ViewGroup.java
@Override
protected void dispatchDraw(Canvas canvas) {
    for (int i = 0; i < childrenCount; i++) {
        if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
                child.getAnimation() != null) {
            more |= drawChild(canvas, child, drawingTime);
        }
    }
}
 
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
    return child.draw(canvas, this, drawingTime);
}

此处调用View.java的三个参数的draw函数中,也会调用
updateDisplayListIfDirty()
函数。并且如果是硬件绘制会重新判断
mRecreateDisplayList

这样就实现了View视图的递归绘制。



boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
    ...
        if (hardwareAcceleratedCanvas) {
            // Clear INVALIDATED flag to allow invalidation to occur during rendering, but
            // retain the flag's value temporarily in the mRecreateDisplayList flag
            mRecreateDisplayList = (mPrivateFlags & PFLAG_INVALIDATED) != 0;
            mPrivateFlags &= ~PFLAG_INVALIDATED;
        }
    ...
}

如有绘制fading edges,恢复canvas’ layers。绘制修饰,如滚动条。


5.3.2. APP绘制案例代码

假如APP代码中创建一个非常简单的视图,并且用Canvas随便绘制了一点东西,代码如下所示:



public class MyView extends View {
    private Paint mPaint;
    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    }
 
    @Override
    protected void onDraw(Canvas canvas) {
        mPaint.setColor(Color.YELLOW);
        canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
        mPaint.setColor(Color.BLUE);
        mPaint.setTextSize(20);
        String text = "Hello View";
        canvas.drawText(text, 0, getHeight() / 2, mPaint);
    }
}

此处创建了一个自定义的MyView继承自View,并在MyView的构造函数中创建了一个Paint对象。Paint就像是一个画笔一样,配合着Canvas就可以进行绘制。

绘制逻辑比较简单,在
onDraw()
方法中先是把画笔设置成黄色,然后调用Canvas的drawRect()方法绘制一个矩形。然后在把画笔设置成蓝色,并调整了一下文字的大小,然后调用drawText()方法绘制了一段文字。

然后在XML布局假如这个视图,将MyView的宽度设置成200dp,高度设置成100dp。



<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
 
    <com.example.diyview.MyView
        android:layout_width="200dp"
        android:layout_height="100dp" />
 
</LinearLayout>

6. 关闭硬件加速

android提供了以下四个级别的硬件加速控制:

Application:
<application android:hardwareAccelerated="true" ...>
Activity:例如启用全局的硬件加速,但却禁止了一个Activity的硬件加速:



<application android:hardwareAccelerated="true">
    <activity ... />
    <activity android:hardwareAccelerated="false" />
</application>

Window:针对给定的Window来启用硬件加速:



getWindow().setFlags(
    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);

View级别:针对一个独立的View对象使用下列代码来禁止硬件加速:
myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

android关闭硬件加速的方法:

APK中,在AndroidManifest.xml中设置
android:hardwareAccelerated="false"
,这是关闭整个app的硬件加速,慎用!View有个方法支持单独的View关闭硬件加速,可以设置
mView.setLaterType(View.LAYER_TYPE_SOFTWARE);
,或者关闭某一个控件的硬件加速功能使用
findViewById(R.id.btn).setLayerType(View.LAYER_TYPE_SOFTWARE,null);


7. 总结

在Java层,硬件渲染由ThreadedRenderer负责,每个窗体根视图ViewRootImpl下都有一个ThreadedRenderer,保存在AttachInfo,
它的draw方法是硬件渲染绘制的入口
。从ViewRootImpl开始,一般视图会创建ThreadedRenderer,启用硬件渲染,关键点在遍历每一个视图,根据视图RenderNode创建画布,
有效绘制记录存储在RenderNode关联的底层DisplayListData
绘制架构包含
RenderNode节点,DisplayListCanvas画布,底层DisplayListData对象,CanvasState状态存储对象
,做完这些初始化工作,就可以在Java层画布上执行绘制操作方法。
树形视图结构每一节点都有一个DisplayListCanvas,利用Canvas#drawXxx方法分别记录一些绘制操作,drawXxx画点、圆、矩形等操作,将这些操作存储在一个DisplayList集合中,这是App的UI线程负责的任务

onDraw方法自己在APP中重写
,View和ViewGroup有什么自己需要的绘制在这里完成。View的dispatchDraw是空方法,不做任何操作。ViewGroup重写dispatchDraw方法,实现绘制派发到子视图。容器视图一般没有自己要绘制的东西,可能在updateDisplayListIfDirty方法就已经选择dispatchDraw了;顶层视图绘制入口是draw(一个参数)方法,在draw(一个参数)中,包含六个步骤,第四步会派发每个子视图,子视图绘制入口是draw(三个参数),在draw(三个参数)中,会根据硬件渲染,进入每个子视图updateDisplayListIfDirty方法,实现递归绘制;当走到RenderNode的
endRecording
方法时,表示视图本身以及子视图已经全部绘制完毕,也就是说当DecorView的RenderNode#end方准备执行时,所有draw已经完成View构造方法创建每一个视图的RenderNode。每一个RenderNode都会创建DisplayListCanvas,使用时是一一对应关系;

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容