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