GrClipStackClip.cpp revision cb31e51d9355adb1d25ce3e121cde21b05ecf63e
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 "GrDrawingManager.h"
12#include "GrDrawContextPriv.h"
13#include "GrFixedClip.h"
14#include "GrGpuResourcePriv.h"
15#include "GrRenderTargetPriv.h"
16#include "GrStencilAttachment.h"
17#include "GrSWMaskHelper.h"
18#include "effects/GrConvexPolyEffect.h"
19#include "effects/GrRRectEffect.h"
20#include "effects/GrTextureDomain.h"
21
22typedef SkClipStack::Element Element;
23typedef GrReducedClip::InitialState InitialState;
24typedef GrReducedClip::ElementList ElementList;
25
26static const int kMaxAnalyticElements = 4;
27
28bool GrClipStackClip::quickContains(const SkRect& rect) const {
29    if (!fStack || fStack->isWideOpen()) {
30        return true;
31    }
32    return fStack->quickContains(rect.makeOffset(SkIntToScalar(fOrigin.x()),
33                                                 SkIntToScalar(fOrigin.y())));
34}
35
36bool GrClipStackClip::quickContains(const SkRRect& rrect) const {
37    if (!fStack || fStack->isWideOpen()) {
38        return true;
39    }
40    return fStack->quickContains(rrect.makeOffset(SkIntToScalar(fOrigin.fX),
41                                                  SkIntToScalar(fOrigin.fY)));
42}
43
44bool GrClipStackClip::isRRect(const SkRect& origRTBounds, SkRRect* rr, bool* aa) const {
45    if (!fStack) {
46        return false;
47    }
48    const SkRect* rtBounds = &origRTBounds;
49    SkRect tempRTBounds;
50    bool origin = fOrigin.fX || fOrigin.fY;
51    if (origin) {
52        tempRTBounds = origRTBounds;
53        tempRTBounds.offset(SkIntToScalar(fOrigin.fX), SkIntToScalar(fOrigin.fY));
54        rtBounds = &tempRTBounds;
55    }
56    if (fStack->isRRect(*rtBounds, rr, aa)) {
57        if (origin) {
58            rr->offset(-SkIntToScalar(fOrigin.fX), -SkIntToScalar(fOrigin.fY));
59        }
60        return true;
61    }
62    return false;
63}
64
65void 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(-fOrigin.x(), -fOrigin.y(), width, height, &devBounds,
76                                  isIntersectionOfRects);
77    devBounds.roundOut(devResult);
78}
79
80////////////////////////////////////////////////////////////////////////////////
81// set up the draw state to enable the aa clipping mask. Besides setting up the
82// stage matrix this also alters the vertex layout
83static sk_sp<GrFragmentProcessor> create_fp_for_mask(GrTexture* result,
84                                                     const SkIRect &devBound) {
85    SkMatrix mat;
86    // We use device coords to compute the texture coordinates. We set our matrix to be a
87    // translation to the devBound, and then a scaling matrix to normalized coords.
88    mat.setIDiv(result->width(), result->height());
89    mat.preTranslate(SkIntToScalar(-devBound.fLeft),
90                     SkIntToScalar(-devBound.fTop));
91
92    SkIRect domainTexels = SkIRect::MakeWH(devBound.width(), devBound.height());
93    return sk_sp<GrFragmentProcessor>(GrTextureDomainEffect::Make(
94                                         result,
95                                         nullptr,
96                                         mat,
97                                         GrTextureDomain::MakeTexelDomain(result, domainTexels),
98                                         GrTextureDomain::kDecal_Mode,
99                                         GrTextureParams::kNone_FilterMode,
100                                         kDevice_GrCoordSet));
101}
102
103// Does the path in 'element' require SW rendering? If so, return true (and,
104// optionally, set 'prOut' to NULL. If not, return false (and, optionally, set
105// 'prOut' to the non-SW path renderer that will do the job).
106bool GrClipStackClip::PathNeedsSWRenderer(GrContext* context,
107                                          bool hasUserStencilSettings,
108                                          const GrDrawContext* drawContext,
109                                          const SkMatrix& viewMatrix,
110                                          const Element* element,
111                                          GrPathRenderer** prOut,
112                                          bool needsStencil) {
113    if (Element::kRect_Type == element->getType()) {
114        // rects can always be drawn directly w/o using the software path
115        // TODO: skip rrects once we're drawing them directly.
116        if (prOut) {
117            *prOut = nullptr;
118        }
119        return false;
120    } else {
121        // We shouldn't get here with an empty clip element.
122        SkASSERT(Element::kEmpty_Type != element->getType());
123
124        // the gpu alpha mask will draw the inverse paths as non-inverse to a temp buffer
125        SkPath path;
126        element->asPath(&path);
127        if (path.isInverseFillType()) {
128            path.toggleInverseFillType();
129        }
130
131        GrPathRendererChain::DrawType type;
132
133        if (needsStencil) {
134            type = element->isAA()
135                            ? GrPathRendererChain::kStencilAndColorAntiAlias_DrawType
136                            : GrPathRendererChain::kStencilAndColor_DrawType;
137        } else {
138            type = element->isAA()
139                            ? GrPathRendererChain::kColorAntiAlias_DrawType
140                            : GrPathRendererChain::kColor_DrawType;
141        }
142
143        GrShape shape(path, GrStyle::SimpleFill());
144        GrPathRenderer::CanDrawPathArgs canDrawArgs;
145        canDrawArgs.fShaderCaps = context->caps()->shaderCaps();
146        canDrawArgs.fViewMatrix = &viewMatrix;
147        canDrawArgs.fShape = &shape;
148        canDrawArgs.fAntiAlias = element->isAA();
149        canDrawArgs.fHasUserStencilSettings = hasUserStencilSettings;
150        canDrawArgs.fIsStencilBufferMSAA = drawContext->isStencilBufferMultisampled();
151
152        // the 'false' parameter disallows use of the SW path renderer
153        GrPathRenderer* pr = context->drawingManager()->getPathRenderer(canDrawArgs, false, type);
154        if (prOut) {
155            *prOut = pr;
156        }
157        return SkToBool(!pr);
158    }
159}
160
161/*
162 * This method traverses the clip stack to see if the GrSoftwarePathRenderer
163 * will be used on any element. If so, it returns true to indicate that the
164 * entire clip should be rendered in SW and then uploaded en masse to the gpu.
165 */
166bool GrClipStackClip::UseSWOnlyPath(GrContext* context,
167                                    bool hasUserStencilSettings,
168                                    const GrDrawContext* drawContext,
169                                    const SkVector& clipToMaskOffset,
170                                    const ElementList& elements) {
171    // TODO: generalize this function so that when
172    // a clip gets complex enough it can just be done in SW regardless
173    // of whether it would invoke the GrSoftwarePathRenderer.
174
175    // Set the matrix so that rendered clip elements are transformed to mask space from clip
176    // space.
177    const SkMatrix translate = SkMatrix::MakeTrans(clipToMaskOffset.fX, clipToMaskOffset.fY);
178
179    for (ElementList::Iter iter(elements); iter.get(); iter.next()) {
180        const Element* element = iter.get();
181
182        SkRegion::Op op = element->getOp();
183        bool invert = element->isInverseFilled();
184        bool needsStencil = invert ||
185                            SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op;
186
187        if (PathNeedsSWRenderer(context, hasUserStencilSettings,
188                                drawContext, translate, element, nullptr, needsStencil)) {
189            return true;
190        }
191    }
192    return false;
193}
194
195static bool get_analytic_clip_processor(const ElementList& elements,
196                                        bool abortIfAA,
197                                        const SkVector& clipToRTOffset,
198                                        const SkRect& drawBounds,
199                                        sk_sp<GrFragmentProcessor>* resultFP) {
200    SkRect boundsInClipSpace;
201    boundsInClipSpace = drawBounds.makeOffset(-clipToRTOffset.fX, -clipToRTOffset.fY);
202    SkASSERT(elements.count() <= kMaxAnalyticElements);
203    SkSTArray<kMaxAnalyticElements, sk_sp<GrFragmentProcessor>> fps;
204    ElementList::Iter iter(elements);
205    while (iter.get()) {
206        SkRegion::Op op = iter.get()->getOp();
207        bool invert;
208        bool skip = false;
209        switch (op) {
210            case SkRegion::kReplace_Op:
211                SkASSERT(iter.get() == elements.head());
212                // Fallthrough, handled same as intersect.
213            case SkRegion::kIntersect_Op:
214                invert = false;
215                if (iter.get()->contains(boundsInClipSpace)) {
216                    skip = true;
217                }
218                break;
219            case SkRegion::kDifference_Op:
220                invert = true;
221                // We don't currently have a cheap test for whether a rect is fully outside an
222                // element's primitive, so don't attempt to set skip.
223                break;
224            default:
225                return false;
226        }
227        if (!skip) {
228            GrPrimitiveEdgeType edgeType;
229            if (iter.get()->isAA()) {
230                if (abortIfAA) {
231                    return false;
232                }
233                edgeType =
234                    invert ? kInverseFillAA_GrProcessorEdgeType : kFillAA_GrProcessorEdgeType;
235            } else {
236                edgeType =
237                    invert ? kInverseFillBW_GrProcessorEdgeType : kFillBW_GrProcessorEdgeType;
238            }
239
240            switch (iter.get()->getType()) {
241                case SkClipStack::Element::kPath_Type:
242                    fps.emplace_back(GrConvexPolyEffect::Make(edgeType, iter.get()->getPath(),
243                                                              &clipToRTOffset));
244                    break;
245                case SkClipStack::Element::kRRect_Type: {
246                    SkRRect rrect = iter.get()->getRRect();
247                    rrect.offset(clipToRTOffset.fX, clipToRTOffset.fY);
248                    fps.emplace_back(GrRRectEffect::Make(edgeType, rrect));
249                    break;
250                }
251                case SkClipStack::Element::kRect_Type: {
252                    SkRect rect = iter.get()->getRect();
253                    rect.offset(clipToRTOffset.fX, clipToRTOffset.fY);
254                    fps.emplace_back(GrConvexPolyEffect::Make(edgeType, rect));
255                    break;
256                }
257                default:
258                    break;
259            }
260            if (!fps.back()) {
261                return false;
262            }
263        }
264        iter.next();
265    }
266
267    *resultFP = nullptr;
268    if (fps.count()) {
269        *resultFP = GrFragmentProcessor::RunInSeries(fps.begin(), fps.count());
270    }
271    return true;
272}
273
274////////////////////////////////////////////////////////////////////////////////
275// sort out what kind of clip mask needs to be created: alpha, stencil,
276// scissor, or entirely software
277bool GrClipStackClip::apply(GrContext* context, GrDrawContext* drawContext, bool useHWAA,
278                            bool hasUserStencilSettings, GrAppliedClip* out) const {
279    if (!fStack || fStack->isWideOpen()) {
280        return true;
281    }
282
283    SkRect devBounds = SkRect::MakeIWH(drawContext->width(), drawContext->height());
284    if (!devBounds.intersect(out->clippedDrawBounds())) {
285        return false;
286    }
287
288    const SkScalar clipX = SkIntToScalar(fOrigin.x()),
289                   clipY = SkIntToScalar(fOrigin.y());
290
291    SkRect clipSpaceDevBounds = devBounds.makeOffset(clipX, clipY);
292    const GrReducedClip reducedClip(*fStack, clipSpaceDevBounds);
293
294    if (reducedClip.hasIBounds() &&
295        !GrClip::IsInsideClip(reducedClip.ibounds(), clipSpaceDevBounds)) {
296        SkIRect scissorSpaceIBounds(reducedClip.ibounds());
297        scissorSpaceIBounds.offset(-fOrigin);
298        out->addScissor(scissorSpaceIBounds);
299    }
300
301    if (reducedClip.elements().isEmpty()) {
302        return InitialState::kAllIn == reducedClip.initialState();
303    }
304
305    SkASSERT(reducedClip.hasIBounds());
306
307    // Attempt to implement difference clip rects with window rectangles. This will eventually
308    // become more comprehensive.
309    if (drawContext->accessRenderTarget()->renderTargetPriv().supportsWindowRectangles() &&
310        1 == reducedClip.elements().count() && !reducedClip.requiresAA() &&
311        InitialState::kAllIn == reducedClip.initialState()) {
312        const Element* element = reducedClip.elements().head();
313        SkRegion::Op op = element->getOp();
314        if (Element::kRect_Type == element->getType() &&
315            (SkRegion::kDifference_Op == op || SkRegion::kXOR_Op == op)) {
316            SkIRect window;
317            element->getRect().round(&window);
318            window.offset(-fOrigin);
319            out->addWindowRectangle(window);
320            return true;
321        }
322    }
323
324    // An element count of 4 was chosen because of the common pattern in Blink of:
325    //   isect RR
326    //   diff  RR
327    //   isect convex_poly
328    //   isect convex_poly
329    // when drawing rounded div borders. This could probably be tuned based on a
330    // configuration's relative costs of switching RTs to generate a mask vs
331    // longer shaders.
332    if (reducedClip.elements().count() <= kMaxAnalyticElements) {
333        // When there are multiple samples we want to do per-sample clipping, not compute a
334        // fractional pixel coverage.
335        bool disallowAnalyticAA = drawContext->isStencilBufferMultisampled();
336        if (disallowAnalyticAA && !drawContext->numColorSamples()) {
337            // With a single color sample, any coverage info is lost from color once it hits the
338            // color buffer anyway, so we may as well use coverage AA if nothing else in the pipe
339            // is multisampled.
340            disallowAnalyticAA = useHWAA || hasUserStencilSettings;
341        }
342        sk_sp<GrFragmentProcessor> clipFP;
343        if (reducedClip.requiresAA() &&
344            get_analytic_clip_processor(reducedClip.elements(), disallowAnalyticAA,
345                                        {-clipX, -clipY}, devBounds, &clipFP)) {
346            out->addCoverageFP(std::move(clipFP));
347            return true;
348        }
349    }
350
351    // If the stencil buffer is multisampled we can use it to do everything.
352    if (!drawContext->isStencilBufferMultisampled() && reducedClip.requiresAA()) {
353        sk_sp<GrTexture> result;
354
355        // The top-left of the mask corresponds to the top-left corner of the bounds.
356        SkVector clipToMaskOffset = {
357            SkIntToScalar(-reducedClip.left()),
358            SkIntToScalar(-reducedClip.top())
359        };
360
361        if (UseSWOnlyPath(context, hasUserStencilSettings, drawContext,
362                          clipToMaskOffset, reducedClip.elements())) {
363            // The clip geometry is complex enough that it will be more efficient to create it
364            // entirely in software
365            result = CreateSoftwareClipMask(context->textureProvider(), reducedClip,
366                                            clipToMaskOffset);
367        } else {
368            result = CreateAlphaClipMask(context, reducedClip, clipToMaskOffset);
369            // If createAlphaClipMask fails it means UseSWOnlyPath has a bug
370            SkASSERT(result);
371        }
372
373        if (result) {
374            // The mask's top left coord should be pinned to the rounded-out top left corner of
375            // clipSpace bounds. We determine the mask's position WRT to the render target here.
376            SkIRect rtSpaceMaskBounds = reducedClip.ibounds();
377            rtSpaceMaskBounds.offset(-fOrigin);
378            out->addCoverageFP(create_fp_for_mask(result.get(), rtSpaceMaskBounds));
379            return true;
380        }
381        // if alpha clip mask creation fails fall through to the non-AA code paths
382    }
383
384    // use the stencil clip if we can't represent the clip as a rectangle.
385    SkIPoint clipSpaceToStencilSpaceOffset = -fOrigin;
386    CreateStencilClipMask(context, drawContext, reducedClip, clipSpaceToStencilSpaceOffset);
387    out->addStencilClip();
388    return true;
389}
390
391static bool stencil_element(GrDrawContext* dc,
392                            const GrFixedClip& clip,
393                            const GrUserStencilSettings* ss,
394                            const SkMatrix& viewMatrix,
395                            const SkClipStack::Element* element) {
396
397    // TODO: Draw rrects directly here.
398    switch (element->getType()) {
399        case Element::kEmpty_Type:
400            SkDEBUGFAIL("Should never get here with an empty element.");
401            break;
402        case Element::kRect_Type:
403            return dc->drawContextPriv().drawAndStencilRect(clip, ss,
404                                                            element->getOp(),
405                                                            element->isInverseFilled(),
406                                                            element->isAA(),
407                                                            viewMatrix, element->getRect());
408            break;
409        default: {
410            SkPath path;
411            element->asPath(&path);
412            if (path.isInverseFillType()) {
413                path.toggleInverseFillType();
414            }
415
416            return dc->drawContextPriv().drawAndStencilPath(clip, ss,
417                                                            element->getOp(),
418                                                            element->isInverseFilled(),
419                                                            element->isAA(), viewMatrix, path);
420            break;
421        }
422    }
423
424    return false;
425}
426
427static void draw_element(GrDrawContext* dc,
428                         const GrClip& clip, // TODO: can this just always be WideOpen?
429                         const GrPaint &paint,
430                         const SkMatrix& viewMatrix,
431                         const SkClipStack::Element* element) {
432
433    // TODO: Draw rrects directly here.
434    switch (element->getType()) {
435        case Element::kEmpty_Type:
436            SkDEBUGFAIL("Should never get here with an empty element.");
437            break;
438        case Element::kRect_Type:
439            dc->drawRect(clip, paint, viewMatrix, element->getRect());
440            break;
441        default: {
442            SkPath path;
443            element->asPath(&path);
444            if (path.isInverseFillType()) {
445                path.toggleInverseFillType();
446            }
447
448            dc->drawPath(clip, paint, viewMatrix, path, GrStyle::SimpleFill());
449            break;
450        }
451    }
452}
453
454////////////////////////////////////////////////////////////////////////////////
455// Create a 8-bit clip mask in alpha
456
457static void GetClipMaskKey(int32_t clipGenID, const SkIRect& bounds, GrUniqueKey* key) {
458    static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
459    GrUniqueKey::Builder builder(key, kDomain, 3);
460    builder[0] = clipGenID;
461    builder[1] = SkToU16(bounds.fLeft) | (SkToU16(bounds.fRight) << 16);
462    builder[2] = SkToU16(bounds.fTop) | (SkToU16(bounds.fBottom) << 16);
463}
464
465sk_sp<GrTexture> GrClipStackClip::CreateAlphaClipMask(GrContext* context,
466                                                      const GrReducedClip& reducedClip,
467                                                      const SkVector& clipToMaskOffset) {
468    GrResourceProvider* resourceProvider = context->resourceProvider();
469    GrUniqueKey key;
470    GetClipMaskKey(reducedClip.elementsGenID(), reducedClip.ibounds(), &key);
471    if (GrTexture* texture = resourceProvider->findAndRefTextureByUniqueKey(key)) {
472        return sk_sp<GrTexture>(texture);
473    }
474
475    // There's no texture in the cache. Let's try to allocate it then.
476    GrPixelConfig config = kRGBA_8888_GrPixelConfig;
477    if (context->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) {
478        config = kAlpha_8_GrPixelConfig;
479    }
480
481    sk_sp<GrDrawContext> dc(context->makeDrawContext(SkBackingFit::kApprox,
482                                                     reducedClip.width(),
483                                                     reducedClip.height(),
484                                                     config, nullptr));
485    if (!dc) {
486        return nullptr;
487    }
488
489    // The texture may be larger than necessary, this rect represents the part of the texture
490    // we populate with a rasterization of the clip.
491    SkIRect maskSpaceIBounds = SkIRect::MakeWH(reducedClip.width(), reducedClip.height());
492
493    // The scratch texture that we are drawing into can be substantially larger than the mask. Only
494    // clear the part that we care about.
495    dc->clear(&maskSpaceIBounds, InitialState::kAllIn == reducedClip.initialState() ? -1 : 0, true);
496
497    // Set the matrix so that rendered clip elements are transformed to mask space from clip
498    // space.
499    const SkMatrix translate = SkMatrix::MakeTrans(clipToMaskOffset.fX, clipToMaskOffset.fY);
500
501    // It is important that we use maskSpaceIBounds as the stencil rect in the below loop.
502    // The second pass that zeros the stencil buffer renders the rect maskSpaceIBounds so the first
503    // pass must not set values outside of this bounds or stencil values outside the rect won't be
504    // cleared.
505
506    // walk through each clip element and perform its set op
507    for (ElementList::Iter iter(reducedClip.elements()); iter.get(); iter.next()) {
508        const Element* element = iter.get();
509        SkRegion::Op op = element->getOp();
510        bool invert = element->isInverseFilled();
511        if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) {
512            GrFixedClip clip(maskSpaceIBounds);
513
514            // draw directly into the result with the stencil set to make the pixels affected
515            // by the clip shape be non-zero.
516            static constexpr GrUserStencilSettings kStencilInElement(
517                 GrUserStencilSettings::StaticInit<
518                     0xffff,
519                     GrUserStencilTest::kAlways,
520                     0xffff,
521                     GrUserStencilOp::kReplace,
522                     GrUserStencilOp::kReplace,
523                     0xffff>()
524            );
525            if (!stencil_element(dc.get(), clip, &kStencilInElement,
526                                 translate, element)) {
527                return nullptr;
528            }
529
530            // Draw to the exterior pixels (those with a zero stencil value).
531            static constexpr GrUserStencilSettings kDrawOutsideElement(
532                 GrUserStencilSettings::StaticInit<
533                     0x0000,
534                     GrUserStencilTest::kEqual,
535                     0xffff,
536                     GrUserStencilOp::kZero,
537                     GrUserStencilOp::kZero,
538                     0xffff>()
539            );
540            if (!dc->drawContextPriv().drawAndStencilRect(clip, &kDrawOutsideElement,
541                                                          op, !invert, false,
542                                                          translate,
543                                                          SkRect::Make(reducedClip.ibounds()))) {
544                return nullptr;
545            }
546        } else {
547            // all the remaining ops can just be directly draw into the accumulation buffer
548            GrPaint paint;
549            paint.setAntiAlias(element->isAA());
550            paint.setCoverageSetOpXPFactory(op, false);
551
552            draw_element(dc.get(), GrNoClip(), paint, translate, element);
553        }
554    }
555
556    sk_sp<GrTexture> texture(dc->asTexture());
557    SkASSERT(texture);
558    texture->resourcePriv().setUniqueKey(key);
559    return texture;
560}
561
562////////////////////////////////////////////////////////////////////////////////
563// Create a 1-bit clip mask in the stencil buffer. 'devClipBounds' are in device
564// (as opposed to canvas) coordinates
565bool GrClipStackClip::CreateStencilClipMask(GrContext* context,
566                                            GrDrawContext* drawContext,
567                                            const GrReducedClip& reducedClip,
568                                            const SkIPoint& clipSpaceToStencilOffset) {
569    SkASSERT(drawContext);
570
571    GrStencilAttachment* stencilAttachment = context->resourceProvider()->attachStencilAttachment(
572                                                    drawContext->accessRenderTarget());
573    if (nullptr == stencilAttachment) {
574        return false;
575    }
576
577    // TODO: these need to be swapped over to using a StencilAttachmentProxy
578    if (stencilAttachment->mustRenderClip(reducedClip.elementsGenID(), reducedClip.ibounds(),
579                                          clipSpaceToStencilOffset)) {
580        stencilAttachment->setLastClip(reducedClip.elementsGenID(), reducedClip.ibounds(),
581                                       clipSpaceToStencilOffset);
582        // Set the matrix so that rendered clip elements are transformed from clip to stencil space.
583        SkVector translate = {
584            SkIntToScalar(clipSpaceToStencilOffset.fX),
585            SkIntToScalar(clipSpaceToStencilOffset.fY)
586        };
587        SkMatrix viewMatrix;
588        viewMatrix.setTranslate(translate);
589
590        // We set the current clip to the bounds so that our recursive draws are scissored to them.
591        SkIRect stencilSpaceIBounds(reducedClip.ibounds());
592        stencilSpaceIBounds.offset(clipSpaceToStencilOffset);
593        GrFixedClip clip(stencilSpaceIBounds);
594
595        bool insideClip = InitialState::kAllIn == reducedClip.initialState();
596        drawContext->drawContextPriv().clearStencilClip(stencilSpaceIBounds, insideClip);
597
598        // walk through each clip element and perform its set op
599        // with the existing clip.
600        for (ElementList::Iter iter(reducedClip.elements()); iter.get(); iter.next()) {
601            const Element* element = iter.get();
602            bool useHWAA = element->isAA() && drawContext->isStencilBufferMultisampled();
603
604            bool fillInverted = false;
605            // enabled at bottom of loop
606            clip.disableStencilClip();
607
608            // This will be used to determine whether the clip shape can be rendered into the
609            // stencil with arbitrary stencil settings.
610            GrPathRenderer::StencilSupport stencilSupport;
611
612            SkRegion::Op op = element->getOp();
613
614            GrPathRenderer* pr = nullptr;
615            SkPath clipPath;
616            if (Element::kRect_Type == element->getType()) {
617                stencilSupport = GrPathRenderer::kNoRestriction_StencilSupport;
618                fillInverted = false;
619            } else {
620                element->asPath(&clipPath);
621                fillInverted = clipPath.isInverseFillType();
622                if (fillInverted) {
623                    clipPath.toggleInverseFillType();
624                }
625
626                GrShape shape(clipPath, GrStyle::SimpleFill());
627                GrPathRenderer::CanDrawPathArgs canDrawArgs;
628                canDrawArgs.fShaderCaps = context->caps()->shaderCaps();
629                canDrawArgs.fViewMatrix = &viewMatrix;
630                canDrawArgs.fShape = &shape;
631                canDrawArgs.fAntiAlias = false;
632                canDrawArgs.fHasUserStencilSettings = false;
633                canDrawArgs.fIsStencilBufferMSAA = drawContext->isStencilBufferMultisampled();
634
635                GrDrawingManager* dm = context->drawingManager();
636                pr = dm->getPathRenderer(canDrawArgs, false,
637                                         GrPathRendererChain::kStencilOnly_DrawType,
638                                         &stencilSupport);
639                if (!pr) {
640                    return false;
641                }
642            }
643
644            bool canRenderDirectToStencil =
645                GrPathRenderer::kNoRestriction_StencilSupport == stencilSupport;
646            bool drawDirectToClip; // Given the renderer, the element,
647                                   // fill rule, and set operation should
648                                   // we render the element directly to
649                                   // stencil bit used for clipping.
650            GrUserStencilSettings const* const* stencilPasses =
651                GrStencilSettings::GetClipPasses(op, canRenderDirectToStencil, fillInverted,
652                                                 &drawDirectToClip);
653
654            // draw the element to the client stencil bits if necessary
655            if (!drawDirectToClip) {
656                static constexpr GrUserStencilSettings kDrawToStencil(
657                     GrUserStencilSettings::StaticInit<
658                         0x0000,
659                         GrUserStencilTest::kAlways,
660                         0xffff,
661                         GrUserStencilOp::kIncMaybeClamp,
662                         GrUserStencilOp::kIncMaybeClamp,
663                         0xffff>()
664                );
665                if (Element::kRect_Type == element->getType()) {
666                    drawContext->drawContextPriv().stencilRect(clip, &kDrawToStencil, useHWAA,
667                                                               viewMatrix, element->getRect());
668                } else {
669                    if (!clipPath.isEmpty()) {
670                        GrShape shape(clipPath, GrStyle::SimpleFill());
671                        if (canRenderDirectToStencil) {
672                            GrPaint paint;
673                            paint.setXPFactory(GrDisableColorXPFactory::Make());
674                            paint.setAntiAlias(element->isAA());
675
676                            GrPathRenderer::DrawPathArgs args;
677                            args.fResourceProvider = context->resourceProvider();
678                            args.fPaint = &paint;
679                            args.fUserStencilSettings = &kDrawToStencil;
680                            args.fDrawContext = drawContext;
681                            args.fClip = &clip;
682                            args.fViewMatrix = &viewMatrix;
683                            args.fShape = &shape;
684                            args.fAntiAlias = false;
685                            args.fGammaCorrect = false;
686                            pr->drawPath(args);
687                        } else {
688                            GrPathRenderer::StencilPathArgs args;
689                            args.fResourceProvider = context->resourceProvider();
690                            args.fDrawContext = drawContext;
691                            args.fClip = &clip;
692                            args.fViewMatrix = &viewMatrix;
693                            args.fIsAA = element->isAA();
694                            args.fShape = &shape;
695                            pr->stencilPath(args);
696                        }
697                    }
698                }
699            }
700
701            // Just enable stencil clip. The passes choose whether or not they will actually use it.
702            clip.enableStencilClip();
703
704            // now we modify the clip bit by rendering either the clip
705            // element directly or a bounding rect of the entire clip.
706            for (GrUserStencilSettings const* const* pass = stencilPasses; *pass; ++pass) {
707                if (drawDirectToClip) {
708                    if (Element::kRect_Type == element->getType()) {
709                        drawContext->drawContextPriv().stencilRect(clip, *pass, useHWAA, viewMatrix,
710                                                                   element->getRect());
711                    } else {
712                        GrShape shape(clipPath, GrStyle::SimpleFill());
713                        GrPaint paint;
714                        paint.setXPFactory(GrDisableColorXPFactory::Make());
715                        paint.setAntiAlias(element->isAA());
716                        GrPathRenderer::DrawPathArgs args;
717                        args.fResourceProvider = context->resourceProvider();
718                        args.fPaint = &paint;
719                        args.fUserStencilSettings = *pass;
720                        args.fDrawContext = drawContext;
721                        args.fClip = &clip;
722                        args.fViewMatrix = &viewMatrix;
723                        args.fShape = &shape;
724                        args.fAntiAlias = false;
725                        args.fGammaCorrect = false;
726                        pr->drawPath(args);
727                    }
728                } else {
729                    // The view matrix is setup to do clip space -> stencil space translation, so
730                    // draw rect in clip space.
731                    drawContext->drawContextPriv().stencilRect(clip, *pass, false, viewMatrix,
732                                                               SkRect::Make(reducedClip.ibounds()));
733                }
734            }
735        }
736    }
737    return true;
738}
739
740////////////////////////////////////////////////////////////////////////////////
741sk_sp<GrTexture> GrClipStackClip::CreateSoftwareClipMask(GrTextureProvider* texProvider,
742                                                         const GrReducedClip& reducedClip,
743                                                         const SkVector& clipToMaskOffset) {
744    GrUniqueKey key;
745    GetClipMaskKey(reducedClip.elementsGenID(), reducedClip.ibounds(), &key);
746    if (GrTexture* texture = texProvider->findAndRefTextureByUniqueKey(key)) {
747        return sk_sp<GrTexture>(texture);
748    }
749
750    // The mask texture may be larger than necessary. We round out the clip space bounds and pin
751    // the top left corner of the resulting rect to the top left of the texture.
752    SkIRect maskSpaceIBounds = SkIRect::MakeWH(reducedClip.width(), reducedClip.height());
753
754    GrSWMaskHelper helper(texProvider);
755
756    // Set the matrix so that rendered clip elements are transformed to mask space from clip
757    // space.
758    SkMatrix translate;
759    translate.setTranslate(clipToMaskOffset);
760
761    helper.init(maskSpaceIBounds, &translate);
762    helper.clear(InitialState::kAllIn == reducedClip.initialState() ? 0xFF : 0x00);
763
764    for (ElementList::Iter iter(reducedClip.elements()); iter.get(); iter.next()) {
765        const Element* element = iter.get();
766        SkRegion::Op op = element->getOp();
767
768        if (SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) {
769            // Intersect and reverse difference require modifying pixels outside of the geometry
770            // that is being "drawn". In both cases we erase all the pixels outside of the geometry
771            // but leave the pixels inside the geometry alone. For reverse difference we invert all
772            // the pixels before clearing the ones outside the geometry.
773            if (SkRegion::kReverseDifference_Op == op) {
774                SkRect temp = SkRect::Make(reducedClip.ibounds());
775                // invert the entire scene
776                helper.drawRect(temp, SkRegion::kXOR_Op, false, 0xFF);
777            }
778            SkPath clipPath;
779            element->asPath(&clipPath);
780            clipPath.toggleInverseFillType();
781            GrShape shape(clipPath, GrStyle::SimpleFill());
782            helper.drawShape(shape, SkRegion::kReplace_Op, element->isAA(), 0x00);
783            continue;
784        }
785
786        // The other ops (union, xor, diff) only affect pixels inside
787        // the geometry so they can just be drawn normally
788        if (Element::kRect_Type == element->getType()) {
789            helper.drawRect(element->getRect(), op, element->isAA(), 0xFF);
790        } else {
791            SkPath path;
792            element->asPath(&path);
793            GrShape shape(path, GrStyle::SimpleFill());
794            helper.drawShape(shape, op, element->isAA(), 0xFF);
795        }
796    }
797
798    // Allocate clip mask texture
799    GrSurfaceDesc desc;
800    desc.fWidth = reducedClip.width();
801    desc.fHeight = reducedClip.height();
802    desc.fConfig = kAlpha_8_GrPixelConfig;
803
804    sk_sp<GrTexture> result(texProvider->createApproxTexture(desc));
805    if (!result) {
806        return nullptr;
807    }
808    result->resourcePriv().setUniqueKey(key);
809
810    helper.toTexture(result.get());
811
812    return result;
813}
814