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