硬件渲染源码分析流程详解

2022-10-09 12:56:04 浏览数 (1)

theme: condensed-night-purple highlight: atom-one-dark

硬件渲染中采用AttachInfo的mThreadRenderer.draw方法传入view,attachinfo和ViewRootImpl开始硬件渲染

代码语言:javascript复制
private boolean draw(boolean fullRedrawNeeded) {
    if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
        if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {
            // 硬件渲染
            mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
        } else {
            // 软件渲染
        }
    }
}

ThreadRender的初始化

resume的时候会调用ViewRootImpl的setView方法创建windowSession和WMS通信,之后会调用enbleHardwareAccleration方法判断是否开启硬件渲染

代码语言:javascript复制
>ViewRootImpl.java 
private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {
     // ...
    
      else if (!ThreadedRenderer.sRendererDisabled
                    || (ThreadedRenderer.sSystemRendererDisabled && forceHwAccelerated)) {
     // 进程默认 开启硬件绘制         
     mAttachInfo.mThreadedRenderer = ThreadedRenderer.create(mContext, translucent,
                        attrs.getTitle().toString());
     // ...
     }                   
}


>ThreadedRenderer.java 
  public static ThreadedRenderer create(Context context, boolean translucent, String name) {
        ThreadedRenderer renderer = null;
        if (isAvailable()) {
            renderer = new ThreadedRenderer(context, translucent, name);
        }
        return renderer;
    }
 // 构造方法 
 ThreadedRenderer(Context context, boolean translucent, String name) {
        // 调用父类的构造
        super();
        setName(name);
        setOpaque(!translucent);

        // ...
    }
    
    
>HardwareRenderer.java 
//     父类的构造
 public HardwareRenderer() {
 
        // 创建一个Java层的 根RootNode 节点
        mRootNode = RenderNode.adopt(nCreateRootRenderNode());
        mRootNode.setClipToBounds(false);
        
        // 创建一个 native层的 RenderProxy 对象,  用来和RenderThread线程通信
        mNativeProxy = nCreateProxy(!mOpaque, mRootNode.mNativeRenderNode);
        if (mNativeProxy == 0) {
            throw new OutOfMemoryError("Unable to create hardware renderer");
        }
        Cleaner.create(this, new DestroyContextRunnable(mNativeProxy));
        
        // 往AMS设置 renderThread的线程tid
        ProcessInitializer.sInstance.init(mNativeProxy);
    }

  public static RenderNode adopt(long nativePtr) {
        //创建Java层的RenderNode, 持有native层的node 引用 
        return new RenderNode(nativePtr);
    }

调用父类的构造方法流程如下:

  • ncreateRootRenderNode会创建,native层的RootRenderNode对象,并设置node的name为RootRenderNode
  • 接着创建一个Java层的根RootNode节点持有natve层的node引用
代码语言:javascript复制
static jlong android_view_ThreadedRenderer_createProxy(JNIEnv* env, jobject clazz,
        jboolean translucent, jlong rootRenderNodePtr) {
     // 获取之前创建的  rootRenderNode
    RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootRenderNodePtr);
    // 创建 ContextFactoryImpl 对象
    ContextFactoryImpl factory(rootRenderNode);
    // new 一个 RenderProxy对象
    RenderProxy* proxy = new RenderProxy(translucent, rootRenderNode, &amp;factory);
    return (jlong) proxy;
}

// RenderProxy 的构造函数
>frameworks/base/libs/hwui/renderthread/RenderProxy.cpp
RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode,
                         IContextFactory* contextFactory)
         //mRenderThread 赋值。一个应用只会拥有一个RenderThread线程
        : mRenderThread(RenderThread::getInstance()), mContext(nullptr) {
         
        
        //往RenderThread的队列中post一个消息创建CanvasContext对象
    mContext = mRenderThread.queue().runSync([&amp;]() -> CanvasContext* {
        // 在RenderThread线程中,创建CanvasContext对象。用于是链接OpenGL/Vulkan和graphicBuffer缓冲区的关键
        //最终要使用这个contetx保存的surface的GraphBuffer进行渲染
        return CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory);
    });
    
    // mDrawFrameTask 设置context 
    mDrawFrameTask.setContext(&amp;mRenderThread, mContext, rootRenderNode,
                              pthread_gettid_np(pthread_self()), getRenderThreadTid());
}

>frameworks/base/libs/hwui/renderthread/CanvasContext.cpp
CanvasContext* CanvasContext::create(RenderThread&amp; thread, bool translucent,
                                     RenderNode* rootRenderNode, IContextFactory* contextFactory) {
    // 根据渲染管道的配置
    auto renderType = Properties::getRenderPipelineType();

    switch (renderType) {
        case RenderPipelineType::SkiaGL:
            // skiaOpenGl渲染管道
            return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
                                     std::make_unique<skiapipeline::SkiaOpenGLPipeline>(thread));
        case RenderPipelineType::SkiaVulkan:
            // 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;
}
  • nCreateProxy方法传入native的rootRenderNode,利用之前创建的rootRenderNode创建ContextFactoryImpl对象,紧接着创建一个RenderProxy对象(用于和)
  • Renderproxy对象的构造函数中获取到RenderThread单例线程,然后在这个线程中去创建CanvasContext对象(链接OpenGL/Vulkan和graphicBuffer缓冲区的关键)。

RenderThread的第一个drawFrameTask任务就是去创建CanvasContext

创建过程会根据渲染管道配置去创建不同的canvascontext。t比如skiaOpenGl渲染管道还是skiaVulkan渲染管道;接着给drawFarmeTask设置上面创建的context。

  • 将创建出来的RenderProxy传入到AMS中,也就是像AMS设置renderThread的线程tid

小结:

ThreadedRenderer 在ViewRootImpl的setView()中被初始化。构造方法做了如下初始化:

  1. 在构造方法中会创建Java和native层两个 根RenderNode节点。
  2. 创建native层的RenderProxy对象,持有 RenderThread 单例线程的引用。
  3. 给RenderThread线程,设置渲染的上下文。根据配置该CanvasContext采用的是管道:SkiaOpenGLPipeline 或者 SkiaVulkanPipeline。

Java层的都是renderNode节点对象,只有native层才可以创建rootRenderNode对象。在setView初始化的时候会创建native层的rootrenderNode之后创建一个rendernode绑定这个native的rootRenderNode

RenderNode

代码语言:javascript复制
>View.java
public View(Context context) {
        mContext = context;
        mResources = context != null ? context.getResources() : null;
        // ...
        
        // 生成一个 RenderNode节点
        mRenderNode = RenderNode.create(getClass().getName(), new ViewAnimationHostBridge(this));
        
}

> RenderNode.java
/** @hide */
public static RenderNode create(String name, @Nullable AnimationHost animationHost) {
    return new RenderNode(name, animationHost);
}

//构造方法 
 private RenderNode(String name, AnimationHost animationHost) {
        // 调用native方法 生成
        mNativeRenderNode = nCreate(name);
        NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativeRenderNode);
        mAnimationHost = animationHost;
    }


>frameworks/base/libs/hwui/jni/android_graphics_RenderNode.cpp
static jlong android_view_RenderNode_create(JNIEnv* env, jobject, jstring name) {
    // 创建一个 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);
}

RenderNode有两个构造方法。一个是根View专用的接受native层的RootRenderNode对象;还有一个是普通的除了根View用的接受View名字的构造方法。第二种最终是调用的nCreate创建的c层的RenderNode绑定

RenderNode 是根据view树来建立的。每一个View对应一个RenderNode节点。

一个RenderNode节点包含了当前view的绘制命令drawOp( 如 drawLines->drawLinesOp), 同时还包括绘制子RenderNode节点的命令:DrawRenderNodeOp。因此,可看成一颗RenderNode树。 每一个drawXXXOp命令都有对应的OpenGL/Vulkan 命令与之对应。

RecordingCanvas

RecordingCanvas 用来记录 View树 中的硬件加速绘制动作drawOp。对应native层的 SkiaRecordingCanvas。 主要功能都是由 native来完成。 通过obtain()方法来获得一个 RecordingCanvas

obtain方法需要传入RenderNode对象和宽高信息来创建RecordingCanvas,第一次通过new创建之后进行修改器node宽高内部属性不再进行创建。RecordingCanvas构造方法中最终在c层创建了一个SkiaRecordingCanvas也就是和java层的RecordingCanvas对应

ThreadedRenderer.draw()

代码语言:javascript复制
void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks) {
    // 构建View的 drawXXXOp树 
    updateRootDisplayList(view, callbacks);
    
    // 通知 RenderThread  线程 开始绘制
    int syncResult = syncAndDrawFrame(choreographer.mFrameInfo);
}

updateRootDisplayList

代码语言:javascript复制
> ThreadedRenderer.java 
private void updateRootDisplayList(View view, DrawCallbacks callbacks) {
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Record View#draw()");
    //构建View的drawXXXop树也就是displayList绘制命令
    //更新传入view对应的 RenderNode中的displayList(drawOp树)
    updateViewTreeDisplayList(view);

    // ...
    // 当根节点重绘时需要重新计算drawOp树
    if (mRootNodeNeedsUpdate || !mRootNode.hasDisplayList()) {
        // 从RecordingCanvas缓存池中,获取一个 RecordingCanvas 对象,包含了当前RenderNode、width、height信息
        RecordingCanvas canvas = mRootNode.beginRecording(mSurfaceWidth, mSurfaceHeight);
        try {
            final int saveCount = canvas.save();
            canvas.translate(mInsetLeft, mInsetTop);
            callbacks.onPreDraw(canvas);

            canvas.enableZ();
            // 返回view对应的node,开始绘制RenderNode
            canvas.drawRenderNode(view.updateDisplayListIfDirty());
            canvas.disableZ();

            callbacks.onPostDraw(canvas);
            canvas.restoreToCount(saveCount);
            mRootNodeNeedsUpdate = false;
        } finally {
            // 结束绘制,加入到Nodes集合 
            mRootNode.endRecording();
        }
    }
    Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}

>RenderNode.java
public @NonNull RecordingCanvas beginRecording(int width, int height) {
    // 一个 RenderNode节点 只能有一个 RecordingCanvas对象 
    if (mCurrentRecordingCanvas != null) {
        throw new IllegalStateException(
                "Recording currently in progress - missing #endRecording() call?");
    }
    mCurrentRecordingCanvas = RecordingCanvas.obtain(this, width, height);
    return mCurrentRecordingCanvas;
}
updateViewTreeDisplayList 更新View树的DisplayList
代码语言:javascript复制
private void updateViewTreeDisplayList(View view) {
    view.mPrivateFlags |= View.PFLAG_DRAWN;
    view.mRecreateDisplayList = (view.mPrivateFlags &amp; View.PFLAG_INVALIDATED)
            == View.PFLAG_INVALIDATED;
    view.mPrivateFlags &amp;= ~View.PFLAG_INVALIDATED;
    // 调用了view的 updateDisplayListIfDirty
    view.updateDisplayListIfDirty();
    view.mRecreateDisplayList = false;
}

传入View获取到对应RenderNode中的displayList;如果根节点需要更新并且拥有drawOp命令需要获取到根节点的RecordingCanvas对象(包含当前node宽高信息)并调用drawRenderNode进行更新view的displayList( updateDisplayListIfDirty )最后调用rootnode的endRecording加入到nodes集合中

updateDisplayListIfDirty
代码语言:javascript复制
public RenderNode updateDisplayListIfDirty() {
    // 拿到对应的 renderNode
    final RenderNode renderNode = mRenderNode;
    if (!canHaveDisplayList()) {
        // can't populate RenderNode, don't try
        return renderNode;
    }

    if ((mPrivateFlags &amp; PFLAG_DRAWING_CACHE_VALID) == 0
            || !renderNode.hasDisplayList()
            || (mRecreateDisplayList)) {
        // Don't need to recreate the display list, just need to tell our
        // children to restore/recreate theirs
        // 如果当前view的缓存可用,则重用drawOp展示列表。告知子view去重建displayList
        if (renderNode.hasDisplayList()
                &amp;&amp; !mRecreateDisplayList) {
            mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
            mPrivateFlags &amp;= ~PFLAG_DIRTY_MASK;
            // 分发子view去 重建displayList
            dispatchGetDisplayList();
            return renderNode; // no work needed
        }
        int width = mRight - mLeft;
        int height = mBottom - mTop;
        int layerType = getLayerType();
        
        // 如果走到这里表示缓存不可用需要drawOp树重建,开始记录drawOp
        //获取RecordingCanvas后续draw的时候是绘制到这块canvas的
        final RecordingCanvas canvas = renderNode.beginRecording(width, height);

        try {
            if (layerType == LAYER_TYPE_SOFTWARE) {
               // 如果当前view拥有LAYER_TYPE_SOFTWARE类型,及时开了硬件加速,也只会使用
               //Android软件渲染管道来绘制。
                buildDrawingCache(true);
                Bitmap cache = getDrawingCache(true);
                if (cache != null) {
                    canvas.drawBitmap(cache, 0, 0, mLayerPaint);
                }
            } else {
                // Fast path for layouts with no backgrounds
                if ((mPrivateFlags &amp; PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
                    // 如果自身不需要绘制,则分发到子view,让子view去完成绘制
                    dispatchDraw(canvas);
                    
                    drawAutofilledHighlight(canvas);
                    if (mOverlay != null &amp;&amp; !mOverlay.isEmpty()) {
                        mOverlay.getOverlayView().draw(canvas);
                    }
                    if (debugDraw()) {
                        debugDrawFocus(canvas);
                    }
                } else {
                    //查看下行代码解释
                    draw(canvas);
                }
            }
        } finally {
            // 结束记录drawOp
            renderNode.endRecording();
            setDisplayListProperties(renderNode);
        }
    } else {
        mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
        mPrivateFlags &amp;= ~PFLAG_DIRTY_MASK;
    }
    return renderNode;
}

// draw 方法 
public void draw(Canvas canvas) {
     /*
      * Draw traversal performs several drawing steps which must be executed
      * in the appropriate order:
      *
      *      1. Draw the background
      *      2. If necessary, save the canvas' layers to prepare for fading
      *      3. Draw view's content
      *      4. Draw children
      *      5. If necessary, draw the fading edges and restore layers
      *      6. Draw decorations (scrollbars for instance)
      */
     }
}    

和根View一样调用updateDisplayListIfDirty;如果当前View缓存可用则重用drawOp列表。分发子View去重建displayList;如果缓存不可用表示需要重建DrawOp树

重建DrawOp树的过程起始点

重建DrawOp树的过程起始点就是renderNode.beginRecording获取到RecordingCanvas记录绘制命令

重建DrawOp树记录绘制指令

调用 draw方法内部会将命令添加到RecordingCanvas(本质是native层的SkiaCanvas) 中(六个顺序背景,内容等)

重建DrawOp树结束将canvas记录的DisplayList返回给java层
代码语言:javascript复制
> RenderNode.java 
public void endRecording() {
    if (mCurrentRecordingCanvas == null) {
        throw new IllegalStateException(
                "No recording in progress, forgot to call #beginRecording()?");
    }
    RecordingCanvas canvas = mCurrentRecordingCanvas;
    mCurrentRecordingCanvas = null;
    // 1 结束绘制动作记录,调用到native,返回native层的SkiaDisplayList对象的引用
    long displayList = canvas.finishRecording();
    //释放displayList
    nSetDisplayList(mNativeRenderNode, displayList);
    canvas.recycle();
}

> nSetDisplayList
> frameworks/base/core/jni/android_view_RenderNode.cpp
static void android_view_RenderNode_setDisplayList(JNIEnv* env,
        jobject clazz, jlong renderNodePtr, jlong displayListPtr) {
    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
    DisplayList* newData = reinterpret_cast<DisplayList*>(displayListPtr);
    renderNode->setStagingDisplayList(newData);
}

> frameworks/base/libs/hwui/RenderNode.cpp
void RenderNode::setStagingDisplayList(DisplayList* displayList) {
      //
    mValid = (displayList != nullptr);
    mNeedsDisplayListSync = true;
    //释放之前的 displayList
    delete mStagingDisplayList;
    // 赋值新的 displayList
    mStagingDisplayList = displayList;
}

RecordingCanvasrenderNode.endRecording()结束记录drawOP树 node调用endRecording(),内部最终调用的是native层的 SkiaRecordingCanvas的finishRecording()方法,返回 displayList对象的引用给java层。

DrawOp树重建总结
  1. 通过ThreadedRenderer.updateRootDisplayList(),根据view树来构建对应的RenderNode 树(一个view对应一个node) 。每个node中包含了当前view的绘制指令,如drawXXXop,如果有子view还会包含drawRenderNodeOp命令。 这些Op又称为统称为displayList。
  2. 调用node.beginRecording()开始记录,得到java层的RecordingCanvas 对象,同时得到native层的 SkiaRecordingCanvas对象当前view开始执行draw(canvas) ,以及自己的子view的draw(canvas)方法,最终都是调用canvas的api,记录到RecordingCanvas中
  3. 调用node.endRecording()结束绘制, RenderNode.endRecording()做了两件事:调用native层 SkiaRecordingCanvas的finishRecording(),结束记录,同时返回displayList的引用到java层

至此,所有的绘制指令都存储到了对应的node中。

canvas.drawRenderNode绘制Node

上一步中只是更新了所有RenderNodenode中的displayList,也就是完成了canvas.drawRenderNode(view.updateDisplayListIfDirty())中view.updateDisplayListIfDirty()的部分,接下来就是调用drawRenderNode来对displayList进行绘制

canva.drawNode本质上是调用native层的Skiacanvas的drawRenderNode方法

代码语言:javascript复制
public void drawRenderNode(@NonNull RenderNode renderNode) {
     // 其实就是调用native的 canvas->drawRenderNode(...
    nDrawRenderNode(mNativeCanvasWrapper, renderNode.mNativeRenderNode);
}

>frameworks/base/core/jni/android_view_DisplayListCanvas.cpp
static void android_view_DisplayListCanvas_drawRenderNode(jlong canvasPtr, jlong renderNodePtr) {
    Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
    canvas->drawRenderNode(renderNode);
}

>frameworks/base/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
void SkiaRecordingCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) {
    // mChildNodes 是 std::deque<RenderNodeDrawable>类型。 
    //把renderNode转换成drawable对象,创建一个 RenderNodeDrawable对象,存入队列中
    mDisplayList->mChildNodes.emplace_back(renderNode, asSkCanvas(), true, mCurrentBarrier);
    
    // 取出 RenderNodeDrawable 对象
    //RenderNodeDrawable 封装了一个node对象,让node可以被记录为 一系列的 skia 绘制命令。
    auto&amp; renderNodeDrawable = mDisplayList->mChildNodes.back();
    
    // 开始绘制 
    drawDrawable(&amp;renderNodeDrawable);

    // use staging property, since recording on UI thread
    if (renderNode->stagingProperties().isProjectionReceiver()) {
        mDisplayList->mProjectionReceiver = &amp;renderNodeDrawable;
    }
}

// 开始绘制
>frameworks/base/libs/hwui/SkiaCanvas.h
void drawDrawable(SkDrawable* drawable) { 
   //mCanvas 是 SKCanvas对象
   mCanvas->drawDrawable(drawable); 
}

// 此时已经到了skia引擎库
>external/skia/src/core/SkCanvas.cpp
void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
    // 继续调用
    this->onDrawDrawable(dr, matrix);
}

void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
    // drawable bounds are no longer reliable (e.g. android displaylist)
    // so don't use them for quick-reject
    if (this->predrawNotify()) {
        this->topDevice()->drawDrawable(this, dr, matrix);
    }
}

SkBaseDevice* SkCanvas::topDevice() const {
    SkASSERT(fMCRec->fDevice);
    return fMCRec->fDevice;
}

topDevice() 返回的是 SkDevice对象。

>external/skia/src/core/SkDevice.cpp  
void SkBaseDevice::drawDrawable(SkCanvas* canvas, SkDrawable* drawable, const SkMatrix* matrix) {
    //able是 SKDrawable类型,上面传入的具体实现类是 RenderNodeDrawable 
    drawable->draw(canvas, matrix);
}

> external/skia/src/core/SkDrawable.cpp
void SkDrawable::draw(SkCanvas* canvas, const SkMatrix* matrix) {
    SkAutoCanvasRestore acr(canvas, true);
    if (matrix) {
      //结合skia的 matrix 
        canvas->concat(*matrix);
    }
    // onDraw是一个抽象方法,具体实现的是xxxDrawable,而这里传入的是 RenderNodeDrawable
    this->onDraw(canvas);

    if ((false)) {
        draw_bbox(canvas, this->getBounds());
    }
}


RenderNodeDrawable
>frameworks/base/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
void RenderNodeDrawable::onDraw(SkCanvas* canvas) {
    // negative and positive Z order are drawn out of order, if this render node drawable is in
    // a reordering section
    if ((!mInReorderingSection) || MathUtils::isZero(mRenderNode->properties().getZ())) {
        this->forceDraw(canvas);
    }
}

void RenderNodeDrawable::forceDraw(SkCanvas* canvas) const {
    RenderNode* renderNode = mRenderNode.get();
    //最重要的是这句,将displayList转换为SkiaDl语法
    SkiaDisplayList* displayList = renderNode->getDisplayList().asSkiaDl();

    displayList->mProjectedOutline = nullptr;
}
renderNode转换成RenderNodeDrawable对象

将renderNode转换成drawable对象创建一个RenderNodeDrawable对象,存入队列中

RenderNodeDrawable 封装了一个node对象,让node可以被记录为 一系列的 skia 绘制命令。

RenderNodeDrawable中将node转换成skia命令

这时候已经到了SkiaCanvas内部,调用SkCanvas的drawAble所有的绘制DrawOp命令最后都会转换成skia绘制命令

DrawFrameTask::drawFrame()

接着就开始进入到同步过程了。把renderNode同步到渲染线程也就是调用renderProxy的syncAndDrawFrame,其内部会调用mDrawFrameTask.drawFrame方法

代码语言:javascript复制
// post到 RenderTHread 线程
postAndWait();其最后会调用到下一行:

//往渲染线程的队列中 post任务进去 ,回调run方法。
mRenderThread->queue().post([this]() { run(); });

也就是会往RenderThread的队列中post自己,最后会调用DrawFrmaeTask的run方法。

代码语言:javascript复制
// run方法:
void DrawFrameTask::run() {
    bool canUnblockUiThread;
    bool canDrawThisFrame;
    {
        // 构造一个 treeInfo 对象 
        TreeInfo info(TreeInfo::MODE_FULL, *mContext);
        // 遍历renderNode结合,转变为TreeInfo 信息
        canUnblockUiThread = syncFrameState(info);
    }
    //赋值 CanvasContext 对象
    CanvasContext* context = mContext;
    // ... 
    nsecs_t dequeueBufferDuration = 0;
    if (CC_LIKELY(canDrawThisFrame)) {
         // 调用canvasContext的draw方法开始绘制 
        dequeueBufferDuration = context->draw();
    }
}

//syncFrameState方法
bool DrawFrameTask::syncFrameState(TreeInfo&amp; info) {
    // 准备eglcontext 上下文,用来链接OpenGL和 GraphicBuffer之间的桥梁
    bool canDraw = mContext->makeCurrent();
    for (size_t i = 0; i < mLayers.size(); i  ) {
        mLayers[i]->apply();
    }
}

// makeCurrent()方法的具体实现为 SkiaOpenGLPipeline或者 SkiaVulkanPipeline。
>frameworks/base/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
MakeCurrentResult SkiaOpenGLPipeline::makeCurrent() {
    // 调用 mEglManager 来初始化
    if (!mEglManager.makeCurrent(mEglSurface, &amp;error)) {
        return MakeCurrentResult::AlreadyCurrent;
    }
    return error ? MakeCurrentResult::Failed : MakeCurrentResult::Succeeded;
}


>frameworks/base/libs/hwui/renderthread/EglManager.cpp 
bool EglManager::makeCurrent(EGLSurface surface, EGLint* errOut, bool force) {
    if (!force &amp;&amp; isCurrent(surface)) return false;

    if (surface == EGL_NO_SURFACE) {
        // Ensure we always have a valid surface &amp; context
        // 确保surface有效
        surface = mPBufferSurface;
    }
    
    //egl模块给OpenGL提供Surface和eglContext
    if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) {
        ...
    }
    return true;
}

run方法中会创建一个TreeInfo对象遍历renderNode转换成TreeInfo,接着调用makeCurrent初始化eglContext上下文(用来链接opengl和graphicbuffer的桥梁),主要是绑定surface让egl模块给opengl提供surface窗口和eglcontext上下文。通过EglManager的 makeCurrent()终于让OpenGL和本地窗口surface 联系了起来。因此,通过CanvasContext.draw() 就可以实现OpenGL/Vulkan绘制,最终通过Skia/Vulkan 引擎(SkiaOpenGLPipeline/SkiaVulkanPipeline)把数据渲染到缓冲区GraphicBuffer中

CanvasContext介绍

CanvasContext 作用用于管理当前绘制区域。每个RenderThread对象有一个 CanvasContext。 管理全局EGL context和当前绘制区域surface。

绑定Surface

在ViewRootImpl的 setView方法,如果是硬件绘制则会请求Buffer,通过mAttachInfo.mThreadRender.alloateBuffers()

代码语言:javascript复制
//mAttachInfo.mThreadRender.alloateBuffers
public void allocateBuffers() {
    // JNI方法
     nAllocateBuffers(mNativeProxy);
}


private static native void nAllocateBuffers(long nativeProxy);

>frameworks/base/core/jni/android_view_ThreadedRenderer.cpp 
static void android_view_ThreadedRenderer_allocateBuffers(JNIEnv* env, jobject clazz,jlong proxyPtr) {
    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
    //调用RenderProxy的allocateBuffers
    proxy->allocateBuffers();
}
 
>frameworks/base/libs/hwui/renderthread/RenderProxy.cpp 
void RenderProxy::allocateBuffers() {
    // post到renderThread线程中
    //run方法中会调用CanvasContext.allocateBuffers()
    mRenderThread.queue().post([=]() { mContext->allocateBuffers(); });
} 

>frameworks/base/libs/hwui/renderthread/CanvasContext.cpp
void CanvasContext::allocateBuffers() {
    if (mNativeSurface &amp;&amp; Properties::isDrawingEnabled()) {
        // ANativeWindow的hook方法
        ANativeWindow_tryAllocateBuffers(mNativeSurface->getNativeWindow());
    }
}

void ANativeWindow_tryAllocateBuffers(ANativeWindow* window) {
    if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) {
        return;
    }
    // 最后调用到 ANativeWindow的perform函数,surface的hook_perform钩子函数也会回调
    window->perform(window, NATIVE_WINDOW_ALLOCATE_BUFFERS);
}

在 Surface 中会进行申请buffer最终还是和软件绘制一样buffer是通过GraphBufferProducer进行申请的。请求到buffer后会调用ThreadProxy的init方法调用context.setSurface完成canvascontext和Surface的绑定

0 人点赞