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