Home | History | Annotate | Download | only in gpu
      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