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