Add general quad methods to GrRTC
Refactor compositor GM to use GrRTC directly instead of adding draw ops.
Adds a test row for using drawTextureSet now that it takes dst clips.
Bug: skia:
Change-Id: I6863ef1286cab0f0e5cf989e4aaef8ff2ca0abb8
Reviewed-on: https://skia-review.googlesource.com/c/193023
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/GrRect.h b/src/gpu/GrRect.h
index 8c44ed7..aa00534 100644
--- a/src/gpu/GrRect.h
+++ b/src/gpu/GrRect.h
@@ -72,4 +72,14 @@
SkASSERT(!b.isFinite() || (b.fLeft <= b.fRight && b.fTop <= b.fBottom));
return a.fRight >= b.fLeft && a.fBottom >= b.fTop && b.fRight >= a.fLeft && b.fBottom >= a.fTop;
}
+
+/**
+ * Apply the transform from 'inRect' to 'outRect' to each point in 'inPts', storing the mapped point
+ * into the parallel index of 'outPts'.
+ */
+static inline void GrMapRectPoints(const SkRect& inRect, const SkRect& outRect,
+ const SkPoint inPts[], SkPoint outPts[], int ptCount) {
+ SkMatrix rectTransform = SkMatrix::MakeRectToRect(inRect, outRect, SkMatrix::kFill_ScaleToFit);
+ rectTransform.mapPoints(outPts, inPts, ptCount);
+}
#endif
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index c870ef1..910fcdb 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -928,6 +928,22 @@
this->addDrawOp(clip, std::move(op));
}
+void GrRenderTargetContext::fillQuadWithEdgeAA(const GrClip& clip, GrPaint&& paint, GrAA aa,
+ GrQuadAAFlags edgeAA, const SkMatrix& viewMatrix,
+ const SkPoint quad[4], const SkPoint localQuad[4]) {
+ ASSERT_SINGLE_OWNER
+ RETURN_IF_ABANDONED
+ SkDEBUGCODE(this->validate();)
+ GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext", "fillQuadWithEdgeAA", fContext);
+
+ GrAAType aaType = this->chooseAAType(aa, GrAllowMixedSamples::kNo);
+
+ AutoCheckFlush acf(this->drawingManager());
+ // MakePerEdgeQuad automatically does the right thing if localQuad is null or not
+ this->addDrawOp(clip, GrFillRectOp::MakePerEdgeQuad(fContext, std::move(paint), aaType, edgeAA,
+ viewMatrix, quad, localQuad));
+}
+
// Creates a paint for GrFillRectOp that matches behavior of GrTextureOp
static void draw_texture_to_grpaint(sk_sp<GrTextureProxy> proxy, const SkRect* domain,
GrSamplerState::Filter filter, SkBlendMode mode,
@@ -938,7 +954,12 @@
std::unique_ptr<GrFragmentProcessor> fp;
if (domain) {
- fp = GrTextureDomainEffect::Make(std::move(proxy), SkMatrix::I(), *domain,
+ SkRect correctedDomain = *domain;
+ if (filter == GrSamplerState::Filter::kBilerp) {
+ // Inset by 1/2 pixel, which GrTextureOp and GrTextureAdjuster handle automatically
+ correctedDomain.inset(0.5f, 0.5f);
+ }
+ fp = GrTextureDomainEffect::Make(std::move(proxy), SkMatrix::I(), correctedDomain,
GrTextureDomain::kClamp_Mode, filter);
} else {
fp = GrSimpleTextureEffect::Make(std::move(proxy), SkMatrix::I(), filter);
@@ -982,16 +1003,9 @@
filter = GrSamplerState::Filter::kNearest;
}
- SkRect domain = srcRect;
- if (constraint == SkCanvas::kStrict_SrcRectConstraint &&
- filter == GrSamplerState::Filter::kBilerp) {
- // Inset by 1/2 pixel, which GrTextureOp and GrTextureAdjuster handle automatically
- domain.inset(0.5f, 0.5f);
- }
-
GrPaint paint;
draw_texture_to_grpaint(std::move(proxy),
- constraint == SkCanvas::kStrict_SrcRectConstraint ? &domain : nullptr,
+ constraint == SkCanvas::kStrict_SrcRectConstraint ? &srcRect : nullptr,
filter, mode, color, std::move(textureColorSpaceXform), &paint);
op = GrFillRectOp::MakePerEdgeWithLocalRect(fContext, std::move(paint), aaType, aaFlags,
viewMatrix, clippedDstRect, clippedSrcRect);
@@ -1005,6 +1019,44 @@
this->addDrawOp(clip, std::move(op));
}
+void GrRenderTargetContext::drawTextureQuad(const GrClip& clip, sk_sp<GrTextureProxy> proxy,
+ GrSamplerState::Filter filter, SkBlendMode mode,
+ const SkPMColor4f& color, const SkPoint srcQuad[4],
+ const SkPoint dstQuad[4], GrAA aa,
+ GrQuadAAFlags aaFlags, const SkRect* domain,
+ const SkMatrix& viewMatrix,
+ sk_sp<GrColorSpaceXform> texXform) {
+ ASSERT_SINGLE_OWNER
+ RETURN_IF_ABANDONED
+ SkDEBUGCODE(this->validate();)
+ GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext", "drawTextureQuad", fContext);
+ if (domain && domain->contains(proxy->getWorstCaseBoundsRect())) {
+ domain = nullptr;
+ }
+
+ GrAAType aaType = this->chooseAAType(aa, GrAllowMixedSamples::kNo);
+
+ // Unlike drawTexture(), don't bother cropping or optimizing the filter type since we're
+ // sampling an arbitrary quad of the texture.
+ AutoCheckFlush acf(this->drawingManager());
+ std::unique_ptr<GrDrawOp> op;
+ if (mode != SkBlendMode::kSrcOver) {
+ // Emulation mode, but don't bother converting to kNearest filter since it's an arbitrary
+ // quad that is being drawn, which makes the tests too expensive here
+ GrPaint paint;
+ draw_texture_to_grpaint(
+ std::move(proxy), domain, filter, mode, color, std::move(texXform), &paint);
+ op = GrFillRectOp::MakePerEdgeQuad(fContext, std::move(paint), aaType, aaFlags, viewMatrix,
+ dstQuad, srcQuad);
+ } else {
+ // Use lighter weight GrTextureOp
+ op = GrTextureOp::MakeQuad(fContext, std::move(proxy), filter, color, srcQuad, dstQuad,
+ aaType, aaFlags, domain, viewMatrix, std::move(texXform));
+ }
+
+ this->addDrawOp(clip, std::move(op));
+}
+
void GrRenderTargetContext::drawTextureSet(const GrClip& clip, const TextureSetEntry set[], int cnt,
GrSamplerState::Filter filter, SkBlendMode mode,
GrAA aa, const SkMatrix& viewMatrix,
@@ -1019,9 +1071,23 @@
// Draw one at a time with GrFillRectOp and a GrPaint that emulates what GrTextureOp does
for (int i = 0; i < cnt; ++i) {
float alpha = set[i].fAlpha;
- this->drawTexture(clip, set[i].fProxy, filter, mode, {alpha, alpha, alpha, alpha},
- set[i].fSrcRect, set[i].fDstRect, aa, set[i].fAAFlags,
- SkCanvas::kFast_SrcRectConstraint, viewMatrix, texXform);
+ if (set[i].fDstClip == nullptr) {
+ // Stick with original rectangles, which allows the ops to know more about what's
+ // being drawn.
+ this->drawTexture(clip, set[i].fProxy, filter, mode, {alpha, alpha, alpha, alpha},
+ set[i].fSrcRect, set[i].fDstRect, aa, set[i].fAAFlags,
+ SkCanvas::kFast_SrcRectConstraint, viewMatrix, texXform);
+ } else {
+ // Generate interpolated texture coordinates to match the dst clip
+ SkPoint srcQuad[4];
+ GrMapRectPoints(set[i].fDstRect, set[i].fSrcRect, set[i].fDstClip, srcQuad, 4);
+ // Don't send srcRect as the domain, since the normal case doesn't use a constraint
+ // with the entire srcRect, so sampling into dstRect outside of dstClip will just
+ // keep seams look more correct.
+ this->drawTextureQuad(clip, set[i].fProxy, filter, mode,
+ {alpha, alpha, alpha, alpha}, srcQuad, set[i].fDstClip,
+ aa, set[i].fAAFlags, nullptr, viewMatrix, texXform);
+ }
}
} else {
// Can use a single op, avoiding GrPaint creation, and can batch across proxies
diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h
index 443a03e..d21af5a 100644
--- a/src/gpu/GrRenderTargetContext.h
+++ b/src/gpu/GrRenderTargetContext.h
@@ -132,11 +132,30 @@
/**
* Creates an op that draws a fill rect with per-edge control over anti-aliasing.
+ *
+ * This is a specialized version of fillQuadWithEdgeAA, but is kept separate since knowing
+ * the geometry is a rectangle affords more optimizations.
*/
void fillRectWithEdgeAA(const GrClip& clip, GrPaint&& paint, GrAA aa, GrQuadAAFlags edgeAA,
const SkMatrix& viewMatrix, const SkRect& rect,
const SkRect* optionalLocalRect = nullptr);
+ /**
+ * Similar to fillRectWithEdgeAA but draws an arbitrary 2D convex quadrilateral transformed
+ * by 'viewMatrix', with per-edge control over anti-aliasing. The quad should follow the
+ * ordering used by SkRect::toQuad(), which determines how the edge AA is applied:
+ * - "top" = points [0] and [1]
+ * - "right" = points[1] and [2]
+ * - "bottom" = points[2] and [3]
+ * - "left" = points[3] and [0]
+ *
+ * The last argument, 'optionalLocalQuad', can be null if no separate local coordinates are
+ * necessary.
+ */
+ void fillQuadWithEdgeAA(const GrClip& clip, GrPaint&& paint, GrAA aa, GrQuadAAFlags edgeAA,
+ const SkMatrix& viewMatrix, const SkPoint quad[4],
+ const SkPoint optionalLocalQuad[4]);
+
/** Used with drawQuadSet */
struct QuadSetEntry {
SkRect fRect;
@@ -160,17 +179,32 @@
const SkRect& dstRect, GrAA, GrQuadAAFlags, SkCanvas::SrcRectConstraint,
const SkMatrix& viewMatrix, sk_sp<GrColorSpaceXform> texXform);
+ /**
+ * Variant of drawTexture that instead draws the texture applied to 'dstQuad' transformed by
+ * 'viewMatrix', using the 'srcQuad' texture coordinates clamped to the optional 'domain'. If
+ * 'domain' is null, it's equivalent to using the fast src rect constraint. If 'domain' is
+ * provided, the strict src rect constraint is applied using 'domain'.
+ */
+ void drawTextureQuad(const GrClip& clip, sk_sp<GrTextureProxy>, GrSamplerState::Filter,
+ SkBlendMode mode, const SkPMColor4f&, const SkPoint srcQuad[4],
+ const SkPoint dstQuad[4], GrAA, GrQuadAAFlags, const SkRect* domain,
+ const SkMatrix& viewMatrix, sk_sp<GrColorSpaceXform> texXform);
+
/** Used with drawTextureSet */
struct TextureSetEntry {
sk_sp<GrTextureProxy> fProxy;
SkRect fSrcRect;
SkRect fDstRect;
+ SkPoint* fDstClip; // Must be null, or point to an array of 4 points
float fAlpha;
GrQuadAAFlags fAAFlags;
};
/**
* Draws a set of textures with a shared filter, color, view matrix, color xform, and
* texture color xform. The textures must all have the same GrTextureType and GrConfig.
+ *
+ * If any entries provide a non-null fDstClip array, it will be read from immediately based on
+ * fDstClipCount, so the pointer can become invalid after this returns.
*/
void drawTextureSet(const GrClip&, const TextureSetEntry[], int cnt, GrSamplerState::Filter,
SkBlendMode mode, GrAA aa, const SkMatrix& viewMatrix,
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index ba4a48f..cb5c3dc 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1468,6 +1468,7 @@
}
textures[i].fSrcRect = set[i].fSrcRect;
textures[i].fDstRect = set[i].fDstRect;
+ textures[i].fDstClip = nullptr; // TODO(michaelludwig) Not exposed in SkGpuDevice API yet
textures[i].fAlpha = set[i].fAlpha;
textures[i].fAAFlags = SkToGrQuadAAFlags(set[i].fAAFlags);
if (n > 0 &&
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index cb97b9b..5cf35ec 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -307,9 +307,10 @@
GrAAType overallAAType = GrAAType::kNone; // aa type maximally compatible with all dst rects
bool mustFilter = false;
fCanSkipAllocatorGather = static_cast<unsigned>(true);
- // All dst rects are transformed by the same view matrix, so their quad types are identical
- GrQuadType quadType = GrQuadTypeForTransformedRect(viewMatrix);
- fQuads.reserve(cnt, quadType);
+ // All dst rects are transformed by the same view matrix, so their quad types are identical,
+ // unless an entry provides a dstClip that forces quad type to be at least standard.
+ GrQuadType baseQuadType = GrQuadTypeForTransformedRect(viewMatrix);
+ fQuads.reserve(cnt, baseQuadType);
for (unsigned p = 0; p < fProxyCnt; ++p) {
fProxies[p].fProxy = SkRef(set[p].fProxy.get());
@@ -319,7 +320,16 @@
if (!fProxies[p].fProxy->canSkipResourceAllocator()) {
fCanSkipAllocatorGather = static_cast<unsigned>(false);
}
- auto quad = GrPerspQuad::MakeFromRect(set[p].fDstRect, viewMatrix);
+
+ // Use dstRect unless dstClip is provided, which is assumed to be a quad
+ auto quad = set[p].fDstClip == nullptr ?
+ GrPerspQuad::MakeFromRect(set[p].fDstRect, viewMatrix) :
+ GrPerspQuad::MakeFromSkQuad(set[p].fDstClip, viewMatrix);
+ GrQuadType quadType = baseQuadType;
+ if (set[p].fDstClip && baseQuadType != GrQuadType::kPerspective) {
+ quadType = GrQuadType::kStandard;
+ }
+
bounds.joinPossiblyEmptyRect(quad.bounds(quadType));
GrQuadAAFlags aaFlags;
// Don't update the overall aaType, might be inappropriate for some of the quads
@@ -337,9 +347,18 @@
}
float alpha = SkTPin(set[p].fAlpha, 0.f, 1.f);
SkPMColor4f color{alpha, alpha, alpha, alpha};
- // TODO(michaelludwig) - Once TextureSetEntry is updated to include a dstClip, fSrcQuads
- // will need to be used similarly to the single-image ctor.
- fQuads.push_back(quad, quadType, {color, set[p].fSrcRect, -1, Domain::kNo, aaFlags});
+ int srcQuadIndex = -1;
+ if (set[p].fDstClip) {
+ // Derive new source coordinates that match dstClip's relative locations in dstRect,
+ // but with respect to srcRect
+ SkPoint srcQuad[4];
+ GrMapRectPoints(set[p].fDstRect, set[p].fSrcRect, set[p].fDstClip, srcQuad, 4);
+ fSrcQuads.push_back(GrPerspQuad::MakeFromSkQuad(srcQuad, SkMatrix::I()),
+ GrQuadType::kStandard);
+ srcQuadIndex = fSrcQuads.count() - 1;
+ }
+ fQuads.push_back(quad, quadType,
+ {color, set[p].fSrcRect, srcQuadIndex, Domain::kNo, aaFlags});
}
fAAType = static_cast<unsigned>(overallAAType);
if (!mustFilter) {