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