Android 源码 图形系统之硬件渲染器同步和绘制帧

在《Android 源码 图形系统之硬件渲染器绘制》一节中没有分析 nSyncAndDrawFrame(…) 同步和绘制帧,这一节继续分析。

frameworks/base/core/java/android/view/ThreadedRenderer.java

public class ThreadedRenderer extends HardwareRenderer {
    ......
    @Override
    void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) {
        ......
        final long[] frameInfo = choreographer.mFrameInfo.mFrameInfo;
        // 3. 同步和绘制帧
        int syncResult = nSyncAndDrawFrame(mNativeProxy, frameInfo, frameInfo.length);
        ......
    }
    ......
    private static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size);
    ......
}
  1. proxyPtr 强转为 RenderProxy* 指针
  2. 将 frameInfo jlongArray 复制到缓冲区(proxy->frameInfo())中
  3. 调用 RenderProxy syncAndDrawFrame() 方法

frameworks/base/core/jni/android_view_ThreadedRenderer.cpp

static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
        jlong proxyPtr, jlongArray frameInfo, jint frameInfoSize) {
    LOG_ALWAYS_FATAL_IF(frameInfoSize != UI_THREAD_FRAME_INFO_SIZE,
            "Mismatched size expectations, given %d expected %d",
            frameInfoSize, UI_THREAD_FRAME_INFO_SIZE);
    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
    env->GetLongArrayRegion(frameInfo, 0, frameInfoSize, proxy->frameInfo());
    return proxy->syncAndDrawFrame();
}

syncAndDrawFrame() 内调用了 DrawFrameTask 的 drawFrame() 方法。

frameworks/base/libs/hwui/renderthread/RenderProxy.cpp

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

这里主要调用了 postAndWait(),从方法名不难推测出是将任务推到队列并等待。

frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp

int DrawFrameTask::drawFrame() {
    LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!");

    mSyncResult = kSync_OK;
    mSyncQueued = systemTime(CLOCK_MONOTONIC);
    postAndWait();

    return mSyncResult;
}

原来推到了 RenderThread 的队列了。

frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp

void DrawFrameTask::postAndWait() {
    AutoMutex _lock(mLock);
    mRenderThread->queue(this);
    mSignal.wait(mLock);
}
  1. 将 RenderTask 推到任务队列 TaskQueue 中
  2. 如果任务运行时间点小于下次唤醒点,调用 Looper wake() 方法进行唤醒

frameworks/base/libs/hwui/renderthread/RenderThread.cpp

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

入队方法很简单,根据任务运行时间点,在队列中从小到大进行排列。

frameworks/base/libs/hwui/renderthread/RenderThread.cpp

void TaskQueue::queue(RenderTask* task) {
    // Since the RenderTask itself forms the linked list it is not allowed
    // to have the same task queued twice
    LOG_ALWAYS_FATAL_IF(task->mNext || mTail == task, "Task is already in the queue!");
    if (mTail) {
        // Fast path if we can just append
        if (mTail->mRunAt <= task->mRunAt) {
            mTail->mNext = task;
            mTail = task;
        } else {
            // Need to find the proper insertion point
            RenderTask* previous = nullptr;
            RenderTask* next = mHead;
            while (next && next->mRunAt <= task->mRunAt) {
                previous = next;
                next = next->mNext;
            }
            if (!previous) {
                task->mNext = mHead;
                mHead = task;
            } else {
                previous->mNext = task;
                if (next) {
                    task->mNext = next;
                } else {
                    mTail = task;
                }
            }
        }
    } else {
        mTail = mHead = task;
    }
}

假设 Looper 之前在 RenderThread 类 threadLoop() 中 pollOnce(…) 方法上睡眠,则入队渲染任务(RenderTask)以后立马会唤醒。接着就会调用 nextTask(…) 从队列中取出渲染任务。接着就会调用其 run() 方法执行。

frameworks/base/libs/hwui/renderthread/RenderThread.cpp

bool RenderThread::threadLoop() {
    ......
    int timeoutMillis = -1;
    for (;;) {
        int result = mLooper->pollOnce(timeoutMillis);
        LOG_ALWAYS_FATAL_IF(result == Looper::POLL_ERROR,
                "RenderThread Looper POLL_ERROR!");

        nsecs_t nextWakeup;
        // Process our queue, if we have anything
        while (RenderTask* task = nextTask(&nextWakeup)) {
            task->run();
            // task may have deleted itself, do not reference it again
        }
        ......
    }

    return false;
}
  1. 调用 syncFrameState(…) 同步帧状态
  2. 调用 CanvasContext 类 draw() 方法绘制帧

frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp

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

    bool canUnblockUiThread;
    bool canDrawThisFrame;
    {
        TreeInfo info(TreeInfo::MODE_FULL, mRenderThread->renderState());
        canUnblockUiThread = syncFrameState(info);
        canDrawThisFrame = info.out.canDrawThisFrame;
    }

    // Grab a copy of everything we need
    CanvasContext* context = mContext;

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

    if (CC_LIKELY(canDrawThisFrame)) {
        context->draw();
    }

    if (!canUnblockUiThread) {
        unblockUiThread();
    }
}
  1. 调用 CanvasContext 类 makeCurrent() 方法创建 EGL 渲染上下文等
  2. 处理层更新
  3. 通过 CanvasContext 的 prepareTree() 继而调用 RenderNode 的 prepareTree() 同步渲染信息。最后会输出 TreeInfo 结构,其中的 prepareTextures 代表纹理上传是否成功。如果为 false,说明纹理缓存空间用完了。这样为了防止渲染线程在渲染过程中使用的资源和主线程竞争

frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp

bool DrawFrameTask::syncFrameState(TreeInfo& info) {
    ATRACE_CALL();
    int64_t vsync = mFrameInfo[static_cast<int>(FrameInfoIndex::Vsync)];
    mRenderThread->timeLord().vsyncReceived(vsync);
    mContext->makeCurrent();
    Caches::getInstance().textureCache.resetMarkInUse(mContext);

    for (size_t i = 0; i < mLayers.size(); i++) {
        mContext->processLayerUpdate(mLayers[i].get());
    }
    mLayers.clear();
    mContext->prepareTree(info, mFrameInfo, mSyncQueued);

    // 这是在 prepareTree 之后,这样任何挂起的操作(RenderNode tree状态,预抓取的层等等)都将被刷新。
    if (CC_UNLIKELY(!mContext->hasSurface())) {
        mSyncResult |= kSync_LostSurfaceRewardIfFound;
    }

    if (info.out.hasAnimations) {
        if (info.out.requiresUiRedraw) {
            mSyncResult |= kSync_UIRedrawRequired;
        }
    }
    // 如果 prepareTextures 为 false,我们就用完了纹理缓存空间
    return info.prepareTextures;
}

此方法实际调用了 EglManager 类 makeCurrent(…) 完成实际工作。

frameworks/base/libs/hwui/renderthread/CanvasContext.cpp

void CanvasContext::makeCurrent() {
    // TODO: Figure out why this workaround is needed, see b/13913604
    // In the meantime this matches the behavior of GLRenderer, so it is not a regression
    EGLint error = 0;
    mHaveNewSurface |= mEglManager.makeCurrent(mEglSurface, &error);
    if (error) {
        setSurface(nullptr);
    }
}

如果当前 Surface 已改变,则返回 true;如果已经是当前 Surface,则返回 false。主要调用 eglMakeCurrent(…) 完成创建 EGL 渲染上下文等动作, eglMakeCurrent(…) 函数位于 opengl 库中。

frameworks/base/libs/hwui/renderthread/EglManager.cpp

bool EglManager::makeCurrent(EGLSurface surface, EGLint* errOut) {
    if (isCurrent(surface)) return false;

    if (surface == EGL_NO_SURFACE) {
        // 确保我们总是有一个有效的 Surface 和上下文
        surface = mPBufferSurface;
    }
    if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) {
        if (errOut) {
            *errOut = eglGetError();
            ALOGW("Failed to make current on surface %p, error=%s",
                    (void*)surface, egl_error_str(*errOut));
        } else {
            LOG_ALWAYS_FATAL("Failed to make current on surface %p, error=%s",
                    (void*)surface, egl_error_str());
        }
    }
    mCurrentSurface = surface;
    return true;
}

处理层更新。

  1. 调用 DeferredLayerUpdater 类 apply() 处理
  2. 获取 DeferredLayerUpdater 的 backingLayer() ,也就是 Layer。如果需要延迟安排处理,调用 OpenGLRenderer 类 pushLayerUpdate(…) 方法将层添加到容器(Vector< sp >,它代表在一个帧的开始要更新的层的列表)中

frameworks/base/libs/hwui/renderthread/CanvasContext.cpp

void CanvasContext::processLayerUpdate(DeferredLayerUpdater* layerUpdater) {
    bool success = layerUpdater->apply();
    LOG_ALWAYS_FATAL_IF(!success, "Failed to update layer!");
    if (layerUpdater->backingLayer()->deferredUpdateScheduled) {
        mCanvas->pushLayerUpdate(layerUpdater->backingLayer());
    }
}

DeferredLayerUpdater 类代表一个容器,用于保存应在渲染过程开始时将图层设置为的属性。

apply 有应用的意思,也就是将某些属性应用的层上。另外,还调用了 SurfaceTexture 类 attachToContext(…) 方法将层的 TextureId attach 到上下文;更新了 Texture Image;加载了 Transform。

frameworks/base/libs/hwui/DeferredLayerUpdater.cpp

bool DeferredLayerUpdater::apply() {
    bool success = true;
    // 这些属性对这两种层类型应用相同
    mLayer->setColorFilter(mColorFilter);
    mLayer->setAlpha(mAlpha, mMode);

    if (mSurfaceTexture.get()) {
        if (mNeedsGLContextAttach) {
            mNeedsGLContextAttach = false;
            mSurfaceTexture->attachToContext(mLayer->getTextureId());
        }
        if (mUpdateTexImage) {
            mUpdateTexImage = false;
            doUpdateTexImage();
        }
        if (mTransform) {
            mLayer->getTransform().load(*mTransform);
            setTransform(nullptr);
        }
    }
    return success;
}

进一步调用 RenderNode 类 prepareTree(…) 准备树信息(TreeInfo)。mRootRenderNode 是 RenderNode 类型。

frameworks/base/libs/hwui/renderthread/CanvasContext.cpp

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

    // 如果前一帧被丢弃,我们不需要保留它,所以只要继续使用前一帧的结构即可
    if (!wasSkipped(mCurrentFrameInfo)) {
        mCurrentFrameInfo = &mFrames.next();
    }
    mCurrentFrameInfo->importUiThreadInfo(uiFrameInfo);
    mCurrentFrameInfo->set(FrameInfoIndex::SyncQueued) = syncQueued;
    mCurrentFrameInfo->markSyncStart();

    info.damageAccumulator = &mDamageAccumulator;
    info.renderer = mCanvas;
    info.canvasContext = this;

    mAnimationContext->startFrame(info.mode);
    mRootRenderNode->prepareTree(info);
    mAnimationContext->runRemainingAnimations(info);
    ......
}

在 RenderNode 类 prepareTree() 方法内具体实现是靠 prepareTreeImpl(…) 方法。

frameworks/base/libs/hwui/RenderNode.cpp

void RenderNode::prepareTree(TreeInfo& info) {
    ATRACE_CALL();
    LOG_ALWAYS_FATAL_IF(!info.damageAccumulator, "DamageAccumulator missing");

    // Functors don't correctly handle stencil usage of overdraw debugging - shove 'em in a layer.
    bool functorsNeedLayer = Properties::debugOverdraw;

    prepareTreeImpl(info, functorsNeedLayer);
}

遍历绘制树以准备帧。

MODE_FULL = UI线程驱动(因此必须同步属性),否则 RT 驱动。

遍历树时,如果可能需要使用模版缓冲区的任何内容,则 functorsNeedLayer 标志设置为 true。使用仿函数(functor )绘制的 View 将被强制放置在 Layer 上。

下面重点来分析一下 prepareSubTree(…) 和 pushLayerUpdate(…)。

frameworks/base/libs/hwui/RenderNode.cpp

void RenderNode::prepareTreeImpl(TreeInfo& info, bool functorsNeedLayer) {
    info.damageAccumulator->pushTransform(this);

    if (info.mode == TreeInfo::MODE_FULL) {
        pushStagingPropertiesChanges(info);
    }
    uint32_t animatorDirtyMask = 0;
    if (CC_LIKELY(info.runAnimations)) {
        animatorDirtyMask = mAnimatorManager.animate(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) {
        pushStagingDisplayListChanges(info);
    }
    prepareSubTree(info, childFunctorsNeedLayer, mDisplayListData);
    pushLayerUpdate(info);

    info.damageAccumulator->popTransform();
}

prepareSubTree(…) 内部递归调用 prepareTreeImpl(…) 准备树信息。

frameworks/base/libs/hwui/RenderNode.cpp

void RenderNode::prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayListData* subtree) {
    if (subtree) {
        TextureCache& cache = Caches::getInstance().textureCache;
        info.out.hasFunctors |= subtree->functors.size();
        for (size_t i = 0; info.prepareTextures && i < subtree->bitmapResources.size(); i++) {
            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
                    // Recorded with non-rect clip, or canvas-rotated by parent
                    || op->mRecordedWithPotentialStencilClip;
            childNode->prepareTreeImpl(info, childFunctorsNeedLayer);
            info.damageAccumulator->popTransform();
        }
    }
}

pushLayerUpdate(…) 创建了图层。

frameworks/base/libs/hwui/RenderNode.cpp

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) {
        // 创建图层
        mLayer = LayerRenderer::createRenderLayer(info.renderState, getWidth(), getHeight());
        applyLayerPropertiesToLayer(info);
        damageSelf(info);
        transformUpdateNeeded = true;
    } else if (mLayer->layer.getWidth() != getWidth() || mLayer->layer.getHeight() != getHeight()) {
        // 图层尺寸变化
        if (!LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight())) {
            LayerRenderer::destroyLayer(mLayer);
            mLayer = nullptr;
        }
        damageSelf(info);
        transformUpdateNeeded = true;
    }

    SkRect dirty;
    info.damageAccumulator->peekAtDirty(&dirty);

    ......

    if (transformUpdateNeeded) {
        // 更新图层窗口中的变换,重置其原始 wrt 光源的位置
        Matrix4 windowTransform;
        info.damageAccumulator->computeCurrentTransform(&windowTransform);
        mLayer->setWindowTransform(windowTransform);
    }

    if (dirty.intersect(0, 0, getWidth(), getHeight())) {
        dirty.roundOut(&dirty);
        mLayer->updateDeferred(this, dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom);
    }
    // 如果因为我们可能在没有渲染器的先前准备过程中调用了 updateDeferred,则不在上述内容之内
    if (info.renderer && mLayer->deferredUpdateScheduled) {
        info.renderer->pushLayerUpdate(mLayer);
    }

    if (info.canvasContext) {
        // 可能存在需要考虑的预取层。
        // 告诉 CanvasContext 这层在树中,不应该被销毁。
        info.canvasContext->markLayerInUse(this);
    }
}

最后来分析调用 CanvasContext 类 draw() 方法绘制帧。mCanvas 指向 OpenGLRenderer 对象。

  1. 如果帧大小发生了变化,则更改 OpenGLRenderer 视图端口大小
  2. OpenGLRenderer 准备脏区大小
  3. 调用 OpenGLRenderer drawRenderNode(…) 绘制节点
  4. 调用 swapBuffers 方法绘制完成后交换 Buffer

frameworks/base/libs/hwui/renderthread/CanvasContext.cpp

void CanvasContext::draw() {
    LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE,
            "drawRenderNode called on a context with no canvas or surface!");

    SkRect dirty;
    mDamageAccumulator.finish(&dirty);

    mCurrentFrameInfo->markIssueDrawCommandsStart();

    EGLint width, height;
    mEglManager.beginFrame(mEglSurface, &width, &height);
    if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) {
        mCanvas->setViewport(width, height);
        dirty.setEmpty();
    } else if (!mBufferPreserved || mHaveNewSurface) {
        dirty.setEmpty();
    } else {
        if (!dirty.isEmpty() && !dirty.intersect(0, 0, width, height)) {
            ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?",
                    SK_RECT_ARGS(dirty), width, height);
            dirty.setEmpty();
        }
        profiler().unionDirty(&dirty);
    }

    if (!dirty.isEmpty()) {
        mCanvas->prepareDirty(dirty.fLeft, dirty.fTop,
                dirty.fRight, dirty.fBottom, mOpaque);
    } else {
        mCanvas->prepare(mOpaque);
    }

    Rect outBounds;
    mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds);

    profiler().draw(mCanvas);

    bool drew = mCanvas->finish();

    // 即使我们决定取消这个帧,从 jank 度量的角度来看,这个帧在此时已经被交换了
    mCurrentFrameInfo->markSwapBuffers();

    if (drew) {
        swapBuffers(dirty, width, height);
    }

    // TODO: Use a fence for real completion?
    mCurrentFrameInfo->markFrameCompleted();
    mJankTracker.addFrame(*mCurrentFrameInfo);
    mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo);
}

后面 OpenGL 接管后,才真正使用 GPU 进行了绘制。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TYYJ-洪伟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值