安卓RenderEffect原理

安卓的RenderEffect可以给View添加一些渲染特效,例如模糊效果等。

RenderEffect定义:(frameworks/base/graphics/java/android/graphics/RenderEffect.java)

/**
 * Intermediate rendering step used to render drawing commands with a corresponding
 * visual effect. A {@link RenderEffect} can be configured on a {@link RenderNode} through
 * {@link RenderNode#setRenderEffect(RenderEffect)} and will be applied when drawn through
 * {@link Canvas#drawRenderNode(RenderNode)}.
 * Additionally a {@link RenderEffect} can be applied to a View's backing RenderNode through
 * {@link android.view.View#setRenderEffect(RenderEffect)}
 */
public final class RenderEffect {
    private static class RenderEffectHolder {
        public static final NativeAllocationRegistry RENDER_EFFECT_REGISTRY =
                NativeAllocationRegistry.createMalloced(
                        RenderEffect.class.getClassLoader(), nativeGetFinalizer());
    }

    private final long mNativeRenderEffect;

    /* only constructed from static factory methods */
    private RenderEffect(long nativeRenderEffect) {
        mNativeRenderEffect = nativeRenderEffect;
        RenderEffectHolder.RENDER_EFFECT_REGISTRY.registerNativeAllocation(
                this, mNativeRenderEffect);
    }

    /**
     * Obtain the pointer to the underlying RenderEffect to be configured
     * on a RenderNode object via {@link RenderNode#setRenderEffect(RenderEffect)}
     */
    /* package */ long getNativeInstance() {
        return mNativeRenderEffect;
    }
}

RenderEffect只有一个成员变量mNativeRenderEffect,保存了一个C++层的指针。所以RenderEffect的实现原理基本在C++层。

接下来本文以给View添加模糊效果为例分析RenderEffect的原理。

给View添加模糊效果

安卓的RenderEffect可以给View添加模糊效果,添加方法:

RenderEffect renderEffect = RenderEffect.createBlurEffect(
        radiusX, radiusY, Shader.TileMode.CLAMP);
view.setRenderEffect(renderEffect);

首先创建了RenderEffect对象,然后调用View.setRenderEffect传入刚才创建的RenderEffect对象,就完成了给View添加模糊效果。

创建RenderEffect

RenderEffect.createBlurEffect方法:(frameworks/base/graphics/java/android/graphics/RenderEffect.java)

    /**
     * Create a {@link RenderEffect} that blurs the contents of the
     * {@link android.graphics.RenderNode} that this RenderEffect is installed on with the
     * specified radius along the x and y axis.
     * @param radiusX Radius of blur along the X axis
     * @param radiusY Radius of blur along the Y axis
     * @param edgeTreatment Policy for how to blur content near edges of the blur kernel
     */
    @NonNull
    public static RenderEffect createBlurEffect(
            float radiusX,
            float radiusY,
            @NonNull TileMode edgeTreatment
    ) {
        return new RenderEffect(
                nativeCreateBlurEffect(
                        radiusX,
                        radiusY,
                        0,
                        edgeTreatment.nativeInt
                )
            );
    }

主要是使用了nativeCreateBlurEffect方法创建C++对象:(frameworks/base/libs/hwui/jni/RenderEffect.cpp)

static jlong createBlurEffect(JNIEnv* env , jobject, jfloat radiusX,
        jfloat radiusY, jlong inputFilterHandle, jint edgeTreatment) {
    auto* inputImageFilter = reinterpret_cast<SkImageFilter*>(inputFilterHandle);
    sk_sp<SkImageFilter> blurFilter =
            SkImageFilters::Blur(
                    Blur::convertRadiusToSigma(radiusX),
                    Blur::convertRadiusToSigma(radiusY),
                    static_cast<SkTileMode>(edgeTreatment),
                    sk_ref_sp(inputImageFilter),
                    nullptr);
    return reinterpret_cast<jlong>(blurFilter.release());
}

这里主要是调用SkImageFilters::Blur创建了一个SkBlurImageFilter对象:(external/skia/src/effects/imagefilters/SkBlurImageFilter.cpp)

sk_sp<SkImageFilter> SkImageFilters::Blur(
        SkScalar sigmaX, SkScalar sigmaY, SkTileMode tileMode, sk_sp<SkImageFilter> input,
        const CropRect& cropRect) {
    if (!SkScalarsAreFinite(sigmaX, sigmaY) || sigmaX < 0.f || sigmaY < 0.f) {
        // Non-finite or negative sigmas are error conditions. We allow 0 sigma for X and/or Y
        // for 1D blurs; onFilterImage() will detect when no visible blurring would occur based on
        // the Context mapping.
        return nullptr;
    }

    // Temporarily allow tiling with no crop rect
    if (tileMode != SkTileMode::kDecal && !cropRect) {
        return sk_make_sp<SkBlurImageFilter>(SkSize{sigmaX, sigmaY}, tileMode, std::move(input));
    }

    // The 'tileMode' behavior is not well-defined if there is no crop. We only apply it if
    // there is a provided 'cropRect'.
    sk_sp<SkImageFilter> filter = std::move(input);
    if (tileMode != SkTileMode::kDecal && cropRect) {
        // Historically the input image was restricted to the cropRect when tiling was not
        // kDecal, so that the kernel evaluated the tiled edge conditions, while a kDecal crop
        // only affected the output.
        filter = SkImageFilters::Crop(*cropRect, tileMode, std::move(filter));
    }

    filter = sk_make_sp<SkBlurImageFilter>(SkSize{sigmaX, sigmaY}, std::move(filter));
    if (cropRect) {
        // But regardless of the tileMode, the output is always decal cropped
        filter = SkImageFilters::Crop(*cropRect, SkTileMode::kDecal, std::move(filter));
    }
    return filter;
}

小结

创建java的RenderEffect实际上是创建了一个C++的SkImageFilter对象。

给View设置RenderEffect

View.setRenderEffect方法:(frameworks/base/core/java/android/view/View.java)

    /**
     * Configure the {@link android.graphics.RenderEffect} to apply to this View.
     * This will apply a visual effect to the results of the View before it is drawn. For example if
     * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, Shader.TileMode)}
     * is provided, the contents will be drawn in a separate layer, then this layer will be blurred
     * when this View is drawn.
     * @param renderEffect to be applied to the View. Passing null clears the previously configured
     *                     {@link RenderEffect}
     */
    public void setRenderEffect(@Nullable RenderEffect renderEffect) {
        if (mRenderNode.setRenderEffect(renderEffect)) {
            invalidateViewProperty(true, true);
        }
    }

主要是调用了RenderNode.setRenderEffect方法:(frameworks/base/graphics/java/android/graphics/RenderNode.java)

    /**
     * Configure the {@link android.graphics.RenderEffect} to apply to this RenderNode. This
     * will apply a visual effect to the end result of the contents of this RenderNode before
     * it is drawn into the destination. For example if
     * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, Shader.TileMode)}
     * is provided, the contents will be drawn in a separate layer, then this layer will
     * be blurred when this RenderNode is drawn into the destination.
     * @param renderEffect to be applied to the RenderNode. Passing null clears all previously
     *          configured RenderEffects
     * @return True if the value changed, false if the new value was the same as the previous value.
     */
    public boolean setRenderEffect(@Nullable RenderEffect renderEffect) {
        return nSetRenderEffect(mNativeRenderNode,
                renderEffect != null ? renderEffect.getNativeInstance() : 0);
    }

这里调用了C++层的setRenderEffect方法:

static jboolean android_view_RenderNode_setRenderEffect(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
        jlong renderEffectPtr) {
    SkImageFilter* imageFilter = reinterpret_cast<SkImageFilter*>(renderEffectPtr);
    return SET_AND_DIRTY(mutateLayerProperties().setImageFilter, imageFilter, RenderNode::GENERIC);
}

RenderNode有一个类型为RenderProperties的成员变量mStagingProperties,

RenderProperties有一个类型为LayerProperties的成员变量mLayerProperties,

LayerProperties有一个类型为sk_sp的成员变量mImageFilter,

这里将imageFilter设置到了mImageFilter上面:(frameworks/base/libs/hwui/RenderProperties.cpp)

bool LayerProperties::setImageFilter(SkImageFilter* imageFilter) {
    if (mImageFilter.get() == imageFilter) return false;
    mImageFilter = sk_ref_sp(imageFilter);
    return true;
}

小结

给View设置RenderEffect实际上是将SkImageFilter设置到了View.mRenderNode.mStagingProperties.mLayerProperties.mImageFilter上面。

录制RenderNode

在UI线程录制绘制操作的时候,会遍历整个View树。父View通过drawRenderNode方法录制一个子View的renderNode为子节点:(frameworks/base/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp)

void SkiaRecordingCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) {
    // Record the child node. Drawable dtor will be invoked when mChildNodes deque is cleared.
    mDisplayList->mChildNodes.emplace_back(renderNode, asSkCanvas(), true, mCurrentBarrier);
    auto& renderNodeDrawable = mDisplayList->mChildNodes.back();
    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
        // Put Vulkan WebViews with non-rectangular clips in a HW layer
        renderNode->mutateStagingProperties().setClipMayBeComplex(mRecorder.isClipMayBeComplex());
    }

    // draw backdrop filter drawable if needed.
    if (renderNode->stagingProperties().layerProperties().getBackdropImageFilter()) {
        auto* backdropFilterDrawable =
                mDisplayList->allocateDrawable<BackdropFilterDrawable>(renderNode, asSkCanvas());
        drawDrawable(backdropFilterDrawable);
    }

    drawDrawable(&renderNodeDrawable);

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

这里mDisplayList->mChildNodes的类型为std::deque,通过emplace_back方法调用了RenderNodeDrawable的构造器,构建了mComposeLayer为true的子RenderNodeDrawable。

再通过drawDrawable方法去调用RecordingCanvas的onDrawDrawable方法,添加这个RenderNodeDrawable到DisplayListData中。

RenderNodeDrawable

RenderNodeDrawable的mComposeLayer成员变量定义如下:(frameworks/base/libs/hwui/pipeline/skia/RenderNodeDrawable.h)

    /**
     * If mRenderNode's layer type is RenderLayer this flag determines whether we
     * should draw into the contents of the layer or compose the existing contents
     * of the layer into the canvas.
     */
    const bool mComposeLayer;

mComposeLayer如果为false,则绘制这个layer的内容,如果为true,则合成已存在的内容到canvas上。

mComposeLayer在RenderNodeDrawable的构造器中赋值,并且默认值为true:(frameworks/base/libs/hwui/pipeline/skia/RenderNodeDrawable.h)

    /**
     * Creates a new RenderNodeDrawable backed by a render node.
     *
     * @param node that has to be drawn
     * @param canvas is a recording canvas used to extract its matrix
     * @param composeLayer if the node's layer type is RenderLayer this flag determines whether
     *      we should draw into the contents of the layer or compose the existing contents of the
     *      layer into the canvas.
     */
    explicit RenderNodeDrawable(RenderNode* node, SkCanvas* canvas, bool composeLayer = true,
                                bool inReorderingSection = false);

RenderNodeDrawable会随着mComposeLayer的值不同而在绘制时有不同的行为,所以每次构建RenderNodeDrawable的时候可以关注下其mComposeLayer的值是什么。

小结

父View录制绘制子View时,使用SkiaRecordingCanvas::drawRenderNode方法,将子View的renderNode装在了一个mComposeLayer为true的RenderNodeDrawable中,存到了SkiaRecordingCanvas.mDisplayList.mChildNodes里面。

创建layer的SkSurface

View的绘制操作在UI线程录制完成后,再切换到RenderThread线程,首先执行prepareTree操作,在prepareTree中将有RenderEffect的RenderNode视为layer,创建独立的SkSurface。

prepareTree调用栈

RenderNode::prepareTreeImpl

RenderNode::prepareTree

CanvasContext::prepareTree

DrawFrameTask::syncFrameState

DrawFrameTask::run

创建SkSurface

绘制前hwui会调用RenderNode的prepareTreeImpl方法:(frameworks/base/libs/hwui/RenderNode.cpp)

/**
 * Traverse down the the draw tree to prepare for a frame.
 *
 * MODE_FULL = UI Thread-driven (thus properties must be synced), otherwise RT driven
 *
 * While traversing down the tree, functorsNeedLayer flag is set to true if anything that uses the
 * stencil buffer may be needed. Views that use a functor to draw will be forced onto a layer.
 */
void RenderNode::prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer) {
    ...
    pushLayerUpdate(info);
    ...
}

调用了RenderNode的pushLayerUpdate方法:(frameworks/base/libs/hwui/RenderNode.cpp)

void RenderNode::pushLayerUpdate(TreeInfo& info) {
#ifdef __ANDROID__ // Layoutlib does not support CanvasContext and Layers
    LayerType layerType = properties().effectiveLayerType();
    // If we are not a layer OR we cannot be rendered (eg, view was detached)
    // we need to destroy any Layers we may have had previously
    if (CC_LIKELY(layerType != LayerType::RenderLayer) || CC_UNLIKELY(!isRenderable()) ||
        CC_UNLIKELY(properties().getWidth() == 0) || CC_UNLIKELY(properties().getHeight() == 0) ||
        CC_UNLIKELY(!properties().fitsOnLayer())) {
        if (CC_UNLIKELY(hasLayer())) {
            this->setLayerSurface(nullptr);
        }
        return;
    }

    if (info.canvasContext.createOrUpdateLayer(this, *info.damageAccumulator, info.errorHandler)) {
        damageSelf(info);
    }

    if (!hasLayer()) {
        return;
    }

    SkRect dirty;
    info.damageAccumulator->peekAtDirty(&dirty);
    info.layerUpdateQueue->enqueueLayerWithDamage(this, dirty);
    if (!dirty.isEmpty()) {
      mStretchMask.markDirty();
    }

    // There might be prefetched layers that need to be accounted for.
    // That might be us, so tell CanvasContext that this layer is in the
    // tree and should not be destroyed.
    info.canvasContext.markLayerInUse(this);
#endif
}

这里首先调用RenderProperties的effectiveLayerType方法判断这个RenderNode是否是一个layer:(frameworks/base/libs/hwui/RenderProperties.h)

    bool fitsOnLayer() const {
        const DeviceInfo* deviceInfo = DeviceInfo::get();
        return mPrimitiveFields.mWidth <= deviceInfo->maxTextureSize() &&
               mPrimitiveFields.mHeight <= deviceInfo->maxTextureSize();
    }

    bool promotedToLayer() const {
        return mLayerProperties.mType == LayerType::None && fitsOnLayer() &&
               (mComputedFields.mNeedLayerForFunctors || mLayerProperties.mImageFilter != nullptr ||
                mLayerProperties.getStretchEffect().requiresLayer() ||
                (!MathUtils::isZero(mPrimitiveFields.mAlpha) && mPrimitiveFields.mAlpha < 1 &&
                 mPrimitiveFields.mHasOverlappingRendering));
    }

    LayerType effectiveLayerType() const {
        return CC_UNLIKELY(promotedToLayer()) ? LayerType::RenderLayer : mLayerProperties.mType;
    }

由于我们之前设置了mImageFilter,mLayerProperties.mImageFilter != nullptr是true,导致promotedToLayer是true,从而导致effectiveLayerType方法返回LayerType::RenderLayer。

因此这个RenderNode被认为是layer,继续去调用CanvasContext的createOrUpdateLayer方法:(frameworks/base/libs/hwui/renderthread/CanvasContext.h)

    /**
     * Update or create a layer specific for the provided RenderNode. The layer
     * attached to the node will be specific to the RenderPipeline used by this
     * context
     *
     *  @return true if the layer has been created or updated
     */
    bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& dmgAccumulator,
                             ErrorHandler* errorHandler) {
        return mRenderPipeline->createOrUpdateLayer(node, dmgAccumulator, errorHandler);
    }

SkiaPipeline的createOrUpdateLayer方法:(frameworks/base/libs/hwui/pipeline/skia/SkiaPipeline.cpp)

bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator,
                                       ErrorHandler* errorHandler) {
    // compute the size of the surface (i.e. texture) to be allocated for this layer
    const int surfaceWidth = ceilf(node->getWidth() / float(LAYER_SIZE)) * LAYER_SIZE;
    const int surfaceHeight = ceilf(node->getHeight() / float(LAYER_SIZE)) * LAYER_SIZE;

    SkSurface* layer = node->getLayerSurface();
    if (!layer || layer->width() != surfaceWidth || layer->height() != surfaceHeight) {
        SkImageInfo info;
        info = SkImageInfo::Make(surfaceWidth, surfaceHeight, getSurfaceColorType(),
                                 kPremul_SkAlphaType, getSurfaceColorSpace());
        SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
        SkASSERT(mRenderThread.getGrContext() != nullptr);
        node->setLayerSurface(SkSurfaces::RenderTarget(mRenderThread.getGrContext(),
                                                       skgpu::Budgeted::kYes, info, 0,
                                                       this->getSurfaceOrigin(), &props));
        if (node->getLayerSurface()) {
            // update the transform in window of the layer to reset its origin wrt light source
            // position
            Matrix4 windowTransform;
            damageAccumulator.computeCurrentTransform(&windowTransform);
            node->getSkiaLayer()->inverseTransformInWindow.loadInverse(windowTransform);
        } else {
            String8 cachesOutput;
            mRenderThread.cacheManager().dumpMemoryUsage(cachesOutput,
                                                         &mRenderThread.renderState());
            ALOGE("%s", cachesOutput.c_str());
            if (errorHandler) {
                std::ostringstream err;
                err << "Unable to create layer for " << node->getName();
                const int maxTextureSize = DeviceInfo::get()->maxTextureSize();
                err << ", size " << info.width() << "x" << info.height() << " max size "
                    << maxTextureSize << " color type " << (int)info.colorType() << " has context "
                    << (int)(mRenderThread.getGrContext() != nullptr);
                errorHandler->onError(err.str());
            }
        }
        return true;
    }
    return false;
}

这里最终创建了这个RenderNode的LayerSurface。

如果一个View有是layer的RenderNode,这个View又被称为离屏View

LayerSurface创建完成后,在RenderNode的pushLayerUpdate方法后面还通过info.layerUpdateQueue->enqueueLayerWithDamage(this, dirty)将这个RenderNode添加到了layerUpdateQueue里面。

小结

RenderNode有ImageFilter之后,可以被看成是一个单独的layer,会在prepareTree阶段创建自己的SkSurface,并保存在layerUpdateQueue中。

绘制RenderNode

在RenderThread线程执行完prepareTree之后,就要开始绘制RenderNode了。

绘制调用栈

SkiaPipeline::renderFrame

SkiaVulkanPipeline::draw

CanvasContext::draw

DrawFrameTask::run

绘制layer

在SkiaPipeline的renderFrame方法:(frameworks/base/libs/hwui/pipeline/skia/SkiaPipeline.cpp)

void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
                               const std::vector<sp<RenderNode>>& nodes, bool opaque,
                               const Rect& contentDrawBounds, sk_sp<SkSurface> surface,
                               const SkMatrix& preTransform) {
    bool previousSkpEnabled = Properties::skpCaptureEnabled;
    if (mPictureCapturedCallback) {
        Properties::skpCaptureEnabled = true;
    }

    // Initialize the canvas for the current frame, that might be a recording canvas if SKP
    // capture is enabled.
    SkCanvas* canvas = tryCapture(surface.get(), nodes[0].get(), layers);

    // draw all layers up front
    renderLayersImpl(layers, opaque);

    renderFrameImpl(clip, nodes, opaque, contentDrawBounds, canvas, preTransform);

    endCapture(surface.get());

    if (CC_UNLIKELY(Properties::debugOverdraw)) {
        renderOverdraw(clip, nodes, contentDrawBounds, surface, preTransform);
    }

    Properties::skpCaptureEnabled = previousSkpEnabled;
}

首先调用了SkiaPipeline的renderLayersImpl方法:(frameworks/base/libs/hwui/pipeline/skia/SkiaPipeline.cpp)

void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) {
    sk_sp<GrDirectContext> cachedContext;

    // Render all layers that need to be updated, in order.
    for (size_t i = 0; i < layers.entries().size(); i++) {
        RenderNode* layerNode = layers.entries()[i].renderNode.get();
        // only schedule repaint if node still on layer - possible it may have been
        // removed during a dropped frame, but layers may still remain scheduled so
        // as not to lose info on what portion is damaged
        if (CC_UNLIKELY(layerNode->getLayerSurface() == nullptr)) {
            continue;
        }
        SkASSERT(layerNode->getLayerSurface());
        SkiaDisplayList* displayList = layerNode->getDisplayList().asSkiaDl();
        if (!displayList || displayList->isEmpty()) {
            ALOGE("%p drawLayers(%s) : missing drawable", layerNode, layerNode->getName());
            return;
        }

        const Rect& layerDamage = layers.entries()[i].damage;

        SkCanvas* layerCanvas = layerNode->getLayerSurface()->getCanvas();

        int saveCount = layerCanvas->save();
        SkASSERT(saveCount == 1);

        layerCanvas->androidFramework_setDeviceClipRestriction(layerDamage.toSkIRect());

        // TODO: put localized light center calculation and storage to a drawable related code.
        // It does not seem right to store something localized in a global state
        // fix here and in recordLayers
        const Vector3 savedLightCenter(LightingInfo::getLightCenterRaw());
        Vector3 transformedLightCenter(savedLightCenter);
        // map current light center into RenderNode's coordinate space
        layerNode->getSkiaLayer()->inverseTransformInWindow.mapPoint3d(transformedLightCenter);
        LightingInfo::setLightCenterRaw(transformedLightCenter);

        const RenderProperties& properties = layerNode->properties();
        const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight());
        if (properties.getClipToBounds() && layerCanvas->quickReject(bounds)) {
            return;
        }

        ATRACE_FORMAT("drawLayer [%s] %.1f x %.1f", layerNode->getName(), bounds.width(),
                      bounds.height());

        layerNode->getSkiaLayer()->hasRenderedSinceRepaint = false;
        layerCanvas->clear(SK_ColorTRANSPARENT);

        RenderNodeDrawable root(layerNode, layerCanvas, false);
        root.forceDraw(layerCanvas);
        layerCanvas->restoreToCount(saveCount);

        LightingInfo::setLightCenterRaw(savedLightCenter);

        // cache the current context so that we can defer flushing it until
        // either all the layers have been rendered or the context changes
        GrDirectContext* currentContext =
            GrAsDirectContext(layerNode->getLayerSurface()->getCanvas()->recordingContext());
        if (cachedContext.get() != currentContext) {
            if (cachedContext.get()) {
                ATRACE_NAME("flush layers (context changed)");
                cachedContext->flushAndSubmit();
            }
            cachedContext.reset(SkSafeRef(currentContext));
        }
    }

    if (cachedContext.get()) {
        ATRACE_NAME("flush layers");
        cachedContext->flushAndSubmit();
    }
}

这里遍历了所有的layer,使用mComposeLayer为false的RenderNodeDrawable的forceDraw方法,将这个layer的内容绘制到了之前创建的SkSurface上。

合成layer

SkiaPipeline::renderFrame后面还调用了SkiaPipeline::renderFrameImpl方法:(frameworks/base/libs/hwui/pipeline/skia/SkiaPipeline.cpp)

void SkiaPipeline::renderFrameImpl(const SkRect& clip,
                                   const std::vector<sp<RenderNode>>& nodes, bool opaque,
                                   const Rect& contentDrawBounds, SkCanvas* canvas,
                                   const SkMatrix& preTransform) {
    SkAutoCanvasRestore saver(canvas, true);
    auto clipRestriction = preTransform.mapRect(clip).roundOut();
    if (CC_UNLIKELY(isCapturingSkp())) {
        canvas->drawAnnotation(SkRect::Make(clipRestriction), "AndroidDeviceClipRestriction",
            nullptr);
    } else {
        // clip drawing to dirty region only when not recording SKP files (which should contain all
        // draw ops on every frame)
        canvas->androidFramework_setDeviceClipRestriction(clipRestriction);
    }
    canvas->concat(preTransform);

    if (!opaque) {
        canvas->clear(SK_ColorTRANSPARENT);
    }

    if (1 == nodes.size()) {
        if (!nodes[0]->nothingToDraw()) {
            RenderNodeDrawable root(nodes[0].get(), canvas);
            root.draw(canvas);
        }
    } else if (0 == nodes.size()) {
        // nothing to draw
    } else {
        ......
    }
}

这里对RootRenderNode,使用mComposeLayer为true的RenderNodeDrawable的forceDraw方法(RenderNodeDrawable::draw还是调用了RenderNodeDrawable::forceDraw),将所有内容合成绘制到了这个窗口的Surface上。

小结

hwui绘制时,先把每个layer的所有displayList绘制到之前创建的SkCanvas上,最后再一起合成绘制到最终的Surface上面。

绘制模糊效果

在合成layer的时候调用了RenderNodeDrawable的forceDraw方法:(frameworks/base/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp)

void RenderNodeDrawable::forceDraw(SkCanvas* canvas) const {
    RenderNode* renderNode = mRenderNode.get();
    MarkDraw _marker{*canvas, *renderNode};

    // We only respect the nothingToDraw check when we are composing a layer. This
    // ensures that we paint the layer even if it is not currently visible in the
    // event that the properties change and it becomes visible.
    if ((mProjectedDisplayList == nullptr && !renderNode->isRenderable()) ||
        (renderNode->nothingToDraw() && mComposeLayer)) {
        return;
    }

    SkiaDisplayList* displayList = renderNode->getDisplayList().asSkiaDl();

    SkAutoCanvasRestore acr(canvas, true);
    const RenderProperties& properties = this->getNodeProperties();
    // pass this outline to the children that may clip backward projected nodes
    displayList->mProjectedOutline =
            displayList->containsProjectionReceiver() ? &properties.getOutline() : nullptr;
    if (!properties.getProjectBackwards()) {
        drawContent(canvas);
        if (mProjectedDisplayList) {
            acr.restore();  // draw projected children using parent matrix
            LOG_ALWAYS_FATAL_IF(!mProjectedDisplayList->mProjectedOutline);
            const bool shouldClip = mProjectedDisplayList->mProjectedOutline->getPath();
            SkAutoCanvasRestore acr2(canvas, shouldClip);
            canvas->setMatrix(mProjectedDisplayList->mParentMatrix);
            if (shouldClip) {
                canvas->clipPath(*mProjectedDisplayList->mProjectedOutline->getPath());
            }
            drawBackwardsProjectedNodes(canvas, *mProjectedDisplayList);
        }
    }
    displayList->mProjectedOutline = nullptr;
}

调用了RenderNodeDrawable的drawContent方法:(frameworks/base/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp)

void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
    RenderNode* renderNode = mRenderNode.get();
    float alphaMultiplier = 1.0f;
    const RenderProperties& properties = renderNode->properties();

    // If we are drawing the contents of layer, we don't want to apply any of
    // the RenderNode's properties during this pass. Those will all be applied
    // when the layer is composited.
    if (mComposeLayer) {
        setViewProperties(properties, canvas, &alphaMultiplier);
    }
    SkiaDisplayList* displayList = mRenderNode->getDisplayList().asSkiaDl();
    displayList->mParentMatrix = canvas->getTotalMatrix();

    // TODO should we let the bound of the drawable do this for us?
    const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight());
    bool quickRejected = properties.getClipToBounds() && canvas->quickReject(bounds);
    if (!quickRejected) {
        auto clipBounds = canvas->getLocalClipBounds();
        SkIRect srcBounds = SkIRect::MakeWH(bounds.width(), bounds.height());
        SkIPoint offset = SkIPoint::Make(0.0f, 0.0f);
        SkiaDisplayList* displayList = renderNode->getDisplayList().asSkiaDl();
        const LayerProperties& layerProperties = properties.layerProperties();
        // composing a hardware layer
        if (renderNode->getLayerSurface() && mComposeLayer) { // 如果这个RenderNode是layer
            ...
            if (!Properties::enableRenderEffectCache) {
                ...
            } else { // 使用imageFilter生成snapshotImage
                const auto snapshotResult = renderNode->updateSnapshotIfRequired(
                        recordingContext, layerProperties.getImageFilter(), clipBounds.roundOut());
                snapshotImage = snapshotResult->snapshot;
                srcBounds = snapshotResult->outSubset;
                offset = snapshotResult->outOffset;
            }
            ...
            const StretchEffect& stretch = properties.layerProperties().getStretchEffect();
            if (stretch.isEmpty() ||
                Properties::getStretchEffectBehavior() == StretchEffectBehavior::UniformScale) {
                ...
                // 将snapshotImage绘制到canvas上
                canvas->drawImageRect(snapshotImage, SkRect::Make(srcBounds),
                                      SkRect::Make(dstBounds), sampling, &paint,
                                      SkCanvas::kStrict_SrcRectConstraint);
            } else {
                ...
            }
            ...
        } else { // 如果这个RenderNode不是layer,直接将displayList绘制到canvas上
            if (alphaMultiplier < 1.0f) {
                // Non-layer draw for a view with getHasOverlappingRendering=false, will apply
                // the alpha to the paint of each nested draw.
                AlphaFilterCanvas alphaCanvas(canvas, alphaMultiplier);
                displayList->draw(&alphaCanvas);
            } else {
                displayList->draw(canvas);
            }
        }
    }
}

最终合成时,作为layer的RenderNode的内容首先经过imageFilter绘制到snapshotImage上,然后通过canvas->drawImageRect(snapshotImage, …)方法绘制到最终的Surface上面。

非layer的RenderNode则直接将displayList绘制到canvas上,displayList中可能还会有RenderNodeDrawable,则会递归调用这个RenderNodeDrawable::drawContent方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

SSSxCCC

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

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

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

打赏作者

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

抵扣说明:

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

余额充值