Add non-AA support to OvalFactoryOps.
Non-AA stroked ovals and rounded rects were falling back to a path renderer, when they can use
the much faster OvalFactoryOps. And since CircleOp is slightly faster than FillRRectOp for AA
circles, this extends it to work with non-AA circles as well.
Change-Id: I7e3576e081f076056a00ed57ac6a805be7348b28
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/206700
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: Chris Dalton <csmartdalton@google.com>
Commit-Queue: Jim Van Verth <jvanverth@google.com>
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index c4db904..a9c6021 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -1235,11 +1235,11 @@
op = GrFillRRectOp::Make(
fContext, aaType, viewMatrix, rrect, *this->caps(), std::move(paint));
}
- if (!op && GrAAType::kCoverage == aaType) {
+ if (!op) {
assert_alive(paint);
op = GrOvalOpFactory::MakeRRectOp(
- fContext, std::move(paint), viewMatrix, rrect, stroke, this->caps()->shaderCaps());
-
+ fContext, std::move(paint), aaType, viewMatrix, rrect, stroke,
+ this->caps()->shaderCaps());
}
if (op) {
this->addDrawOp(*clip, std::move(op));
@@ -1470,8 +1470,7 @@
return false;
}
- if (GrAAType::kCoverage == aaType && SkRRectPriv::IsCircle(*inner)
- && SkRRectPriv::IsCircle(*outer)) {
+ if (SkRRectPriv::IsCircle(*inner) && SkRRectPriv::IsCircle(*outer)) {
auto outerR = outer->width() / 2.f;
auto innerR = inner->width() / 2.f;
auto cx = outer->getBounds().fLeft + outerR;
@@ -1482,7 +1481,7 @@
auto circleBounds = SkRect::MakeLTRB(cx - avgR, cy - avgR, cx + avgR, cy + avgR);
SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
stroke.setStrokeStyle(outerR - innerR);
- auto op = GrOvalOpFactory::MakeOvalOp(fContext, std::move(paint), viewMatrix,
+ auto op = GrOvalOpFactory::MakeOvalOp(fContext, std::move(paint), aaType, viewMatrix,
circleBounds, GrStyle(stroke, nullptr),
this->caps()->shaderCaps());
if (op) {
@@ -1643,18 +1642,18 @@
// inside the oval's inner diamond). Given these optimizations, it's a clear win to draw
// ovals the exact same way we do round rects.
//
- // However, we still don't draw true circles as round rects in coverage mode, because it can
- // cause perf regressions on some platforms as compared to the dedicated circle Op.
- if (GrAAType::kCoverage != aaType || oval.height() != oval.width()) {
+ // However, we still don't draw true circles as round rects, because it can cause perf
+ // regressions on some platforms as compared to the dedicated circle Op.
+ if (!SkScalarNearlyEqual(oval.height(), oval.width())) {
assert_alive(paint);
op = GrFillRRectOp::Make(fContext, aaType, viewMatrix, SkRRect::MakeOval(oval),
*this->caps(), std::move(paint));
}
}
- if (!op && GrAAType::kCoverage == aaType) {
+ if (!op) {
assert_alive(paint);
- op = GrOvalOpFactory::MakeOvalOp(fContext, std::move(paint), viewMatrix, oval, style,
- this->caps()->shaderCaps());
+ op = GrOvalOpFactory::MakeOvalOp(fContext, std::move(paint), aaType, viewMatrix, oval,
+ style, this->caps()->shaderCaps());
}
if (op) {
this->addDrawOp(clip, std::move(op));
@@ -1684,23 +1683,23 @@
AutoCheckFlush acf(this->drawingManager());
GrAAType aaType = this->chooseAAType(aa);
- if (GrAAType::kCoverage == aaType) {
- const GrShaderCaps* shaderCaps = this->caps()->shaderCaps();
- std::unique_ptr<GrDrawOp> op = GrOvalOpFactory::MakeArcOp(fContext,
- std::move(paint),
- viewMatrix,
- oval,
- startAngle,
- sweepAngle,
- useCenter,
- style,
- shaderCaps);
- if (op) {
- this->addDrawOp(clip, std::move(op));
- return;
- }
- assert_alive(paint);
+ const GrShaderCaps* shaderCaps = this->caps()->shaderCaps();
+ std::unique_ptr<GrDrawOp> op = GrOvalOpFactory::MakeArcOp(fContext,
+ std::move(paint),
+ aaType,
+ viewMatrix,
+ oval,
+ startAngle,
+ sweepAngle,
+ useCenter,
+ style,
+ shaderCaps);
+ if (op) {
+ this->addDrawOp(clip, std::move(op));
+ return;
}
+ assert_alive(paint);
+
this->drawShapeUsingPathRenderer(
clip, std::move(paint), aa, viewMatrix,
GrShape::MakeArc(oval, startAngle, sweepAngle, useCenter, style));
diff --git a/src/gpu/ops/GrOvalOpFactory.cpp b/src/gpu/ops/GrOvalOpFactory.cpp
index f7ea93e..730c0fc 100644
--- a/src/gpu/ops/GrOvalOpFactory.cpp
+++ b/src/gpu/ops/GrOvalOpFactory.cpp
@@ -63,10 +63,11 @@
class CircleGeometryProcessor : public GrGeometryProcessor {
public:
- CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
+ CircleGeometryProcessor(bool aa, bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
bool roundCaps, bool wideColor, const SkMatrix& localMatrix)
: INHERITED(kCircleGeometryProcessor_ClassID)
, fLocalMatrix(localMatrix)
+ , fAA(aa)
, fStroke(stroke) {
fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
fInColor = MakeColorAttribute("inColor", wideColor);
@@ -159,27 +160,35 @@
fragBuilder->codeAppend("float d = length(circleEdge.xy);");
fragBuilder->codeAppend("half distanceToOuterEdge = half(circleEdge.z * (1.0 - d));");
- fragBuilder->codeAppend("half edgeAlpha = saturate(distanceToOuterEdge);");
+ if (cgp.fAA) {
+ fragBuilder->codeAppend("half edgeAlpha = saturate(distanceToOuterEdge);");
+ } else {
+ fragBuilder->codeAppend("half edgeAlpha = step(0, distanceToOuterEdge);");
+ }
if (cgp.fStroke) {
fragBuilder->codeAppend(
"half distanceToInnerEdge = half(circleEdge.z * (d - circleEdge.w));");
- fragBuilder->codeAppend("half innerAlpha = saturate(distanceToInnerEdge);");
+ if (cgp.fAA) {
+ fragBuilder->codeAppend("half innerAlpha = saturate(distanceToInnerEdge);");
+ } else {
+ fragBuilder->codeAppend("half innerAlpha = step(0, distanceToInnerEdge);");
+ }
fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
}
if (cgp.fInClipPlane.isInitialized()) {
fragBuilder->codeAppend(
- "half clip = half(saturate(circleEdge.z * dot(circleEdge.xy, "
- "clipPlane.xy) + clipPlane.z));");
+ "half clip = half(saturate(circleEdge.z * dot(circleEdge.xy, "
+ "clipPlane.xy) + clipPlane.z));");
if (cgp.fInIsectPlane.isInitialized()) {
fragBuilder->codeAppend(
- "clip *= half(saturate(circleEdge.z * dot(circleEdge.xy, "
- "isectPlane.xy) + isectPlane.z));");
+ "clip *= half(saturate(circleEdge.z * dot(circleEdge.xy, "
+ "isectPlane.xy) + isectPlane.z));");
}
if (cgp.fInUnionPlane.isInitialized()) {
fragBuilder->codeAppend(
- "clip = saturate(clip + half(saturate(circleEdge.z * dot(circleEdge.xy,"
- " unionPlane.xy) + unionPlane.z)));");
+ "clip = saturate(clip + half(saturate(circleEdge.z * dot(circleEdge.xy,"
+ " unionPlane.xy) + unionPlane.z)));");
}
fragBuilder->codeAppend("edgeAlpha *= clip;");
if (cgp.fInRoundCapCenters.isInitialized()) {
@@ -210,6 +219,7 @@
key |= cgp.fInIsectPlane.isInitialized() ? 0x08 : 0x0;
key |= cgp.fInUnionPlane.isInitialized() ? 0x10 : 0x0;
key |= cgp.fInRoundCapCenters.isInitialized() ? 0x20 : 0x0;
+ key |= cgp.fAA ? 0x40 : 0x0;
b->add32(key);
}
@@ -234,6 +244,7 @@
Attribute fInUnionPlane;
Attribute fInRoundCapCenters;
+ bool fAA;
bool fStroke;
GR_DECLARE_GEOMETRY_PROCESSOR_TEST
@@ -244,6 +255,7 @@
#if GR_TEST_UTILS
sk_sp<GrGeometryProcessor> CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
+ bool aa = d->fRandom->nextBool();
bool stroke = d->fRandom->nextBool();
bool roundCaps = stroke ? d->fRandom->nextBool() : false;
bool wideColor = d->fRandom->nextBool();
@@ -252,7 +264,7 @@
bool unionPlane = d->fRandom->nextBool();
const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
return sk_sp<GrGeometryProcessor>(new CircleGeometryProcessor(
- stroke, clipPlane, isectPlane, unionPlane, roundCaps, wideColor, matrix));
+ aa, stroke, clipPlane, isectPlane, unionPlane, roundCaps, wideColor, matrix));
}
#endif
@@ -507,10 +519,11 @@
class EllipseGeometryProcessor : public GrGeometryProcessor {
public:
- EllipseGeometryProcessor(bool stroke, bool wideColor, bool useScale,
+ EllipseGeometryProcessor(bool aa, bool stroke, bool wideColor, bool useScale,
const SkMatrix& localMatrix)
: INHERITED(kEllipseGeometryProcessor_ClassID)
, fLocalMatrix(localMatrix)
+ , fAA(aa)
, fStroke(stroke)
, fUseScale(useScale) {
fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
@@ -611,7 +624,11 @@
} else {
fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
}
- fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
+ if (egp.fAA) {
+ fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
+ } else {
+ fragBuilder->codeAppend("float edgeAlpha = step(0, -test*invlen);");
+ }
// for inner curve
if (egp.fStroke) {
@@ -634,7 +651,12 @@
} else {
fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
}
- fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
+ if (egp.fAA) {
+ fragBuilder->codeAppend("float innerEdgeAlpha = saturate(0.5+test*invlen);");
+ } else {
+ fragBuilder->codeAppend("float innerEdgeAlpha = step(0, test*invlen);");
+ }
+ fragBuilder->codeAppend("edgeAlpha *= innerEdgeAlpha;");
}
fragBuilder->codeAppendf("%s = half4(half(edgeAlpha));", args.fOutputCoverage);
@@ -646,6 +668,7 @@
const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
uint16_t key = egp.fStroke ? 0x1 : 0x0;
key |= egp.fLocalMatrix.hasPerspective() ? 0x2 : 0x0;
+ key |= egp.fAA ? 0x4 : 0x0;
b->add32(key);
}
@@ -665,6 +688,7 @@
Attribute fInEllipseRadii;
SkMatrix fLocalMatrix;
+ bool fAA;
bool fStroke;
bool fUseScale;
@@ -679,7 +703,8 @@
sk_sp<GrGeometryProcessor> EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
return sk_sp<GrGeometryProcessor>(
new EllipseGeometryProcessor(d->fRandom->nextBool(), d->fRandom->nextBool(),
- d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
+ d->fRandom->nextBool(), d->fRandom->nextBool(),
+ GrTest::TestMatrix(d->fRandom)));
}
#endif
@@ -698,10 +723,11 @@
class DIEllipseGeometryProcessor : public GrGeometryProcessor {
public:
- DIEllipseGeometryProcessor(bool wideColor, bool useScale, const SkMatrix& viewMatrix,
+ DIEllipseGeometryProcessor(bool aa, bool wideColor, bool useScale, const SkMatrix& viewMatrix,
DIEllipseStyle style)
: INHERITED(kDIEllipseGeometryProcessor_ClassID)
, fViewMatrix(viewMatrix)
+ , fAA(aa)
, fUseScale(useScale)
, fStyle(style) {
fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
@@ -796,10 +822,19 @@
}
if (DIEllipseStyle::kHairline == diegp.fStyle) {
// can probably do this with one step
- fragBuilder->codeAppend("float edgeAlpha = saturate(1.0-test*invlen);");
- fragBuilder->codeAppend("edgeAlpha *= saturate(1.0+test*invlen);");
+ if (diegp.fAA) {
+ fragBuilder->codeAppend("float edgeAlpha = saturate(1.0-test*invlen);");
+ fragBuilder->codeAppend("edgeAlpha *= saturate(1.0+test*invlen);");
+ } else {
+ fragBuilder->codeAppend("float edgeAlpha = step(0, 0.5-test*invlen);");
+ fragBuilder->codeAppend("edgeAlpha *= step(0, 0.5+test*invlen);");
+ }
} else {
- fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
+ if (diegp.fAA) {
+ fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
+ } else {
+ fragBuilder->codeAppend("float edgeAlpha = step(0, -test*invlen);");
+ }
}
// for inner curve
@@ -823,7 +858,11 @@
if (diegp.fUseScale) {
fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
}
- fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
+ if (diegp.fAA) {
+ fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
+ } else {
+ fragBuilder->codeAppend("edgeAlpha *= step(0, test*invlen);");
+ }
}
fragBuilder->codeAppendf("%s = half4(half(edgeAlpha));", args.fOutputCoverage);
@@ -833,8 +872,9 @@
const GrShaderCaps&,
GrProcessorKeyBuilder* b) {
const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
- uint16_t key = static_cast<uint16_t>(diegp.fStyle);
- key |= ComputePosKey(diegp.fViewMatrix) << 10;
+ uint16_t key = diegp.fAA ? 0x1 : 0x0;
+ key |= static_cast<uint16_t>(diegp.fStyle) << 1;
+ key |= ComputePosKey(diegp.fViewMatrix) << 11;
b->add32(key);
}
@@ -865,6 +905,7 @@
Attribute fInEllipseOffsets1;
SkMatrix fViewMatrix;
+ bool fAA;
bool fUseScale;
DIEllipseStyle fStyle;
@@ -878,8 +919,8 @@
#if GR_TEST_UTILS
sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
return sk_sp<GrGeometryProcessor>(new DIEllipseGeometryProcessor(
- d->fRandom->nextBool(), d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom),
- (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2))));
+ d->fRandom->nextBool(), d->fRandom->nextBool(), d->fRandom->nextBool(),
+ GrTest::TestMatrix(d->fRandom), (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2))));
}
#endif
@@ -976,6 +1017,7 @@
static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
GrPaint&& paint,
+ GrAAType aaType,
const SkMatrix& viewMatrix,
SkPoint center,
SkScalar radius,
@@ -1012,11 +1054,11 @@
break;
}
}
- return Helper::FactoryHelper<CircleOp>(context, std::move(paint), viewMatrix, center,
- radius, style, arcParams);
+ return Helper::FactoryHelper<CircleOp>(context, std::move(paint), aaType, viewMatrix,
+ center, radius, style, arcParams);
}
- CircleOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
+ CircleOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color, GrAAType aaType,
const SkMatrix& viewMatrix, SkPoint center, SkScalar radius, const GrStyle& style,
const ArcParams* arcParams)
: GrMeshDrawOp(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
@@ -1024,7 +1066,7 @@
SkStrokeRec::Style recStyle = stroke.getStyle();
fRoundCaps = false;
-
+ fAA = (GrAAType::kCoverage == aaType);
viewMatrix.mapPoints(¢er, 1);
radius = viewMatrix.mapRadius(radius);
SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
@@ -1053,8 +1095,10 @@
// simpler computation because the computed alpha is zero, rather than 50%, at the radius.
// Second, the outer radius is used to compute the verts of the bounding box that is
// rendered and the outset ensures the box will cover all partially covered by the circle.
- outerRadius += SK_ScalarHalf;
- innerRadius -= SK_ScalarHalf;
+ if (fAA) {
+ outerRadius += SK_ScalarHalf;
+ innerRadius -= SK_ScalarHalf;
+ }
bool stroked = isStrokeOnly && innerRadius > 0.0f;
fViewMatrixIfUsingLocalCoords = viewMatrix;
@@ -1191,7 +1235,7 @@
radius += halfWidth;
this->setBounds(
{center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
- HasAABloat::kYes, IsZeroArea::kNo);
+ fAA ? HasAABloat::kYes : HasAABloat::kNo, IsZeroArea::kNo);
fVertCount = circle_type_to_vert_count(stroked);
fIndexCount = circle_type_to_index_count(stroked);
fAllFill = !stroked;
@@ -1240,8 +1284,8 @@
// Setup geometry processor
sk_sp<GrGeometryProcessor> gp(new CircleGeometryProcessor(
- !fAllFill, fClipPlane, fClipPlaneIsect, fClipPlaneUnion, fRoundCaps, fWideColor,
- localMatrix));
+ fAA, !fAllFill, fClipPlane, fClipPlaneIsect, fClipPlaneUnion, fRoundCaps,
+ fWideColor, localMatrix));
sk_sp<const GrBuffer> vertexBuffer;
int firstVertex;
@@ -1387,6 +1431,10 @@
return CombineResult::kCannotCombine;
}
+ if (fAA != that->fAA) {
+ return CombineResult::kCannotCombine;
+ }
+
// Because we've set up the ops that don't use the planes with noop values
// we can just accumulate used planes by later ops.
fClipPlane |= that->fClipPlane;
@@ -1419,6 +1467,7 @@
SkSTArray<1, Circle, true> fCircles;
int fVertCount;
int fIndexCount;
+ bool fAA;
bool fAllFill;
bool fClipPlane;
bool fClipPlaneIsect;
@@ -1723,6 +1772,7 @@
static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
GrPaint&& paint,
+ GrAAType aaType,
const SkMatrix& viewMatrix,
const SkRect& ellipse,
const SkStrokeRec& stroke) {
@@ -1794,15 +1844,16 @@
return nullptr;
}
- return Helper::FactoryHelper<EllipseOp>(context, std::move(paint), viewMatrix,
+ return Helper::FactoryHelper<EllipseOp>(context, std::move(paint), aaType, viewMatrix,
params, stroke);
}
EllipseOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
- const SkMatrix& viewMatrix, const DeviceSpaceParams& params,
+ GrAAType aaType, const SkMatrix& viewMatrix, const DeviceSpaceParams& params,
const SkStrokeRec& stroke)
: INHERITED(ClassID())
, fHelper(helperArgs, GrAAType::kCoverage)
+ , fAA(GrAAType::kCoverage == aaType)
, fUseScale(false) {
SkStrokeRec::Style style = stroke.getStyle();
bool isStrokeOnly =
@@ -1815,10 +1866,13 @@
params.fCenter.fX + params.fXRadius,
params.fCenter.fY + params.fYRadius)});
- this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo);
+ this->setBounds(fEllipses.back().fDevBounds, fAA ? HasAABloat::kYes : HasAABloat::kNo,
+ IsZeroArea::kNo);
// Outset bounds to include half-pixel width antialiasing.
- fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
+ if (fAA) {
+ fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
+ }
fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
fViewMatrixIfUsingLocalCoords = viewMatrix;
@@ -1868,8 +1922,8 @@
}
// Setup geometry processor
- sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, fWideColor, fUseScale,
- localMatrix));
+ sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fAA, fStroked, fWideColor,
+ fUseScale, localMatrix));
QuadHelper helper(target, gp->vertexStride(), fEllipses.count());
GrVertexWriter verts{helper.vertices()};
if (!verts.fPtr) {
@@ -1888,8 +1942,12 @@
SkScalarInvert(ellipse.fInnerXRadius),
SkScalarInvert(ellipse.fInnerYRadius)
};
- SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
- SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
+ SkScalar xMaxOffset = xRadius;
+ SkScalar yMaxOffset = yRadius;
+ if (fAA) {
+ xMaxOffset += SK_ScalarHalf;
+ yMaxOffset += SK_ScalarHalf;
+ }
if (!fStroked) {
// For filled ellipses we map a unit circle in the vertex attributes rather than
@@ -1923,6 +1981,10 @@
return CombineResult::kCannotCombine;
}
+ if (fAA != that->fAA) {
+ return CombineResult::kCannotCombine;
+ }
+
if (fHelper.usesLocalCoords() &&
!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
return CombineResult::kCannotCombine;
@@ -1944,6 +2006,7 @@
SkMatrix fViewMatrixIfUsingLocalCoords;
Helper fHelper;
+ bool fAA;
bool fStroked;
bool fWideColor;
bool fUseScale;
@@ -1972,6 +2035,7 @@
static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
GrPaint&& paint,
+ GrAAType aaType,
const SkMatrix& viewMatrix,
const SkRect& ellipse,
const SkStrokeRec& stroke) {
@@ -2039,21 +2103,27 @@
(params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
params.fStyle = DIEllipseStyle::kFill;
}
- return Helper::FactoryHelper<DIEllipseOp>(context, std::move(paint), params, viewMatrix);
+ return Helper::FactoryHelper<DIEllipseOp>(context, std::move(paint), aaType, params,
+ viewMatrix);
}
- DIEllipseOp(Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
+ DIEllipseOp(Helper::MakeArgs& helperArgs, const SkPMColor4f& color, GrAAType aaType,
const DeviceSpaceParams& params, const SkMatrix& viewMatrix)
: INHERITED(ClassID())
, fHelper(helperArgs, GrAAType::kCoverage)
+ , fAA(GrAAType::kCoverage == aaType)
, fUseScale(false) {
- // This expands the outer rect so that after CTM we end up with a half-pixel border
- SkScalar a = viewMatrix[SkMatrix::kMScaleX];
- SkScalar b = viewMatrix[SkMatrix::kMSkewX];
- SkScalar c = viewMatrix[SkMatrix::kMSkewY];
- SkScalar d = viewMatrix[SkMatrix::kMScaleY];
- SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
- SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
+ SkScalar geoDx = 0;
+ SkScalar geoDy = 0;
+ if (fAA) {
+ // This expands the outer rect so that after CTM we end up with a half-pixel border
+ SkScalar a = viewMatrix[SkMatrix::kMScaleX];
+ SkScalar b = viewMatrix[SkMatrix::kMSkewX];
+ SkScalar c = viewMatrix[SkMatrix::kMSkewY];
+ SkScalar d = viewMatrix[SkMatrix::kMScaleY];
+ geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
+ geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
+ }
fEllipses.emplace_back(
Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
@@ -2062,7 +2132,8 @@
params.fCenter.fY - params.fYRadius - geoDy,
params.fCenter.fX + params.fXRadius + geoDx,
params.fCenter.fY + params.fYRadius + geoDy)});
- this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
+ this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix,
+ fAA ? HasAABloat::kYes : HasAABloat::kNo,
IsZeroArea::kNo);
}
@@ -2106,7 +2177,7 @@
void onPrepareDraws(Target* target) override {
// Setup geometry processor
sk_sp<GrGeometryProcessor> gp(
- new DIEllipseGeometryProcessor(fWideColor, fUseScale, this->viewMatrix(),
+ new DIEllipseGeometryProcessor(fAA, fWideColor, fUseScale, this->viewMatrix(),
this->style()));
QuadHelper helper(target, gp->vertexStride(), fEllipses.count());
@@ -2154,6 +2225,10 @@
return CombineResult::kCannotCombine;
}
+ if (this->fAA != that->fAA) {
+ return CombineResult::kCannotCombine;
+ }
+
if (this->style() != that->style()) {
return CombineResult::kCannotCombine;
}
@@ -2185,6 +2260,7 @@
};
Helper fHelper;
+ bool fAA;
bool fWideColor;
bool fUseScale;
SkSTArray<1, Ellipse, true> fEllipses;
@@ -2329,21 +2405,23 @@
// whether the rrect is only stroked or stroked and filled.
static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
GrPaint&& paint,
+ GrAAType aaType,
const SkMatrix& viewMatrix,
const SkRect& devRect,
float devRadius,
float devStrokeWidth,
bool strokeOnly) {
- return Helper::FactoryHelper<CircularRRectOp>(context, std::move(paint), viewMatrix,
+ return Helper::FactoryHelper<CircularRRectOp>(context, std::move(paint), aaType, viewMatrix,
devRect, devRadius,
devStrokeWidth, strokeOnly);
}
- CircularRRectOp(Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
+ CircularRRectOp(Helper::MakeArgs& helperArgs, const SkPMColor4f& color, GrAAType aaType,
const SkMatrix& viewMatrix, const SkRect& devRect, float devRadius,
float devStrokeWidth, bool strokeOnly)
: INHERITED(ClassID())
, fViewMatrixIfUsingLocalCoords(viewMatrix)
- , fHelper(helperArgs, GrAAType::kCoverage) {
+ , fHelper(helperArgs, GrAAType::kCoverage)
+ , fAA(GrAAType::kCoverage == aaType) {
SkRect bounds = devRect;
SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
SkScalar innerRadius = 0.0f;
@@ -2371,18 +2449,20 @@
bounds.outset(halfWidth, halfWidth);
}
- // The radii are outset for two reasons. First, it allows the shader to simply perform
- // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
- // Second, the outer radius is used to compute the verts of the bounding box that is
- // rendered and the outset ensures the box will cover all partially covered by the rrect
- // corners.
- outerRadius += SK_ScalarHalf;
- innerRadius -= SK_ScalarHalf;
+ this->setBounds(bounds, fAA ? HasAABloat::kYes : HasAABloat::kNo, IsZeroArea::kNo);
- this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
+ if (fAA) {
+ // The radii are outset for two reasons. First, it allows the shader to simply perform
+ // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
+ // Second, the outer radius is used to compute the verts of the bounding box that is
+ // rendered and the outset ensures the box will cover all partially covered by the rrect
+ // corners.
+ outerRadius += SK_ScalarHalf;
+ innerRadius -= SK_ScalarHalf;
- // Expand the rect for aa to generate correct vertices.
- bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
+ // Expand the rect for aa to generate correct vertices.
+ bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
+ }
fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
fVertCount = rrect_type_to_vert_count(type);
@@ -2484,8 +2564,8 @@
// Setup geometry processor
sk_sp<GrGeometryProcessor> gp(
- new CircleGeometryProcessor(!fAllFill, false, false, false, false, fWideColor,
- localMatrix));
+ new CircleGeometryProcessor(fAA, !fAllFill, false, false, false, false,
+ fWideColor, localMatrix));
sk_sp<const GrBuffer> vertexBuffer;
int firstVertex;
@@ -2598,6 +2678,10 @@
return CombineResult::kCannotCombine;
}
+ if (fAA != that->fAA) {
+ return CombineResult::kCannotCombine;
+ }
+
fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
fVertCount += that->fVertCount;
fIndexCount += that->fIndexCount;
@@ -2618,6 +2702,7 @@
Helper fHelper;
int fVertCount;
int fIndexCount;
+ bool fAA;
bool fAllFill;
bool fWideColor;
SkSTArray<1, RRect, true> fRRects;
@@ -2659,6 +2744,7 @@
// whether the rrect is only stroked or stroked and filled.
static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
GrPaint&& paint,
+ GrAAType aaType,
const SkMatrix& viewMatrix,
const SkRect& devRect,
float devXRadius,
@@ -2694,16 +2780,17 @@
}
}
return Helper::FactoryHelper<EllipticalRRectOp>(context, std::move(paint),
- viewMatrix, devRect,
+ aaType, viewMatrix, devRect,
devXRadius, devYRadius, devStrokeWidths,
strokeOnly);
}
- EllipticalRRectOp(Helper::MakeArgs helperArgs, const SkPMColor4f& color,
+ EllipticalRRectOp(Helper::MakeArgs helperArgs, const SkPMColor4f& color, GrAAType aaType,
const SkMatrix& viewMatrix, const SkRect& devRect, float devXRadius,
float devYRadius, SkVector devStrokeHalfWidths, bool strokeOnly)
: INHERITED(ClassID())
, fHelper(helperArgs, GrAAType::kCoverage)
+ , fAA(GrAAType::kCoverage == aaType)
, fUseScale(false) {
SkScalar innerXRadius = 0.0f;
SkScalar innerYRadius = 0.0f;
@@ -2724,9 +2811,11 @@
fStroked = stroked;
fViewMatrixIfUsingLocalCoords = viewMatrix;
- this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
- // Expand the rect for aa in order to generate the correct vertices.
- bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
+ this->setBounds(bounds, fAA ? HasAABloat::kYes : HasAABloat::kNo, IsZeroArea::kNo);
+ if (fAA) {
+ // Expand the rect for aa in order to generate the correct vertices.
+ bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
+ }
fRRects.emplace_back(
RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
}
@@ -2774,7 +2863,7 @@
}
// Setup geometry processor
- sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, fWideColor, fUseScale,
+ sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fAA, fStroked, fWideColor, fUseScale,
localMatrix));
// drop out the middle quad if we're stroked
@@ -2806,8 +2895,12 @@
};
// Extend the radii out half a pixel to antialias.
- SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf;
- SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf;
+ SkScalar xOuterRadius = rrect.fXRadius;
+ SkScalar yOuterRadius = rrect.fYRadius;
+ if (fAA) {
+ xOuterRadius += SK_ScalarHalf;
+ yOuterRadius += SK_ScalarHalf;
+ }
SkScalar xMaxOffset = xOuterRadius;
SkScalar yMaxOffset = yOuterRadius;
@@ -2872,6 +2965,10 @@
return CombineResult::kCannotCombine;
}
+ if (fAA != that->fAA) {
+ return CombineResult::kCannotCombine;
+ }
+
if (fHelper.usesLocalCoords() &&
!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
return CombineResult::kCannotCombine;
@@ -2893,6 +2990,7 @@
SkMatrix fViewMatrixIfUsingLocalCoords;
Helper fHelper;
+ bool fAA;
bool fStroked;
bool fWideColor;
bool fUseScale;
@@ -2903,9 +3001,14 @@
static std::unique_ptr<GrDrawOp> make_rrect_op(GrRecordingContext* context,
GrPaint&& paint,
+ GrAAType aaType,
const SkMatrix& viewMatrix,
const SkRRect& rrect,
const SkStrokeRec& stroke) {
+ if (GrAAType::kMSAA == aaType) {
+ return nullptr;
+ }
+
SkASSERT(viewMatrix.rectStaysRect());
SkASSERT(rrect.isSimple());
SkASSERT(!rrect.isOval());
@@ -2963,23 +3066,24 @@
// if the corners are circles, use the circle renderer
if (isCircular) {
- return CircularRRectOp::Make(context, std::move(paint), viewMatrix, bounds, xRadius,
+ return CircularRRectOp::Make(context, std::move(paint), aaType, viewMatrix, bounds, xRadius,
scaledStroke.fX, isStrokeOnly);
// otherwise we use the ellipse renderer
} else {
- return EllipticalRRectOp::Make(context, std::move(paint), viewMatrix, bounds,
+ return EllipticalRRectOp::Make(context, std::move(paint), aaType, viewMatrix, bounds,
xRadius, yRadius, scaledStroke, isStrokeOnly);
}
}
std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeRRectOp(GrRecordingContext* context,
GrPaint&& paint,
+ GrAAType aaType,
const SkMatrix& viewMatrix,
const SkRRect& rrect,
const SkStrokeRec& stroke,
const GrShaderCaps* shaderCaps) {
if (rrect.isOval()) {
- return MakeOvalOp(context, std::move(paint), viewMatrix, rrect.getBounds(),
+ return MakeOvalOp(context, std::move(paint), aaType, viewMatrix, rrect.getBounds(),
GrStyle(stroke, nullptr), shaderCaps);
}
@@ -2987,17 +3091,21 @@
return nullptr;
}
- return make_rrect_op(context, std::move(paint), viewMatrix, rrect, stroke);
+ return make_rrect_op(context, std::move(paint), aaType, viewMatrix, rrect, stroke);
}
///////////////////////////////////////////////////////////////////////////////
std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeOvalOp(GrRecordingContext* context,
GrPaint&& paint,
+ GrAAType aaType,
const SkMatrix& viewMatrix,
const SkRect& oval,
const GrStyle& style,
const GrShaderCaps* shaderCaps) {
+ if (GrAAType::kMSAA == aaType) {
+ return nullptr;
+ }
// we can draw circles
SkScalar width = oval.width();
if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
@@ -3015,12 +3123,16 @@
auto offInterval = style.dashIntervals()[1];
if (offInterval == 0) {
GrStyle strokeStyle(style.strokeRec(), nullptr);
- return MakeOvalOp(context, std::move(paint), viewMatrix, oval,
+ return MakeOvalOp(context, std::move(paint), aaType, viewMatrix, oval,
strokeStyle, shaderCaps);
} else if (onInterval == 0) {
// There is nothing to draw but we have no way to indicate that here.
return nullptr;
}
+ // TODO: handle non-AA butt caps
+ if (GrAAType::kCoverage != aaType) {
+ return nullptr;
+ }
auto angularOnInterval = onInterval / r;
auto angularOffInterval = offInterval / r;
auto phaseAngle = style.dashPhase() / r;
@@ -3031,7 +3143,7 @@
style.strokeRec().getWidth(), kStartAngle,
angularOnInterval, angularOffInterval, phaseAngle);
}
- return CircleOp::Make(context, std::move(paint), viewMatrix, center, r, style);
+ return CircleOp::Make(context, std::move(paint), aaType, viewMatrix, center, r, style);
}
if (style.pathEffect()) {
@@ -3040,7 +3152,8 @@
// prefer the device space ellipse op for batchability
if (viewMatrix.rectStaysRect()) {
- return EllipseOp::Make(context, std::move(paint), viewMatrix, oval, style.strokeRec());
+ return EllipseOp::Make(context, std::move(paint), aaType, viewMatrix, oval,
+ style.strokeRec());
}
// Otherwise, if we have shader derivative support, render as device-independent
@@ -3051,7 +3164,7 @@
SkScalar d = viewMatrix[SkMatrix::kMScaleY];
// Check for near-degenerate matrix
if (a*a + c*c > SK_ScalarNearlyZero && b*b + d*d > SK_ScalarNearlyZero) {
- return DIEllipseOp::Make(context, std::move(paint), viewMatrix, oval,
+ return DIEllipseOp::Make(context, std::move(paint), aaType, viewMatrix, oval,
style.strokeRec());
}
}
@@ -3063,11 +3176,15 @@
std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeArcOp(GrRecordingContext* context,
GrPaint&& paint,
+ GrAAType aaType,
const SkMatrix& viewMatrix,
const SkRect& oval, SkScalar startAngle,
SkScalar sweepAngle, bool useCenter,
const GrStyle& style,
const GrShaderCaps* shaderCaps) {
+ if (GrAAType::kMSAA == aaType) {
+ return nullptr;
+ }
SkASSERT(!oval.isEmpty());
SkASSERT(sweepAngle);
SkScalar width = oval.width();
@@ -3080,7 +3197,7 @@
SkPoint center = {oval.centerX(), oval.centerY()};
CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
useCenter};
- return CircleOp::Make(context, std::move(paint), viewMatrix,
+ return CircleOp::Make(context, std::move(paint), aaType, viewMatrix,
center, width / 2.f, style, &arcParams);
}
@@ -3104,6 +3221,8 @@
SkRect circle = GrTest::TestSquare(random);
SkPoint center = {circle.centerX(), circle.centerY()};
SkScalar radius = circle.width() / 2.f;
+ // Only test kNone and kCoverage aaTypes (kMSAA returns nullptr)
+ GrAAType aaType = random->nextBool() ? GrAAType::kNone : GrAAType::kCoverage;
SkStrokeRec stroke = GrTest::TestStrokeRec(random);
CircleOp::ArcParams arcParamsTmp;
const CircleOp::ArcParams* arcParams = nullptr;
@@ -3113,7 +3232,7 @@
arcParamsTmp.fUseCenter = random->nextBool();
arcParams = &arcParamsTmp;
}
- std::unique_ptr<GrDrawOp> op = CircleOp::Make(context, std::move(paint), viewMatrix,
+ std::unique_ptr<GrDrawOp> op = CircleOp::Make(context, std::move(paint), aaType, viewMatrix,
center, radius,
GrStyle(stroke, nullptr), arcParams);
if (op) {
@@ -3149,23 +3268,29 @@
}
GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
+ // Only test kNone and kCoverage aaTypes (kMSAA returns nullptr)
+ GrAAType aaType = random->nextBool() ? GrAAType::kNone : GrAAType::kCoverage;
SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
SkRect ellipse = GrTest::TestSquare(random);
- return EllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
+ return EllipseOp::Make(context, std::move(paint), aaType, viewMatrix, ellipse,
GrTest::TestStrokeRec(random));
}
GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
+ // Only test kNone and kCoverage aaTypes (kMSAA returns nullptr)
+ GrAAType aaType = random->nextBool() ? GrAAType::kNone : GrAAType::kCoverage;
SkMatrix viewMatrix = GrTest::TestMatrix(random);
SkRect ellipse = GrTest::TestSquare(random);
- return DIEllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
+ return DIEllipseOp::Make(context, std::move(paint), aaType, viewMatrix, ellipse,
GrTest::TestStrokeRec(random));
}
GR_DRAW_OP_TEST_DEFINE(RRectOp) {
+ // Only test kNone and kCoverage aaTypes (kMSAA returns nullptr)
+ GrAAType aaType = random->nextBool() ? GrAAType::kNone : GrAAType::kCoverage;
SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
const SkRRect& rrect = GrTest::TestRRectSimple(random);
- return make_rrect_op(context, std::move(paint), viewMatrix, rrect,
+ return make_rrect_op(context, std::move(paint), aaType, viewMatrix, rrect,
GrTest::TestStrokeRec(random));
}
diff --git a/src/gpu/ops/GrOvalOpFactory.h b/src/gpu/ops/GrOvalOpFactory.h
index b0f0034..ff00212 100644
--- a/src/gpu/ops/GrOvalOpFactory.h
+++ b/src/gpu/ops/GrOvalOpFactory.h
@@ -20,6 +20,7 @@
struct SkRect;
class SkRRect;
class SkStrokeRec;
+enum class GrAAType : unsigned int;
/*
* This namespace wraps helper functions that draw ovals, rrects, and arcs (filled & stroked)
@@ -28,6 +29,7 @@
public:
static std::unique_ptr<GrDrawOp> MakeOvalOp(GrRecordingContext*,
GrPaint&&,
+ GrAAType aaType,
const SkMatrix&,
const SkRect& oval,
const GrStyle& style,
@@ -35,6 +37,7 @@
static std::unique_ptr<GrDrawOp> MakeRRectOp(GrRecordingContext*,
GrPaint&&,
+ GrAAType aaType,
const SkMatrix&,
const SkRRect&,
const SkStrokeRec&,
@@ -42,6 +45,7 @@
static std::unique_ptr<GrDrawOp> MakeArcOp(GrRecordingContext*,
GrPaint&&,
+ GrAAType aaType,
const SkMatrix&,
const SkRect& oval,
SkScalar startAngle,