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
This commit is contained in:
@@ -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(); });
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -59,8 +59,7 @@ void StretchMask::draw(GrRecordingContext* context,
|
||||
}
|
||||
|
||||
sk_sp<SkImage> maskImage = mMaskSurface->makeImageSnapshot();
|
||||
sk_sp<SkShader> maskStretchShader = stretch.getShader(
|
||||
width, height, maskImage);
|
||||
sk_sp<SkShader> maskStretchShader = stretch.getShader(width, height, maskImage, nullptr);
|
||||
|
||||
SkPaint maskPaint;
|
||||
maskPaint.setShader(maskStretchShader);
|
||||
|
||||
Reference in New Issue
Block a user