blob: c4bd9b9e1b57b34a46cbf69ef8571d9c3dee8d17 [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"
13#include "src/gpu/GrRenderTargetContextPriv.h"
14#include "src/gpu/GrStencilSettings.h"
15#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
276static void draw_stencil_rect(GrRenderTargetContext* rtc, const GrHardClip& clip,
277 const GrUserStencilSettings* ss, const SkMatrix& matrix,
278 const SkRect& rect, GrAA aa) {
279 GrPaint paint;
280 paint.setXPFactory(GrDisableColorXPFactory::Get());
Michael Ludwig7c12e282020-05-29 09:54:07 -0400281 rtc->priv().stencilRect(&clip, ss, std::move(paint), aa, matrix, rect);
Michael Ludwig828d3412020-05-12 13:15:35 -0400282}
283
284static void draw_path(GrRecordingContext* context, GrRenderTargetContext* 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 // 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
308static void stencil_path(GrRecordingContext* context, GrRenderTargetContext* 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
323static GrAA supported_aa(GrRenderTargetContext* rtc, GrAA aa) {
324 // MIXED SAMPLES TODO: We can use stencil with mixed samples as well.
325 return rtc->numSamples() > 1 ? aa : GrAA::kNo;
326}
327
328} // anonymous
329
330bool GrStencilMaskHelper::init(const SkIRect& bounds, uint32_t genID,
331 const GrWindowRectangles& windowRects, int numFPs) {
332 if (!fRTC->priv().mustRenderClip(genID, bounds, numFPs)) {
333 return false;
334 }
335
336 fClip.setStencilClip(genID);
Michael Ludwig4926b072020-06-04 19:39:42 +0000337 fClip.fixedClip().setScissor(bounds);
Michael Ludwig828d3412020-05-12 13:15:35 -0400338 if (!windowRects.empty()) {
339 fClip.fixedClip().setWindowRectangles(
340 windowRects, GrWindowRectsState::Mode::kExclusive);
341 }
342 fNumFPs = numFPs;
343 return true;
344}
345
346void GrStencilMaskHelper::drawRect(const SkRect& rect,
347 const SkMatrix& matrix,
348 SkRegion::Op op,
349 GrAA aa) {
350 if (rect.isEmpty()) {
351 return;
352 }
353
354 bool drawDirectToClip;
355 auto passes = get_stencil_passes(op, GrPathRenderer::kNoRestriction_StencilSupport, false,
356 &drawDirectToClip);
357 aa = supported_aa(fRTC, aa);
358
359 if (!drawDirectToClip) {
360 // Draw to client stencil bits first
361 draw_stencil_rect(fRTC, fClip.fixedClip(), &gDrawToStencil, matrix, rect, aa);
362 }
363
364 // Now modify the clip bit (either by rendering directly), or by covering the bounding box
365 // of the clip
366 for (GrUserStencilSettings const* const* pass = passes; *pass; ++pass) {
367 if (drawDirectToClip) {
368 draw_stencil_rect(fRTC, fClip, *pass, matrix, rect, aa);
369 } else {
370 draw_stencil_rect(fRTC, fClip, *pass, SkMatrix::I(),
371 SkRect::Make(fClip.fixedClip().scissorRect()), aa);
372 }
373 }
374}
375
376bool GrStencilMaskHelper::drawPath(const SkPath& path,
377 const SkMatrix& matrix,
378 SkRegion::Op op,
379 GrAA aa) {
380 if (path.isEmpty()) {
381 return true;
382 }
383
384 // drawPath follows a similar approach to drawRect(), where we either draw directly to the clip
385 // bit or first draw to client bits and then apply a cover pass. The complicating factor is that
386 // we rely on path rendering and how the chosen path renderer uses the stencil buffer.
387 aa = supported_aa(fRTC, aa);
388
389 GrAAType pathAAType = aa == GrAA::kYes ? GrAAType::kMSAA : GrAAType::kNone;
390
391 // This will be used to determine whether the clip shape can be rendered into the
392 // stencil with arbitrary stencil settings.
393 GrPathRenderer::StencilSupport stencilSupport;
394
395 // Make path canonical with regards to fill type (inverse handled by stencil settings).
396 bool fillInverted = path.isInverseFillType();
397 SkTCopyOnFirstWrite<SkPath> clipPath(path);
398 if (fillInverted) {
399 clipPath.writable()->toggleInverseFillType();
400 }
401
402 GrStyledShape shape(*clipPath, GrStyle::SimpleFill());
403 SkASSERT(!shape.inverseFilled());
404
405 GrPathRenderer::CanDrawPathArgs canDrawArgs;
406 canDrawArgs.fCaps = fContext->priv().caps();
407 canDrawArgs.fProxy = fRTC->asRenderTargetProxy();
408 canDrawArgs.fClipConservativeBounds = &fClip.fixedClip().scissorRect();
409 canDrawArgs.fViewMatrix = &matrix;
410 canDrawArgs.fShape = &shape;
411 canDrawArgs.fPaint = nullptr;
412 canDrawArgs.fAAType = pathAAType;
413 canDrawArgs.fHasUserStencilSettings = false;
414 canDrawArgs.fTargetIsWrappedVkSecondaryCB = fRTC->wrapsVkSecondaryCB();
415
416 GrPathRenderer* pr = fContext->priv().drawingManager()->getPathRenderer(
417 canDrawArgs, false, GrPathRendererChain::DrawType::kStencil, &stencilSupport);
418 if (!pr) {
419 return false;
420 }
421
422 bool drawDirectToClip;
423 auto passes = get_stencil_passes(op, stencilSupport, fillInverted, &drawDirectToClip);
424
425 // Write to client bits if necessary
426 if (!drawDirectToClip) {
427 if (stencilSupport == GrPathRenderer::kNoRestriction_StencilSupport) {
428 draw_path(fContext, fRTC, pr, fClip.fixedClip(), fClip.fixedClip().scissorRect(),
429 &gDrawToStencil, matrix, shape, aa);
430 } else {
431 stencil_path(fContext, fRTC, pr, fClip.fixedClip(), matrix, shape, aa);
432 }
433 }
434
435 // Now modify the clip bit (either by rendering directly), or by covering the bounding box
436 // of the clip
437 for (GrUserStencilSettings const* const* pass = passes; *pass; ++pass) {
438 if (drawDirectToClip) {
439 draw_path(fContext, fRTC, pr, fClip, fClip.fixedClip().scissorRect(),
440 *pass, matrix, shape, aa);
441 } else {
442 draw_stencil_rect(fRTC, fClip, *pass, SkMatrix::I(),
443 SkRect::Make(fClip.fixedClip().scissorRect()), aa);
444 }
445 }
446
447 return true;
448}
449
450bool GrStencilMaskHelper::drawShape(const GrShape& shape,
451 const SkMatrix& matrix,
452 SkRegion::Op op,
453 GrAA aa) {
454 if (shape.isRect() && !shape.inverted()) {
455 this->drawRect(shape.rect(), matrix, op, aa);
456 return true;
457 } else {
458 SkPath p;
459 shape.asPath(&p);
460 return this->drawPath(p, matrix, op, aa);
461 }
462}
463
464void GrStencilMaskHelper::clear(bool insideStencil) {
Michael Ludwig81d41722020-05-26 16:57:38 -0400465 if (fClip.fixedClip().hasWindowRectangles()) {
466 // Use a draw to benefit from window rectangles when resetting the stencil buffer; for
467 // large buffers with MSAA this can be significant.
468 draw_stencil_rect(fRTC, fClip.fixedClip(),
469 GrStencilSettings::SetClipBitSettings(insideStencil), SkMatrix::I(),
470 SkRect::Make(fClip.fixedClip().scissorRect()), GrAA::kNo);
471 } else {
472 fRTC->priv().clearStencilClip(fClip.fixedClip().scissorRect(), insideStencil);
473 }
Michael Ludwig828d3412020-05-12 13:15:35 -0400474}
475
476void GrStencilMaskHelper::finish() {
477 fRTC->priv().setLastClip(fClip.stencilStackID(), fClip.fixedClip().scissorRect(), fNumFPs);
478}