硬件渲染-数据同步

2018-07-10  本文已影响89人  gczxbb

Java层,ThreadedRenderer的draw方法,首先记录树形结构每个视图节点绘制操作,底层DisplayListData保存,然后JNI#方法,nSyncAndDrawFrame同步。
根据指针,找到底层RenderProxy代理。
RenderProxy#syncAndDrawFrame方法。

int RenderProxy::syncAndDrawFrame() {
    return mDrawFrameTask.drawFrame();
}

DrawFrameTask任务,绘制帧


唤醒渲染线程

int DrawFrameTask::drawFrame() {
    mSyncResult = kSync_OK;
    mSyncQueued = systemTime(CLOCK_MONOTONIC);
    postAndWait();
    return mSyncResult;
}

主线程等待wait。

void DrawFrameTask::postAndWait() {
    AutoMutex _lock(mLock);
    mRenderThread->queue(this);
    mSignal.wait(mLock);
}

渲染线程,RenderThread继承Thread,单例,内部任务队列TaskQueue,封装RenderTask任务操作。DrawFrameTask继承RenderTask。

void RenderThread::queue(RenderTask* task) {
    AutoMutex _lock(mLock);
    mQueue.queue(task);
    if (mNextWakeup && task->mRunAt < mNextWakeup) {
        mNextWakeup = 0;
        mLooper->wake();
    }
}

插入队列TaskQueue,RenderThread的queue方法。判断下次唤醒的时间。唤醒渲染线程。
渲染线程利用Looper休眠。

RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>()
    ... {
    mFrameCallbackTask = new DispatchFrameCallbacks(this);
    mLooper = new Looper(false);
    run("RenderThread");
}

构造方法,Looper创建,线程启动,threadLoop循环。

bool RenderThread::threadLoop() {
    setpriority(PRIO_PROCESS, 0, PRIORITY_DISPLAY);
    initThreadLocals();
    int timeoutMillis = -1;
    for (;;) {//循环
        int result = mLooper->pollOnce(timeoutMillis);
        nsecs_t nextWakeup;
        while (RenderTask* task = nextTask(&nextWakeup)) {
            task->run();
        }
        if (nextWakeup == LLONG_MAX) {
            timeoutMillis = -1;
        } else {
            nsecs_t timeoutNanos = nextWakeup - systemTime(SYSTEM_TIME_MONOTONIC);
            timeoutMillis = nanoseconds_to_milliseconds(timeoutNanos);
            if (timeoutMillis < 0) {
                timeoutMillis = 0;
            }
        }
        ...
    }
    return false;
}

渲染线程在Looper#pollOnce处休眠,当主线程插入任务时,Looper#wake唤醒渲染线程。
队列的RenderTask任务依次执行。

总结:
渲染线程专注于与gpu交互,而主线程负责App视图绘制记录,在App,每一次树形视图绘制都是一个渲染任务。
那么,这一次渲染任务到底做什么呢?


一次渲染任务
渲染线程,两个功能。
1:同步帧状态,syncFrameState。
2:绘制,draw。

void DrawFrameTask::run() {
    bool canUnblockUiThread;
    bool canDrawThisFrame;
    {
        TreeInfo info(TreeInfo::MODE_FULL, mRenderThread->renderState());
        canUnblockUiThread = syncFrameState(info);
        canDrawThisFrame = info.out.canDrawThisFrame;
    }
    //任务内部的CanvasContext
    CanvasContext* context = mContext;
    if (canUnblockUiThread) {//唤醒主线程
        unblockUiThread();
    }
    if (CC_LIKELY(canDrawThisFrame)) {
        context->draw();
    }
    if (!canUnblockUiThread) {
        unblockUiThread();
    }
}
整流程图如下所示。 硬件渲染一次任务整体流程图.png

功能点已在图中标记,下面分别分析。


同步帧状态

一个任务,就是一帧数据,syncFrameState方法同步帧的状态和数据。

bool DrawFrameTask::syncFrameState(TreeInfo& info) {
    int64_t vsync = mFrameInfo[static_cast<int>(FrameInfoIndex::Vsync)];
    mRenderThread->timeLord().vsyncReceived(vsync);
    mContext->makeCurrent();
    Caches::getInstance().textureCache.resetMarkInUse(mContext);
    //第一步,遍历DeferredLayerUpdater数组Vector
    //触发CanvasContext的processLayerUpdate方法。
    for (size_t i = 0; i < mLayers.size(); i++) {
        mContext->processLayerUpdate(mLayers[i].get());
    }
    //每次push结束后清理数组
    mLayers.clear();
    //第二步
    mContext->prepareTree(info, mFrameInfo, mSyncQueued);
    if (info.out.hasAnimations) {
        if (info.out.requiresUiRedraw) {
            mSyncResult |= kSync_UIRedrawRequired;
        }
    }
    //如果返回的是false,说明我们耗尽了textureCache的空间
    return info.prepareTextures;
}

第一步 遍历Layers数组

分析一下什么样的视图会将Layer加入DrawFrameTask的Layers数组?

DrawFrameTask#pushLayerUpdate负责添加元素。追溯到Java层。
ThreadedRenderer#pushLayerUpdate方法。

@Override
void pushLayerUpdate(HardwareLayer layer) {
    nPushLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater());
}

通过JNI#方法,触发RenderProxy#pushLayerUpdate方法。

void RenderProxy::pushLayerUpdate(DeferredLayerUpdater* layer) {
    mDrawFrameTask.pushLayerUpdate(layer);
}

因此,向Layers数组添加元素,由Java层ThreadedRenderer的pushLayerUpdate发起的。那么,这是什么样的视图呢?
以下两个HardwareLayer#均触发ThreadedRenderer#pushLayerUpdate方法。**

public void setSurfaceTexture(SurfaceTexture surface) {
    nSetSurfaceTexture(mFinalizer.get(), surface, false);
    mRenderer.pushLayerUpdate(this);//mRenderer即ThreadedRenderer。
}

public void updateSurfaceTexture() {
    nUpdateSurfaceTexture(mFinalizer.get());
    mRenderer.pushLayerUpdate(this);
}

TextureView视图有一个HardwareLayer,HardwareLayer对应底层DeferredLayerUpdater,封装真正的Layer,因此,可以得出结论。

TextureView视图刷新时,利用ThreadedRenderer将HardwareLayer底层DeferredLayerUpdater加入DrawFrameTask的数组,第一步遍历数组针对TextureView视图。
普通视图,数组是空。

针对TextureView视图处理

遍历每一个DeferredLayerUpdater,CanvasContext#processLayerUpdate方法。

void CanvasContext::processLayerUpdate(DeferredLayerUpdater* layerUpdater) {
    bool success = layerUpdater->apply();
    if (layerUpdater->backingLayer()->deferredUpdateScheduled) {
        //mCanvas是OpenGLRenderer。
        mCanvas->pushLayerUpdate(layerUpdater->backingLayer());
    }
}

HardwareLayer结构图如下。

HardwareLayer结构图.jpg 由代码可知,将deferredUpdateScheduled标志的Layer加入OpenGLRenderer的Layer数组
此标志从哪里来呢?TextureView视图的Layer有此标志吗?

先看一下LayerType。上层配置三种。
LAYER_TYPE_NONE。
LAYER_TYPE_SOFTWARE。
LAYER_TYPE_HARDWARE。
底层对应None = 0,Software = 1,RenderLayer = 2。
View#setLayerType设置LayerType,TextureView重写此方法,忽略类型,默认不需要,LAYER_TYPE_NONE。

从源码中可知,Layer#updateDeferred方法设置deferredUpdateScheduled标志,Layer渲染后重置。此方法仅在RenderNode#pushLayerUpdate中调用,由RenderNode内部Layer触发。
LayerType是RenderLayer。

因此:上层专门设置LAYER_TYPE_HARDWARE的视图才会在RenderNode内部创建Layer,并有此标志。目前看来,TextureView视图底层Layer没有deferredUpdateScheduled标志,不会被加入OpenGLRenderer的Layer数组。

总结

第一步,处理和TextureView视图相关,底层Layer,普通视图无HardwareLayer,因此,DrawFrameTask数组是空。


第二步 准备ViewTree的数据

void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, 
                int64_t syncQueued) {
    mRenderThread.removeFrameCallback(this);
    //当前帧FrameInfo无SkippedFrame标志
    //说明之前的帧没有被跳过,mCurrentFrameInfo指向缓冲区新节点。
    if (!wasSkipped(mCurrentFrameInfo)) {
        mCurrentFrameInfo = &mFrames.next();//RingBuffer<FrameInfo, 120>循环缓冲区
    }
    //若之前的帧被跳过,继续用当前指向的节点进行初始化。
    mCurrentFrameInfo->importUiThreadInfo(uiFrameInfo);
    mCurrentFrameInfo->set(FrameInfoIndex::SyncQueued) = syncQueued;
    mCurrentFrameInfo->markSyncStart();
    //初始化info信息
    info.damageAccumulator = &mDamageAccumulator;
    info.renderer = mCanvas;
    info.canvasContext = this;
    mAnimationContext->startFrame(info.mode);
    //底层根节点RenderNode准备,树形结构遍历
    mRootRenderNode->prepareTree(info);
    mAnimationContext->runRemainingAnimations(info);
    //不存在mNativeWindow退出,无法绘制该帧canDrawThisFrame设false
    int runningBehind = 0;
    mNativeWindow->query(mNativeWindow.get(),
            NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind);
    info.out.canDrawThisFrame = !runningBehind;
    if (!info.out.canDrawThisFrame) {
        mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
    }
    if (info.out.hasAnimations || !info.out.canDrawThisFrame) {
        if (!info.out.requiresUiRedraw) {
            mRenderThread.postFrameCallback(this);
        }
    }
}

CanvasContext的底层根节点RootRenderNode,在CanvasContext构造方法初始化。在上层ThreadedRenderer保存指针,RootRenderNode继承RenderNode。

void RenderNode::prepareTree(TreeInfo& info) {
    bool functorsNeedLayer = Properties::debugOverdraw;
    prepareTreeImpl(info, functorsNeedLayer);
}

从根节点开始,递归遍历。

void RenderNode::prepareTreeImpl(TreeInfo& info, bool functorsNeedLayer) {
    info.damageAccumulator->pushTransform(this);
    //还有一种模式MODE_RT_ONLY
    if (info.mode == TreeInfo::MODE_FULL) {
        //属性改变,赋值给mProperties。
        pushStagingPropertiesChanges(info);
    }
    bool willHaveFunctor = false;
    if (info.mode == TreeInfo::MODE_FULL && mStagingDisplayListData) {
        willHaveFunctor = !mStagingDisplayListData->functors.isEmpty();
    } else if (mDisplayListData) {
        willHaveFunctor = !mDisplayListData->functors.isEmpty();
    }
    bool childFunctorsNeedLayer = mProperties.prepareForFunctorPresence(
            willHaveFunctor, functorsNeedLayer);
    prepareLayer(info, animatorDirtyMask);
    if (info.mode == TreeInfo::MODE_FULL) {
        //赋值给mDisplayListData。
        pushStagingDisplayListChanges(info);
    }
    prepareSubTree(info, childFunctorsNeedLayer, mDisplayListData);
    pushLayerUpdate(info);

    info.damageAccumulator->popTransform();
}

prepareTree准备树形结构的流程如下图所示。

CanvasContext的prepareTree准备树形结构的流程.jpg 关注两个方法。
prepareSubTree方法,递归遍历子节点。
pushLayerUpdate方法,RenderNode节点的内部Layer处理。
void RenderNode::prepareSubTree(TreeInfo& info, bool functorsNeedLayer, 
                DisplayListData* subtree) {
    //该节点的DisplayListData存在
    if (subtree) {
        //获取TextureCahe对象
        TextureCache& cache = Caches::getInstance().textureCache;
        //数据中Functor集合
        info.out.hasFunctors |= subtree->functors.size();
        for (size_t i = 0; info.prepareTextures && i < 
                        subtree->bitmapResources.size(); i++) {
            //若有一个texture失败,是0,赋值prepareTextures,不再遍历。
            info.prepareTextures = cache.prefetchAndMarkInUse(
                    info.canvasContext, subtree->bitmapResources[i]);
        }
        for (size_t i = 0; i < subtree->children().size(); i++) {
            DrawRenderNodeOp* op = subtree->children()[i];
            RenderNode* childNode = op->mRenderNode;
            info.damageAccumulator->pushTransform(&op->mTransformFromParent);
            bool childFunctorsNeedLayer = functorsNeedLayer
                    || op->mRecordedWithPotentialStencilClip;
            childNode->prepareTreeImpl(info, childFunctorsNeedLayer);
            info.damageAccumulator->popTransform();
        }
    }
}

SkBitmap数组元素从哪里来呢?

画布绘制Bitmap,DisplayListCanvas#drawBitmap方法。

void DisplayListCanvas::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) {
    //refBitmap会向mDisplayListData的bitmapResources数组中保存一份localBitmap
    bitmap = refBitmap(*bitmap); 
    paint = refPaint(paint);
    addDrawOp(new (alloc()) DrawBitmapOp(bitmap, paint));
}
inline const SkBitmap* refBitmap(const SkBitmap& bitmap) {
    SkBitmap* localBitmap = new (alloc()) SkBitmap(bitmap);
    alloc().autoDestroy(localBitmap);
    mDisplayListData->bitmapResources.push_back(localBitmap);
    return localBitmap;
}

增加一个DrawBitmapOp操作,向数组保存一个SkBitmap。

bool TextureCache::prefetchAndMarkInUse(void* ownerToken, const SkBitmap* bitmap) {
    Texture* texture = getCachedTexture(bitmap, AtlasUsageType::Use);
    if (texture) {//texture不为0
        texture->isInUse = ownerToken;
    }
    return texture;
}

若所有Texture存在,在syncFrameState结束后,返回info.prepareTextures标志true,它会影响唤醒主线程的时机。

DrawRenderNodeOp数组是如何加入的呢?

追溯到Java层,DisplayListCanvas#drawRenderNode方法将子节点RenderNode写入。底层创建一个DrawRenderNodeOp操作,封装被绘制视图底层RenderNode节点,将操作加入到DrawRenderNodeOp数组。

RenderNode#pushLayerUpdate方法。

void RenderNode::pushLayerUpdate(TreeInfo& info) {
    LayerType layerType = properties().effectiveLayerType();
    if (CC_LIKELY(layerType != LayerType::RenderLayer) || 
                CC_UNLIKELY(!isRenderable())) {
        if (CC_UNLIKELY(mLayer)) {
            LayerRenderer::destroyLayer(mLayer);
            mLayer = nullptr;
        }
        return;
    }
    bool transformUpdateNeeded = false;
    if (!mLayer) {
        //内部Layer不存在,由LayerRenderer创建。
        mLayer = LayerRenderer::createRenderLayer(info.renderState, 
                getWidth(), getHeight());
        applyLayerPropertiesToLayer(info);
        damageSelf(info);
        transformUpdateNeeded = true;
    }
    ...
    if (dirty.intersect(0, 0, getWidth(), getHeight())) {
        dirty.roundOut(&dirty);
        //初始化Layer的RenderNode,deferredUpdateScheduled标志
        //初始化渲染器render为LayerRenderer。
        mLayer->updateDeferred(this, dirty.fLeft, dirty.fTop, 
                dirty.fRight, dirty.fBottom);
    }
    if (info.renderer && mLayer->deferredUpdateScheduled) {
        info.renderer->pushLayerUpdate(mLayer);//push进数组
    }
    ...
}

满足两个条件执行。
LayerType必须是RenderLayer类型(Java层,View#setLayerType方法设置)。
DisplayListData不是空。
否则退出方法。
初始化创建RenderNode内部Layer,TreeInfo的OpenGLRenderer,将该Layer加入OpenGLRenderer的Layer数组,等待OpenGLRenderer#updateLayers更新。

底层RenderNode结构图如下。 底层RenderNode结构图.jpg
总结

1,底层根节点RootRenderNode开始,执行prepareTreeImpl方法,prepareSubTree进入子视图,从当前RenderNode的DisplayListData中查找子视图RenderNode节点,递归底层子视图节点RenderNode的prepareTreeImpl方法。
2,底层根节点RootRenderNode并非顶层视图节点,而是上层ThreadedRenderer内部RenderNode对应的底层节点。
3,上层画布触发过drawBitmap时,存在SkBitmap数组。
4,上层设置LAYER_TYPE_HARDWARE的视图,底层RenderNode内部创建一个Layer。

到这里,就结束了根RenderNode的prepareTreeImpl方法,并递归遍历子节点的prepareTreeImpl。继续回到CanvasContext的prepareTree。
接下来是NativeWindow#query方法。
那么何时初始化NativeWindow呢?

追溯到Java层,ViewRootImpl#performTraversals,调用ThreadedRenderer的initialize方法,该方法传入由Wms初始化的Surface。

@Override
boolean initialize(Surface surface) throws OutOfResourcesException {
    mInitialized = true;
    updateEnabledState(surface);
    boolean status = nInitialize(mNativeProxy, surface);
    return status;
}

根据上层Surface获取底层NativeWindow。从JNI#触发RenderProxy#initialize方法,到CanvasContext的initialize方法。

bool CanvasContext::initialize(ANativeWindow* window) {
    setSurface(window);
    mCanvas = new OpenGLRenderer(mRenderThread.renderState());
    mCanvas->initProperties();
    return true;
}

初始化OpenGLRenderer。
利用NativeWindow创建一个mEglSurface。

void CanvasContext::setSurface(ANativeWindow* window) {
    mNativeWindow = window;
    ...
    if (window) {
        mEglSurface = mEglManager.createSurface(window);
    }
}

NativeWindow是Java层Surface的底层对象
query方法的目的是查询生产者和消费者缓冲区的运行情况。

NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND的官方解释为是否消费者比生产者对缓冲区的处理落后大于1个缓冲区,runningBehind返回结果,如果runningBehind为false说明未落后,则info.out.canDrawThisFrame置为true,说明该帧可以绘制,否则canDrawThisFrame设为false跳过该帧。

总结:

第二步,prepareTree逻辑是绘制准备,将属性与DisplayList数据交接,DrawRenderNodeOp操作对应节点递归处理,RenderNode必要时初始化Layer,准备NativeWindow查询是否该帧需要跳过。
syncFrameState同步结束,返回info.prepareTextures结果,赋值canUnblockUiThread。
若info.prepareTextures标志,唤醒主线程。Signal#signal()。
否则,说明存在未准备好的Texture。先执行CanvasContext#draw方法,再唤醒主线程。


任重而道远

上一篇下一篇

猜你喜欢

热点阅读