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:
parent
939e6719ab
commit
2fad74a0fd
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user