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 "GrClipStackClip.h"
      9 
     10 #include "GrAppliedClip.h"
     11 #include "GrContextPriv.h"
     12 #include "GrDrawingManager.h"
     13 #include "GrRenderTargetContextPriv.h"
     14 #include "GrFixedClip.h"
     15 #include "GrGpuResourcePriv.h"
     16 #include "GrRenderTargetPriv.h"
     17 #include "GrResourceProvider.h"
     18 #include "GrStencilAttachment.h"
     19 #include "GrSWMaskHelper.h"
     20 #include "GrTextureProxy.h"
     21 #include "effects/GrConvexPolyEffect.h"
     22 #include "effects/GrRRectEffect.h"
     23 #include "effects/GrTextureDomain.h"
     24 #include "SkClipOpPriv.h"
     25 
     26 typedef SkClipStack::Element Element;
     27 typedef GrReducedClip::InitialState InitialState;
     28 typedef GrReducedClip::ElementList ElementList;
     29 
     30 static const int kMaxAnalyticElements = 4;
     31 const char GrClipStackClip::kMaskTestTag[] = "clip_mask";
     32 
     33 bool GrClipStackClip::quickContains(const SkRect& rect) const {
     34     if (!fStack || fStack->isWideOpen()) {
     35         return true;
     36     }
     37     return fStack->quickContains(rect);
     38 }
     39 
     40 bool GrClipStackClip::quickContains(const SkRRect& rrect) const {
     41     if (!fStack || fStack->isWideOpen()) {
     42         return true;
     43     }
     44     return fStack->quickContains(rrect);
     45 }
     46 
     47 bool GrClipStackClip::isRRect(const SkRect& origRTBounds, SkRRect* rr, GrAA* aa) const {
     48     if (!fStack) {
     49         return false;
     50     }
     51     const SkRect* rtBounds = &origRTBounds;
     52     bool isAA;
     53     if (fStack->isRRect(*rtBounds, rr, &isAA)) {
     54         *aa = GrBoolToAA(isAA);
     55         return true;
     56     }
     57     return false;
     58 }
     59 
     60 void GrClipStackClip::getConservativeBounds(int width, int height, SkIRect* devResult,
     61                                             bool* isIntersectionOfRects) const {
     62     if (!fStack) {
     63         devResult->setXYWH(0, 0, width, height);
     64         if (isIntersectionOfRects) {
     65             *isIntersectionOfRects = true;
     66         }
     67         return;
     68     }
     69     SkRect devBounds;
     70     fStack->getConservativeBounds(0, 0, width, height, &devBounds, isIntersectionOfRects);
     71     devBounds.roundOut(devResult);
     72 }
     73 
     74 ////////////////////////////////////////////////////////////////////////////////
     75 // set up the draw state to enable the aa clipping mask.
     76 static sk_sp<GrFragmentProcessor> create_fp_for_mask(sk_sp<GrTextureProxy> mask,
     77                                                      const SkIRect &devBound) {
     78     SkIRect domainTexels = SkIRect::MakeWH(devBound.width(), devBound.height());
     79     return GrDeviceSpaceTextureDecalFragmentProcessor::Make(std::move(mask), domainTexels,
     80                                                             {devBound.fLeft, devBound.fTop});
     81 }
     82 
     83 // Does the path in 'element' require SW rendering? If so, return true (and,
     84 // optionally, set 'prOut' to NULL. If not, return false (and, optionally, set
     85 // 'prOut' to the non-SW path renderer that will do the job).
     86 bool GrClipStackClip::PathNeedsSWRenderer(GrContext* context,
     87                                           bool hasUserStencilSettings,
     88                                           const GrRenderTargetContext* renderTargetContext,
     89                                           const SkMatrix& viewMatrix,
     90                                           const Element* element,
     91                                           GrPathRenderer** prOut,
     92                                           bool needsStencil) {
     93     if (Element::kRect_Type == element->getType()) {
     94         // rects can always be drawn directly w/o using the software path
     95         // TODO: skip rrects once we're drawing them directly.
     96         if (prOut) {
     97             *prOut = nullptr;
     98         }
     99         return false;
    100     } else {
    101         // We shouldn't get here with an empty clip element.
    102         SkASSERT(Element::kEmpty_Type != element->getType());
    103 
    104         // the gpu alpha mask will draw the inverse paths as non-inverse to a temp buffer
    105         SkPath path;
    106         element->asPath(&path);
    107         if (path.isInverseFillType()) {
    108             path.toggleInverseFillType();
    109         }
    110 
    111         GrPathRendererChain::DrawType type =
    112                 needsStencil ? GrPathRendererChain::DrawType::kStencilAndColor
    113                              : GrPathRendererChain::DrawType::kColor;
    114 
    115         GrShape shape(path, GrStyle::SimpleFill());
    116         GrPathRenderer::CanDrawPathArgs canDrawArgs;
    117         canDrawArgs.fCaps = context->caps();
    118         canDrawArgs.fViewMatrix = &viewMatrix;
    119         canDrawArgs.fShape = &shape;
    120         canDrawArgs.fAAType = GrChooseAAType(GrBoolToAA(element->isAA()),
    121                                              renderTargetContext->fsaaType(),
    122                                              GrAllowMixedSamples::kYes,
    123                                              *context->caps());
    124         canDrawArgs.fHasUserStencilSettings = hasUserStencilSettings;
    125 
    126         // the 'false' parameter disallows use of the SW path renderer
    127         GrPathRenderer* pr =
    128             context->contextPriv().drawingManager()->getPathRenderer(canDrawArgs, false, type);
    129         if (prOut) {
    130             *prOut = pr;
    131         }
    132         return SkToBool(!pr);
    133     }
    134 }
    135 
    136 /*
    137  * This method traverses the clip stack to see if the GrSoftwarePathRenderer
    138  * will be used on any element. If so, it returns true to indicate that the
    139  * entire clip should be rendered in SW and then uploaded en masse to the gpu.
    140  */
    141 bool GrClipStackClip::UseSWOnlyPath(GrContext* context,
    142                                     bool hasUserStencilSettings,
    143                                     const GrRenderTargetContext* renderTargetContext,
    144                                     const GrReducedClip& reducedClip) {
    145     // TODO: generalize this function so that when
    146     // a clip gets complex enough it can just be done in SW regardless
    147     // of whether it would invoke the GrSoftwarePathRenderer.
    148 
    149     // If we're avoiding stencils, always use SW:
    150     if (context->caps()->avoidStencilBuffers())
    151         return true;
    152 
    153     // Set the matrix so that rendered clip elements are transformed to mask space from clip
    154     // space.
    155     SkMatrix translate;
    156     translate.setTranslate(SkIntToScalar(-reducedClip.left()), SkIntToScalar(-reducedClip.top()));
    157 
    158     for (ElementList::Iter iter(reducedClip.elements()); iter.get(); iter.next()) {
    159         const Element* element = iter.get();
    160 
    161         SkClipOp op = element->getOp();
    162         bool invert = element->isInverseFilled();
    163         bool needsStencil = invert ||
    164                             kIntersect_SkClipOp == op || kReverseDifference_SkClipOp == op;
    165 
    166         if (PathNeedsSWRenderer(context, hasUserStencilSettings,
    167                                 renderTargetContext, translate, element, nullptr, needsStencil)) {
    168             return true;
    169         }
    170     }
    171     return false;
    172 }
    173 
    174 static bool get_analytic_clip_processor(const ElementList& elements,
    175                                         bool abortIfAA,
    176                                         const SkRect& drawDevBounds,
    177                                         sk_sp<GrFragmentProcessor>* resultFP) {
    178     SkASSERT(elements.count() <= kMaxAnalyticElements);
    179     SkSTArray<kMaxAnalyticElements, sk_sp<GrFragmentProcessor>> fps;
    180     ElementList::Iter iter(elements);
    181     while (iter.get()) {
    182         SkClipOp op = iter.get()->getOp();
    183         bool invert;
    184         bool skip = false;
    185         switch (op) {
    186             case kReplace_SkClipOp:
    187                 SkASSERT(iter.get() == elements.head());
    188                 // Fallthrough, handled same as intersect.
    189             case kIntersect_SkClipOp:
    190                 invert = false;
    191                 if (iter.get()->contains(drawDevBounds)) {
    192                     skip = true;
    193                 }
    194                 break;
    195             case kDifference_SkClipOp:
    196                 invert = true;
    197                 // We don't currently have a cheap test for whether a rect is fully outside an
    198                 // element's primitive, so don't attempt to set skip.
    199                 break;
    200             default:
    201                 return false;
    202         }
    203         if (!skip) {
    204             GrPrimitiveEdgeType edgeType;
    205             if (iter.get()->isAA()) {
    206                 if (abortIfAA) {
    207                     return false;
    208                 }
    209                 edgeType =
    210                     invert ? kInverseFillAA_GrProcessorEdgeType : kFillAA_GrProcessorEdgeType;
    211             } else {
    212                 edgeType =
    213                     invert ? kInverseFillBW_GrProcessorEdgeType : kFillBW_GrProcessorEdgeType;
    214             }
    215 
    216             switch (iter.get()->getType()) {
    217                 case SkClipStack::Element::kPath_Type:
    218                     fps.emplace_back(GrConvexPolyEffect::Make(edgeType, iter.get()->getPath()));
    219                     break;
    220                 case SkClipStack::Element::kRRect_Type: {
    221                     fps.emplace_back(GrRRectEffect::Make(edgeType, iter.get()->getRRect()));
    222                     break;
    223                 }
    224                 case SkClipStack::Element::kRect_Type: {
    225                     fps.emplace_back(GrConvexPolyEffect::Make(edgeType, iter.get()->getRect()));
    226                     break;
    227                 }
    228                 default:
    229                     break;
    230             }
    231             if (!fps.back()) {
    232                 return false;
    233             }
    234         }
    235         iter.next();
    236     }
    237 
    238     *resultFP = nullptr;
    239     if (fps.count()) {
    240         *resultFP = GrFragmentProcessor::RunInSeries(fps.begin(), fps.count());
    241     }
    242     return true;
    243 }
    244 
    245 ////////////////////////////////////////////////////////////////////////////////
    246 // sort out what kind of clip mask needs to be created: alpha, stencil,
    247 // scissor, or entirely software
    248 bool GrClipStackClip::apply(GrContext* context, GrRenderTargetContext* renderTargetContext,
    249                             bool useHWAA, bool hasUserStencilSettings, GrAppliedClip* out,
    250                             SkRect* bounds) const {
    251     SkRect devBounds = SkRect::MakeIWH(renderTargetContext->width(), renderTargetContext->height());
    252     if (!devBounds.intersect(*bounds)) {
    253         return false;
    254     }
    255 
    256     if (!fStack || fStack->isWideOpen()) {
    257         return true;
    258     }
    259 
    260     const GrReducedClip reducedClip(*fStack, devBounds,
    261                                     renderTargetContext->priv().maxWindowRectangles());
    262 
    263     if (reducedClip.hasIBounds() && !GrClip::IsInsideClip(reducedClip.ibounds(), devBounds)) {
    264         out->addScissor(reducedClip.ibounds(), bounds);
    265     }
    266 
    267     if (!reducedClip.windowRectangles().empty()) {
    268         out->addWindowRectangles(reducedClip.windowRectangles(),
    269                                  GrWindowRectsState::Mode::kExclusive);
    270     }
    271 
    272     if (reducedClip.elements().isEmpty()) {
    273         return InitialState::kAllIn == reducedClip.initialState();
    274     }
    275 
    276 #ifdef SK_DEBUG
    277     SkASSERT(reducedClip.hasIBounds());
    278     SkIRect rtIBounds = SkIRect::MakeWH(renderTargetContext->width(),
    279                                         renderTargetContext->height());
    280     const SkIRect& clipIBounds = reducedClip.ibounds();
    281     SkASSERT(rtIBounds.contains(clipIBounds)); // Mask shouldn't be larger than the RT.
    282 #endif
    283 
    284     bool avoidStencilBuffers = context->caps()->avoidStencilBuffers();
    285 
    286     // An element count of 4 was chosen because of the common pattern in Blink of:
    287     //   isect RR
    288     //   diff  RR
    289     //   isect convex_poly
    290     //   isect convex_poly
    291     // when drawing rounded div borders. This could probably be tuned based on a
    292     // configuration's relative costs of switching RTs to generate a mask vs
    293     // longer shaders.
    294     if (reducedClip.elements().count() <= kMaxAnalyticElements) {
    295         // When there are multiple samples we want to do per-sample clipping, not compute a
    296         // fractional pixel coverage.
    297         bool disallowAnalyticAA =
    298                 GrFSAAType::kNone != renderTargetContext->fsaaType() && !avoidStencilBuffers;
    299         if (disallowAnalyticAA && !renderTargetContext->numColorSamples()) {
    300             // With a single color sample, any coverage info is lost from color once it hits the
    301             // color buffer anyway, so we may as well use coverage AA if nothing else in the pipe
    302             // is multisampled.
    303             disallowAnalyticAA = useHWAA || hasUserStencilSettings;
    304         }
    305         sk_sp<GrFragmentProcessor> clipFP;
    306         if ((reducedClip.requiresAA() || avoidStencilBuffers) &&
    307             get_analytic_clip_processor(reducedClip.elements(), disallowAnalyticAA, devBounds,
    308                                         &clipFP)) {
    309             out->addCoverageFP(std::move(clipFP));
    310             return true;
    311         }
    312     }
    313 
    314     // If the stencil buffer is multisampled we can use it to do everything.
    315     if ((GrFSAAType::kNone == renderTargetContext->fsaaType() && reducedClip.requiresAA()) ||
    316         avoidStencilBuffers) {
    317         sk_sp<GrTextureProxy> result;
    318         if (UseSWOnlyPath(context, hasUserStencilSettings, renderTargetContext, reducedClip)) {
    319             // The clip geometry is complex enough that it will be more efficient to create it
    320             // entirely in software
    321             result = this->createSoftwareClipMask(context, reducedClip);
    322         } else {
    323             result = this->createAlphaClipMask(context, reducedClip);
    324         }
    325 
    326         if (result) {
    327             // The mask's top left coord should be pinned to the rounded-out top left corner of
    328             // the clip's device space bounds.
    329             out->addCoverageFP(create_fp_for_mask(std::move(result), reducedClip.ibounds()));
    330             return true;
    331         }
    332 
    333         // If alpha or software clip mask creation fails, fall through to the stencil code paths,
    334         // unless stencils are disallowed.
    335         if (context->caps()->avoidStencilBuffers()) {
    336             SkDebugf("WARNING: Clip mask requires stencil, but stencil unavailable. Clip will be ignored.\n");
    337             return false;
    338         }
    339     }
    340 
    341     GrRenderTarget* rt = renderTargetContext->accessRenderTarget();
    342     if (!rt) {
    343         return true;
    344     }
    345 
    346     // use the stencil clip if we can't represent the clip as a rectangle.
    347     if (!context->resourceProvider()->attachStencilAttachment(rt)) {
    348         SkDebugf("WARNING: failed to attach stencil buffer for clip mask. Clip will be ignored.\n");
    349         return true;
    350     }
    351 
    352     // This relies on the property that a reduced sub-rect of the last clip will contain all the
    353     // relevant window rectangles that were in the last clip. This subtle requirement will go away
    354     // after clipping is overhauled.
    355     if (renderTargetContext->priv().mustRenderClip(reducedClip.elementsGenID(),
    356                                                    reducedClip.ibounds())) {
    357         reducedClip.drawStencilClipMask(context, renderTargetContext);
    358         renderTargetContext->priv().setLastClip(reducedClip.elementsGenID(), reducedClip.ibounds());
    359     }
    360     out->addStencilClip(reducedClip.elementsGenID());
    361     return true;
    362 }
    363 
    364 ////////////////////////////////////////////////////////////////////////////////
    365 // Create a 8-bit clip mask in alpha
    366 
    367 static void create_clip_mask_key(uint32_t clipGenID, const SkIRect& bounds, GrUniqueKey* key) {
    368     static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
    369     GrUniqueKey::Builder builder(key, kDomain, 3, GrClipStackClip::kMaskTestTag);
    370     builder[0] = clipGenID;
    371     // SkToS16 because image filters outset layers to a size indicated by the filter, which can
    372     // sometimes result in negative coordinates from device space.
    373     builder[1] = SkToS16(bounds.fLeft) | (SkToS16(bounds.fRight) << 16);
    374     builder[2] = SkToS16(bounds.fTop) | (SkToS16(bounds.fBottom) << 16);
    375 }
    376 
    377 static void add_invalidate_on_pop_message(const SkClipStack& stack, uint32_t clipGenID,
    378                                           const GrUniqueKey& clipMaskKey) {
    379     SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
    380     while (const Element* element = iter.prev()) {
    381         if (element->getGenID() == clipGenID) {
    382             std::unique_ptr<GrUniqueKeyInvalidatedMessage> msg(
    383                     new GrUniqueKeyInvalidatedMessage(clipMaskKey));
    384             element->addResourceInvalidationMessage(std::move(msg));
    385             return;
    386         }
    387     }
    388     SkDEBUGFAIL("Gen ID was not found in stack.");
    389 }
    390 
    391 sk_sp<GrTextureProxy> GrClipStackClip::createAlphaClipMask(GrContext* context,
    392                                                            const GrReducedClip& reducedClip) const {
    393     GrResourceProvider* resourceProvider = context->resourceProvider();
    394     GrUniqueKey key;
    395     create_clip_mask_key(reducedClip.elementsGenID(), reducedClip.ibounds(), &key);
    396 
    397     sk_sp<GrTextureProxy> proxy(resourceProvider->findProxyByUniqueKey(key));
    398     if (proxy) {
    399         return proxy;
    400     }
    401 
    402     sk_sp<GrRenderTargetContext> rtc(context->makeDeferredRenderTargetContextWithFallback(
    403                                                                              SkBackingFit::kApprox,
    404                                                                              reducedClip.width(),
    405                                                                              reducedClip.height(),
    406                                                                              kAlpha_8_GrPixelConfig,
    407                                                                              nullptr));
    408     if (!rtc) {
    409         return nullptr;
    410     }
    411 
    412     if (!reducedClip.drawAlphaClipMask(rtc.get())) {
    413         return nullptr;
    414     }
    415 
    416     sk_sp<GrTextureProxy> result(rtc->asTextureProxyRef());
    417     if (!result) {
    418         return nullptr;
    419     }
    420 
    421     resourceProvider->assignUniqueKeyToProxy(key, result.get());
    422     // MDB TODO (caching): this has to play nice with the GrSurfaceProxy's caching
    423     add_invalidate_on_pop_message(*fStack, reducedClip.elementsGenID(), key);
    424 
    425     return result;
    426 }
    427 
    428 sk_sp<GrTextureProxy> GrClipStackClip::createSoftwareClipMask(
    429                                                           GrContext* context,
    430                                                           const GrReducedClip& reducedClip) const {
    431     GrUniqueKey key;
    432     create_clip_mask_key(reducedClip.elementsGenID(), reducedClip.ibounds(), &key);
    433 
    434     sk_sp<GrTextureProxy> proxy(context->resourceProvider()->findProxyByUniqueKey(key));
    435     if (proxy) {
    436         return proxy;
    437     }
    438 
    439     // The mask texture may be larger than necessary. We round out the clip bounds and pin the top
    440     // left corner of the resulting rect to the top left of the texture.
    441     SkIRect maskSpaceIBounds = SkIRect::MakeWH(reducedClip.width(), reducedClip.height());
    442 
    443     GrSWMaskHelper helper;
    444 
    445     // Set the matrix so that rendered clip elements are transformed to mask space from clip
    446     // space.
    447     SkMatrix translate;
    448     translate.setTranslate(SkIntToScalar(-reducedClip.left()), SkIntToScalar(-reducedClip.top()));
    449 
    450     if (!helper.init(maskSpaceIBounds, &translate)) {
    451         return nullptr;
    452     }
    453     helper.clear(InitialState::kAllIn == reducedClip.initialState() ? 0xFF : 0x00);
    454 
    455     for (ElementList::Iter iter(reducedClip.elements()); iter.get(); iter.next()) {
    456         const Element* element = iter.get();
    457         SkClipOp op = element->getOp();
    458         GrAA aa = GrBoolToAA(element->isAA());
    459 
    460         if (kIntersect_SkClipOp == op || kReverseDifference_SkClipOp == op) {
    461             // Intersect and reverse difference require modifying pixels outside of the geometry
    462             // that is being "drawn". In both cases we erase all the pixels outside of the geometry
    463             // but leave the pixels inside the geometry alone. For reverse difference we invert all
    464             // the pixels before clearing the ones outside the geometry.
    465             if (kReverseDifference_SkClipOp == op) {
    466                 SkRect temp = SkRect::Make(reducedClip.ibounds());
    467                 // invert the entire scene
    468                 helper.drawRect(temp, SkRegion::kXOR_Op, GrAA::kNo, 0xFF);
    469             }
    470             SkPath clipPath;
    471             element->asPath(&clipPath);
    472             clipPath.toggleInverseFillType();
    473             GrShape shape(clipPath, GrStyle::SimpleFill());
    474             helper.drawShape(shape, SkRegion::kReplace_Op, aa, 0x00);
    475             continue;
    476         }
    477 
    478         // The other ops (union, xor, diff) only affect pixels inside
    479         // the geometry so they can just be drawn normally
    480         if (Element::kRect_Type == element->getType()) {
    481             helper.drawRect(element->getRect(), (SkRegion::Op)op, aa, 0xFF);
    482         } else {
    483             SkPath path;
    484             element->asPath(&path);
    485             GrShape shape(path, GrStyle::SimpleFill());
    486             helper.drawShape(shape, (SkRegion::Op)op, aa, 0xFF);
    487         }
    488     }
    489 
    490     sk_sp<GrTextureProxy> result(helper.toTextureProxy(context, SkBackingFit::kApprox));
    491 
    492     context->resourceProvider()->assignUniqueKeyToProxy(key, result.get());
    493     // MDB TODO (caching): this has to play nice with the GrSurfaceProxy's caching
    494     add_invalidate_on_pop_message(*fStack, reducedClip.elementsGenID(), key);
    495     return result;
    496 }
    497