NSyncAndDrawFrame (…) is not analyzed in the Android source graphics system hardware renderer drawing section. Synchronizing and drawing frames continues in this section.

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. Synchronize and draw frames
        intsyncResult = nSyncAndDrawFrame(mNativeProxy, frameInfo, frameInfo.length); . }...private static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size); . }Copy the code
  1. ProxyPtr is strongly converted to RenderProxy* pointer
  2. Copy frameInfo jlongArray to buffer (proxy->frameInfo())
  3. Call the RenderProxy syncAndDrawFrame() method

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(a); }Copy the code

The drawFrame() method of DrawFrameTask is called inside syncAndDrawFrame().

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

int RenderProxy::syncAndDrawFrame(a) {
    return mDrawFrameTask.drawFrame(a); }Copy the code

The main call here is postAndWait(), which, as you might guess from the name of the method, pushes the task to a queue and waits.

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

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

    mSyncResult = kSync_OK;
    mSyncQueued = systemTime(CLOCK_MONOTONIC);
    postAndWait(a);return mSyncResult;
}
Copy the code

The RenderThread queue was pushed.

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

void DrawFrameTask::postAndWait(a) {
    AutoMutex _lock(mLock);
    mRenderThread->queue(this);
    mSignal.wait(mLock);
}
Copy the code
  1. Push RenderTask to the TaskQueue TaskQueue
  2. If the task runs at a time less than the next wake-up point, call the Looper wake() method to wake it up

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();
    }
}
Copy the code

The method of joining the queue is very simple, according to the running time of tasks in the queue, from the smallest to the largest.

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; }}Copy the code

Suppose Looper had pollOnce(…) in RenderThread () before. Methodologically, RenderTask will wake up immediately after joining the team. NextTask (…) is then called. Fetch the render task from the queue. It then calls its run() method to execute.

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

bool RenderThread::threadLoop(a) {...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(a);// task may have deleted itself, do not reference it again}... }return false;
}
Copy the code
  1. Call syncFrameState (…). Sync frame state
  2. Call the draw() method of the CanvasContext class to draw the frame

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

void DrawFrameTask::run(a) {
    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(a); }if (CC_LIKELY(canDrawThisFrame)) {
        context->draw(a); }if(! canUnblockUiThread) {unblockUiThread();
    }
}
Copy the code
  1. Call the makeCurrent() method of the CanvasContext class to create the EGL rendering context, etc
  2. Processing layer update
  3. The render information is synchronized by calling prepareTree() of RenderNode from prepareTree() of CanvasContext. The final output is a TreeInfo structure, where prepareTextures indicates whether the texture was uploaded successfully. If false, the texture cache space is used up. This prevents the rendering thread from competing with the main thread for resources used during rendering

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

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

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

    // This is after prepareTree, so that any pending operations (RenderNode Tree state, pre-fetched layers, etc.) will be refreshed.
    if (CC_UNLIKELY(! mContext->hasSurface())) {
        mSyncResult |= kSync_LostSurfaceRewardIfFound;
    }

    if (info.out.hasAnimations) {
        if(info.out.requiresUiRedraw) { mSyncResult |= kSync_UIRedrawRequired; }}// If prepareTextures is false, we run out of texture cache space
    return info.prepareTextures;
}
Copy the code

This method actually calls the EglManager class makeCurrent(…) Get the real work done.

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

void CanvasContext::makeCurrent(a) {
    // 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); }}Copy the code

Return true if the current Surface has changed; If it is already the current Surface, return false. Main call eglMakeCurrent(…) EglMakeCurrent (…) The function is in the OpengL library.

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

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

    if (surface == EGL_NO_SURFACE) {
        // Make sure we always have a valid Surface and context
        surface = mPBufferSurface;
    }
    if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) {
        if (errOut) {
            *errOut = eglGetError(a);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;
}
Copy the code

Process layer updates.

  1. Call the DeferredLaydater class apply()
  2. Gets the backingLayer() of the DeferredLaydater, which is the Layer. To delay processing, call OpenGLRenderer class pushLayerUpdate(…). The Vector< sp > method adds layers to the container (Vector< sp >, which represents the list of layers to update at the beginning of a frame)

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

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

The DeferredLayerUpdater class represents a container for holding properties that should be set to layers at the beginning of the rendering process.

Apply refers to the layer to which certain attributes are applied. The SurfaceTexture class is also called to attachToContext(…). The TextureId method attaches the layer’s TextureId to the context; Updated the Texture Image; Transform is loaded.

frameworks/base/libs/hwui/DeferredLayerUpdater.cpp

bool DeferredLayerUpdater::apply(a) {
    bool success = true;
    // These attributes apply equally to both layer types
    mLayer->setColorFilter(mColorFilter);
    mLayer->setAlpha(mAlpha, mMode);

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

Further call the RenderNode class prepareTree(…) Prepare tree information (TreeInfo). MRootRenderNode is a RenderNode type.

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

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

    // If the previous frame is discarded, we don't need to keep it, so just continue with the structure of the previous frame
    if (!wasSkipped(mCurrentFrameInfo)) {
        mCurrentFrameInfo = &mFrames.next(a); } mCurrentFrameInfo->importUiThreadInfo(uiFrameInfo);
    mCurrentFrameInfo->set(FrameInfoIndex::SyncQueued) = syncQueued;
    mCurrentFrameInfo->markSyncStart(a); info.damageAccumulator = &mDamageAccumulator; info.renderer = mCanvas; info.canvasContext =this;

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

In the prepareTree() method of the RenderNode class, it is implemented by prepareTreeImpl(…). Methods.

frameworks/base/libs/hwui/RenderNode.cpp

void RenderNode::prepareTree(TreeInfo& info) {
    ATRACE_CALL(a);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);
}
Copy the code

Traverse the draw tree to prepare the frame.

MODE_FULL = UI thread driver (therefore attributes must be synchronized), otherwise RT driver.

When traversing the tree, the functorsNeedLayer flag is set to true if any of the contents of the template buffer may be needed. Views drawn using functor will be forced onto the Layer.

PrepareSubTree (…) And 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(a); }else if(mDisplayListData) { willHaveFunctor = ! mDisplayListData->functors.isEmpty(a); }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(a); }Copy the code

PrepareSubTree (…). Internal recursive call prepareTreeImpl(…) Prepare tree information.

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(a);for (size_t i = 0; info.prepareTextures && i < subtree->bitmapResources.size(a); i++) { info.prepareTextures = cache.prefetchAndMarkInUse(
                    info.canvasContext, subtree->bitmapResources[i]);
        }
        for (size_t i = 0; i < subtree->children().size(a); 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(a); }}}Copy the code

PushLayerUpdate (…). Create a layer.

frameworks/base/libs/hwui/RenderNode.cpp

void RenderNode::pushLayerUpdate(TreeInfo& info) {
    LayerType layerType = properties().effectiveLayerType(a);// If we are not a layer, or cannot render (for example, the view is separated), we need to destroy all layers we might have previously
    if (CC_LIKELY(layerType ! = LayerType::RenderLayer) ||CC_UNLIKELY(!isRenderable())) {
        if (CC_UNLIKELY(mLayer)) {
            LayerRenderer::destroyLayer(mLayer);
            mLayer = nullptr;
        }
        return;
    }

    bool transformUpdateNeeded = false;
    if(! mLayer) {// Create a layer
        mLayer = LayerRenderer::createRenderLayer(info.renderState, getWidth(), getHeight());
        applyLayerPropertiesToLayer(info);
        damageSelf(info);
        transformUpdateNeeded = true;
    } else if (mLayer->layer.getWidth() != getWidth() || mLayer->layer.getHeight() != getHeight()) {
        // Layer size changes
        if(! LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight())) {
            LayerRenderer::destroyLayer(mLayer);
            mLayer = nullptr;
        }
        damageSelf(info);
        transformUpdateNeeded = true;
    }

    SkRect dirty;
    info.damageAccumulator->peekAtDirty(&dirty); .if (transformUpdateNeeded) {
        // Update the transform in the Layers window to reset the position of its original WRT light source
        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);
    }
    Not if we called updateDeferred because we might have done a previous prep without a renderer
    if (info.renderer && mLayer->deferredUpdateScheduled) {
        info.renderer->pushLayerUpdate(mLayer);
    }

    if (info.canvasContext) {
        // There may be prefetch layers that need to be considered.
        // Tell CanvasContext that this layer is in the tree and should not be destroyed.
        info.canvasContext->markLayerInUse(this); }}Copy the code

Finally, draw frames by calling the Draw () method of the CanvasContext class. MCanvas points to an OpenGLRenderer object.

  1. If the frame size changes, change the OpenGLRenderer view port size
  2. OpenGLRenderer ready dirty area size
  3. Call OpenGLRenderer drawRenderNode (…). Draw the node
  4. Call the swapBuffers method to swapBuffers after drawing

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

void CanvasContext::draw(a) {
    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(a); EGLint width, height; mEglManager.beginFrame(mEglSurface, &width, &height);
    if(width ! = mCanvas->getViewportWidth() || height ! = mCanvas->getViewportHeight()) {
        mCanvas->setViewport(width, height);
        dirty.setEmpty(a); }else if(! mBufferPreserved || mHaveNewSurface) { dirty.setEmpty(a); }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(a); }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(a);// Even if we decide to cancel the frame, from jank's metric point of view, the frame has already been swapped at this point
    mCurrentFrameInfo->markSwapBuffers(a);if (drew) {
        swapBuffers(dirty, width, height);
    }

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

After OpenGL took over, it really used GPU to draw.