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