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