Add non-srcover fallback for drawImageSet
Bug: skia:
Change-Id: Ie0b950f0b8bf0986e8419c49594a7b85c42e0295
Reviewed-on: https://skia-review.googlesource.com/c/184921
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/gm/drawimageset.cpp b/gm/drawimageset.cpp
index 6afd360..f993a7c 100644
--- a/gm/drawimageset.cpp
+++ b/gm/drawimageset.cpp
@@ -148,14 +148,14 @@
canvas->experimental_DrawImageSetV1(fSet, kM * kN, fm, SkBlendMode::kSrcOver);
canvas->restore();
}
- // A more exotic case with an unusual blend mode, all aa flags set, and alpha,
+ // A more exotic case with an unusual blend mode, mixed aa flags set, and alpha,
// subsets the image
SkCanvas::ImageSetEntry entry;
entry.fSrcRect = SkRect::MakeWH(kTileW, kTileH).makeInset(kTileW / 4.f, kTileH / 4.f);
entry.fDstRect = SkRect::MakeWH(2 * kTileW, 2 * kTileH).makeOffset(d / 4, 2 * d);
entry.fImage = fSet[0].fImage;
entry.fAlpha = 0.7f;
- entry.fAAFlags = SkCanvas::kAll_QuadAAFlags;
+ entry.fAAFlags = SkCanvas::kLeft_QuadAAFlag | SkCanvas::kTop_QuadAAFlag;
canvas->save();
canvas->rotate(3.f);
canvas->experimental_DrawImageSetV1(&entry, 1, fm, SkBlendMode::kExclusion);
diff --git a/src/gpu/GrColorSpaceXform.cpp b/src/gpu/GrColorSpaceXform.cpp
index 2cd5e5a..986ac7a 100644
--- a/src/gpu/GrColorSpaceXform.cpp
+++ b/src/gpu/GrColorSpaceXform.cpp
@@ -171,3 +171,16 @@
return std::unique_ptr<GrFragmentProcessor>(new GrColorSpaceXformEffect(std::move(child),
std::move(xform)));
}
+
+std::unique_ptr<GrFragmentProcessor> GrColorSpaceXformEffect::Make(
+ std::unique_ptr<GrFragmentProcessor> child, sk_sp<GrColorSpaceXform> colorXform) {
+ if (!child) {
+ return nullptr;
+ }
+ if (!colorXform) {
+ return child;
+ }
+
+ return std::unique_ptr<GrFragmentProcessor>(new GrColorSpaceXformEffect(std::move(child),
+ std::move(colorXform)));
+}
diff --git a/src/gpu/GrColorSpaceXform.h b/src/gpu/GrColorSpaceXform.h
index 242eeb5..7b28164 100644
--- a/src/gpu/GrColorSpaceXform.h
+++ b/src/gpu/GrColorSpaceXform.h
@@ -61,6 +61,13 @@
SkColorSpace* src, SkAlphaType srcAT,
SkColorSpace* dst);
+ /**
+ * Returns a fragment processor that calls the passed in FP and then converts it with the given
+ * color xform. Returns null if child is null, returns child if the xform is null (e.g. noop).
+ */
+ static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> child,
+ sk_sp<GrColorSpaceXform> colorXform);
+
const char* name() const override { return "ColorSpaceXform"; }
std::unique_ptr<GrFragmentProcessor> clone() const override;
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index e6c59c7..ddfb13a 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -939,17 +939,47 @@
}
void GrRenderTargetContext::drawTextureSet(const GrClip& clip, const TextureSetEntry set[], int cnt,
- GrSamplerState::Filter filter,
+ GrSamplerState::Filter filter, SkBlendMode mode,
const SkMatrix& viewMatrix,
sk_sp<GrColorSpaceXform> texXform) {
ASSERT_SINGLE_OWNER
RETURN_IF_ABANDONED
SkDEBUGCODE(this->validate();)
GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext", "drawTextureSet", fContext);
+
+ AutoCheckFlush acf(this->drawingManager());
+
GrAAType aaType = this->chooseAAType(GrAA::kYes, GrAllowMixedSamples::kNo);
- auto op =
- GrTextureOp::Make(fContext, set, cnt, filter, aaType, viewMatrix, std::move(texXform));
- this->addDrawOp(clip, std::move(op));
+ if (mode != SkBlendMode::kSrcOver ||
+ !fContext->contextPriv().caps()->dynamicStateArrayGeometryProcessorTextureSupport()) {
+ // Draw one at a time with GrFillRectOp and a GrPaint that emulates what GrTextureOp does
+ const GrXPFactory* xpFactory = SkBlendMode_AsXPFactory(mode);
+ for (int i = 0; i < cnt; ++i) {
+ GrPaint paint;
+ paint.setColor4f({set[i].fAlpha, set[i].fAlpha, set[i].fAlpha, set[i].fAlpha});
+ paint.setXPFactory(xpFactory);
+
+ // See if we can disable bilerp filtering when the src and dst rects line up
+ if (filter != GrSamplerState::Filter::kNearest &&
+ !GrTextureOp::GetFilterHasEffect(viewMatrix, set[i].fSrcRect, set[i].fDstRect)) {
+ filter = GrSamplerState::Filter::kNearest;
+ }
+
+ auto fp = GrSimpleTextureEffect::Make(set[i].fProxy, SkMatrix::I(), filter);
+ fp = GrColorSpaceXformEffect::Make(std::move(fp), texXform);
+ paint.addColorFragmentProcessor(std::move(fp));
+
+ auto op = GrFillRectOp::MakePerEdgeWithLocalRect(fContext, std::move(paint), aaType,
+ set[i].fAAFlags, viewMatrix,
+ set[i].fDstRect, set[i].fSrcRect);
+ this->addDrawOp(clip, std::move(op));
+ }
+ } else {
+ // Can use a single op, avoiding GrPaint creation, and can batch across proxies
+ auto op = GrTextureOp::Make(fContext, set, cnt, filter, aaType, viewMatrix,
+ std::move(texXform));
+ this->addDrawOp(clip, std::move(op));
+ }
}
void GrRenderTargetContext::fillRectWithLocalMatrix(const GrClip& clip,
diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h
index 6d5e002..1dfe59a 100644
--- a/src/gpu/GrRenderTargetContext.h
+++ b/src/gpu/GrRenderTargetContext.h
@@ -162,7 +162,8 @@
* texture color xform. The textures must all have the same GrTextureType and GrConfig.
*/
void drawTextureSet(const GrClip&, const TextureSetEntry[], int cnt, GrSamplerState::Filter,
- const SkMatrix& viewMatrix, sk_sp<GrColorSpaceXform> texXform);
+ SkBlendMode mode, const SkMatrix& viewMatrix,
+ sk_sp<GrColorSpaceXform> texXform);
/**
* Draw a roundrect using a paint.
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index a5fd260..c2fff53 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1496,11 +1496,7 @@
void SkGpuDevice::drawImageSet(const SkCanvas::ImageSetEntry set[], int count,
SkFilterQuality filterQuality, SkBlendMode mode) {
SkASSERT(count > 0);
- if (mode != SkBlendMode::kSrcOver ||
- !fContext->contextPriv().caps()->dynamicStateArrayGeometryProcessorTextureSupport()) {
- INHERITED::drawImageSet(set, count, filterQuality, mode);
- return;
- }
+
GrSamplerState sampler;
sampler.setFilterMode(kNone_SkFilterQuality == filterQuality ? GrSamplerState::Filter::kNearest
: GrSamplerState::Filter::kBilerp);
@@ -1514,7 +1510,7 @@
set[base].fImage->colorSpace(), set[base].fImage->alphaType(),
fRenderTargetContext->colorSpaceInfo().colorSpace(), kPremul_SkAlphaType);
fRenderTargetContext->drawTextureSet(this->clip(), textures.get() + base, n,
- sampler.filter(), this->ctm(),
+ sampler.filter(), mode, this->ctm(),
std::move(textureXform));
}
};
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index e83b435..e3594d3 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -40,20 +40,6 @@
using VertexSpec = GrQuadPerEdgeAA::VertexSpec;
using ColorType = GrQuadPerEdgeAA::ColorType;
-static bool filter_has_effect_for_rect_stays_rect(const GrPerspQuad& quad, const SkRect& srcRect) {
- SkASSERT(quad.quadType() == GrQuadType::kRect);
- float ql = quad.x(0);
- float qt = quad.y(0);
- float qr = quad.x(3);
- float qb = quad.y(3);
- // Disable filtering when there is no scaling of the src rect and the src rect and dst rect
- // align fractionally. If we allow inverted src rects this logic needs to consider that.
- SkASSERT(srcRect.isSorted());
- return (qr - ql) != srcRect.width() || (qb - qt) != srcRect.height() ||
- SkScalarFraction(ql) != SkScalarFraction(srcRect.fLeft) ||
- SkScalarFraction(qt) != SkScalarFraction(srcRect.fTop);
-}
-
// if normalizing the domain then pass 1/width, 1/height, 1 for iw, ih, h. Otherwise pass
// 1, 1, and height.
static SkRect compute_domain(Domain domain, GrSamplerState::Filter filter, GrSurfaceOrigin origin,
@@ -229,7 +215,7 @@
// Disable filtering if possible (note AA optimizations for rects are automatically
// handled above in GrResolveAATypeForQuad).
if (this->filter() != GrSamplerState::Filter::kNearest &&
- !filter_has_effect_for_rect_stays_rect(quad, srcRect)) {
+ !GrTextureOp::GetFilterHasEffect(viewMatrix, srcRect, dstRect)) {
fFilter = static_cast<unsigned>(GrSamplerState::Filter::kNearest);
}
}
@@ -291,7 +277,8 @@
}
if (!mustFilter && this->filter() != GrSamplerState::Filter::kNearest) {
mustFilter = quadType != GrQuadType::kRect ||
- filter_has_effect_for_rect_stays_rect(quad, set[p].fSrcRect);
+ GrTextureOp::GetFilterHasEffect(viewMatrix, set[p].fSrcRect,
+ set[p].fDstRect);
}
float alpha = SkTPin(set[p].fAlpha, 0.f, 1.f);
SkPMColor4f color{alpha, alpha, alpha, alpha};
@@ -575,6 +562,29 @@
std::move(textureColorSpaceXform));
}
+bool GetFilterHasEffect(const SkMatrix& viewMatrix, const SkRect& srcRect, const SkRect& dstRect) {
+ // Hypothetically we could disable bilerp filtering when flipping or rotating 90 degrees, but
+ // that makes the math harder and we don't want to increase the overhead of the checks
+ if (!viewMatrix.isScaleTranslate() ||
+ viewMatrix.getScaleX() < 0.0f || viewMatrix.getScaleY() < 0.0f) {
+ return true;
+ }
+
+ // Given the matrix conditions ensured above, this computes the device space coordinates for
+ // the top left corner of dstRect and its size.
+ SkScalar dw = viewMatrix.getScaleX() * dstRect.width();
+ SkScalar dh = viewMatrix.getScaleY() * dstRect.height();
+ SkScalar dl = viewMatrix.getScaleX() * dstRect.fLeft + viewMatrix.getTranslateX();
+ SkScalar dt = viewMatrix.getScaleY() * dstRect.fTop + viewMatrix.getTranslateY();
+
+ // Disable filtering when there is no scaling of the src rect and the src rect and dst rect
+ // align fractionally. If we allow inverted src rects this logic needs to consider that.
+ SkASSERT(srcRect.isSorted());
+ return dw != srcRect.width() || dh != srcRect.height() ||
+ SkScalarFraction(dl) != SkScalarFraction(srcRect.fLeft) ||
+ SkScalarFraction(dt) != SkScalarFraction(srcRect.fTop);
+}
+
} // namespace GrTextureOp
#if GR_TEST_UTILS
diff --git a/src/gpu/ops/GrTextureOp.h b/src/gpu/ops/GrTextureOp.h
index 44d34ab..1942344 100644
--- a/src/gpu/ops/GrTextureOp.h
+++ b/src/gpu/ops/GrTextureOp.h
@@ -46,4 +46,11 @@
GrAAType,
const SkMatrix& viewMatrix,
sk_sp<GrColorSpaceXform> textureXform);
+
+/**
+ * Returns true if bilerp texture filtering matters when rendering the src rect
+ * texels to dst rect, with the given view matrix.
+ */
+bool GetFilterHasEffect(const SkMatrix& viewMatrix, const SkRect& srcRect, const SkRect& dstRect);
+
}