1 /* 2 * Copyright 2016 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 "GrReducedClip.h" 9 10 #include "GrAppliedClip.h" 11 #include "GrClip.h" 12 #include "GrColor.h" 13 #include "GrContextPriv.h" 14 #include "GrRenderTargetContext.h" 15 #include "GrRenderTargetContextPriv.h" 16 #include "GrDrawingManager.h" 17 #include "GrFixedClip.h" 18 #include "GrPathRenderer.h" 19 #include "GrStencilSettings.h" 20 #include "GrStencilClip.h" 21 #include "GrStyle.h" 22 #include "GrUserStencilSettings.h" 23 #include "SkClipOpPriv.h" 24 #include "ccpr/GrCoverageCountingPathRenderer.h" 25 #include "effects/GrAARectEffect.h" 26 #include "effects/GrConvexPolyEffect.h" 27 #include "effects/GrRRectEffect.h" 28 29 /** 30 * There are plenty of optimizations that could be added here. Maybe flips could be folded into 31 * earlier operations. Or would inserting flips and reversing earlier ops ever be a win? Perhaps 32 * for the case where the bounds are kInsideOut_BoundsType. We could restrict earlier operations 33 * based on later intersect operations, and perhaps remove intersect-rects. We could optionally 34 * take a rect in case the caller knows a bound on what is to be drawn through this clip. 35 */ 36 GrReducedClip::GrReducedClip(const SkClipStack& stack, const SkRect& queryBounds, 37 const GrShaderCaps* caps, int maxWindowRectangles, int maxAnalyticFPs, 38 GrCoverageCountingPathRenderer* ccpr) 39 : fCaps(caps) 40 , fMaxWindowRectangles(maxWindowRectangles) 41 , fMaxAnalyticFPs(maxAnalyticFPs) 42 , fCCPR(fMaxAnalyticFPs ? ccpr : nullptr) { 43 SkASSERT(!queryBounds.isEmpty()); 44 SkASSERT(fMaxWindowRectangles <= GrWindowRectangles::kMaxWindows); 45 fHasScissor = false; 46 fAAClipRectGenID = SK_InvalidGenID; 47 48 if (stack.isWideOpen()) { 49 fInitialState = InitialState::kAllIn; 50 return; 51 } 52 53 SkClipStack::BoundsType stackBoundsType; 54 SkRect stackBounds; 55 bool iior; 56 stack.getBounds(&stackBounds, &stackBoundsType, &iior); 57 58 if (GrClip::IsOutsideClip(stackBounds, queryBounds)) { 59 bool insideOut = SkClipStack::kInsideOut_BoundsType == stackBoundsType; 60 fInitialState = insideOut ? InitialState::kAllIn : InitialState::kAllOut; 61 return; 62 } 63 64 if (iior) { 65 // "Is intersection of rects" means the clip is a single rect indicated by the stack bounds. 66 // This should only be true if aa/non-aa status matches among all elements. 67 SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType); 68 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart); 69 if (!iter.prev()->isAA() || GrClip::IsPixelAligned(stackBounds)) { 70 // The clip is a non-aa rect. Here we just implement the entire thing using fScissor. 71 stackBounds.round(&fScissor); 72 fHasScissor = true; 73 fInitialState = fScissor.isEmpty() ? InitialState::kAllOut : InitialState::kAllIn; 74 return; 75 } 76 if (GrClip::IsInsideClip(stackBounds, queryBounds)) { 77 fInitialState = InitialState::kAllIn; 78 return; 79 } 80 81 SkRect tightBounds; 82 SkAssertResult(tightBounds.intersect(stackBounds, queryBounds)); 83 fScissor = GrClip::GetPixelIBounds(tightBounds); 84 if (fScissor.isEmpty()) { 85 fInitialState = InitialState::kAllOut; 86 return; 87 } 88 fHasScissor = true; 89 90 fAAClipRect = stackBounds; 91 fAAClipRectGenID = stack.getTopmostGenID(); 92 SkASSERT(SK_InvalidGenID != fAAClipRectGenID); 93 94 fInitialState = InitialState::kAllIn; 95 } else { 96 SkRect tighterQuery = queryBounds; 97 if (SkClipStack::kNormal_BoundsType == stackBoundsType) { 98 // Tighten the query by introducing a new clip at the stack's pixel boundaries. (This 99 // new clip will be enforced by the scissor.) 100 SkAssertResult(tighterQuery.intersect(GrClip::GetPixelBounds(stackBounds))); 101 } 102 103 fScissor = GrClip::GetPixelIBounds(tighterQuery); 104 if (fScissor.isEmpty()) { 105 fInitialState = InitialState::kAllOut; 106 return; 107 } 108 fHasScissor = true; 109 110 // Now that we have determined the bounds to use and filtered out the trivial cases, call 111 // the helper that actually walks the stack. 112 this->walkStack(stack, tighterQuery); 113 } 114 115 if (SK_InvalidGenID != fAAClipRectGenID && // Is there an AA clip rect? 116 ClipResult::kNotClipped == this->addAnalyticFP(fAAClipRect, Invert::kNo, GrAA::kYes)) { 117 if (fMaskElements.isEmpty()) { 118 // Use a replace since it is faster than intersect. 119 fMaskElements.addToHead(fAAClipRect, SkMatrix::I(), kReplace_SkClipOp, true /*doAA*/); 120 fInitialState = InitialState::kAllOut; 121 } else { 122 fMaskElements.addToTail(fAAClipRect, SkMatrix::I(), kIntersect_SkClipOp, true /*doAA*/); 123 } 124 fMaskRequiresAA = true; 125 fMaskGenID = fAAClipRectGenID; 126 } 127 } 128 129 void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBounds) { 130 // walk backwards until we get to: 131 // a) the beginning 132 // b) an operation that is known to make the bounds all inside/outside 133 // c) a replace operation 134 135 enum class InitialTriState { 136 kUnknown = -1, 137 kAllIn = (int)GrReducedClip::InitialState::kAllIn, 138 kAllOut = (int)GrReducedClip::InitialState::kAllOut 139 } initialTriState = InitialTriState::kUnknown; 140 141 // During our backwards walk, track whether we've seen ops that either grow or shrink the clip. 142 // TODO: track these per saved clip so that we can consider them on the forward pass. 143 bool embiggens = false; 144 bool emsmallens = false; 145 146 // We use a slightly relaxed set of query bounds for element containment tests. This is to 147 // account for floating point rounding error that may have occurred during coord transforms. 148 SkRect relaxedQueryBounds = queryBounds.makeInset(GrClip::kBoundsTolerance, 149 GrClip::kBoundsTolerance); 150 if (relaxedQueryBounds.isEmpty()) { 151 relaxedQueryBounds = queryBounds; 152 } 153 154 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart); 155 int numAAElements = 0; 156 while (InitialTriState::kUnknown == initialTriState) { 157 const Element* element = iter.prev(); 158 if (nullptr == element) { 159 initialTriState = InitialTriState::kAllIn; 160 break; 161 } 162 if (SkClipStack::kEmptyGenID == element->getGenID()) { 163 initialTriState = InitialTriState::kAllOut; 164 break; 165 } 166 if (SkClipStack::kWideOpenGenID == element->getGenID()) { 167 initialTriState = InitialTriState::kAllIn; 168 break; 169 } 170 171 bool skippable = false; 172 bool isFlip = false; // does this op just flip the in/out state of every point in the bounds 173 174 switch (element->getOp()) { 175 case kDifference_SkClipOp: 176 // check if the shape subtracted either contains the entire bounds (and makes 177 // the clip empty) or is outside the bounds and therefore can be skipped. 178 if (element->isInverseFilled()) { 179 if (element->contains(relaxedQueryBounds)) { 180 skippable = true; 181 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { 182 initialTriState = InitialTriState::kAllOut; 183 skippable = true; 184 } else if (!embiggens) { 185 ClipResult result = this->clipInsideElement(element); 186 if (ClipResult::kMadeEmpty == result) { 187 return; 188 } 189 skippable = (ClipResult::kClipped == result); 190 } 191 } else { 192 if (element->contains(relaxedQueryBounds)) { 193 initialTriState = InitialTriState::kAllOut; 194 skippable = true; 195 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { 196 skippable = true; 197 } else if (!embiggens) { 198 ClipResult result = this->clipOutsideElement(element); 199 if (ClipResult::kMadeEmpty == result) { 200 return; 201 } 202 skippable = (ClipResult::kClipped == result); 203 } 204 } 205 if (!skippable) { 206 emsmallens = true; 207 } 208 break; 209 case kIntersect_SkClipOp: 210 // check if the shape intersected contains the entire bounds and therefore can 211 // be skipped or it is outside the entire bounds and therefore makes the clip 212 // empty. 213 if (element->isInverseFilled()) { 214 if (element->contains(relaxedQueryBounds)) { 215 initialTriState = InitialTriState::kAllOut; 216 skippable = true; 217 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { 218 skippable = true; 219 } else if (!embiggens) { 220 ClipResult result = this->clipOutsideElement(element); 221 if (ClipResult::kMadeEmpty == result) { 222 return; 223 } 224 skippable = (ClipResult::kClipped == result); 225 } 226 } else { 227 if (element->contains(relaxedQueryBounds)) { 228 skippable = true; 229 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { 230 initialTriState = InitialTriState::kAllOut; 231 skippable = true; 232 } else if (!embiggens) { 233 ClipResult result = this->clipInsideElement(element); 234 if (ClipResult::kMadeEmpty == result) { 235 return; 236 } 237 skippable = (ClipResult::kClipped == result); 238 } 239 } 240 if (!skippable) { 241 emsmallens = true; 242 } 243 break; 244 case kUnion_SkClipOp: 245 // If the union-ed shape contains the entire bounds then after this element 246 // the bounds is entirely inside the clip. If the union-ed shape is outside the 247 // bounds then this op can be skipped. 248 if (element->isInverseFilled()) { 249 if (element->contains(relaxedQueryBounds)) { 250 skippable = true; 251 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { 252 initialTriState = InitialTriState::kAllIn; 253 skippable = true; 254 } 255 } else { 256 if (element->contains(relaxedQueryBounds)) { 257 initialTriState = InitialTriState::kAllIn; 258 skippable = true; 259 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { 260 skippable = true; 261 } 262 } 263 if (!skippable) { 264 embiggens = true; 265 } 266 break; 267 case kXOR_SkClipOp: 268 // If the bounds is entirely inside the shape being xor-ed then the effect is 269 // to flip the inside/outside state of every point in the bounds. We may be 270 // able to take advantage of this in the forward pass. If the xor-ed shape 271 // doesn't intersect the bounds then it can be skipped. 272 if (element->isInverseFilled()) { 273 if (element->contains(relaxedQueryBounds)) { 274 skippable = true; 275 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { 276 isFlip = true; 277 } 278 } else { 279 if (element->contains(relaxedQueryBounds)) { 280 isFlip = true; 281 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { 282 skippable = true; 283 } 284 } 285 if (!skippable) { 286 emsmallens = embiggens = true; 287 } 288 break; 289 case kReverseDifference_SkClipOp: 290 // When the bounds is entirely within the rev-diff shape then this behaves like xor 291 // and reverses every point inside the bounds. If the shape is completely outside 292 // the bounds then we know after this element is applied that the bounds will be 293 // all outside the current clip.B 294 if (element->isInverseFilled()) { 295 if (element->contains(relaxedQueryBounds)) { 296 initialTriState = InitialTriState::kAllOut; 297 skippable = true; 298 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { 299 isFlip = true; 300 } 301 } else { 302 if (element->contains(relaxedQueryBounds)) { 303 isFlip = true; 304 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { 305 initialTriState = InitialTriState::kAllOut; 306 skippable = true; 307 } 308 } 309 if (!skippable) { 310 emsmallens = embiggens = true; 311 } 312 break; 313 314 case kReplace_SkClipOp: 315 // Replace will always terminate our walk. We will either begin the forward walk 316 // at the replace op or detect here than the shape is either completely inside 317 // or completely outside the bounds. In this latter case it can be skipped by 318 // setting the correct value for initialTriState. 319 if (element->isInverseFilled()) { 320 if (element->contains(relaxedQueryBounds)) { 321 initialTriState = InitialTriState::kAllOut; 322 skippable = true; 323 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { 324 initialTriState = InitialTriState::kAllIn; 325 skippable = true; 326 } else if (!embiggens) { 327 ClipResult result = this->clipOutsideElement(element); 328 if (ClipResult::kMadeEmpty == result) { 329 return; 330 } 331 if (ClipResult::kClipped == result) { 332 initialTriState = InitialTriState::kAllIn; 333 skippable = true; 334 } 335 } 336 } else { 337 if (element->contains(relaxedQueryBounds)) { 338 initialTriState = InitialTriState::kAllIn; 339 skippable = true; 340 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { 341 initialTriState = InitialTriState::kAllOut; 342 skippable = true; 343 } else if (!embiggens) { 344 ClipResult result = this->clipInsideElement(element); 345 if (ClipResult::kMadeEmpty == result) { 346 return; 347 } 348 if (ClipResult::kClipped == result) { 349 initialTriState = InitialTriState::kAllIn; 350 skippable = true; 351 } 352 } 353 } 354 if (!skippable) { 355 initialTriState = InitialTriState::kAllOut; 356 embiggens = emsmallens = true; 357 } 358 break; 359 default: 360 SkDEBUGFAIL("Unexpected op."); 361 break; 362 } 363 if (!skippable) { 364 if (fMaskElements.isEmpty()) { 365 // This will be the last element. Record the stricter genID. 366 fMaskGenID = element->getGenID(); 367 } 368 369 // if it is a flip, change it to a bounds-filling rect 370 if (isFlip) { 371 SkASSERT(kXOR_SkClipOp == element->getOp() || 372 kReverseDifference_SkClipOp == element->getOp()); 373 fMaskElements.addToHead(SkRect::Make(fScissor), SkMatrix::I(), 374 kReverseDifference_SkClipOp, false); 375 } else { 376 Element* newElement = fMaskElements.addToHead(*element); 377 if (newElement->isAA()) { 378 ++numAAElements; 379 } 380 // Intersecting an inverse shape is the same as differencing the non-inverse shape. 381 // Replacing with an inverse shape is the same as setting initialState=kAllIn and 382 // differencing the non-inverse shape. 383 bool isReplace = kReplace_SkClipOp == newElement->getOp(); 384 if (newElement->isInverseFilled() && 385 (kIntersect_SkClipOp == newElement->getOp() || isReplace)) { 386 newElement->invertShapeFillType(); 387 newElement->setOp(kDifference_SkClipOp); 388 if (isReplace) { 389 SkASSERT(InitialTriState::kAllOut == initialTriState); 390 initialTriState = InitialTriState::kAllIn; 391 } 392 } 393 } 394 } 395 } 396 397 if ((InitialTriState::kAllOut == initialTriState && !embiggens) || 398 (InitialTriState::kAllIn == initialTriState && !emsmallens)) { 399 fMaskElements.reset(); 400 numAAElements = 0; 401 } else { 402 Element* element = fMaskElements.headIter().get(); 403 while (element) { 404 bool skippable = false; 405 switch (element->getOp()) { 406 case kDifference_SkClipOp: 407 // subtracting from the empty set yields the empty set. 408 skippable = InitialTriState::kAllOut == initialTriState; 409 break; 410 case kIntersect_SkClipOp: 411 // intersecting with the empty set yields the empty set 412 if (InitialTriState::kAllOut == initialTriState) { 413 skippable = true; 414 } else { 415 // We can clear to zero and then simply draw the clip element. 416 initialTriState = InitialTriState::kAllOut; 417 element->setOp(kReplace_SkClipOp); 418 } 419 break; 420 case kUnion_SkClipOp: 421 if (InitialTriState::kAllIn == initialTriState) { 422 // unioning the infinite plane with anything is a no-op. 423 skippable = true; 424 } else { 425 // unioning the empty set with a shape is the shape. 426 element->setOp(kReplace_SkClipOp); 427 } 428 break; 429 case kXOR_SkClipOp: 430 if (InitialTriState::kAllOut == initialTriState) { 431 // xor could be changed to diff in the kAllIn case, not sure it's a win. 432 element->setOp(kReplace_SkClipOp); 433 } 434 break; 435 case kReverseDifference_SkClipOp: 436 if (InitialTriState::kAllIn == initialTriState) { 437 // subtracting the whole plane will yield the empty set. 438 skippable = true; 439 initialTriState = InitialTriState::kAllOut; 440 } else { 441 // this picks up flips inserted in the backwards pass. 442 skippable = element->isInverseFilled() ? 443 GrClip::IsOutsideClip(element->getBounds(), queryBounds) : 444 element->contains(relaxedQueryBounds); 445 if (skippable) { 446 initialTriState = InitialTriState::kAllIn; 447 } else { 448 element->setOp(kReplace_SkClipOp); 449 } 450 } 451 break; 452 case kReplace_SkClipOp: 453 skippable = false; // we would have skipped it in the backwards walk if we 454 // could've. 455 break; 456 default: 457 SkDEBUGFAIL("Unexpected op."); 458 break; 459 } 460 if (!skippable) { 461 break; 462 } else { 463 if (element->isAA()) { 464 --numAAElements; 465 } 466 fMaskElements.popHead(); 467 element = fMaskElements.headIter().get(); 468 } 469 } 470 } 471 fMaskRequiresAA = numAAElements > 0; 472 473 SkASSERT(InitialTriState::kUnknown != initialTriState); 474 fInitialState = static_cast<GrReducedClip::InitialState>(initialTriState); 475 } 476 477 GrReducedClip::ClipResult GrReducedClip::clipInsideElement(const Element* element) { 478 SkIRect elementIBounds; 479 if (!element->isAA()) { 480 element->getBounds().round(&elementIBounds); 481 } else { 482 elementIBounds = GrClip::GetPixelIBounds(element->getBounds()); 483 } 484 SkASSERT(fHasScissor); 485 if (!fScissor.intersect(elementIBounds)) { 486 this->makeEmpty(); 487 return ClipResult::kMadeEmpty; 488 } 489 490 switch (element->getDeviceSpaceType()) { 491 case Element::DeviceSpaceType::kEmpty: 492 return ClipResult::kMadeEmpty; 493 494 case Element::DeviceSpaceType::kRect: 495 SkASSERT(element->getBounds() == element->getDeviceSpaceRect()); 496 SkASSERT(!element->isInverseFilled()); 497 if (element->isAA()) { 498 if (SK_InvalidGenID == fAAClipRectGenID) { // No AA clip rect yet? 499 fAAClipRect = element->getDeviceSpaceRect(); 500 // fAAClipRectGenID is the value we should use for fMaskGenID if we end up 501 // moving the AA clip rect into the mask. The mask GenID is simply the topmost 502 // element's GenID. And since we walk the stack backwards, this means it's just 503 // the first element we don't skip during our walk. 504 fAAClipRectGenID = fMaskElements.isEmpty() ? element->getGenID() : fMaskGenID; 505 SkASSERT(SK_InvalidGenID != fAAClipRectGenID); 506 } else if (!fAAClipRect.intersect(element->getDeviceSpaceRect())) { 507 this->makeEmpty(); 508 return ClipResult::kMadeEmpty; 509 } 510 } 511 return ClipResult::kClipped; 512 513 case Element::DeviceSpaceType::kRRect: 514 SkASSERT(!element->isInverseFilled()); 515 return this->addAnalyticFP(element->getDeviceSpaceRRect(), Invert::kNo, 516 GrAA(element->isAA())); 517 518 case Element::DeviceSpaceType::kPath: 519 return this->addAnalyticFP(element->getDeviceSpacePath(), 520 Invert(element->isInverseFilled()), GrAA(element->isAA())); 521 } 522 523 SK_ABORT("Unexpected DeviceSpaceType"); 524 return ClipResult::kNotClipped; 525 } 526 527 GrReducedClip::ClipResult GrReducedClip::clipOutsideElement(const Element* element) { 528 switch (element->getDeviceSpaceType()) { 529 case Element::DeviceSpaceType::kEmpty: 530 return ClipResult::kMadeEmpty; 531 532 case Element::DeviceSpaceType::kRect: 533 SkASSERT(!element->isInverseFilled()); 534 if (fWindowRects.count() < fMaxWindowRectangles) { 535 // Clip out the inside of every rect. We won't be able to entirely skip the AA ones, 536 // but it saves processing time. 537 this->addWindowRectangle(element->getDeviceSpaceRect(), element->isAA()); 538 if (!element->isAA()) { 539 return ClipResult::kClipped; 540 } 541 } 542 return this->addAnalyticFP(element->getDeviceSpaceRect(), Invert::kYes, 543 GrAA(element->isAA())); 544 545 case Element::DeviceSpaceType::kRRect: { 546 SkASSERT(!element->isInverseFilled()); 547 const SkRRect& clipRRect = element->getDeviceSpaceRRect(); 548 ClipResult clipResult = this->addAnalyticFP(clipRRect, Invert::kYes, 549 GrAA(element->isAA())); 550 if (fWindowRects.count() >= fMaxWindowRectangles) { 551 return clipResult; 552 } 553 554 // Clip out the interiors of round rects with two window rectangles in the shape of a 555 // "plus". This doesn't let us skip the clip element, but still saves processing time. 556 SkVector insetTL = clipRRect.radii(SkRRect::kUpperLeft_Corner); 557 SkVector insetBR = clipRRect.radii(SkRRect::kLowerRight_Corner); 558 if (SkRRect::kComplex_Type == clipRRect.getType()) { 559 const SkVector& insetTR = clipRRect.radii(SkRRect::kUpperRight_Corner); 560 const SkVector& insetBL = clipRRect.radii(SkRRect::kLowerLeft_Corner); 561 insetTL.fX = SkTMax(insetTL.x(), insetBL.x()); 562 insetTL.fY = SkTMax(insetTL.y(), insetTR.y()); 563 insetBR.fX = SkTMax(insetBR.x(), insetTR.x()); 564 insetBR.fY = SkTMax(insetBR.y(), insetBL.y()); 565 } 566 const SkRect& bounds = clipRRect.getBounds(); 567 if (insetTL.x() + insetBR.x() >= bounds.width() || 568 insetTL.y() + insetBR.y() >= bounds.height()) { 569 return clipResult; // The interior "plus" is empty. 570 } 571 572 SkRect horzRect = SkRect::MakeLTRB(bounds.left(), bounds.top() + insetTL.y(), 573 bounds.right(), bounds.bottom() - insetBR.y()); 574 this->addWindowRectangle(horzRect, element->isAA()); 575 576 if (fWindowRects.count() < fMaxWindowRectangles) { 577 SkRect vertRect = SkRect::MakeLTRB(bounds.left() + insetTL.x(), bounds.top(), 578 bounds.right() - insetBR.x(), bounds.bottom()); 579 this->addWindowRectangle(vertRect, element->isAA()); 580 } 581 582 return clipResult; 583 } 584 585 case Element::DeviceSpaceType::kPath: 586 return this->addAnalyticFP(element->getDeviceSpacePath(), 587 Invert(!element->isInverseFilled()), GrAA(element->isAA())); 588 } 589 590 SK_ABORT("Unexpected DeviceSpaceType"); 591 return ClipResult::kNotClipped; 592 } 593 594 inline void GrReducedClip::addWindowRectangle(const SkRect& elementInteriorRect, bool elementIsAA) { 595 SkIRect window; 596 if (!elementIsAA) { 597 elementInteriorRect.round(&window); 598 } else { 599 elementInteriorRect.roundIn(&window); 600 } 601 if (!window.isEmpty()) { // Skip very thin windows that round to zero or negative dimensions. 602 fWindowRects.addWindow(window); 603 } 604 } 605 606 GrClipEdgeType GrReducedClip::GetClipEdgeType(Invert invert, GrAA aa) { 607 if (Invert::kNo == invert) { 608 return (GrAA::kYes == aa) ? GrClipEdgeType::kFillAA : GrClipEdgeType::kFillBW; 609 } else { 610 return (GrAA::kYes == aa) ? GrClipEdgeType::kInverseFillAA : GrClipEdgeType::kInverseFillBW; 611 } 612 } 613 614 GrReducedClip::ClipResult GrReducedClip::addAnalyticFP(const SkRect& deviceSpaceRect, 615 Invert invert, GrAA aa) { 616 if (this->numAnalyticFPs() >= fMaxAnalyticFPs) { 617 return ClipResult::kNotClipped; 618 } 619 620 fAnalyticFPs.push_back(GrAARectEffect::Make(GetClipEdgeType(invert, aa), deviceSpaceRect)); 621 SkASSERT(fAnalyticFPs.back()); 622 623 return ClipResult::kClipped; 624 } 625 626 GrReducedClip::ClipResult GrReducedClip::addAnalyticFP(const SkRRect& deviceSpaceRRect, 627 Invert invert, GrAA aa) { 628 if (this->numAnalyticFPs() >= fMaxAnalyticFPs) { 629 return ClipResult::kNotClipped; 630 } 631 632 if (auto fp = GrRRectEffect::Make(GetClipEdgeType(invert, aa), deviceSpaceRRect, *fCaps)) { 633 fAnalyticFPs.push_back(std::move(fp)); 634 return ClipResult::kClipped; 635 } 636 637 SkPath deviceSpacePath; 638 deviceSpacePath.setIsVolatile(true); 639 deviceSpacePath.addRRect(deviceSpaceRRect); 640 return this->addAnalyticFP(deviceSpacePath, invert, aa); 641 } 642 643 GrReducedClip::ClipResult GrReducedClip::addAnalyticFP(const SkPath& deviceSpacePath, 644 Invert invert, GrAA aa) { 645 if (this->numAnalyticFPs() >= fMaxAnalyticFPs) { 646 return ClipResult::kNotClipped; 647 } 648 649 if (auto fp = GrConvexPolyEffect::Make(GetClipEdgeType(invert, aa), deviceSpacePath)) { 650 fAnalyticFPs.push_back(std::move(fp)); 651 return ClipResult::kClipped; 652 } 653 654 if (fCCPR && GrAA::kYes == aa && fCCPR->canMakeClipProcessor(deviceSpacePath)) { 655 // Set aside CCPR paths for later. We will create their clip FPs once we know the ID of the 656 // opList they will operate in. 657 SkPath& ccprClipPath = fCCPRClipPaths.push_back(deviceSpacePath); 658 if (Invert::kYes == invert) { 659 ccprClipPath.toggleInverseFillType(); 660 } 661 return ClipResult::kClipped; 662 } 663 664 return ClipResult::kNotClipped; 665 } 666 667 void GrReducedClip::makeEmpty() { 668 fHasScissor = false; 669 fAAClipRectGenID = SK_InvalidGenID; 670 fWindowRects.reset(); 671 fMaskElements.reset(); 672 fInitialState = InitialState::kAllOut; 673 } 674 675 //////////////////////////////////////////////////////////////////////////////// 676 // Create a 8-bit clip mask in alpha 677 678 static bool stencil_element(GrRenderTargetContext* rtc, 679 const GrFixedClip& clip, 680 const GrUserStencilSettings* ss, 681 const SkMatrix& viewMatrix, 682 const SkClipStack::Element* element) { 683 GrAA aa = GrAA(element->isAA()); 684 switch (element->getDeviceSpaceType()) { 685 case SkClipStack::Element::DeviceSpaceType::kEmpty: 686 SkDEBUGFAIL("Should never get here with an empty element."); 687 break; 688 case SkClipStack::Element::DeviceSpaceType::kRect: 689 return rtc->priv().drawAndStencilRect(clip, ss, (SkRegion::Op)element->getOp(), 690 element->isInverseFilled(), aa, viewMatrix, 691 element->getDeviceSpaceRect()); 692 break; 693 default: { 694 SkPath path; 695 element->asDeviceSpacePath(&path); 696 if (path.isInverseFillType()) { 697 path.toggleInverseFillType(); 698 } 699 700 return rtc->priv().drawAndStencilPath(clip, ss, (SkRegion::Op)element->getOp(), 701 element->isInverseFilled(), aa, viewMatrix, path); 702 break; 703 } 704 } 705 706 return false; 707 } 708 709 static void draw_element(GrRenderTargetContext* rtc, 710 const GrClip& clip, // TODO: can this just always be WideOpen? 711 GrPaint&& paint, 712 GrAA aa, 713 const SkMatrix& viewMatrix, 714 const SkClipStack::Element* element) { 715 // TODO: Draw rrects directly here. 716 switch (element->getDeviceSpaceType()) { 717 case SkClipStack::Element::DeviceSpaceType::kEmpty: 718 SkDEBUGFAIL("Should never get here with an empty element."); 719 break; 720 case SkClipStack::Element::DeviceSpaceType::kRect: 721 rtc->drawRect(clip, std::move(paint), aa, viewMatrix, element->getDeviceSpaceRect()); 722 break; 723 default: { 724 SkPath path; 725 element->asDeviceSpacePath(&path); 726 if (path.isInverseFillType()) { 727 path.toggleInverseFillType(); 728 } 729 730 rtc->drawPath(clip, std::move(paint), aa, viewMatrix, path, GrStyle::SimpleFill()); 731 break; 732 } 733 } 734 } 735 736 bool GrReducedClip::drawAlphaClipMask(GrRenderTargetContext* rtc) const { 737 // The texture may be larger than necessary, this rect represents the part of the texture 738 // we populate with a rasterization of the clip. 739 GrFixedClip clip(SkIRect::MakeWH(fScissor.width(), fScissor.height())); 740 741 if (!fWindowRects.empty()) { 742 clip.setWindowRectangles(fWindowRects.makeOffset(-fScissor.left(), -fScissor.top()), 743 GrWindowRectsState::Mode::kExclusive); 744 } 745 746 // The scratch texture that we are drawing into can be substantially larger than the mask. Only 747 // clear the part that we care about. 748 GrColor initialCoverage = InitialState::kAllIn == this->initialState() ? -1 : 0; 749 rtc->priv().clear(clip, initialCoverage, GrRenderTargetContext::CanClearFullscreen::kYes); 750 751 // Set the matrix so that rendered clip elements are transformed to mask space from clip space. 752 SkMatrix translate; 753 translate.setTranslate(SkIntToScalar(-fScissor.left()), SkIntToScalar(-fScissor.top())); 754 755 // walk through each clip element and perform its set op 756 for (ElementList::Iter iter(fMaskElements); iter.get(); iter.next()) { 757 const Element* element = iter.get(); 758 SkRegion::Op op = (SkRegion::Op)element->getOp(); 759 GrAA aa = GrAA(element->isAA()); 760 bool invert = element->isInverseFilled(); 761 if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) { 762 // draw directly into the result with the stencil set to make the pixels affected 763 // by the clip shape be non-zero. 764 static constexpr GrUserStencilSettings kStencilInElement( 765 GrUserStencilSettings::StaticInit< 766 0xffff, 767 GrUserStencilTest::kAlways, 768 0xffff, 769 GrUserStencilOp::kReplace, 770 GrUserStencilOp::kReplace, 771 0xffff>() 772 ); 773 if (!stencil_element(rtc, clip, &kStencilInElement, translate, element)) { 774 return false; 775 } 776 777 // Draw to the exterior pixels (those with a zero stencil value). 778 static constexpr GrUserStencilSettings kDrawOutsideElement( 779 GrUserStencilSettings::StaticInit< 780 0x0000, 781 GrUserStencilTest::kEqual, 782 0xffff, 783 GrUserStencilOp::kZero, 784 GrUserStencilOp::kZero, 785 0xffff>() 786 ); 787 if (!rtc->priv().drawAndStencilRect(clip, &kDrawOutsideElement, op, !invert, GrAA::kNo, 788 translate, SkRect::Make(fScissor))) { 789 return false; 790 } 791 } else { 792 // all the remaining ops can just be directly draw into the accumulation buffer 793 GrPaint paint; 794 paint.setCoverageSetOpXPFactory(op, false); 795 796 draw_element(rtc, clip, std::move(paint), aa, translate, element); 797 } 798 } 799 800 return true; 801 } 802 803 //////////////////////////////////////////////////////////////////////////////// 804 // Create a 1-bit clip mask in the stencil buffer. 805 806 bool GrReducedClip::drawStencilClipMask(GrContext* context, 807 GrRenderTargetContext* renderTargetContext) const { 808 // We set the current clip to the bounds so that our recursive draws are scissored to them. 809 GrStencilClip stencilClip(fScissor, this->maskGenID()); 810 811 if (!fWindowRects.empty()) { 812 stencilClip.fixedClip().setWindowRectangles(fWindowRects, 813 GrWindowRectsState::Mode::kExclusive); 814 } 815 816 bool initialState = InitialState::kAllIn == this->initialState(); 817 renderTargetContext->priv().clearStencilClip(stencilClip.fixedClip(), initialState); 818 819 // walk through each clip element and perform its set op with the existing clip. 820 for (ElementList::Iter iter(fMaskElements); iter.get(); iter.next()) { 821 const Element* element = iter.get(); 822 GrAAType aaType = GrAAType::kNone; 823 if (element->isAA() && GrFSAAType::kNone != renderTargetContext->fsaaType()) { 824 aaType = GrAAType::kMSAA; 825 } 826 827 bool fillInverted = false; 828 829 // This will be used to determine whether the clip shape can be rendered into the 830 // stencil with arbitrary stencil settings. 831 GrPathRenderer::StencilSupport stencilSupport; 832 833 SkRegion::Op op = (SkRegion::Op)element->getOp(); 834 835 GrPathRenderer* pr = nullptr; 836 SkPath clipPath; 837 if (Element::DeviceSpaceType::kRect == element->getDeviceSpaceType()) { 838 stencilSupport = GrPathRenderer::kNoRestriction_StencilSupport; 839 fillInverted = false; 840 } else { 841 element->asDeviceSpacePath(&clipPath); 842 fillInverted = clipPath.isInverseFillType(); 843 if (fillInverted) { 844 clipPath.toggleInverseFillType(); 845 } 846 847 GrShape shape(clipPath, GrStyle::SimpleFill()); 848 GrPathRenderer::CanDrawPathArgs canDrawArgs; 849 canDrawArgs.fCaps = context->caps(); 850 canDrawArgs.fClipConservativeBounds = &stencilClip.fixedClip().scissorRect(); 851 canDrawArgs.fViewMatrix = &SkMatrix::I(); 852 canDrawArgs.fShape = &shape; 853 canDrawArgs.fAAType = aaType; 854 canDrawArgs.fHasUserStencilSettings = false; 855 856 GrDrawingManager* dm = context->contextPriv().drawingManager(); 857 pr = dm->getPathRenderer(canDrawArgs, false, GrPathRendererChain::DrawType::kStencil, 858 &stencilSupport); 859 if (!pr) { 860 return false; 861 } 862 } 863 864 bool canRenderDirectToStencil = 865 GrPathRenderer::kNoRestriction_StencilSupport == stencilSupport; 866 bool drawDirectToClip; // Given the renderer, the element, 867 // fill rule, and set operation should 868 // we render the element directly to 869 // stencil bit used for clipping. 870 GrUserStencilSettings const* const* stencilPasses = 871 GrStencilSettings::GetClipPasses(op, canRenderDirectToStencil, fillInverted, 872 &drawDirectToClip); 873 874 // draw the element to the client stencil bits if necessary 875 if (!drawDirectToClip) { 876 static constexpr GrUserStencilSettings kDrawToStencil( 877 GrUserStencilSettings::StaticInit< 878 0x0000, 879 GrUserStencilTest::kAlways, 880 0xffff, 881 GrUserStencilOp::kIncMaybeClamp, 882 GrUserStencilOp::kIncMaybeClamp, 883 0xffff>() 884 ); 885 if (Element::DeviceSpaceType::kRect == element->getDeviceSpaceType()) { 886 renderTargetContext->priv().stencilRect(stencilClip.fixedClip(), &kDrawToStencil, 887 aaType, SkMatrix::I(), 888 element->getDeviceSpaceRect()); 889 } else { 890 if (!clipPath.isEmpty()) { 891 GrShape shape(clipPath, GrStyle::SimpleFill()); 892 if (canRenderDirectToStencil) { 893 GrPaint paint; 894 paint.setXPFactory(GrDisableColorXPFactory::Get()); 895 896 GrPathRenderer::DrawPathArgs args{context, 897 std::move(paint), 898 &kDrawToStencil, 899 renderTargetContext, 900 &stencilClip.fixedClip(), 901 &stencilClip.fixedClip().scissorRect(), 902 &SkMatrix::I(), 903 &shape, 904 aaType, 905 false}; 906 pr->drawPath(args); 907 } else { 908 GrPathRenderer::StencilPathArgs args; 909 args.fContext = context; 910 args.fRenderTargetContext = renderTargetContext; 911 args.fClip = &stencilClip.fixedClip(); 912 args.fClipConservativeBounds = &stencilClip.fixedClip().scissorRect(); 913 args.fViewMatrix = &SkMatrix::I(); 914 args.fAAType = aaType; 915 args.fShape = &shape; 916 pr->stencilPath(args); 917 } 918 } 919 } 920 } 921 922 // now we modify the clip bit by rendering either the clip 923 // element directly or a bounding rect of the entire clip. 924 for (GrUserStencilSettings const* const* pass = stencilPasses; *pass; ++pass) { 925 if (drawDirectToClip) { 926 if (Element::DeviceSpaceType::kRect == element->getDeviceSpaceType()) { 927 renderTargetContext->priv().stencilRect(stencilClip, *pass, aaType, 928 SkMatrix::I(), 929 element->getDeviceSpaceRect()); 930 } else { 931 GrShape shape(clipPath, GrStyle::SimpleFill()); 932 GrPaint paint; 933 paint.setXPFactory(GrDisableColorXPFactory::Get()); 934 GrPathRenderer::DrawPathArgs args{context, 935 std::move(paint), 936 *pass, 937 renderTargetContext, 938 &stencilClip, 939 &stencilClip.fixedClip().scissorRect(), 940 &SkMatrix::I(), 941 &shape, 942 aaType, 943 false}; 944 pr->drawPath(args); 945 } 946 } else { 947 // The view matrix is setup to do clip space -> stencil space translation, so 948 // draw rect in clip space. 949 renderTargetContext->priv().stencilRect(stencilClip, *pass, aaType, SkMatrix::I(), 950 SkRect::Make(fScissor)); 951 } 952 } 953 } 954 return true; 955 } 956 957 std::unique_ptr<GrFragmentProcessor> GrReducedClip::finishAndDetachAnalyticFPs( 958 GrProxyProvider* proxyProvider, uint32_t opListID, 959 int rtWidth, int rtHeight) { 960 // Make sure finishAndDetachAnalyticFPs hasn't been called already. 961 SkDEBUGCODE(for (const auto& fp : fAnalyticFPs) { SkASSERT(fp); }) 962 963 if (!fCCPRClipPaths.empty()) { 964 fAnalyticFPs.reserve(fAnalyticFPs.count() + fCCPRClipPaths.count()); 965 for (const SkPath& ccprClipPath : fCCPRClipPaths) { 966 SkASSERT(fHasScissor); 967 auto fp = fCCPR->makeClipProcessor(proxyProvider, opListID, ccprClipPath, fScissor, 968 rtWidth, rtHeight); 969 fAnalyticFPs.push_back(std::move(fp)); 970 } 971 fCCPRClipPaths.reset(); 972 } 973 974 return GrFragmentProcessor::RunInSeries(fAnalyticFPs.begin(), fAnalyticFPs.count()); 975 } 976