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 "GrDeferredProxyUploader.h"
     13 #include "GrDrawingManager.h"
     14 #include "GrFixedClip.h"
     15 #include "GrGpuResourcePriv.h"
     16 #include "GrRenderTargetContextPriv.h"
     17 #include "GrProxyProvider.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 #include "SkMakeUnique.h"
     26 #include "SkTaskGroup.h"
     27 #include "SkTraceEvent.h"
     28 
     29 typedef SkClipStack::Element Element;
     30 typedef GrReducedClip::InitialState InitialState;
     31 typedef GrReducedClip::ElementList ElementList;
     32 
     33 const char GrClipStackClip::kMaskTestTag[] = "clip_mask";
     34 
     35 bool GrClipStackClip::quickContains(const SkRect& rect) const {
     36     if (!fStack || fStack->isWideOpen()) {
     37         return true;
     38     }
     39     return fStack->quickContains(rect);
     40 }
     41 
     42 bool GrClipStackClip::quickContains(const SkRRect& rrect) const {
     43     if (!fStack || fStack->isWideOpen()) {
     44         return true;
     45     }
     46     return fStack->quickContains(rrect);
     47 }
     48 
     49 bool GrClipStackClip::isRRect(const SkRect& origRTBounds, SkRRect* rr, GrAA* aa) const {
     50     if (!fStack) {
     51         return false;
     52     }
     53     const SkRect* rtBounds = &origRTBounds;
     54     bool isAA;
     55     if (fStack->isRRect(*rtBounds, rr, &isAA)) {
     56         *aa = GrAA(isAA);
     57         return true;
     58     }
     59     return false;
     60 }
     61 
     62 void GrClipStackClip::getConservativeBounds(int width, int height, SkIRect* devResult,
     63                                             bool* isIntersectionOfRects) const {
     64     if (!fStack) {
     65         devResult->setXYWH(0, 0, width, height);
     66         if (isIntersectionOfRects) {
     67             *isIntersectionOfRects = true;
     68         }
     69         return;
     70     }
     71     SkRect devBounds;
     72     fStack->getConservativeBounds(0, 0, width, height, &devBounds, isIntersectionOfRects);
     73     devBounds.roundOut(devResult);
     74 }
     75 
     76 ////////////////////////////////////////////////////////////////////////////////
     77 // set up the draw state to enable the aa clipping mask.
     78 static std::unique_ptr<GrFragmentProcessor> create_fp_for_mask(sk_sp<GrTextureProxy> mask,
     79                                                                const SkIRect& devBound) {
     80     SkIRect domainTexels = SkIRect::MakeWH(devBound.width(), devBound.height());
     81     return GrDeviceSpaceTextureDecalFragmentProcessor::Make(std::move(mask), domainTexels,
     82                                                             {devBound.fLeft, devBound.fTop});
     83 }
     84 
     85 // Does the path in 'element' require SW rendering? If so, return true (and,
     86 // optionally, set 'prOut' to NULL. If not, return false (and, optionally, set
     87 // 'prOut' to the non-SW path renderer that will do the job).
     88 bool GrClipStackClip::PathNeedsSWRenderer(GrContext* context,
     89                                           const SkIRect& scissorRect,
     90                                           bool hasUserStencilSettings,
     91                                           const GrRenderTargetContext* renderTargetContext,
     92                                           const SkMatrix& viewMatrix,
     93                                           const Element* element,
     94                                           GrPathRenderer** prOut,
     95                                           bool needsStencil) {
     96     if (Element::DeviceSpaceType::kRect == element->getDeviceSpaceType()) {
     97         // rects can always be drawn directly w/o using the software path
     98         // TODO: skip rrects once we're drawing them directly.
     99         if (prOut) {
    100             *prOut = nullptr;
    101         }
    102         return false;
    103     } else {
    104         // We shouldn't get here with an empty clip element.
    105         SkASSERT(Element::DeviceSpaceType::kEmpty != element->getDeviceSpaceType());
    106 
    107         // the gpu alpha mask will draw the inverse paths as non-inverse to a temp buffer
    108         SkPath path;
    109         element->asDeviceSpacePath(&path);
    110         if (path.isInverseFillType()) {
    111             path.toggleInverseFillType();
    112         }
    113 
    114         GrPathRendererChain::DrawType type =
    115                 needsStencil ? GrPathRendererChain::DrawType::kStencilAndColor
    116                              : GrPathRendererChain::DrawType::kColor;
    117 
    118         GrShape shape(path, GrStyle::SimpleFill());
    119         GrPathRenderer::CanDrawPathArgs canDrawArgs;
    120         canDrawArgs.fCaps = context->caps();
    121         canDrawArgs.fClipConservativeBounds = &scissorRect;
    122         canDrawArgs.fViewMatrix = &viewMatrix;
    123         canDrawArgs.fShape = &shape;
    124         canDrawArgs.fAAType = GrChooseAAType(GrAA(element->isAA()),
    125                                              renderTargetContext->fsaaType(),
    126                                              GrAllowMixedSamples::kYes,
    127                                              *context->caps());
    128         canDrawArgs.fHasUserStencilSettings = hasUserStencilSettings;
    129 
    130         // the 'false' parameter disallows use of the SW path renderer
    131         GrPathRenderer* pr =
    132             context->contextPriv().drawingManager()->getPathRenderer(canDrawArgs, false, type);
    133         if (prOut) {
    134             *prOut = pr;
    135         }
    136         return SkToBool(!pr);
    137     }
    138 }
    139 
    140 /*
    141  * This method traverses the clip stack to see if the GrSoftwarePathRenderer
    142  * will be used on any element. If so, it returns true to indicate that the
    143  * entire clip should be rendered in SW and then uploaded en masse to the gpu.
    144  */
    145 bool GrClipStackClip::UseSWOnlyPath(GrContext* context,
    146                                     bool hasUserStencilSettings,
    147                                     const GrRenderTargetContext* renderTargetContext,
    148                                     const GrReducedClip& reducedClip) {
    149     // TODO: generalize this function so that when
    150     // a clip gets complex enough it can just be done in SW regardless
    151     // of whether it would invoke the GrSoftwarePathRenderer.
    152 
    153     // If we're avoiding stencils, always use SW:
    154     if (context->caps()->avoidStencilBuffers())
    155         return true;
    156 
    157     // Set the matrix so that rendered clip elements are transformed to mask space from clip
    158     // space.
    159     SkMatrix translate;
    160     translate.setTranslate(SkIntToScalar(-reducedClip.left()), SkIntToScalar(-reducedClip.top()));
    161 
    162     for (ElementList::Iter iter(reducedClip.maskElements()); iter.get(); iter.next()) {
    163         const Element* element = iter.get();
    164 
    165         SkClipOp op = element->getOp();
    166         bool invert = element->isInverseFilled();
    167         bool needsStencil = invert ||
    168                             kIntersect_SkClipOp == op || kReverseDifference_SkClipOp == op;
    169 
    170         if (PathNeedsSWRenderer(context, reducedClip.scissor(), hasUserStencilSettings,
    171                                 renderTargetContext, translate, element, nullptr, needsStencil)) {
    172             return true;
    173         }
    174     }
    175     return false;
    176 }
    177 
    178 ////////////////////////////////////////////////////////////////////////////////
    179 // sort out what kind of clip mask needs to be created: alpha, stencil,
    180 // scissor, or entirely software
    181 bool GrClipStackClip::apply(GrContext* context, GrRenderTargetContext* renderTargetContext,
    182                             bool useHWAA, bool hasUserStencilSettings, GrAppliedClip* out,
    183                             SkRect* bounds) const {
    184     SkRect devBounds = SkRect::MakeIWH(renderTargetContext->width(), renderTargetContext->height());
    185     if (!devBounds.intersect(*bounds)) {
    186         return false;
    187     }
    188 
    189     if (!fStack || fStack->isWideOpen()) {
    190         return true;
    191     }
    192 
    193     GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
    194     const auto* caps = context->caps()->shaderCaps();
    195     int maxWindowRectangles = renderTargetContext->priv().maxWindowRectangles();
    196     int maxAnalyticFPs = context->caps()->maxClipAnalyticFPs();
    197     if (GrFSAAType::kNone != renderTargetContext->fsaaType()) {
    198         // With mixed samples (non-msaa color buffer), any coverage info is lost from color once it
    199         // hits the color buffer anyway, so we may as well use coverage AA if nothing else in the
    200         // pipe is multisampled.
    201         if (renderTargetContext->numColorSamples() > 0 || useHWAA || hasUserStencilSettings) {
    202             maxAnalyticFPs = 0;
    203         }
    204         SkASSERT(!context->caps()->avoidStencilBuffers()); // We disable MSAA when avoiding stencil.
    205     }
    206     auto* ccpr = context->contextPriv().drawingManager()->getCoverageCountingPathRenderer();
    207 
    208     GrReducedClip reducedClip(*fStack, devBounds, caps, maxWindowRectangles, maxAnalyticFPs, ccpr);
    209     if (InitialState::kAllOut == reducedClip.initialState() &&
    210         reducedClip.maskElements().isEmpty()) {
    211         return false;
    212     }
    213 
    214     if (reducedClip.hasScissor() && !GrClip::IsInsideClip(reducedClip.scissor(), devBounds)) {
    215         out->hardClip().addScissor(reducedClip.scissor(), bounds);
    216     }
    217 
    218     if (!reducedClip.windowRectangles().empty()) {
    219         out->hardClip().addWindowRectangles(reducedClip.windowRectangles(),
    220                                             GrWindowRectsState::Mode::kExclusive);
    221     }
    222 
    223     if (!reducedClip.maskElements().isEmpty()) {
    224         if (!this->applyClipMask(context, renderTargetContext, reducedClip, hasUserStencilSettings,
    225                                  out)) {
    226             return false;
    227         }
    228     }
    229 
    230     // The opList ID must not be looked up until AFTER producing the clip mask (if any). That step
    231     // can cause a flush or otherwise change which opList our draw is going into.
    232     uint32_t opListID = renderTargetContext->getOpList()->uniqueID();
    233     int rtWidth = renderTargetContext->width(), rtHeight = renderTargetContext->height();
    234     if (auto clipFPs = reducedClip.finishAndDetachAnalyticFPs(proxyProvider, opListID,
    235                                                               rtWidth, rtHeight)) {
    236         out->addCoverageFP(std::move(clipFPs));
    237     }
    238 
    239     return true;
    240 }
    241 
    242 bool GrClipStackClip::applyClipMask(GrContext* context, GrRenderTargetContext* renderTargetContext,
    243                                     const GrReducedClip& reducedClip, bool hasUserStencilSettings,
    244                                     GrAppliedClip* out) const {
    245 #ifdef SK_DEBUG
    246     SkASSERT(reducedClip.hasScissor());
    247     SkIRect rtIBounds = SkIRect::MakeWH(renderTargetContext->width(),
    248                                         renderTargetContext->height());
    249     const SkIRect& scissor = reducedClip.scissor();
    250     SkASSERT(rtIBounds.contains(scissor)); // Mask shouldn't be larger than the RT.
    251 #endif
    252 
    253     // If the stencil buffer is multisampled we can use it to do everything.
    254     if ((GrFSAAType::kNone == renderTargetContext->fsaaType() && reducedClip.maskRequiresAA()) ||
    255         context->caps()->avoidStencilBuffers()) {
    256         sk_sp<GrTextureProxy> result;
    257         if (UseSWOnlyPath(context, hasUserStencilSettings, renderTargetContext, reducedClip)) {
    258             // The clip geometry is complex enough that it will be more efficient to create it
    259             // entirely in software
    260             result = this->createSoftwareClipMask(context, reducedClip, renderTargetContext);
    261         } else {
    262             result = this->createAlphaClipMask(context, reducedClip);
    263         }
    264 
    265         if (result) {
    266             // The mask's top left coord should be pinned to the rounded-out top left corner of
    267             // the clip's device space bounds.
    268             out->addCoverageFP(create_fp_for_mask(std::move(result), reducedClip.scissor()));
    269             return true;
    270         }
    271 
    272         // If alpha or software clip mask creation fails, fall through to the stencil code paths,
    273         // unless stencils are disallowed.
    274         if (context->caps()->avoidStencilBuffers()) {
    275             SkDebugf("WARNING: Clip mask requires stencil, but stencil unavailable. "
    276                      "Clip will be ignored.\n");
    277             return false;
    278         }
    279     }
    280 
    281     renderTargetContext->setNeedsStencil();
    282 
    283     // This relies on the property that a reduced sub-rect of the last clip will contain all the
    284     // relevant window rectangles that were in the last clip. This subtle requirement will go away
    285     // after clipping is overhauled.
    286     if (renderTargetContext->priv().mustRenderClip(reducedClip.maskGenID(), reducedClip.scissor(),
    287                                                    reducedClip.numAnalyticFPs())) {
    288         reducedClip.drawStencilClipMask(context, renderTargetContext);
    289         renderTargetContext->priv().setLastClip(reducedClip.maskGenID(), reducedClip.scissor(),
    290                                                 reducedClip.numAnalyticFPs());
    291     }
    292     // GrAppliedClip doesn't need to figure numAnalyticFPs into its key (used by operator==) because
    293     // it verifies the FPs are also equal.
    294     out->hardClip().addStencilClip(reducedClip.maskGenID());
    295     return true;
    296 }
    297 
    298 ////////////////////////////////////////////////////////////////////////////////
    299 // Create a 8-bit clip mask in alpha
    300 
    301 static void create_clip_mask_key(uint32_t clipGenID, const SkIRect& bounds, int numAnalyticFPs,
    302                                  GrUniqueKey* key) {
    303     static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
    304     GrUniqueKey::Builder builder(key, kDomain, 4, GrClipStackClip::kMaskTestTag);
    305     builder[0] = clipGenID;
    306     // SkToS16 because image filters outset layers to a size indicated by the filter, which can
    307     // sometimes result in negative coordinates from device space.
    308     builder[1] = SkToS16(bounds.fLeft) | (SkToS16(bounds.fRight) << 16);
    309     builder[2] = SkToS16(bounds.fTop) | (SkToS16(bounds.fBottom) << 16);
    310     builder[3] = numAnalyticFPs;
    311 }
    312 
    313 static void add_invalidate_on_pop_message(const SkClipStack& stack, uint32_t clipGenID,
    314                                           const GrUniqueKey& clipMaskKey) {
    315     SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
    316     while (const Element* element = iter.prev()) {
    317         if (element->getGenID() == clipGenID) {
    318             std::unique_ptr<GrUniqueKeyInvalidatedMessage> msg(
    319                     new GrUniqueKeyInvalidatedMessage(clipMaskKey));
    320             element->addResourceInvalidationMessage(std::move(msg));
    321             return;
    322         }
    323     }
    324     SkDEBUGFAIL("Gen ID was not found in stack.");
    325 }
    326 
    327 sk_sp<GrTextureProxy> GrClipStackClip::createAlphaClipMask(GrContext* context,
    328                                                            const GrReducedClip& reducedClip) const {
    329     GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
    330     GrUniqueKey key;
    331     create_clip_mask_key(reducedClip.maskGenID(), reducedClip.scissor(),
    332                          reducedClip.numAnalyticFPs(), &key);
    333 
    334     sk_sp<GrTextureProxy> proxy(proxyProvider->findOrCreateProxyByUniqueKey(
    335                                                                 key, kBottomLeft_GrSurfaceOrigin));
    336     if (proxy) {
    337         return proxy;
    338     }
    339 
    340     sk_sp<GrRenderTargetContext> rtc(context->makeDeferredRenderTargetContextWithFallback(
    341                                                                              SkBackingFit::kApprox,
    342                                                                              reducedClip.width(),
    343                                                                              reducedClip.height(),
    344                                                                              kAlpha_8_GrPixelConfig,
    345                                                                              nullptr));
    346     if (!rtc) {
    347         return nullptr;
    348     }
    349 
    350     if (!reducedClip.drawAlphaClipMask(rtc.get())) {
    351         return nullptr;
    352     }
    353 
    354     sk_sp<GrTextureProxy> result(rtc->asTextureProxyRef());
    355     if (!result) {
    356         return nullptr;
    357     }
    358 
    359     SkASSERT(result->origin() == kBottomLeft_GrSurfaceOrigin);
    360     proxyProvider->assignUniqueKeyToProxy(key, result.get());
    361     add_invalidate_on_pop_message(*fStack, reducedClip.maskGenID(), key);
    362 
    363     return result;
    364 }
    365 
    366 namespace {
    367 
    368 /**
    369  * Payload class for use with GrTDeferredProxyUploader. The clip mask code renders multiple
    370  * elements, each storing their own AA setting (and already transformed into device space). This
    371  * stores all of the information needed by the worker thread to draw all clip elements (see below,
    372  * in createSoftwareClipMask).
    373  */
    374 class ClipMaskData {
    375 public:
    376     ClipMaskData(const GrReducedClip& reducedClip)
    377             : fScissor(reducedClip.scissor())
    378             , fInitialState(reducedClip.initialState()) {
    379         for (ElementList::Iter iter(reducedClip.maskElements()); iter.get(); iter.next()) {
    380             fElements.addToTail(*iter.get());
    381         }
    382     }
    383 
    384     const SkIRect& scissor() const { return fScissor; }
    385     InitialState initialState() const { return fInitialState; }
    386     const ElementList& elements() const { return fElements; }
    387 
    388 private:
    389     SkIRect fScissor;
    390     InitialState fInitialState;
    391     ElementList fElements;
    392 };
    393 
    394 }
    395 
    396 static void draw_clip_elements_to_mask_helper(GrSWMaskHelper& helper, const ElementList& elements,
    397                                               const SkIRect& scissor, InitialState initialState) {
    398     // Set the matrix so that rendered clip elements are transformed to mask space from clip space.
    399     SkMatrix translate;
    400     translate.setTranslate(SkIntToScalar(-scissor.left()), SkIntToScalar(-scissor.top()));
    401 
    402     helper.clear(InitialState::kAllIn == initialState ? 0xFF : 0x00);
    403 
    404     for (ElementList::Iter iter(elements); iter.get(); iter.next()) {
    405         const Element* element = iter.get();
    406         SkClipOp op = element->getOp();
    407         GrAA aa = GrAA(element->isAA());
    408 
    409         if (kIntersect_SkClipOp == op || kReverseDifference_SkClipOp == op) {
    410             // Intersect and reverse difference require modifying pixels outside of the geometry
    411             // that is being "drawn". In both cases we erase all the pixels outside of the geometry
    412             // but leave the pixels inside the geometry alone. For reverse difference we invert all
    413             // the pixels before clearing the ones outside the geometry.
    414             if (kReverseDifference_SkClipOp == op) {
    415                 SkRect temp = SkRect::Make(scissor);
    416                 // invert the entire scene
    417                 helper.drawRect(temp, translate, SkRegion::kXOR_Op, GrAA::kNo, 0xFF);
    418             }
    419             SkPath clipPath;
    420             element->asDeviceSpacePath(&clipPath);
    421             clipPath.toggleInverseFillType();
    422             GrShape shape(clipPath, GrStyle::SimpleFill());
    423             helper.drawShape(shape, translate, SkRegion::kReplace_Op, aa, 0x00);
    424             continue;
    425         }
    426 
    427         // The other ops (union, xor, diff) only affect pixels inside
    428         // the geometry so they can just be drawn normally
    429         if (Element::DeviceSpaceType::kRect == element->getDeviceSpaceType()) {
    430             helper.drawRect(element->getDeviceSpaceRect(), translate, (SkRegion::Op)op, aa, 0xFF);
    431         } else {
    432             SkPath path;
    433             element->asDeviceSpacePath(&path);
    434             GrShape shape(path, GrStyle::SimpleFill());
    435             helper.drawShape(shape, translate, (SkRegion::Op)op, aa, 0xFF);
    436         }
    437     }
    438 }
    439 
    440 sk_sp<GrTextureProxy> GrClipStackClip::createSoftwareClipMask(
    441         GrContext* context, const GrReducedClip& reducedClip,
    442         GrRenderTargetContext* renderTargetContext) const {
    443     GrUniqueKey key;
    444     create_clip_mask_key(reducedClip.maskGenID(), reducedClip.scissor(),
    445                          reducedClip.numAnalyticFPs(), &key);
    446 
    447     GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
    448 
    449     sk_sp<GrTextureProxy> proxy(proxyProvider->findOrCreateProxyByUniqueKey(
    450                                                                   key, kTopLeft_GrSurfaceOrigin));
    451     if (proxy) {
    452         return proxy;
    453     }
    454 
    455     // The mask texture may be larger than necessary. We round out the clip bounds and pin the top
    456     // left corner of the resulting rect to the top left of the texture.
    457     SkIRect maskSpaceIBounds = SkIRect::MakeWH(reducedClip.width(), reducedClip.height());
    458 
    459     SkTaskGroup* taskGroup = context->contextPriv().getTaskGroup();
    460     if (taskGroup && renderTargetContext) {
    461         // Create our texture proxy
    462         GrSurfaceDesc desc;
    463         desc.fOrigin = kTopLeft_GrSurfaceOrigin;
    464         desc.fWidth = maskSpaceIBounds.width();
    465         desc.fHeight = maskSpaceIBounds.height();
    466         desc.fConfig = kAlpha_8_GrPixelConfig;
    467         // MDB TODO: We're going to fill this proxy with an ASAP upload (which is out of order wrt
    468         // to ops), so it can't have any pending IO.
    469         proxy = proxyProvider->createProxy(desc, SkBackingFit::kApprox, SkBudgeted::kYes,
    470                                            GrResourceProvider::kNoPendingIO_Flag);
    471 
    472         auto uploader = skstd::make_unique<GrTDeferredProxyUploader<ClipMaskData>>(reducedClip);
    473         GrTDeferredProxyUploader<ClipMaskData>* uploaderRaw = uploader.get();
    474         auto drawAndUploadMask = [uploaderRaw, maskSpaceIBounds] {
    475             TRACE_EVENT0("skia", "Threaded SW Clip Mask Render");
    476             GrSWMaskHelper helper(uploaderRaw->getPixels());
    477             if (helper.init(maskSpaceIBounds)) {
    478                 draw_clip_elements_to_mask_helper(helper, uploaderRaw->data().elements(),
    479                                                   uploaderRaw->data().scissor(),
    480                                                   uploaderRaw->data().initialState());
    481             } else {
    482                 SkDEBUGFAIL("Unable to allocate SW clip mask.");
    483             }
    484             uploaderRaw->signalAndFreeData();
    485         };
    486 
    487         taskGroup->add(std::move(drawAndUploadMask));
    488         proxy->texPriv().setDeferredUploader(std::move(uploader));
    489     } else {
    490         GrSWMaskHelper helper;
    491         if (!helper.init(maskSpaceIBounds)) {
    492             return nullptr;
    493         }
    494 
    495         draw_clip_elements_to_mask_helper(helper, reducedClip.maskElements(), reducedClip.scissor(),
    496                                           reducedClip.initialState());
    497 
    498         proxy = helper.toTextureProxy(context, SkBackingFit::kApprox);
    499     }
    500 
    501     SkASSERT(proxy->origin() == kTopLeft_GrSurfaceOrigin);
    502     proxyProvider->assignUniqueKeyToProxy(key, proxy.get());
    503     add_invalidate_on_pop_message(*fStack, reducedClip.maskGenID(), key);
    504     return proxy;
    505 }
    506