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