1 /* 2 * Copyright 2011 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "GrDefaultPathRenderer.h" 9 10 #include "GrContext.h" 11 #include "GrDefaultGeoProcFactory.h" 12 #include "GrDrawOpTest.h" 13 #include "GrFixedClip.h" 14 #include "GrMesh.h" 15 #include "GrOpFlushState.h" 16 #include "GrPathUtils.h" 17 #include "GrPipelineBuilder.h" 18 #include "SkGeometry.h" 19 #include "SkString.h" 20 #include "SkStrokeRec.h" 21 #include "SkTLazy.h" 22 #include "SkTraceEvent.h" 23 24 #include "ops/GrMeshDrawOp.h" 25 #include "ops/GrRectOpFactory.h" 26 27 GrDefaultPathRenderer::GrDefaultPathRenderer(bool separateStencilSupport, 28 bool stencilWrapOpsSupport) 29 : fSeparateStencil(separateStencilSupport) 30 , fStencilWrapOps(stencilWrapOpsSupport) { 31 } 32 33 //////////////////////////////////////////////////////////////////////////////// 34 // Helpers for drawPath 35 36 #define STENCIL_OFF 0 // Always disable stencil (even when needed) 37 38 static inline bool single_pass_shape(const GrShape& shape) { 39 #if STENCIL_OFF 40 return true; 41 #else 42 // Inverse fill is always two pass. 43 if (shape.inverseFilled()) { 44 return false; 45 } 46 // This path renderer only accepts simple fill paths or stroke paths that are either hairline 47 // or have a stroke width small enough to treat as hairline. Hairline paths are always single 48 // pass. Filled paths are single pass if they're convex. 49 if (shape.style().isSimpleFill()) { 50 return shape.knownToBeConvex(); 51 } 52 return true; 53 #endif 54 } 55 56 GrPathRenderer::StencilSupport 57 GrDefaultPathRenderer::onGetStencilSupport(const GrShape& shape) const { 58 if (single_pass_shape(shape)) { 59 return GrPathRenderer::kNoRestriction_StencilSupport; 60 } else { 61 return GrPathRenderer::kStencilOnly_StencilSupport; 62 } 63 } 64 65 static inline void append_countour_edge_indices(bool hairLine, 66 uint16_t fanCenterIdx, 67 uint16_t edgeV0Idx, 68 uint16_t** indices) { 69 // when drawing lines we're appending line segments along 70 // the contour. When applying the other fill rules we're 71 // drawing triangle fans around fanCenterIdx. 72 if (!hairLine) { 73 *((*indices)++) = fanCenterIdx; 74 } 75 *((*indices)++) = edgeV0Idx; 76 *((*indices)++) = edgeV0Idx + 1; 77 } 78 79 static inline void add_quad(SkPoint** vert, const SkPoint* base, const SkPoint pts[], 80 SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol, bool indexed, 81 bool isHairline, uint16_t subpathIdxStart, int offset, uint16_t** idx) { 82 // first pt of quad is the pt we ended on in previous step 83 uint16_t firstQPtIdx = (uint16_t)(*vert - base) - 1 + offset; 84 uint16_t numPts = (uint16_t) 85 GrPathUtils::generateQuadraticPoints( 86 pts[0], pts[1], pts[2], 87 srcSpaceTolSqd, vert, 88 GrPathUtils::quadraticPointCount(pts, srcSpaceTol)); 89 if (indexed) { 90 for (uint16_t i = 0; i < numPts; ++i) { 91 append_countour_edge_indices(isHairline, subpathIdxStart, 92 firstQPtIdx + i, idx); 93 } 94 } 95 } 96 97 class DefaultPathOp final : public GrMeshDrawOp { 98 public: 99 DEFINE_OP_CLASS_ID 100 101 static std::unique_ptr<GrMeshDrawOp> Make(GrColor color, const SkPath& path, SkScalar tolerance, 102 uint8_t coverage, const SkMatrix& viewMatrix, 103 bool isHairline, const SkRect& devBounds) { 104 return std::unique_ptr<GrMeshDrawOp>(new DefaultPathOp(color, path, tolerance, coverage, 105 viewMatrix, isHairline, devBounds)); 106 } 107 108 const char* name() const override { return "DefaultPathOp"; } 109 110 SkString dumpInfo() const override { 111 SkString string; 112 string.appendf("Color: 0x%08x Count: %d\n", fColor, fPaths.count()); 113 for (const auto& path : fPaths) { 114 string.appendf("Tolerance: %.2f\n", path.fTolerance); 115 } 116 string.append(DumpPipelineInfo(*this->pipeline())); 117 string.append(INHERITED::dumpInfo()); 118 return string; 119 } 120 121 private: 122 DefaultPathOp(GrColor color, const SkPath& path, SkScalar tolerance, uint8_t coverage, 123 const SkMatrix& viewMatrix, bool isHairline, const SkRect& devBounds) 124 : INHERITED(ClassID()) 125 , fColor(color) 126 , fCoverage(coverage) 127 , fViewMatrix(viewMatrix) 128 , fIsHairline(isHairline) { 129 fPaths.emplace_back(PathData{path, tolerance}); 130 131 this->setBounds(devBounds, HasAABloat::kNo, 132 isHairline ? IsZeroArea::kYes : IsZeroArea::kNo); 133 } 134 135 void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color, 136 GrPipelineAnalysisCoverage* coverage) const override { 137 color->setToConstant(fColor); 138 *coverage = this->coverage() == 0xff ? GrPipelineAnalysisCoverage::kNone 139 : GrPipelineAnalysisCoverage::kSingleChannel; 140 } 141 142 void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override { 143 optimizations.getOverrideColorIfSet(&fColor); 144 fUsesLocalCoords = optimizations.readsLocalCoords(); 145 } 146 147 void onPrepareDraws(Target* target) const override { 148 sk_sp<GrGeometryProcessor> gp; 149 { 150 using namespace GrDefaultGeoProcFactory; 151 Color color(this->color()); 152 Coverage coverage(this->coverage()); 153 LocalCoords localCoords(this->usesLocalCoords() ? LocalCoords::kUsePosition_Type : 154 LocalCoords::kUnused_Type); 155 gp = GrDefaultGeoProcFactory::Make(color, coverage, localCoords, this->viewMatrix()); 156 } 157 158 size_t vertexStride = gp->getVertexStride(); 159 SkASSERT(vertexStride == sizeof(SkPoint)); 160 161 int instanceCount = fPaths.count(); 162 163 // compute number of vertices 164 int maxVertices = 0; 165 166 // We will use index buffers if we have multiple paths or one path with multiple contours 167 bool isIndexed = instanceCount > 1; 168 for (int i = 0; i < instanceCount; i++) { 169 const PathData& args = fPaths[i]; 170 171 int contourCount; 172 maxVertices += GrPathUtils::worstCasePointCount(args.fPath, &contourCount, 173 args.fTolerance); 174 175 isIndexed = isIndexed || contourCount > 1; 176 } 177 178 if (maxVertices == 0 || maxVertices > ((int)SK_MaxU16 + 1)) { 179 //SkDebugf("Cannot render path (%d)\n", maxVertices); 180 return; 181 } 182 183 // determine primitiveType 184 int maxIndices = 0; 185 GrPrimitiveType primitiveType; 186 if (this->isHairline()) { 187 if (isIndexed) { 188 maxIndices = 2 * maxVertices; 189 primitiveType = kLines_GrPrimitiveType; 190 } else { 191 primitiveType = kLineStrip_GrPrimitiveType; 192 } 193 } else { 194 if (isIndexed) { 195 maxIndices = 3 * maxVertices; 196 primitiveType = kTriangles_GrPrimitiveType; 197 } else { 198 primitiveType = kTriangleFan_GrPrimitiveType; 199 } 200 } 201 202 // allocate vertex / index buffers 203 const GrBuffer* vertexBuffer; 204 int firstVertex; 205 206 void* verts = target->makeVertexSpace(vertexStride, maxVertices, 207 &vertexBuffer, &firstVertex); 208 209 if (!verts) { 210 SkDebugf("Could not allocate vertices\n"); 211 return; 212 } 213 214 const GrBuffer* indexBuffer = nullptr; 215 int firstIndex = 0; 216 217 void* indices = nullptr; 218 if (isIndexed) { 219 indices = target->makeIndexSpace(maxIndices, &indexBuffer, &firstIndex); 220 221 if (!indices) { 222 SkDebugf("Could not allocate indices\n"); 223 return; 224 } 225 } 226 227 // fill buffers 228 int vertexOffset = 0; 229 int indexOffset = 0; 230 for (int i = 0; i < instanceCount; i++) { 231 const PathData& args = fPaths[i]; 232 233 int vertexCnt = 0; 234 int indexCnt = 0; 235 if (!this->createGeom(verts, 236 vertexOffset, 237 indices, 238 indexOffset, 239 &vertexCnt, 240 &indexCnt, 241 args.fPath, 242 args.fTolerance, 243 isIndexed)) { 244 return; 245 } 246 247 vertexOffset += vertexCnt; 248 indexOffset += indexCnt; 249 SkASSERT(vertexOffset <= maxVertices && indexOffset <= maxIndices); 250 } 251 252 GrMesh mesh; 253 if (isIndexed) { 254 mesh.initIndexed(primitiveType, vertexBuffer, indexBuffer, firstVertex, firstIndex, 255 vertexOffset, indexOffset); 256 } else { 257 mesh.init(primitiveType, vertexBuffer, firstVertex, vertexOffset); 258 } 259 target->draw(gp.get(), mesh); 260 261 // put back reserves 262 target->putBackIndices((size_t)(maxIndices - indexOffset)); 263 target->putBackVertices((size_t)(maxVertices - vertexOffset), (size_t)vertexStride); 264 } 265 266 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { 267 DefaultPathOp* that = t->cast<DefaultPathOp>(); 268 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(), 269 that->bounds(), caps)) { 270 return false; 271 } 272 273 if (this->color() != that->color()) { 274 return false; 275 } 276 277 if (this->coverage() != that->coverage()) { 278 return false; 279 } 280 281 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) { 282 return false; 283 } 284 285 if (this->isHairline() != that->isHairline()) { 286 return false; 287 } 288 289 fPaths.push_back_n(that->fPaths.count(), that->fPaths.begin()); 290 this->joinBounds(*that); 291 return true; 292 } 293 294 bool createGeom(void* vertices, 295 size_t vertexOffset, 296 void* indices, 297 size_t indexOffset, 298 int* vertexCnt, 299 int* indexCnt, 300 const SkPath& path, 301 SkScalar srcSpaceTol, 302 bool isIndexed) const { 303 SkScalar srcSpaceTolSqd = srcSpaceTol * srcSpaceTol; 304 305 uint16_t indexOffsetU16 = (uint16_t)indexOffset; 306 uint16_t vertexOffsetU16 = (uint16_t)vertexOffset; 307 308 uint16_t* idxBase = reinterpret_cast<uint16_t*>(indices) + indexOffsetU16; 309 uint16_t* idx = idxBase; 310 uint16_t subpathIdxStart = vertexOffsetU16; 311 312 SkPoint* base = reinterpret_cast<SkPoint*>(vertices) + vertexOffset; 313 SkPoint* vert = base; 314 315 SkPoint pts[4]; 316 317 bool first = true; 318 int subpath = 0; 319 320 SkPath::Iter iter(path, false); 321 322 bool done = false; 323 while (!done) { 324 SkPath::Verb verb = iter.next(pts); 325 switch (verb) { 326 case SkPath::kMove_Verb: 327 if (!first) { 328 uint16_t currIdx = (uint16_t) (vert - base) + vertexOffsetU16; 329 subpathIdxStart = currIdx; 330 ++subpath; 331 } 332 *vert = pts[0]; 333 vert++; 334 break; 335 case SkPath::kLine_Verb: 336 if (isIndexed) { 337 uint16_t prevIdx = (uint16_t)(vert - base) - 1 + vertexOffsetU16; 338 append_countour_edge_indices(this->isHairline(), subpathIdxStart, 339 prevIdx, &idx); 340 } 341 *(vert++) = pts[1]; 342 break; 343 case SkPath::kConic_Verb: { 344 SkScalar weight = iter.conicWeight(); 345 SkAutoConicToQuads converter; 346 // Converting in src-space, hance the finer tolerance (0.25) 347 // TODO: find a way to do this in dev-space so the tolerance means something 348 const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.25f); 349 for (int i = 0; i < converter.countQuads(); ++i) { 350 add_quad(&vert, base, quadPts + i*2, srcSpaceTolSqd, srcSpaceTol, 351 isIndexed, this->isHairline(), subpathIdxStart, 352 (int)vertexOffset, &idx); 353 } 354 break; 355 } 356 case SkPath::kQuad_Verb: 357 add_quad(&vert, base, pts, srcSpaceTolSqd, srcSpaceTol, isIndexed, 358 this->isHairline(), subpathIdxStart, (int)vertexOffset, &idx); 359 break; 360 case SkPath::kCubic_Verb: { 361 // first pt of cubic is the pt we ended on in previous step 362 uint16_t firstCPtIdx = (uint16_t)(vert - base) - 1 + vertexOffsetU16; 363 uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints( 364 pts[0], pts[1], pts[2], pts[3], 365 srcSpaceTolSqd, &vert, 366 GrPathUtils::cubicPointCount(pts, srcSpaceTol)); 367 if (isIndexed) { 368 for (uint16_t i = 0; i < numPts; ++i) { 369 append_countour_edge_indices(this->isHairline(), subpathIdxStart, 370 firstCPtIdx + i, &idx); 371 } 372 } 373 break; 374 } 375 case SkPath::kClose_Verb: 376 break; 377 case SkPath::kDone_Verb: 378 done = true; 379 } 380 first = false; 381 } 382 383 *vertexCnt = static_cast<int>(vert - base); 384 *indexCnt = static_cast<int>(idx - idxBase); 385 return true; 386 } 387 388 GrColor color() const { return fColor; } 389 uint8_t coverage() const { return fCoverage; } 390 bool usesLocalCoords() const { return fUsesLocalCoords; } 391 const SkMatrix& viewMatrix() const { return fViewMatrix; } 392 bool isHairline() const { return fIsHairline; } 393 394 struct PathData { 395 SkPath fPath; 396 SkScalar fTolerance; 397 }; 398 399 GrColor fColor; 400 uint8_t fCoverage; 401 SkMatrix fViewMatrix; 402 bool fUsesLocalCoords; 403 bool fIsHairline; 404 SkSTArray<1, PathData, true> fPaths; 405 406 typedef GrMeshDrawOp INHERITED; 407 }; 408 409 bool GrDefaultPathRenderer::internalDrawPath(GrRenderTargetContext* renderTargetContext, 410 GrPaint&& paint, 411 GrAAType aaType, 412 const GrUserStencilSettings& userStencilSettings, 413 const GrClip& clip, 414 const SkMatrix& viewMatrix, 415 const GrShape& shape, 416 bool stencilOnly) { 417 SkASSERT(GrAAType::kCoverage != aaType); 418 SkPath path; 419 shape.asPath(&path); 420 421 SkScalar hairlineCoverage; 422 uint8_t newCoverage = 0xff; 423 bool isHairline = false; 424 if (IsStrokeHairlineOrEquivalent(shape.style(), viewMatrix, &hairlineCoverage)) { 425 newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff); 426 isHairline = true; 427 } else { 428 SkASSERT(shape.style().isSimpleFill()); 429 } 430 431 int passCount = 0; 432 const GrUserStencilSettings* passes[3]; 433 GrDrawFace drawFace[3]; 434 bool reverse = false; 435 bool lastPassIsBounds; 436 437 if (isHairline) { 438 passCount = 1; 439 if (stencilOnly) { 440 passes[0] = &gDirectToStencil; 441 } else { 442 passes[0] = &userStencilSettings; 443 } 444 lastPassIsBounds = false; 445 drawFace[0] = GrDrawFace::kBoth; 446 } else { 447 if (single_pass_shape(shape)) { 448 passCount = 1; 449 if (stencilOnly) { 450 passes[0] = &gDirectToStencil; 451 } else { 452 passes[0] = &userStencilSettings; 453 } 454 drawFace[0] = GrDrawFace::kBoth; 455 lastPassIsBounds = false; 456 } else { 457 switch (path.getFillType()) { 458 case SkPath::kInverseEvenOdd_FillType: 459 reverse = true; 460 // fallthrough 461 case SkPath::kEvenOdd_FillType: 462 passes[0] = &gEOStencilPass; 463 if (stencilOnly) { 464 passCount = 1; 465 lastPassIsBounds = false; 466 } else { 467 passCount = 2; 468 lastPassIsBounds = true; 469 if (reverse) { 470 passes[1] = &gInvEOColorPass; 471 } else { 472 passes[1] = &gEOColorPass; 473 } 474 } 475 drawFace[0] = drawFace[1] = GrDrawFace::kBoth; 476 break; 477 478 case SkPath::kInverseWinding_FillType: 479 reverse = true; 480 // fallthrough 481 case SkPath::kWinding_FillType: 482 if (fSeparateStencil) { 483 if (fStencilWrapOps) { 484 passes[0] = &gWindStencilSeparateWithWrap; 485 } else { 486 passes[0] = &gWindStencilSeparateNoWrap; 487 } 488 passCount = 2; 489 drawFace[0] = GrDrawFace::kBoth; 490 } else { 491 if (fStencilWrapOps) { 492 passes[0] = &gWindSingleStencilWithWrapInc; 493 passes[1] = &gWindSingleStencilWithWrapDec; 494 } else { 495 passes[0] = &gWindSingleStencilNoWrapInc; 496 passes[1] = &gWindSingleStencilNoWrapDec; 497 } 498 // which is cw and which is ccw is arbitrary. 499 drawFace[0] = GrDrawFace::kCW; 500 drawFace[1] = GrDrawFace::kCCW; 501 passCount = 3; 502 } 503 if (stencilOnly) { 504 lastPassIsBounds = false; 505 --passCount; 506 } else { 507 lastPassIsBounds = true; 508 drawFace[passCount-1] = GrDrawFace::kBoth; 509 if (reverse) { 510 passes[passCount-1] = &gInvWindColorPass; 511 } else { 512 passes[passCount-1] = &gWindColorPass; 513 } 514 } 515 break; 516 default: 517 SkDEBUGFAIL("Unknown path fFill!"); 518 return false; 519 } 520 } 521 } 522 523 SkScalar tol = GrPathUtils::kDefaultTolerance; 524 SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds()); 525 526 SkRect devBounds; 527 GetPathDevBounds(path, renderTargetContext->width(), renderTargetContext->height(), viewMatrix, 528 &devBounds); 529 530 for (int p = 0; p < passCount; ++p) { 531 if (lastPassIsBounds && (p == passCount-1)) { 532 SkRect bounds; 533 SkMatrix localMatrix = SkMatrix::I(); 534 if (reverse) { 535 // draw over the dev bounds (which will be the whole dst surface for inv fill). 536 bounds = devBounds; 537 SkMatrix vmi; 538 // mapRect through persp matrix may not be correct 539 if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) { 540 vmi.mapRect(&bounds); 541 } else { 542 if (!viewMatrix.invert(&localMatrix)) { 543 return false; 544 } 545 } 546 } else { 547 bounds = path.getBounds(); 548 } 549 const SkMatrix& viewM = (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() : 550 viewMatrix; 551 std::unique_ptr<GrMeshDrawOp> op(GrRectOpFactory::MakeNonAAFill( 552 paint.getColor(), viewM, bounds, nullptr, &localMatrix)); 553 554 SkASSERT(GrDrawFace::kBoth == drawFace[p]); 555 GrPipelineBuilder pipelineBuilder(std::move(paint), aaType); 556 pipelineBuilder.setDrawFace(drawFace[p]); 557 pipelineBuilder.setUserStencil(passes[p]); 558 renderTargetContext->addMeshDrawOp(pipelineBuilder, clip, std::move(op)); 559 } else { 560 std::unique_ptr<GrMeshDrawOp> op = 561 DefaultPathOp::Make(paint.getColor(), path, srcSpaceTol, newCoverage, 562 viewMatrix, isHairline, devBounds); 563 bool stencilPass = stencilOnly || passCount > 1; 564 GrPaint::MoveOrNew passPaint(paint, stencilPass); 565 if (stencilPass) { 566 passPaint.paint().setXPFactory(GrDisableColorXPFactory::Get()); 567 } 568 GrPipelineBuilder pipelineBuilder(std::move(passPaint), aaType); 569 pipelineBuilder.setDrawFace(drawFace[p]); 570 pipelineBuilder.setUserStencil(passes[p]); 571 renderTargetContext->addMeshDrawOp(pipelineBuilder, clip, std::move(op)); 572 } 573 } 574 return true; 575 } 576 577 bool GrDefaultPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const { 578 // This can draw any path with any simple fill style but doesn't do coverage-based antialiasing. 579 return GrAAType::kCoverage != args.fAAType && 580 (args.fShape->style().isSimpleFill() || 581 IsStrokeHairlineOrEquivalent(args.fShape->style(), *args.fViewMatrix, nullptr)); 582 } 583 584 bool GrDefaultPathRenderer::onDrawPath(const DrawPathArgs& args) { 585 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(), 586 "GrDefaultPathRenderer::onDrawPath"); 587 return this->internalDrawPath(args.fRenderTargetContext, 588 std::move(args.fPaint), 589 args.fAAType, 590 *args.fUserStencilSettings, 591 *args.fClip, 592 *args.fViewMatrix, 593 *args.fShape, 594 false); 595 } 596 597 void GrDefaultPathRenderer::onStencilPath(const StencilPathArgs& args) { 598 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(), 599 "GrDefaultPathRenderer::onStencilPath"); 600 SkASSERT(!args.fShape->inverseFilled()); 601 602 GrPaint paint; 603 paint.setXPFactory(GrDisableColorXPFactory::Get()); 604 605 this->internalDrawPath(args.fRenderTargetContext, std::move(paint), args.fAAType, 606 GrUserStencilSettings::kUnused, *args.fClip, *args.fViewMatrix, 607 *args.fShape, true); 608 } 609 610 /////////////////////////////////////////////////////////////////////////////////////////////////// 611 612 #if GR_TEST_UTILS 613 614 DRAW_OP_TEST_DEFINE(DefaultPathOp) { 615 GrColor color = GrRandomColor(random); 616 SkMatrix viewMatrix = GrTest::TestMatrix(random); 617 618 // For now just hairlines because the other types of draws require two ops. 619 // TODO we should figure out a way to combine the stencil and cover steps into one op. 620 GrStyle style(SkStrokeRec::kHairline_InitStyle); 621 SkPath path = GrTest::TestPath(random); 622 623 // Compute srcSpaceTol 624 SkRect bounds = path.getBounds(); 625 SkScalar tol = GrPathUtils::kDefaultTolerance; 626 SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, bounds); 627 628 viewMatrix.mapRect(&bounds); 629 uint8_t coverage = GrRandomCoverage(random); 630 return DefaultPathOp::Make(color, path, srcSpaceTol, coverage, viewMatrix, true, bounds); 631 } 632 633 #endif 634