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