Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6aff4814 authored by Nader Jawad's avatar Nader Jawad
Browse files

Refactor SkImageFilter usage to cache results.

If an SkImageFilter is used, create an image
snapshot with the filter applied to avoid
re-computing it on each draw invocation

Bug: 188450217
Test: Re-ran CTS tests

Change-Id: Ib790669e14ada9d4ebbfac958d699e2b5242f2d7
parent 466698f9
Loading
Loading
Loading
Loading
+48 −2
Original line number Diff line number Diff line
@@ -306,11 +306,17 @@ void RenderNode::pushStagingPropertiesChanges(TreeInfo& info) {
        info.damageAccumulator->popTransform();
        syncProperties();

        const StretchEffect& stagingStretch =
            mProperties.layerProperties().getStretchEffect();
        auto& layerProperties = mProperties.layerProperties();
        const StretchEffect& stagingStretch = layerProperties.getStretchEffect();
        if (stagingStretch.isEmpty()) {
            mStretchMask.clear();
        }

        if (layerProperties.getImageFilter() == nullptr) {
            mSnapshotResult.snapshot = nullptr;
            mTargetImageFilter = nullptr;
        }

        // We could try to be clever and only re-damage if the matrix changed.
        // However, we don't need to worry about that. The cost of over-damaging
        // here is only going to be a single additional map rect of this node
@@ -321,6 +327,44 @@ void RenderNode::pushStagingPropertiesChanges(TreeInfo& info) {
    }
}

std::optional<RenderNode::SnapshotResult> RenderNode::updateSnapshotIfRequired(
    GrRecordingContext* context,
    const SkImageFilter* imageFilter,
    const SkIRect& clipBounds
) {
    auto* layerSurface = getLayerSurface();
    if (layerSurface == nullptr) {
        return std::nullopt;
    }

    sk_sp<SkImage> snapshot = layerSurface->makeImageSnapshot();
    const auto subset = SkIRect::MakeWH(properties().getWidth(),
                                        properties().getHeight());
    // If we don't have an ImageFilter just return the snapshot
    if (imageFilter == nullptr) {
        mSnapshotResult.snapshot = snapshot;
        mSnapshotResult.outSubset = subset;
        mSnapshotResult.outOffset = SkIPoint::Make(0.0f, 0.0f);
        mImageFilterClipBounds = clipBounds;
        mTargetImageFilter = nullptr;
    } else if (mSnapshotResult.snapshot == nullptr ||
        imageFilter != mTargetImageFilter.get() ||
        mImageFilterClipBounds != clipBounds) {
        // Otherwise create a new snapshot with the given filter and snapshot
        mSnapshotResult.snapshot =
                snapshot->makeWithFilter(context,
                                         imageFilter,
                                         subset,
                                         clipBounds,
                                         &mSnapshotResult.outSubset,
                                         &mSnapshotResult.outOffset);
        mTargetImageFilter = sk_ref_sp(imageFilter);
        mImageFilterClipBounds = clipBounds;
    }

    return mSnapshotResult;
}

void RenderNode::syncDisplayList(TreeObserver& observer, TreeInfo* info) {
    // Make sure we inc first so that we don't fluctuate between 0 and 1,
    // which would thrash the layer cache
@@ -411,6 +455,8 @@ void RenderNode::destroyLayers() {
    if (hasLayer()) {
        this->setLayerSurface(nullptr);
    }
    mSnapshotResult.snapshot = nullptr;
    mTargetImageFilter = nullptr;
    if (mDisplayList) {
        mDisplayList.updateChildren([](RenderNode* child) { child->destroyLayers(); });
    }
+26 −0
Original line number Diff line number Diff line
@@ -345,6 +345,16 @@ public:
        return mSkiaLayer.get() ? mSkiaLayer->layerSurface.get() : nullptr;
    }

    struct SnapshotResult {
        sk_sp<SkImage> snapshot;
        SkIRect outSubset;
        SkIPoint outOffset;
    };

    std::optional<SnapshotResult> updateSnapshotIfRequired(GrRecordingContext* context,
                                            const SkImageFilter* imageFilter,
                                            const SkIRect& clipBounds);

    skiapipeline::SkiaLayer* getSkiaLayer() const { return mSkiaLayer.get(); }

    /**
@@ -375,6 +385,22 @@ private:
     */
    std::unique_ptr<skiapipeline::SkiaLayer> mSkiaLayer;

    /**
     * SkImageFilter used to create the mSnapshotResult
     */
    sk_sp<SkImageFilter> mTargetImageFilter;

    /**
     * Clip bounds used to create the mSnapshotResult
     */
    SkIRect mImageFilterClipBounds;

    /**
     * Result of the most recent snapshot with additional metadata used to
     * determine how to draw the contents
     */
    SnapshotResult mSnapshotResult;

    struct ClippedOutlineCache {
        // keys
        uint32_t outlineID = 0;
+5 −3
Original line number Diff line number Diff line
@@ -188,7 +188,8 @@ static const float ZERO = 0.f;
static const float INTERPOLATION_STRENGTH_VALUE = 0.7f;

sk_sp<SkShader> StretchEffect::getShader(float width, float height,
                                         const sk_sp<SkImage>& snapshotImage) const {
                                         const sk_sp<SkImage>& snapshotImage,
                                         const SkMatrix* matrix) const {
    if (isEmpty()) {
        return nullptr;
    }
@@ -206,8 +207,9 @@ sk_sp<SkShader> StretchEffect::getShader(float width, float height,
        mBuilder = std::make_unique<SkRuntimeShaderBuilder>(getStretchEffect());
    }

    mBuilder->child("uContentTexture") = snapshotImage->makeShader(
            SkTileMode::kClamp, SkTileMode::kClamp, SkSamplingOptions(SkFilterMode::kLinear));
    mBuilder->child("uContentTexture") =
            snapshotImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
                                      SkSamplingOptions(SkFilterMode::kLinear), matrix);
    mBuilder->uniform("uInterpolationStrength").set(&INTERPOLATION_STRENGTH_VALUE, 1);
    mBuilder->uniform("uStretchAffectedDistX").set(&width, 1);
    mBuilder->uniform("uStretchAffectedDistY").set(&height, 1);
+2 −2
Original line number Diff line number Diff line
@@ -93,8 +93,8 @@ public:
     */
    float computeStretchedPositionY(float normalizedY) const;

    sk_sp<SkShader> getShader(float width, float height,
                              const sk_sp<SkImage>& snapshotImage) const;
    sk_sp<SkShader> getShader(float width, float height, const sk_sp<SkImage>& snapshotImage,
                              const SkMatrix* matrix) const;

    float maxStretchAmountX = 0;
    float maxStretchAmountY = 0;
+34 −13
Original line number Diff line number Diff line
@@ -171,17 +171,14 @@ void RenderNodeDrawable::forceDraw(SkCanvas* canvas) const {
    displayList->mProjectedOutline = nullptr;
}

static bool layerNeedsPaint(const sk_sp<SkImage>& snapshotImage, const LayerProperties& properties,
                            float alphaMultiplier, SkPaint* paint) {
static bool layerNeedsPaint(const LayerProperties& properties, float alphaMultiplier,
                            SkPaint* paint) {
    if (alphaMultiplier < 1.0f || properties.alpha() < 255 ||
        properties.xferMode() != SkBlendMode::kSrcOver || properties.getColorFilter() != nullptr ||
        properties.getImageFilter() != nullptr || properties.getStretchEffect().requiresLayer()) {
        properties.getStretchEffect().requiresLayer()) {
        paint->setAlpha(properties.alpha() * alphaMultiplier);
        paint->setBlendMode(properties.xferMode());
        paint->setColorFilter(sk_ref_sp(properties.getColorFilter()));

        sk_sp<SkImageFilter> imageFilter = sk_ref_sp(properties.getImageFilter());
        paint->setImageFilter(std::move(imageFilter));
        return true;
    }
    return false;
@@ -223,6 +220,9 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
    // 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);
    auto clipBounds = canvas->getLocalClipBounds();
    SkIRect srcBounds = SkIRect::MakeWH(bounds.width(), bounds.height());
    SkIPoint offset = SkIPoint::Make(0.0f, 0.0f);
    if (!quickRejected) {
        SkiaDisplayList* displayList = renderNode->getDisplayList().asSkiaDl();
        const LayerProperties& layerProperties = properties.layerProperties();
@@ -230,8 +230,19 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
        if (renderNode->getLayerSurface() && mComposeLayer) {
            SkASSERT(properties.effectiveLayerType() == LayerType::RenderLayer);
            SkPaint paint;
            sk_sp<SkImage> snapshotImage = renderNode->getLayerSurface()->makeImageSnapshot();
            layerNeedsPaint(snapshotImage, layerProperties, alphaMultiplier, &paint);
            layerNeedsPaint(layerProperties, alphaMultiplier, &paint);
            const auto snapshotResult = renderNode->updateSnapshotIfRequired(
                canvas->recordingContext(),
                layerProperties.getImageFilter(),
                clipBounds.roundOut()
            );
            sk_sp<SkImage> snapshotImage = snapshotResult->snapshot;
            srcBounds = snapshotResult->outSubset;
            offset = snapshotResult->outOffset;
            const auto dstBounds = SkIRect::MakeXYWH(offset.x(),
                                                     offset.y(),
                                                     srcBounds.width(),
                                                     srcBounds.height());
            SkSamplingOptions sampling(SkFilterMode::kLinear);

            // surfaces for layers are created on LAYER_SIZE boundaries (which are >= layer size) so
@@ -257,7 +268,8 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
                    TransformCanvas transformCanvas(canvas, SkBlendMode::kClear);
                    displayList->draw(&transformCanvas);
                }
                canvas->drawImageRect(snapshotImage, bounds, bounds, sampling, &paint,
                canvas->drawImageRect(snapshotImage, SkRect::Make(srcBounds),
                                      SkRect::Make(dstBounds), sampling, &paint,
                                      SkCanvas::kStrict_SrcRectConstraint);
            } else {
                // If we do have stretch effects and have hole punches,
@@ -265,6 +277,16 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
                // get the corresponding hole punches.
                // Then apply the stretch to the mask and draw the mask to
                // the destination
                // Also if the stretchy container has an ImageFilter applied
                // to it (i.e. blur) we need to take into account the offset
                // that will be generated with this result. Ex blurs will "grow"
                // the source image by the blur radius so we need to translate
                // the shader by the same amount to render in the same location
                SkMatrix matrix;
                matrix.setTranslate(
                    offset.x() - srcBounds.left(),
                    offset.y() - srcBounds.top()
                );
                if (renderNode->hasHolePunches()) {
                    GrRecordingContext* context = canvas->recordingContext();
                    StretchMask& stretchMask = renderNode->getStretchMask();
@@ -275,11 +297,10 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
                                     canvas);
                }

                sk_sp<SkShader> stretchShader = stretch.getShader(bounds.width(),
                                                                  bounds.height(),
                                                                  snapshotImage);
                sk_sp<SkShader> stretchShader =
                        stretch.getShader(bounds.width(), bounds.height(), snapshotImage, &matrix);
                paint.setShader(stretchShader);
                canvas->drawRect(bounds, paint);
                canvas->drawRect(SkRect::Make(dstBounds), paint);
            }

            if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) {
Loading