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