• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020 Google LLC
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 "src/gpu/GrStencilMaskHelper.h"
9 
10 #include "include/core/SkMatrix.h"
11 #include "include/core/SkPath.h"
12 #include "src/gpu/GrRecordingContextPriv.h"
13 #include "src/gpu/GrStencilSettings.h"
14 #include "src/gpu/geometry/GrShape.h"
15 #include "src/gpu/geometry/GrStyledShape.h"
16 
17 namespace {
18 
19 ////////////////////////////////////////////////////////////////////////////////
20 // Stencil Rules for Merging user stencil space into clip
21 //
22 
23 ///////
24 // Replace
25 static constexpr GrUserStencilSettings gUserToClipReplace(
26     GrUserStencilSettings::StaticInit<
27         0x0000,
28         GrUserStencilTest::kNotEqual,
29         0xffff,
30         GrUserStencilOp::kSetClipAndReplaceUserBits,
31         GrUserStencilOp::kZeroClipAndUserBits,
32         0xffff>()
33 );
34 
35 static constexpr GrUserStencilSettings gInvUserToClipReplace(
36     GrUserStencilSettings::StaticInit<
37         0x0000,
38         GrUserStencilTest::kEqual,
39         0xffff,
40         GrUserStencilOp::kSetClipAndReplaceUserBits,
41         GrUserStencilOp::kZeroClipAndUserBits,
42         0xffff>()
43 );
44 
45 ///////
46 // Intersect
47 static constexpr GrUserStencilSettings gUserToClipIsect(
48     GrUserStencilSettings::StaticInit<
49         0x0000,
50         GrUserStencilTest::kLessIfInClip, // "0 < userBits" is equivalent to "0 != userBits".
51         0xffff,
52         GrUserStencilOp::kSetClipAndReplaceUserBits,
53         GrUserStencilOp::kZeroClipAndUserBits,
54         0xffff>()
55 );
56 
57 ///////
58 // Difference
59 static constexpr GrUserStencilSettings gUserToClipDiff(
60     GrUserStencilSettings::StaticInit<
61         0x0000,
62         GrUserStencilTest::kEqualIfInClip,
63         0xffff,
64         GrUserStencilOp::kSetClipAndReplaceUserBits,
65         GrUserStencilOp::kZeroClipAndUserBits,
66         0xffff>()
67 );
68 
69 ///////
70 // Union
71 static constexpr GrUserStencilSettings gUserToClipUnion(
72     GrUserStencilSettings::StaticInit<
73         0x0000,
74         GrUserStencilTest::kNotEqual,
75         0xffff,
76         GrUserStencilOp::kSetClipAndReplaceUserBits,
77         GrUserStencilOp::kKeep,
78         0xffff>()
79 );
80 
81 static constexpr GrUserStencilSettings gInvUserToClipUnionPass0( // Does not zero user bits.
82     GrUserStencilSettings::StaticInit<
83         0x0000,
84         GrUserStencilTest::kEqual,
85         0xffff,
86         GrUserStencilOp::kSetClipBit,
87         GrUserStencilOp::kKeep,
88         0x0000>()
89 );
90 
91 ///////
92 // Xor
93 static constexpr GrUserStencilSettings gUserToClipXorPass0( // Does not zero user bits.
94     GrUserStencilSettings::StaticInit<
95         0x0000,
96         GrUserStencilTest::kNotEqual,
97         0xffff,
98         GrUserStencilOp::kInvertClipBit,
99         GrUserStencilOp::kKeep,
100         0x0000>()
101 );
102 
103 static constexpr GrUserStencilSettings gInvUserToClipXorPass0( // Does not zero user bits.
104     GrUserStencilSettings::StaticInit<
105         0x0000,
106         GrUserStencilTest::kEqual,
107         0xffff,
108         GrUserStencilOp::kInvertClipBit,
109         GrUserStencilOp::kKeep,
110         0x0000>()
111 );
112 
113 ///////
114 // Reverse Diff
115 static constexpr GrUserStencilSettings gUserToClipRDiffPass0( // Does not zero user bits.
116     GrUserStencilSettings::StaticInit<
117         0x0000,
118         GrUserStencilTest::kNotEqual,
119         0xffff,
120         GrUserStencilOp::kInvertClipBit,
121         GrUserStencilOp::kZeroClipBit,
122         0x0000>()
123 );
124 
125 static constexpr GrUserStencilSettings gInvUserToClipRDiffPass0( // Does not zero user bits.
126     GrUserStencilSettings::StaticInit<
127         0x0000,
128         GrUserStencilTest::kEqual,
129         0xffff,
130         GrUserStencilOp::kInvertClipBit,
131         GrUserStencilOp::kZeroClipBit,
132         0x0000>()
133 );
134 
135 ///////
136 // Second pass to clear user bits (only needed sometimes)
137 static constexpr GrUserStencilSettings gZeroUserBits(
138     GrUserStencilSettings::StaticInit<
139         0x0000,
140         GrUserStencilTest::kNotEqual,
141         0xffff,
142         GrUserStencilOp::kZero,
143         GrUserStencilOp::kKeep,
144         0xffff>()
145 );
146 
147 static constexpr const GrUserStencilSettings* gUserToClipTable[2][1 + SkRegion::kLastOp][3] = {
148     {  /* Normal fill. */
149         {&gUserToClipDiff,           nullptr,         nullptr},  // kDifference_Op.
150         {&gUserToClipIsect,          nullptr,         nullptr},  // kIntersect_Op.
151         {&gUserToClipUnion,          nullptr,         nullptr},  // kUnion_Op.
152         {&gUserToClipXorPass0,       &gZeroUserBits,  nullptr},  // kXOR_Op.
153         {&gUserToClipRDiffPass0,     &gZeroUserBits,  nullptr},  // kReverseDifference_Op.
154         {&gUserToClipReplace,        nullptr,         nullptr}   // kReplace_Op.
155 
156     }, /* Inverse fill. */ {
157         {&gUserToClipIsect,          nullptr,         nullptr},  // ~diff (aka isect).
158         {&gUserToClipDiff,           nullptr,         nullptr},  // ~isect (aka diff).
159         {&gInvUserToClipUnionPass0,  &gZeroUserBits,  nullptr},  // ~union.
160         {&gInvUserToClipXorPass0,    &gZeroUserBits,  nullptr},  // ~xor.
161         {&gInvUserToClipRDiffPass0,  &gZeroUserBits,  nullptr},  // ~reverse diff.
162         {&gInvUserToClipReplace,     nullptr,         nullptr}   // ~replace.
163     }
164 };
165 
166 ///////
167 // Direct to Stencil
168 
169 // We can render a clip element directly without first writing to the client
170 // portion of the clip when the fill is not inverse and the set operation will
171 // only modify the in/out status of samples covered by the clip element.
172 
173 // this one only works if used right after stencil clip was cleared.
174 // Our clip mask creation code doesn't allow midstream replace ops.
175 static constexpr GrUserStencilSettings gReplaceClip(
176     GrUserStencilSettings::StaticInit<
177         0x0000,
178         GrUserStencilTest::kAlways,
179         0xffff,
180         GrUserStencilOp::kSetClipBit,
181         GrUserStencilOp::kSetClipBit,
182         0x0000>()
183 );
184 
185 static constexpr GrUserStencilSettings gUnionClip(
186     GrUserStencilSettings::StaticInit<
187         0x0000,
188         GrUserStencilTest::kAlwaysIfInClip,
189         0xffff,
190         GrUserStencilOp::kKeep,
191         GrUserStencilOp::kSetClipBit,
192         0x0000>()
193 );
194 
195 static constexpr GrUserStencilSettings gXorClip(
196     GrUserStencilSettings::StaticInit<
197         0x0000,
198         GrUserStencilTest::kAlways,
199         0xffff,
200         GrUserStencilOp::kInvertClipBit,
201         GrUserStencilOp::kInvertClipBit,
202         0x0000>()
203 );
204 
205 static constexpr GrUserStencilSettings gDiffClip(
206     GrUserStencilSettings::StaticInit<
207         0x0000,
208         GrUserStencilTest::kAlwaysIfInClip,
209         0xffff,
210         GrUserStencilOp::kZeroClipBit,
211         GrUserStencilOp::kKeep,
212         0x0000>()
213 );
214 
215 static constexpr const GrUserStencilSettings* gDirectDrawTable[1 + SkRegion::kLastOp][2] = {
216     {&gDiffClip,     nullptr},  // kDifference_Op.
217     {nullptr,        nullptr},  // kIntersect_Op.
218     {&gUnionClip,    nullptr},  // kUnion_Op.
219     {&gXorClip,      nullptr},  // kXOR_Op.
220     {nullptr,        nullptr},  // kReverseDifference_Op.
221     {&gReplaceClip,  nullptr}   // kReplace_Op.
222 };
223 
224 static_assert(0 == SkRegion::kDifference_Op);
225 static_assert(1 == SkRegion::kIntersect_Op);
226 static_assert(2 == SkRegion::kUnion_Op);
227 static_assert(3 == SkRegion::kXOR_Op);
228 static_assert(4 == SkRegion::kReverseDifference_Op);
229 static_assert(5 == SkRegion::kReplace_Op);
230 
231 // Settings used to when not allowed to draw directly to the clip to fill the user stencil bits
232 // before applying the covering clip stencil passes.
233 static constexpr GrUserStencilSettings gDrawToStencil(
234     GrUserStencilSettings::StaticInit<
235         0x0000,
236         GrUserStencilTest::kAlways,
237         0xffff,
238         GrUserStencilOp::kIncMaybeClamp,
239         GrUserStencilOp::kIncMaybeClamp,
240         0xffff>()
241 );
242 
243 // Get the stencil settings per-pass to achieve the given fill+region op effect on the
244 // stencil buffer.
245 //
246 // If drawDirectToClip comes back false, the caller must first draw the element into the user
247 // stencil bits, and then cover the clip area with multiple passes using the returned
248 // stencil settings.
249 
250 // If drawDirectToClip is true, the returned array will only have one pass and the
251 // caller should use those stencil settings while drawing the element directly.
252 //
253 // This returns a null-terminated list of const GrUserStencilSettings*
get_stencil_passes(SkRegion::Op op,GrPathRenderer::StencilSupport stencilSupport,bool fillInverted,bool * drawDirectToClip)254 static GrUserStencilSettings const* const* get_stencil_passes(
255         SkRegion::Op op, GrPathRenderer::StencilSupport stencilSupport, bool fillInverted,
256         bool* drawDirectToClip) {
257     bool canRenderDirectToStencil =
258             GrPathRenderer::kNoRestriction_StencilSupport == stencilSupport;
259 
260     // TODO: inverse fill + intersect op can be direct.
261     // TODO: this can be greatly simplified when we only need intersect and difference ops and
262     //       none of the paths will be inverse-filled (just toggle the op instead).
263     SkASSERT((unsigned)op <= SkRegion::kLastOp);
264     if (canRenderDirectToStencil && !fillInverted) {
265         GrUserStencilSettings const* const* directPass = gDirectDrawTable[op];
266         if (directPass[0]) {
267             *drawDirectToClip = true;
268             return directPass;
269         }
270     }
271     *drawDirectToClip = false;
272     return gUserToClipTable[fillInverted][op];
273 }
274 
draw_stencil_rect(GrSurfaceDrawContext * rtc,const GrHardClip & clip,const GrUserStencilSettings * ss,const SkMatrix & matrix,const SkRect & rect,GrAA aa)275 static void draw_stencil_rect(GrSurfaceDrawContext* rtc, const GrHardClip& clip,
276                               const GrUserStencilSettings* ss, const SkMatrix& matrix,
277                               const SkRect& rect, GrAA aa) {
278     GrPaint paint;
279     paint.setXPFactory(GrDisableColorXPFactory::Get());
280     rtc->stencilRect(&clip, ss, std::move(paint), aa, matrix, rect);
281 }
282 
draw_path(GrRecordingContext * context,GrSurfaceDrawContext * rtc,GrPathRenderer * pr,const GrHardClip & clip,const SkIRect & bounds,const GrUserStencilSettings * ss,const SkMatrix & matrix,const GrStyledShape & shape,GrAA aa)283 static void draw_path(GrRecordingContext* context,
284                       GrSurfaceDrawContext* rtc,
285                       GrPathRenderer* pr, const GrHardClip& clip, const SkIRect& bounds,
286                       const GrUserStencilSettings* ss,  const SkMatrix& matrix,
287                       const GrStyledShape& shape, GrAA aa) {
288     GrPaint paint;
289     paint.setXPFactory(GrDisableColorXPFactory::Get());
290 
291     // kMSAA is the only type of AA that's possible on a stencil buffer.
292     GrAAType pathAAType = aa == GrAA::kYes ? GrAAType::kMSAA : GrAAType::kNone;
293 
294     GrPathRenderer::DrawPathArgs args{context,
295                                       std::move(paint),
296                                       ss,
297                                       rtc,
298                                       &clip,
299                                       &bounds,
300                                       &matrix,
301                                       &shape,
302                                       pathAAType,
303                                       false};
304     pr->drawPath(args);
305 }
306 
stencil_path(GrRecordingContext * context,GrSurfaceDrawContext * rtc,GrPathRenderer * pr,const GrFixedClip & clip,const SkMatrix & matrix,const GrStyledShape & shape,GrAA aa)307 static void stencil_path(GrRecordingContext* context,
308                          GrSurfaceDrawContext* rtc,
309                          GrPathRenderer* pr, const GrFixedClip& clip, const SkMatrix& matrix,
310                          const GrStyledShape& shape, GrAA aa) {
311     GrPathRenderer::StencilPathArgs args;
312     args.fContext = context;
313     args.fRenderTargetContext = rtc;
314     args.fClip = &clip;
315     args.fClipConservativeBounds = &clip.scissorRect();
316     args.fViewMatrix = &matrix;
317     args.fShape = &shape;
318     args.fDoStencilMSAA = aa;
319 
320     pr->stencilPath(args);
321 }
322 
supported_aa(GrSurfaceDrawContext * rtc,GrAA aa)323 static GrAA supported_aa(GrSurfaceDrawContext* rtc, GrAA aa) {
324     if (rtc->numSamples() > 1) {
325         if (rtc->caps()->multisampleDisableSupport()) {
326             return aa;
327         } else {
328             return GrAA::kYes;
329         }
330     } else {
331         return GrAA::kNo;
332     }
333 }
334 
335 }  // namespace
336 
init(const SkIRect & bounds,uint32_t genID,const GrWindowRectangles & windowRects,int numFPs)337 bool GrStencilMaskHelper::init(const SkIRect& bounds, uint32_t genID,
338                                const GrWindowRectangles& windowRects, int numFPs) {
339     if (!fRTC->mustRenderClip(genID, bounds, numFPs)) {
340         return false;
341     }
342 
343     fClip.setStencilClip(genID);
344     // Should have caught bounds not intersecting the render target much earlier in clip application
345     SkAssertResult(fClip.fixedClip().setScissor(bounds));
346     if (!windowRects.empty()) {
347         fClip.fixedClip().setWindowRectangles(
348                 windowRects, GrWindowRectsState::Mode::kExclusive);
349     }
350     fNumFPs = numFPs;
351     return true;
352 }
353 
drawRect(const SkRect & rect,const SkMatrix & matrix,SkRegion::Op op,GrAA aa)354 void GrStencilMaskHelper::drawRect(const SkRect& rect,
355                                    const SkMatrix& matrix,
356                                    SkRegion::Op op,
357                                    GrAA aa) {
358     if (rect.isEmpty()) {
359         return;
360     }
361 
362     bool drawDirectToClip;
363     auto passes = get_stencil_passes(op, GrPathRenderer::kNoRestriction_StencilSupport, false,
364                                      &drawDirectToClip);
365     aa = supported_aa(fRTC, aa);
366 
367     if (!drawDirectToClip) {
368         // Draw to client stencil bits first
369         draw_stencil_rect(fRTC, fClip.fixedClip(), &gDrawToStencil, matrix, rect, aa);
370     }
371 
372     // Now modify the clip bit (either by rendering directly), or by covering the bounding box
373     // of the clip
374     for (GrUserStencilSettings const* const* pass = passes; *pass; ++pass) {
375         if (drawDirectToClip) {
376             draw_stencil_rect(fRTC, fClip, *pass, matrix, rect, aa);
377         } else {
378             draw_stencil_rect(fRTC, fClip, *pass, SkMatrix::I(),
379                               SkRect::Make(fClip.fixedClip().scissorRect()), aa);
380         }
381     }
382 }
383 
drawPath(const SkPath & path,const SkMatrix & matrix,SkRegion::Op op,GrAA aa)384 bool GrStencilMaskHelper::drawPath(const SkPath& path,
385                                    const SkMatrix& matrix,
386                                    SkRegion::Op op,
387                                    GrAA aa) {
388     if (path.isEmpty()) {
389         return true;
390     }
391 
392     // drawPath follows a similar approach to drawRect(), where we either draw directly to the clip
393     // bit or first draw to client bits and then apply a cover pass. The complicating factor is that
394     // we rely on path rendering and how the chosen path renderer uses the stencil buffer.
395     aa = supported_aa(fRTC, aa);
396 
397     GrAAType pathAAType = aa == GrAA::kYes ? GrAAType::kMSAA : GrAAType::kNone;
398 
399     // This will be used to determine whether the clip shape can be rendered into the
400     // stencil with arbitrary stencil settings.
401     GrPathRenderer::StencilSupport stencilSupport;
402 
403     // Make path canonical with regards to fill type (inverse handled by stencil settings).
404     bool fillInverted = path.isInverseFillType();
405     SkTCopyOnFirstWrite<SkPath> clipPath(path);
406     if (fillInverted) {
407         clipPath.writable()->toggleInverseFillType();
408     }
409 
410     GrStyledShape shape(*clipPath, GrStyle::SimpleFill());
411     SkASSERT(!shape.inverseFilled());
412 
413     GrPathRenderer::CanDrawPathArgs canDrawArgs;
414     canDrawArgs.fCaps = fContext->priv().caps();
415     canDrawArgs.fProxy = fRTC->asRenderTargetProxy();
416     canDrawArgs.fClipConservativeBounds = &fClip.fixedClip().scissorRect();
417     canDrawArgs.fViewMatrix = &matrix;
418     canDrawArgs.fShape = &shape;
419     canDrawArgs.fPaint = nullptr;
420     canDrawArgs.fSurfaceProps = &fRTC->surfaceProps();
421     canDrawArgs.fAAType = pathAAType;
422     canDrawArgs.fHasUserStencilSettings = false;
423 
424     GrPathRenderer* pr =  fContext->priv().drawingManager()->getPathRenderer(
425             canDrawArgs, false, GrPathRendererChain::DrawType::kStencil, &stencilSupport);
426     if (!pr) {
427         return false;
428     }
429 
430     bool drawDirectToClip;
431     auto passes = get_stencil_passes(op, stencilSupport, fillInverted, &drawDirectToClip);
432 
433     // Write to client bits if necessary
434     if (!drawDirectToClip) {
435         if (stencilSupport == GrPathRenderer::kNoRestriction_StencilSupport) {
436             draw_path(fContext, fRTC, pr, fClip.fixedClip(), fClip.fixedClip().scissorRect(),
437                       &gDrawToStencil, matrix, shape, aa);
438         } else {
439             stencil_path(fContext, fRTC, pr, fClip.fixedClip(), matrix, shape, aa);
440         }
441     }
442 
443     // Now modify the clip bit (either by rendering directly), or by covering the bounding box
444     // of the clip
445     for (GrUserStencilSettings const* const* pass = passes; *pass; ++pass) {
446         if (drawDirectToClip) {
447             draw_path(fContext, fRTC, pr, fClip, fClip.fixedClip().scissorRect(),
448                       *pass, matrix, shape, aa);
449         } else {
450             draw_stencil_rect(fRTC, fClip, *pass, SkMatrix::I(),
451                               SkRect::Make(fClip.fixedClip().scissorRect()), aa);
452         }
453     }
454 
455     return true;
456 }
457 
drawShape(const GrShape & shape,const SkMatrix & matrix,SkRegion::Op op,GrAA aa)458 bool GrStencilMaskHelper::drawShape(const GrShape& shape,
459                                     const SkMatrix& matrix,
460                                     SkRegion::Op op,
461                                     GrAA aa) {
462     if (shape.isRect() && !shape.inverted()) {
463         this->drawRect(shape.rect(), matrix, op, aa);
464         return true;
465     } else {
466         SkPath p;
467         shape.asPath(&p);
468         return this->drawPath(p, matrix, op, aa);
469     }
470 }
471 
clear(bool insideStencil)472 void GrStencilMaskHelper::clear(bool insideStencil) {
473     if (fClip.fixedClip().hasWindowRectangles()) {
474         // Use a draw to benefit from window rectangles when resetting the stencil buffer; for
475         // large buffers with MSAA this can be significant.
476         draw_stencil_rect(fRTC, fClip.fixedClip(),
477                           GrStencilSettings::SetClipBitSettings(insideStencil), SkMatrix::I(),
478                           SkRect::Make(fClip.fixedClip().scissorRect()), GrAA::kNo);
479     } else {
480         fRTC->clearStencilClip(fClip.fixedClip().scissorRect(), insideStencil);
481     }
482 }
483 
finish()484 void GrStencilMaskHelper::finish() {
485     fRTC->setLastClip(fClip.stencilStackID(), fClip.fixedClip().scissorRect(), fNumFPs);
486 }
487