Android view硬件加速 源码学习笔记 (中), 基于AndroidQ

720 阅读5分钟

硬件加速涉及到绘制的核心类

上一篇,比较仓促的就开始说硬件加速的代码了,没有理一下核心类,在这里补一下,首先最核心的入口就是RenderProxy, Proxy的创建在ThreadRender 的 继承类 HandwareRender构造方法里 nCreateProxy()里,创建RenderProxy.

RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode,
                         IContextFactory* contextFactory)
        : mRenderThread(RenderThread::getInstance()), mContext(nullptr) {
    mContext = mRenderThread.queue().runSync([&]() -> CanvasContext* {
        return CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory);
    });
    mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode);
}

可以看到代码初始化时,同时创建了:
1, RenderThread(也就是硬件加速的渲染线程,所有硬件渲染,都会在这个线程的Looper中执行).
2,CanvasContext(所有渲染的上下文,将持有各种渲染的PipleLine,一共找到三个pipeline, SkiaPipeLine, 以及继承自SkiaPipeLine的 (SkiaOpenGlPipeLine, SkiaVulkanPipeLine).
3,DrawFrameTask,绘制某一帧的任务.

CanvasContext* CanvasContext::create(RenderThread& thread, bool translucent,
                                     RenderNode* rootRenderNode, IContextFactory* contextFactory) {
    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;

CanvasContext初始化时,会根据properties的配置选择使用SkiaOpenGl或者SkiaVulkan的一种. 接下来继续看核心类RenderThread: 调用start后的核心代码.

bool RenderThread::threadLoop() {
   //设置优先级,THREAD_PRIORITY_DISPLAY, 是标准显示优先级.标准显示系统优先级,主要是改善UI的刷新.
   
    setpriority(PRIO_PROCESS, 0, PRIORITY_DISPLAY);
    Looper::setForThread(mLooper);
    if (gOnStartHook) {
        gOnStartHook("RenderThread");
    }
    //初始化 当前线程的变量
    initThreadLocals();

    while (true) {
    //循环,阻塞,等待消息进入队列时唤醒,应该类似java层MessageQueue的 nativePollOnce()
    //最终调用的也是Loop.poolOnce()
        waitForWork();
        //处理workQueue里的任务.
        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;
}

先看一下initThreslocals

void RenderThread::initThreadLocals() {
    setupFrameInterval();
    //初始化监听Vsync信号
    initializeDisplayEventReceiver();
    
    mEglManager = new EglManager();
    mRenderState = new RenderState(*this);
    mVkManager = new VulkanManager();
    mCacheManager = new CacheManager(DeviceInfo::get()->displayInfo());
}

EglManager:当使用openGl等相关管道时,会通过这个类进行OpenGl上下文的操作.
VulkanManager: 当使用Vulkan等相关管道时,会通过这个类进行Vulkan上下文操作.
RenderState: 渲染状态,持有了renderThread对象,而renderThread又持有了eglManager和vulkanManager

CacheManager:更像是一个内存缓存管理.里面定义了onTrimMemory等方法.

`

void RenderThread::initializeDisplayEventReceiver() {
    LOG_ALWAYS_FATAL_IF(mVsyncSource, "Initializing a second DisplayEventReceiver?");

    if (!Properties::isolatedProcess) {
        auto receiver = std::make_unique<DisplayEventReceiver>(
            ISurfaceComposer::eVsyncSourceApp,
            ISurfaceComposer::eConfigChangedDispatch);
        status_t status = receiver->initCheck();
        LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
                            "Initialization of DisplayEventReceiver "
                            "failed with status: %d",
                            status);

        // Register the FD
        // looper在这注册了DisplayEventReceiver的监听
        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);
    }
}

这个方法初始化了DisplayEventReceiver ,这个能收到vSync的回调, 同时looper注册了receiver的回调,回调方法是displayEventReceiverCallBack()

displayEventReceiverCallBack,最后又调用了drainDisplayEventQueue(),这个向workQueue添加了一个任务dispatchFrameCallbacks(). 这个IFrameCallback最终会调用CanvasContext中的 doFrame(),prepareTree()

追踪一下,这个代码是ViewRootImpl在scheduleTraversals()中调用的notifyRendererOfFramePending()中添加的.添加目的是什么呢?

/**
 * Notifies the hardware renderer that a call to {@link FrameRenderRequest#syncAndDraw()} will
 * be coming soon. This is used to help schedule when RenderThread-driven animations will
 * happen as the renderer wants to avoid producing more than one frame per vsync signal.
 */
public void notifyFramePending() {
    nNotifyFramePending(mNativeProxy);
}

我们看一下HardwareRender中的notifyFramePending,也就是添加IFrameCallBack的java层调用方法. 意思是通知handware render,一个 syncAndDraw()任务马上就要到了(Choregrapher收到vsync后开始执行的?) 这些有助于安排 render 何时执行 RenderThread-driven 动画,从而避免 每个Vsync周期内,产生超过一帧数据.

硬件加速开始绘制

上文,我们简单提到了硬件加速开始绘制的方法,所以,现在我们直接从DrawFrameTask的run方法向下看

void DrawFrameTask::run() {
    ATRACE_NAME("DrawFrame");

    bool canUnblockUiThread;
    bool canDrawThisFrame;
    {
        TreeInfo info(TreeInfo::MODE_FULL, *mContext);
        canUnblockUiThread = syncFrameState(info);
        canDrawThisFrame = info.out.canDrawThisFrame;
        //为CanvasContext添加一个绘制完成的回调,mFrameCompleteCallBack
        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;

    // From this point on anything in "this" is *UNSAFE TO ACCESS*
    if (canUnblockUiThread) {
        unblockUiThread();
    }

    // Even if we aren't drawing this vsync pulse the next frame number will still be accurate
    if (CC_UNLIKELY(callback)) {
        context->enqueueFrameWork(
                [callback, frameNr = context->getFrameNumber()]() { callback(frameNr); });
    }

    if (CC_LIKELY(canDrawThisFrame)) {
        context->draw();
    } else {
        // wait on fences so tasks don't overlap next frame
        context->waitOnFences();
    }

    if (!canUnblockUiThread) {
        unblockUiThread();
    }
}

比较重要的方法,有两个:

1, syncFrameState() 这个方法里又会调用CanvasContext中的prepareTree(),用来准备渲染树.
2, CanvasContext.draw()绘制流程

准备渲染树

先看一下 syncFrameState()方法:

bool DrawFrameTask::syncFrameState(TreeInfo& info) {
    ATRACE_CALL();
    int64_t vsync = mFrameInfo[static_cast<int>(FrameInfoIndex::Vsync)];
    mRenderThread->timeLord().vsyncReceived(vsync);
    //1, 创建OpenGl或者Vulkan的上下文
    bool canDraw = mContext->makeCurrent();
    //2, 最终调用OpenGl或者Vulkan中的 unpinImages()方法,获取所有纹理并重置.
    mContext->unpinImages();

   //3, 处理保存在mDisplayList中的Layer逻辑
    for (size_t i = 0; i < mLayers.size(); i++) {
        mLayers[i]->apply();
    }
    mLayers.clear();
    mContext->setContentDrawBounds(mContentDrawBounds);
    //4, 调用prepareTree,准备渲染树.
    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;
}

来看prepareTree逻辑.

void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued,
                                RenderNode* target) {
    mRenderThread.removeFrameCallback(this);

    // If the previous frame was dropped we don't need to hold onto it, so
    // just keep using the previous frame's structure instead
    if (!wasSkipped(mCurrentFrameInfo)) {
        mCurrentFrameInfo = mJankTracker.startFrame();
    }
    mCurrentFrameInfo->importUiThreadInfo(uiFrameInfo);
    mCurrentFrameInfo->set(FrameInfoIndex::SyncQueued) = syncQueued;
    mCurrentFrameInfo->markSyncStart();

    info.damageAccumulator = &mDamageAccumulator;
    info.layerUpdateQueue = &mLayerUpdateQueue;
    info.out.canDrawThisFrame = true;

    mAnimationContext->startFrame(info.mode);
    mRenderPipeline->onPrepareTree();
    for (const sp<RenderNode>& node : mRenderNodes) {
        // Only the primary target node will be drawn full - all other nodes would get drawn in
        // real time mode. In case of a window, the primary node is the window content and the other
        // node(s) are non client / filler nodes.
        info.mode = (node.get() == target ? TreeInfo::MODE_FULL : TreeInfo::MODE_RT_ONLY);
        node->prepareTree(info);
        GL_CHECKPOINT(MODERATE);
    }
    mAnimationContext->runRemainingAnimations(info);
    GL_CHECKPOINT(MODERATE);

    freePrefetchedLayers();
    GL_CHECKPOINT(MODERATE);

    mIsDirty = true;

    if (CC_UNLIKELY(!hasSurface())) {
        mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
        info.out.canDrawThisFrame = false;
        return;
    }

    if (CC_LIKELY(mSwapHistory.size() && !Properties::forceDrawFrame)) {
        nsecs_t latestVsync = mRenderThread.timeLord().latestVsync();
        SwapHistory& lastSwap = mSwapHistory.back();
        nsecs_t vsyncDelta = std::abs(lastSwap.vsyncTime - latestVsync);
        // The slight fudge-factor is to deal with cases where
        // the vsync was estimated due to being slow handling the signal.
        // See the logic in TimeLord#computeFrameTimeNanos or in
        // Choreographer.java for details on when this happens
        if (vsyncDelta < 2_ms) {
            // Already drew for this vsync pulse, UI draw request missed
            // the deadline for RT animations
            info.out.canDrawThisFrame = false;
        }
    } else {
        info.out.canDrawThisFrame = true;
    }

    // TODO: Do we need to abort out if the backdrop is added but not ready? Should that even
    // be an allowable combination?
    if (mRenderNodes.size() > 2 && !mRenderNodes[1]->isRenderable()) {
        info.out.canDrawThisFrame = false;
    }

    if (info.out.canDrawThisFrame) {
        int err = mNativeSurface->reserveNext();
        if (err != OK) {
            mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
            info.out.canDrawThisFrame = false;
            ALOGW("reserveNext failed, error = %d (%s)", err, strerror(-err));
            if (err != TIMED_OUT) {
                // A timed out surface can still recover, but assume others are permanently dead.
                setSurface(nullptr);
                return;
            }
        }
    } else {
        mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
    }

    bool postedFrameCallback = false;
    if (info.out.hasAnimations || !info.out.canDrawThisFrame) {
        if (CC_UNLIKELY(!Properties::enableRTAnimations)) {
            info.out.requiresUiRedraw = true;
        }
        if (!info.out.requiresUiRedraw) {
            // If animationsNeedsRedraw is set don't bother posting for an RT anim
            // as we will just end up fighting the UI thread.
            mRenderThread.postFrameCallback(this);
            postedFrameCallback = true;
        }
    }

    if (!postedFrameCallback &&
        info.out.animatedImageDelay != TreeInfo::Out::kNoAnimatedImageDelay) {
        // Subtract the time of one frame so it can be displayed on time.
        const nsecs_t kFrameTime = mRenderThread.timeLord().frameIntervalNanos();
        if (info.out.animatedImageDelay <= kFrameTime) {
            mRenderThread.postFrameCallback(this);
        } else {
            const auto delay = info.out.animatedImageDelay - kFrameTime;
            int genId = mGenerationID;
            mRenderThread.queue().postDelayed(delay, [this, genId]() {
                if (mGenerationID == genId) {
                    mRenderThread.postFrameCallback(this);
                }
            });
        }
    }
}

一般情况下,mRenderNodes里面应该只有一个RenderNode

加下来,会执行RenderNode.prepareTree()