Make GrRenderTargetContext::drawPath() use GrShape to identify simpler

geometries.

Change-Id: I24230efc8bcb60f00c0c855090e3311ad13d7da8
Reviewed-on: https://skia-review.googlesource.com/85962
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
This commit is contained in:
Brian Salomon 2017-12-20 13:28:55 -05:00 committed by Skia Commit-Bot
parent 939e6719ab
commit 2fad74a0fd
6 changed files with 109 additions and 90 deletions

View File

@ -584,11 +584,7 @@ void GrRenderTargetContext::drawRect(const GrClip& clip,
return;
}
}
SkPath path;
path.setIsVolatile(true);
path.addRect(rect);
this->internalDrawPath(clip, std::move(paint), aa, viewMatrix, path, *style);
this->drawShapeUsingPathRenderer(clip, std::move(paint), aa, viewMatrix, GrShape(rect, *style));
}
int GrRenderTargetContextPriv::maxWindowRectangles() const {
@ -747,10 +743,8 @@ void GrRenderTargetContext::fillRectToRect(const GrClip& clip,
}
viewAndUnLocalMatrix.postConcat(viewMatrix);
SkPath path;
path.setIsVolatile(true);
path.addRect(localRect);
this->internalDrawPath(clip, std::move(paint), aa, viewAndUnLocalMatrix, path, GrStyle());
this->drawShapeUsingPathRenderer(clip, std::move(paint), aa, viewAndUnLocalMatrix,
GrShape(localRect));
}
static bool must_filter(const SkRect& src, const SkRect& dst, const SkMatrix& ctm) {
@ -840,7 +834,8 @@ void GrRenderTargetContext::fillRectWithLocalMatrix(const GrClip& clip,
path.setIsVolatile(true);
path.addRect(rectToDraw);
path.transform(localMatrix);
this->internalDrawPath(clip, std::move(paint), aa, viewAndUnLocalMatrix, path, GrStyle());
this->drawShapeUsingPathRenderer(clip, std::move(paint), aa, viewAndUnLocalMatrix,
GrShape(path));
}
void GrRenderTargetContext::drawVertices(const GrClip& clip,
@ -934,10 +929,8 @@ void GrRenderTargetContext::drawRRect(const GrClip& origClip,
}
}
SkPath path;
path.setIsVolatile(true);
path.addRRect(rrect);
this->internalDrawPath(*clip, std::move(paint), aa, viewMatrix, path, style);
this->drawShapeUsingPathRenderer(*clip, std::move(paint), aa, viewMatrix,
GrShape(rrect, style));
}
///////////////////////////////////////////////////////////////////////////////
@ -1276,8 +1269,7 @@ void GrRenderTargetContext::drawDRRect(const GrClip& clip,
path.addRRect(inner);
path.addRRect(outer);
path.setFillType(SkPath::kEvenOdd_FillType);
this->internalDrawPath(clip, std::move(paint), aa, viewMatrix, path, GrStyle::SimpleFill());
this->drawShapeUsingPathRenderer(clip, std::move(paint), aa, viewMatrix, GrShape(path));
}
///////////////////////////////////////////////////////////////////////////////
@ -1347,10 +1339,8 @@ void GrRenderTargetContext::drawOval(const GrClip& clip,
}
}
SkPath path;
path.setIsVolatile(true);
path.addOval(oval);
this->internalDrawPath(clip, std::move(paint), aa, viewMatrix, path, style);
this->drawShapeUsingPathRenderer(clip, std::move(paint), aa, viewMatrix,
GrShape(SkRRect::MakeOval(oval), style));
}
void GrRenderTargetContext::drawArc(const GrClip& clip,
@ -1388,7 +1378,7 @@ void GrRenderTargetContext::drawArc(const GrClip& clip,
SkPath path;
SkPathPriv::CreateDrawArcPath(&path, oval, startAngle, sweepAngle, useCenter,
style.isSimpleFill());
this->internalDrawPath(clip, std::move(paint), aa, viewMatrix, path, style);
this->drawShapeUsingPathRenderer(clip, std::move(paint), aa, viewMatrix, GrShape(path, style));
}
void GrRenderTargetContext::drawImageLattice(const GrClip& clip,
@ -1501,25 +1491,29 @@ void GrRenderTargetContext::drawPath(const GrClip& clip,
GrPaint&& paint,
GrAA aa,
const SkMatrix& viewMatrix,
const SkPath& path,
const SkPath& originalPath,
const GrStyle& style) {
ASSERT_SINGLE_OWNER
RETURN_IF_ABANDONED
SkDEBUGCODE(this->validate();)
GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContextPriv", "drawPath", fContext);
if (path.isEmpty()) {
if (path.isInverseFillType()) {
this->drawPaint(clip, std::move(paint), viewMatrix);
}
return;
GrShape shape(originalPath, style);
if (shape.isEmpty()) {
if (shape.inverseFilled()) {
this->drawPaint(clip, std::move(paint), viewMatrix);
}
return;
}
AutoCheckFlush acf(this->drawingManager());
GrAAType aaType = this->chooseAAType(aa, GrAllowMixedSamples::kNo);
if (GrAAType::kCoverage == aaType && !style.pathEffect()) {
if (style.isSimpleFill() && !path.isConvex()) {
if (GrAAType::kCoverage == aaType) {
// TODO: Make GrShape check for nested rects.
SkPath path;
shape.asPath(&path);
SkRect rects[2];
if (shape.style().isSimpleFill() && fills_as_nested_rects(viewMatrix, path, rects)) {
// Concave AA paths are expensive - try to avoid them for special cases
SkRect rects[2];
@ -1533,26 +1527,26 @@ void GrRenderTargetContext::drawPath(const GrClip& clip,
return;
}
}
SkRect ovalRect;
bool isOval = path.isOval(&ovalRect);
if (isOval && !path.isInverseFillType()) {
const GrShaderCaps* shaderCaps = fContext->caps()->shaderCaps();
std::unique_ptr<GrDrawOp> op = GrOvalOpFactory::MakeOvalOp(
std::move(paint), viewMatrix, ovalRect, style.strokeRec(), shaderCaps);
if (op) {
this->addDrawOp(clip, std::move(op));
}
if (!shape.style().hasPathEffect()) {
SkRRect rrect;
// We can ignore the starting point and direction since there is no path effect.
bool inverted;
if (shape.asRRect(&rrect, nullptr, nullptr, &inverted) && !inverted) {
if (rrect.isRect()) {
this->drawRect(clip, std::move(paint), aa, viewMatrix, rrect.rect(),
&shape.style());
return;
} else if (rrect.isOval()) {
this->drawOval(clip, std::move(paint), aa, viewMatrix, rrect.rect(), shape.style());
return;
}
this->drawRRect(clip, std::move(paint), aa, viewMatrix, rrect, shape.style());
return;
}
}
// Note that internalDrawPath may sw-rasterize the path into a scratch texture.
// Scratch textures can be recycled after they are returned to the texture
// cache. This presents a potential hazard for buffered drawing. However,
// the writePixels that uploads to the scratch will perform a flush so we're
// OK.
this->internalDrawPath(clip, std::move(paint), aa, viewMatrix, path, style);
this->drawShapeUsingPathRenderer(clip, std::move(paint), aa, viewMatrix, shape);
}
bool GrRenderTargetContextPriv::drawAndStencilPath(const GrHardClip& clip,
@ -1633,12 +1627,11 @@ SkBudgeted GrRenderTargetContextPriv::isBudgeted() const {
return fRenderTargetContext->fRenderTargetProxy->isBudgeted();
}
void GrRenderTargetContext::internalDrawPath(const GrClip& clip,
GrPaint&& paint,
GrAA aa,
const SkMatrix& viewMatrix,
const SkPath& path,
const GrStyle& style) {
void GrRenderTargetContext::drawShapeUsingPathRenderer(const GrClip& clip,
GrPaint&& paint,
GrAA aa,
const SkMatrix& viewMatrix,
const GrShape& originalShape) {
ASSERT_SINGLE_OWNER
RETURN_IF_ABANDONED
GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext", "internalDrawPath", fContext);
@ -1646,25 +1639,24 @@ void GrRenderTargetContext::internalDrawPath(const GrClip& clip,
SkIRect clipConservativeBounds;
clip.getConservativeBounds(this->width(), this->height(), &clipConservativeBounds, nullptr);
SkASSERT(!path.isEmpty());
GrShape shape;
GrShape tempShape;
// NVPR cannot handle hairlines, so this would get picked up by a different stencil and
// cover path renderer (i.e. default path renderer). The hairline renderer produces much
// smoother hairlines than MSAA.
GrAllowMixedSamples allowMixedSamples =
style.isSimpleHairline() ? GrAllowMixedSamples::kNo : GrAllowMixedSamples::kYes;
GrAllowMixedSamples allowMixedSamples = originalShape.style().isSimpleHairline()
? GrAllowMixedSamples::kNo
: GrAllowMixedSamples::kYes;
GrAAType aaType = this->chooseAAType(aa, allowMixedSamples);
GrPathRenderer::CanDrawPathArgs canDrawArgs;
canDrawArgs.fCaps = this->drawingManager()->getContext()->caps();
canDrawArgs.fViewMatrix = &viewMatrix;
canDrawArgs.fShape = &shape;
canDrawArgs.fShape = &originalShape;
canDrawArgs.fClipConservativeBounds = &clipConservativeBounds;
canDrawArgs.fHasUserStencilSettings = false;
GrPathRenderer* pr;
static constexpr GrPathRendererChain::DrawType kType = GrPathRendererChain::DrawType::kColor;
shape = GrShape(path, style);
if (shape.isEmpty() && !shape.inverseFilled()) {
if (originalShape.isEmpty() && !originalShape.inverseFilled()) {
return;
}
@ -1674,20 +1666,23 @@ void GrRenderTargetContext::internalDrawPath(const GrClip& clip,
pr = this->drawingManager()->getPathRenderer(canDrawArgs, false, kType);
SkScalar styleScale = GrStyle::MatrixToScaleFactor(viewMatrix);
if (!pr && shape.style().pathEffect()) {
if (!pr && originalShape.style().pathEffect()) {
// It didn't work above, so try again with the path effect applied.
shape = shape.applyStyle(GrStyle::Apply::kPathEffectOnly, styleScale);
if (shape.isEmpty()) {
tempShape = originalShape.applyStyle(GrStyle::Apply::kPathEffectOnly, styleScale);
if (tempShape.isEmpty()) {
return;
}
canDrawArgs.fShape = &tempShape;
pr = this->drawingManager()->getPathRenderer(canDrawArgs, false, kType);
}
if (!pr) {
if (shape.style().applies()) {
shape = shape.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec, styleScale);
if (shape.isEmpty()) {
if (canDrawArgs.fShape->style().applies()) {
tempShape = canDrawArgs.fShape->applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec,
styleScale);
if (tempShape.isEmpty()) {
return;
}
canDrawArgs.fShape = &tempShape;
}
// This time, allow SW renderer
pr = this->drawingManager()->getPathRenderer(canDrawArgs, true, kType);
@ -1707,7 +1702,7 @@ void GrRenderTargetContext::internalDrawPath(const GrClip& clip,
&clip,
&clipConservativeBounds,
&viewMatrix,
&shape,
canDrawArgs.fShape,
aaType,
this->colorSpaceInfo().isGammaCorrect()};
pr->drawPath(args);

View File

@ -30,6 +30,7 @@ class GrFixedClip;
class GrRenderTarget;
class GrRenderTargetContextPriv;
class GrRenderTargetOpList;
class GrShape;
class GrStyle;
class GrTextureProxy;
struct GrUserStencilSettings;
@ -432,8 +433,8 @@ private:
const SkRect& rect,
const GrUserStencilSettings* ss);
void internalDrawPath(
const GrClip&, GrPaint&&, GrAA, const SkMatrix&, const SkPath&, const GrStyle&);
void drawShapeUsingPathRenderer(const GrClip&, GrPaint&&, GrAA, const SkMatrix&,
const GrShape&);
// These perform processing specific to Gr[Mesh]DrawOp-derived ops before recording them into
// the op list. They return the id of the opList to which the op was added, or 0, if it was

View File

@ -539,11 +539,22 @@ void GrShape::attemptToSimplifyPath() {
void GrShape::attemptToSimplifyRRect() {
SkASSERT(Type::kRRect == fType);
SkASSERT(!fInheritedKey.count());
// TODO: This isn't valid for strokes.
if (fRRectData.fRRect.isEmpty()) {
// Dashing ignores the inverseness currently. skbug.com/5421
fType = fRRectData.fInverted && !fStyle.isDashed() ? Type::kInvertedEmpty : Type::kEmpty;
return;
// An empty filled rrect is equivalent to a filled empty path with inversion preserved.
if (fStyle.isSimpleFill()) {
fType = fRRectData.fInverted ? Type::kInvertedEmpty : Type::kEmpty;
fStyle = GrStyle::SimpleFill();
return;
}
// Dashing a rrect with no width or height is equivalent to filling an emtpy path.
// When skbug.com/7387 is fixed this should be modified or removed as a dashed zero length
// line will produce cap geometry if the effect begins in an "on" interval.
if (fStyle.isDashed() && !fRRectData.fRRect.width() && !fRRectData.fRRect.height()) {
// Dashing ignores the inverseness (currently). skbug.com/5421.
fType = Type::kEmpty;
fStyle = GrStyle::SimpleFill();
return;
}
}
if (!this->style().hasPathEffect()) {
fRRectData.fDir = kDefaultRRectDir;

View File

@ -338,7 +338,8 @@ public:
case Type::kRRect:
if (fRRectData.fRRect.getType() == SkRRect::kOval_Type) {
return SkPath::kConic_SegmentMask;
} else if (fRRectData.fRRect.getType() == SkRRect::kRect_Type) {
} else if (fRRectData.fRRect.getType() == SkRRect::kRect_Type ||
fRRectData.fRRect.getType() == SkRRect::kEmpty_Type) {
return SkPath::kLine_SegmentMask;
}
return SkPath::kLine_SegmentMask | SkPath::kConic_SegmentMask;

View File

@ -622,21 +622,6 @@ void SkGpuDevice::drawPath(const SkPath& origSrcPath,
return;
}
}
bool isClosed;
SkRect rect;
if (origSrcPath.isRect(&rect, &isClosed) && isClosed) {
this->drawRect(rect, paint);
return;
}
if (origSrcPath.isOval(&rect)) {
this->drawOval(rect, paint);
return;
}
SkRRect rrect;
if (origSrcPath.isRRect(&rrect)) {
this->drawRRect(rrect, paint);
return;
}
}
GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawPath", fContext.get());

View File

@ -1490,6 +1490,8 @@ DEF_TEST(GrShape_empty_shape, reporter) {
SkPaint stroke;
stroke.setStrokeWidth(2.f);
stroke.setStyle(SkPaint::kStroke_Style);
stroke.setStrokeJoin(SkPaint::kRound_Join);
stroke.setStrokeCap(SkPaint::kRound_Cap);
TestCase strokeEmptyCase(reporter, emptyPath, stroke);
strokeEmptyCase.compare(reporter, fillEmptyCase, TestCase::kAllSame_ComparisonExpecation);
TestCase strokeInvertedEmptyCase(reporter, invertedEmptyPath, stroke);
@ -1509,25 +1511,49 @@ DEF_TEST(GrShape_empty_shape, reporter) {
dashAndStrokeInvertexEmptyCase.compare(reporter, fillEmptyCase,
TestCase::kAllSame_ComparisonExpecation);
// A shape made from an empty rrect should behave the same as an empty path.
SkRRect emptyRRect = SkRRect::MakeRect(SkRect::MakeEmpty());
// A shape made from an empty rrect should behave the same as an empty path when filled but not
// when stroked. However, dashing an empty rrect produces an empty path leaving nothing to
// stroke - so equivalent to filling an empty path.
SkRRect emptyRRect = SkRRect::MakeEmpty();
REPORTER_ASSERT(reporter, emptyRRect.getType() == SkRRect::kEmpty_Type);
TestCase fillEmptyRRectCase(reporter, emptyRRect, fill);
fillEmptyRRectCase.compare(reporter, fillEmptyCase, TestCase::kAllSame_ComparisonExpecation);
TestCase strokeEmptyRRectCase(reporter, emptyRRect, stroke);
strokeEmptyRRectCase.compare(reporter, strokeEmptyCase,
TestCase::kAllDifferent_ComparisonExpecation);
TestCase dashAndStrokeEmptyRRectCase(reporter, emptyRRect, dashAndStroke);
dashAndStrokeEmptyRRectCase.compare(reporter, fillEmptyCase,
TestCase::kAllSame_ComparisonExpecation);
static constexpr SkPath::Direction kDir = SkPath::kCCW_Direction;
static constexpr int kStart = 0;
TestCase fillInvertedEmptyRRectCase(reporter, emptyRRect, kDir, kStart, true, GrStyle(fill));
fillInvertedEmptyRRectCase.compare(reporter, fillInvertedEmptyCase,
TestCase::kAllSame_ComparisonExpecation);
TestCase strokeInvertedEmptyRRectCase(reporter, emptyRRect, kDir, kStart, true,
GrStyle(stroke));
strokeInvertedEmptyRRectCase.compare(reporter, strokeInvertedEmptyCase,
TestCase::kAllDifferent_ComparisonExpecation);
TestCase dashAndStrokeEmptyInvertedRRectCase(reporter, emptyRRect, kDir, kStart, true,
GrStyle(dashAndStroke));
// Dashing ignores inverseness so this is equivalent to the non-inverted empty fill.
dashAndStrokeEmptyInvertedRRectCase.compare(reporter, fillEmptyCase,
TestCase::kAllSame_ComparisonExpecation);
// Same for a rect.
SkRect emptyRect = SkRect::MakeEmpty();
TestCase fillEmptyRectCase(reporter, emptyRect, fill);
fillEmptyRectCase.compare(reporter, fillEmptyCase, TestCase::kAllSame_ComparisonExpecation);
TestCase dashAndStrokeEmptyRectCase(reporter, emptyRect, dashAndStroke);
dashAndStrokeEmptyRectCase.compare(reporter, fillEmptyCase,
TestCase::kAllSame_ComparisonExpecation);
TestCase dashAndStrokeEmptyInvertedRectCase(reporter, SkRRect::MakeRect(emptyRect), kDir,
kStart, true, GrStyle(dashAndStroke));
// Dashing ignores inverseness so this is equivalent to the non-inverted empty fill.