blob: b3e23cc2102b75bbfc8fdeca06711600d6088b4f [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"
14#include "src/gpu/geometry/GrShape.h"
15#include "src/gpu/geometry/GrStyledShape.h"
16
17namespace {
18
19////////////////////////////////////////////////////////////////////////////////
20// Stencil Rules for Merging user stencil space into clip
21//
22
23///////
24// Replace
25static constexpr GrUserStencilSettings gUserToClipReplace(
26 GrUserStencilSettings::StaticInit<
27 0x0000,
28 GrUserStencilTest::kNotEqual,
29 0xffff,
30 GrUserStencilOp::kSetClipAndReplaceUserBits,
31 GrUserStencilOp::kZeroClipAndUserBits,
32 0xffff>()
33);
34
35static 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
47static 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
59static 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
71static constexpr GrUserStencilSettings gUserToClipUnion(
72 GrUserStencilSettings::StaticInit<
73 0x0000,
74 GrUserStencilTest::kNotEqual,
75 0xffff,
76 GrUserStencilOp::kSetClipAndReplaceUserBits,
77 GrUserStencilOp::kKeep,
78 0xffff>()
79);
80
81static 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
93static 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
103static 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
115static 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
125static 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)
137static constexpr GrUserStencilSettings gZeroUserBits(
138 GrUserStencilSettings::StaticInit<
139 0x0000,
140 GrUserStencilTest::kNotEqual,
141 0xffff,
142 GrUserStencilOp::kZero,
143 GrUserStencilOp::kKeep,
144 0xffff>()
145);
146
147static 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.
175static constexpr GrUserStencilSettings gReplaceClip(
176 GrUserStencilSettings::StaticInit<
177 0x0000,
178 GrUserStencilTest::kAlways,
179 0xffff,
180 GrUserStencilOp::kSetClipBit,
181 GrUserStencilOp::kSetClipBit,
182 0x0000>()
183);
184
185static constexpr GrUserStencilSettings gUnionClip(
186 GrUserStencilSettings::StaticInit<
187 0x0000,
188 GrUserStencilTest::kAlwaysIfInClip,
189 0xffff,
190 GrUserStencilOp::kKeep,
191 GrUserStencilOp::kSetClipBit,
192 0x0000>()
193);
194
195static constexpr GrUserStencilSettings gXorClip(
196 GrUserStencilSettings::StaticInit<
197 0x0000,
198 GrUserStencilTest::kAlways,
199 0xffff,
200 GrUserStencilOp::kInvertClipBit,
201 GrUserStencilOp::kInvertClipBit,
202 0x0000>()
203);
204
205static constexpr GrUserStencilSettings gDiffClip(
206 GrUserStencilSettings::StaticInit<
207 0x0000,
208 GrUserStencilTest::kAlwaysIfInClip,
209 0xffff,
210 GrUserStencilOp::kZeroClipBit,
211 GrUserStencilOp::kKeep,
212 0x0000>()
213);
214
215static 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
224static_assert(0 == SkRegion::kDifference_Op);
225static_assert(1 == SkRegion::kIntersect_Op);
226static_assert(2 == SkRegion::kUnion_Op);
227static_assert(3 == SkRegion::kXOR_Op);
228static_assert(4 == SkRegion::kReverseDifference_Op);
229static_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.
233static 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*
254static 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
Brian Salomoneebe7352020-12-09 16:37:04 -0500275static void draw_stencil_rect(GrSurfaceDrawContext* rtc, const GrHardClip& clip,
Michael Ludwig828d3412020-05-12 13:15:35 -0400276 const GrUserStencilSettings* ss, const SkMatrix& matrix,
277 const SkRect& rect, GrAA aa) {
278 GrPaint paint;
279 paint.setXPFactory(GrDisableColorXPFactory::Get());
Brian Salomon70fe17e2020-11-30 14:33:58 -0500280 rtc->stencilRect(&clip, ss, std::move(paint), aa, matrix, rect);
Michael Ludwig828d3412020-05-12 13:15:35 -0400281}
282
Brian Salomoneebe7352020-12-09 16:37:04 -0500283static void draw_path(GrRecordingContext* context,
284 GrSurfaceDrawContext* rtc,
Michael Ludwig828d3412020-05-12 13:15:35 -0400285 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 // Since we are only drawing to the stencil buffer, we can use kMSAA even if the render
292 // target is mixed sampled.
293 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,
309 GrSurfaceDrawContext* rtc,
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;
314 args.fRenderTargetContext = rtc;
315 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
Brian Salomoneebe7352020-12-09 16:37:04 -0500324static GrAA supported_aa(GrSurfaceDrawContext* rtc, GrAA aa) {
Michael Ludwig828d3412020-05-12 13:15:35 -0400325 // MIXED SAMPLES TODO: We can use stencil with mixed samples as well.
Michael Ludwig6397e802020-08-05 15:50:01 -0400326 if (rtc->numSamples() > 1) {
327 if (rtc->caps()->multisampleDisableSupport()) {
328 return aa;
329 } else {
330 return GrAA::kYes;
331 }
332 } else {
333 return GrAA::kNo;
334 }
Michael Ludwig828d3412020-05-12 13:15:35 -0400335}
336
John Stilesa6841be2020-08-06 14:11:56 -0400337} // namespace
Michael Ludwig828d3412020-05-12 13:15:35 -0400338
339bool GrStencilMaskHelper::init(const SkIRect& bounds, uint32_t genID,
340 const GrWindowRectangles& windowRects, int numFPs) {
Brian Salomon70fe17e2020-11-30 14:33:58 -0500341 if (!fRTC->mustRenderClip(genID, bounds, numFPs)) {
Michael Ludwig828d3412020-05-12 13:15:35 -0400342 return false;
343 }
344
345 fClip.setStencilClip(genID);
Michael Ludwigd1d997e2020-06-04 15:52:44 -0400346 // Should have caught bounds not intersecting the render target much earlier in clip application
347 SkAssertResult(fClip.fixedClip().setScissor(bounds));
Michael Ludwig828d3412020-05-12 13:15:35 -0400348 if (!windowRects.empty()) {
349 fClip.fixedClip().setWindowRectangles(
350 windowRects, GrWindowRectsState::Mode::kExclusive);
351 }
352 fNumFPs = numFPs;
353 return true;
354}
355
356void GrStencilMaskHelper::drawRect(const SkRect& rect,
357 const SkMatrix& matrix,
358 SkRegion::Op op,
359 GrAA aa) {
360 if (rect.isEmpty()) {
361 return;
362 }
363
364 bool drawDirectToClip;
365 auto passes = get_stencil_passes(op, GrPathRenderer::kNoRestriction_StencilSupport, false,
366 &drawDirectToClip);
367 aa = supported_aa(fRTC, aa);
368
369 if (!drawDirectToClip) {
370 // Draw to client stencil bits first
371 draw_stencil_rect(fRTC, fClip.fixedClip(), &gDrawToStencil, matrix, rect, aa);
372 }
373
374 // Now modify the clip bit (either by rendering directly), or by covering the bounding box
375 // of the clip
376 for (GrUserStencilSettings const* const* pass = passes; *pass; ++pass) {
377 if (drawDirectToClip) {
378 draw_stencil_rect(fRTC, fClip, *pass, matrix, rect, aa);
379 } else {
380 draw_stencil_rect(fRTC, fClip, *pass, SkMatrix::I(),
381 SkRect::Make(fClip.fixedClip().scissorRect()), aa);
382 }
383 }
384}
385
386bool GrStencilMaskHelper::drawPath(const SkPath& path,
387 const SkMatrix& matrix,
388 SkRegion::Op op,
389 GrAA aa) {
390 if (path.isEmpty()) {
391 return true;
392 }
393
394 // drawPath follows a similar approach to drawRect(), where we either draw directly to the clip
395 // bit or first draw to client bits and then apply a cover pass. The complicating factor is that
396 // we rely on path rendering and how the chosen path renderer uses the stencil buffer.
397 aa = supported_aa(fRTC, aa);
398
399 GrAAType pathAAType = aa == GrAA::kYes ? GrAAType::kMSAA : GrAAType::kNone;
400
401 // This will be used to determine whether the clip shape can be rendered into the
402 // stencil with arbitrary stencil settings.
403 GrPathRenderer::StencilSupport stencilSupport;
404
405 // Make path canonical with regards to fill type (inverse handled by stencil settings).
406 bool fillInverted = path.isInverseFillType();
407 SkTCopyOnFirstWrite<SkPath> clipPath(path);
408 if (fillInverted) {
409 clipPath.writable()->toggleInverseFillType();
410 }
411
412 GrStyledShape shape(*clipPath, GrStyle::SimpleFill());
413 SkASSERT(!shape.inverseFilled());
414
415 GrPathRenderer::CanDrawPathArgs canDrawArgs;
416 canDrawArgs.fCaps = fContext->priv().caps();
417 canDrawArgs.fProxy = fRTC->asRenderTargetProxy();
418 canDrawArgs.fClipConservativeBounds = &fClip.fixedClip().scissorRect();
419 canDrawArgs.fViewMatrix = &matrix;
420 canDrawArgs.fShape = &shape;
421 canDrawArgs.fPaint = nullptr;
422 canDrawArgs.fAAType = pathAAType;
423 canDrawArgs.fHasUserStencilSettings = false;
424 canDrawArgs.fTargetIsWrappedVkSecondaryCB = fRTC->wrapsVkSecondaryCB();
425
426 GrPathRenderer* pr = fContext->priv().drawingManager()->getPathRenderer(
427 canDrawArgs, false, GrPathRendererChain::DrawType::kStencil, &stencilSupport);
428 if (!pr) {
429 return false;
430 }
431
432 bool drawDirectToClip;
433 auto passes = get_stencil_passes(op, stencilSupport, fillInverted, &drawDirectToClip);
434
435 // Write to client bits if necessary
436 if (!drawDirectToClip) {
437 if (stencilSupport == GrPathRenderer::kNoRestriction_StencilSupport) {
438 draw_path(fContext, fRTC, pr, fClip.fixedClip(), fClip.fixedClip().scissorRect(),
439 &gDrawToStencil, matrix, shape, aa);
440 } else {
441 stencil_path(fContext, fRTC, pr, fClip.fixedClip(), matrix, shape, aa);
442 }
443 }
444
445 // Now modify the clip bit (either by rendering directly), or by covering the bounding box
446 // of the clip
447 for (GrUserStencilSettings const* const* pass = passes; *pass; ++pass) {
448 if (drawDirectToClip) {
449 draw_path(fContext, fRTC, pr, fClip, fClip.fixedClip().scissorRect(),
450 *pass, matrix, shape, aa);
451 } else {
452 draw_stencil_rect(fRTC, fClip, *pass, SkMatrix::I(),
453 SkRect::Make(fClip.fixedClip().scissorRect()), aa);
454 }
455 }
456
457 return true;
458}
459
460bool GrStencilMaskHelper::drawShape(const GrShape& shape,
461 const SkMatrix& matrix,
462 SkRegion::Op op,
463 GrAA aa) {
464 if (shape.isRect() && !shape.inverted()) {
465 this->drawRect(shape.rect(), matrix, op, aa);
466 return true;
467 } else {
468 SkPath p;
469 shape.asPath(&p);
470 return this->drawPath(p, matrix, op, aa);
471 }
472}
473
474void GrStencilMaskHelper::clear(bool insideStencil) {
Michael Ludwig81d41722020-05-26 16:57:38 -0400475 if (fClip.fixedClip().hasWindowRectangles()) {
476 // Use a draw to benefit from window rectangles when resetting the stencil buffer; for
477 // large buffers with MSAA this can be significant.
478 draw_stencil_rect(fRTC, fClip.fixedClip(),
479 GrStencilSettings::SetClipBitSettings(insideStencil), SkMatrix::I(),
480 SkRect::Make(fClip.fixedClip().scissorRect()), GrAA::kNo);
481 } else {
Brian Salomon70fe17e2020-11-30 14:33:58 -0500482 fRTC->clearStencilClip(fClip.fixedClip().scissorRect(), insideStencil);
Michael Ludwig81d41722020-05-26 16:57:38 -0400483 }
Michael Ludwig828d3412020-05-12 13:15:35 -0400484}
485
486void GrStencilMaskHelper::finish() {
Brian Salomon70fe17e2020-11-30 14:33:58 -0500487 fRTC->setLastClip(fClip.stencilStackID(), fClip.fixedClip().scissorRect(), fNumFPs);
Michael Ludwig828d3412020-05-12 13:15:35 -0400488}