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