Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
StencilMaskHelper.cpp
Go to the documentation of this file.
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
9
11#include "include/core/SkPath.h"
19
20namespace {
21
22////////////////////////////////////////////////////////////////////////////////
23// Stencil Rules for Merging user stencil space into clip
24//
25
26///////
27// Replace
28static constexpr GrUserStencilSettings gUserToClipReplace(
30 0x0000,
32 0xffff,
35 0xffff>()
36);
37
38static constexpr GrUserStencilSettings gInvUserToClipReplace(
40 0x0000,
42 0xffff,
45 0xffff>()
46);
47
48///////
49// Intersect
50static constexpr GrUserStencilSettings gUserToClipIsect(
52 0x0000,
53 GrUserStencilTest::kLessIfInClip, // "0 < userBits" is equivalent to "0 != userBits".
54 0xffff,
57 0xffff>()
58);
59
60///////
61// Difference
62static constexpr GrUserStencilSettings gUserToClipDiff(
64 0x0000,
66 0xffff,
69 0xffff>()
70);
71
72///////
73// Union
74static constexpr GrUserStencilSettings gUserToClipUnion(
76 0x0000,
78 0xffff,
81 0xffff>()
82);
83
84static constexpr GrUserStencilSettings gInvUserToClipUnionPass0( // Does not zero user bits.
86 0x0000,
88 0xffff,
91 0x0000>()
92);
93
94///////
95// Xor
96static constexpr GrUserStencilSettings gUserToClipXorPass0( // Does not zero user bits.
98 0x0000,
100 0xffff,
103 0x0000>()
104);
105
106static constexpr GrUserStencilSettings gInvUserToClipXorPass0( // Does not zero user bits.
108 0x0000,
110 0xffff,
113 0x0000>()
114);
115
116///////
117// Reverse Diff
118static constexpr GrUserStencilSettings gUserToClipRDiffPass0( // Does not zero user bits.
120 0x0000,
122 0xffff,
125 0x0000>()
126);
127
128static constexpr GrUserStencilSettings gInvUserToClipRDiffPass0( // Does not zero user bits.
130 0x0000,
132 0xffff,
135 0x0000>()
136);
137
138///////
139// Second pass to clear user bits (only needed sometimes)
140static constexpr GrUserStencilSettings gZeroUserBits(
142 0x0000,
144 0xffff,
147 0xffff>()
148);
149
150static constexpr const GrUserStencilSettings* gUserToClipTable[2][1 + SkRegion::kLastOp][3] = {
151 { /* Normal fill. */
152 {&gUserToClipDiff, nullptr, nullptr}, // kDifference_Op.
153 {&gUserToClipIsect, nullptr, nullptr}, // kIntersect_Op.
154 {&gUserToClipUnion, nullptr, nullptr}, // kUnion_Op.
155 {&gUserToClipXorPass0, &gZeroUserBits, nullptr}, // kXOR_Op.
156 {&gUserToClipRDiffPass0, &gZeroUserBits, nullptr}, // kReverseDifference_Op.
157 {&gUserToClipReplace, nullptr, nullptr} // kReplace_Op.
158
159 }, /* Inverse fill. */ {
160 {&gUserToClipIsect, nullptr, nullptr}, // ~diff (aka isect).
161 {&gUserToClipDiff, nullptr, nullptr}, // ~isect (aka diff).
162 {&gInvUserToClipUnionPass0, &gZeroUserBits, nullptr}, // ~union.
163 {&gInvUserToClipXorPass0, &gZeroUserBits, nullptr}, // ~xor.
164 {&gInvUserToClipRDiffPass0, &gZeroUserBits, nullptr}, // ~reverse diff.
165 {&gInvUserToClipReplace, nullptr, nullptr} // ~replace.
166 }
167};
168
169///////
170// Direct to Stencil
171
172// We can render a clip element directly without first writing to the client
173// portion of the clip when the fill is not inverse and the set operation will
174// only modify the in/out status of samples covered by the clip element.
175
176// this one only works if used right after stencil clip was cleared.
177// Our clip mask creation code doesn't allow midstream replace ops.
178static constexpr GrUserStencilSettings gReplaceClip(
180 0x0000,
182 0xffff,
185 0x0000>()
186);
187
188static constexpr GrUserStencilSettings gUnionClip(
190 0x0000,
192 0xffff,
195 0x0000>()
196);
197
198static constexpr GrUserStencilSettings gXorClip(
200 0x0000,
202 0xffff,
205 0x0000>()
206);
207
208static constexpr GrUserStencilSettings gDiffClip(
210 0x0000,
212 0xffff,
215 0x0000>()
216);
217
218static constexpr const GrUserStencilSettings* gDirectDrawTable[1 + SkRegion::kLastOp][2] = {
219 {&gDiffClip, nullptr}, // kDifference_Op.
220 {nullptr, nullptr}, // kIntersect_Op.
221 {&gUnionClip, nullptr}, // kUnion_Op.
222 {&gXorClip, nullptr}, // kXOR_Op.
223 {nullptr, nullptr}, // kReverseDifference_Op.
224 {&gReplaceClip, nullptr} // kReplace_Op.
225};
226
227static_assert(0 == SkRegion::kDifference_Op);
228static_assert(1 == SkRegion::kIntersect_Op);
229static_assert(2 == SkRegion::kUnion_Op);
230static_assert(3 == SkRegion::kXOR_Op);
231static_assert(4 == SkRegion::kReverseDifference_Op);
232static_assert(5 == SkRegion::kReplace_Op);
233
234// Settings used to when not allowed to draw directly to the clip to fill the user stencil bits
235// before applying the covering clip stencil passes.
236static constexpr GrUserStencilSettings gDrawToStencil(
238 0x0000,
240 0xffff,
243 0xffff>()
244);
245
246// Get the stencil settings per-pass to achieve the given fill+region op effect on the
247// stencil buffer.
248//
249// If drawDirectToClip comes back false, the caller must first draw the element into the user
250// stencil bits, and then cover the clip area with multiple passes using the returned
251// stencil settings.
252
253// If drawDirectToClip is true, the returned array will only have one pass and the
254// caller should use those stencil settings while drawing the element directly.
255//
256// This returns a null-terminated list of const GrUserStencilSettings*
257GrUserStencilSettings const* const* get_stencil_passes(
258 SkRegion::Op op,
260 bool fillInverted,
261 bool* drawDirectToClip) {
262 bool canRenderDirectToStencil =
264
265 // TODO: inverse fill + intersect op can be direct.
266 // TODO: this can be greatly simplified when we only need intersect and difference ops and
267 // none of the paths will be inverse-filled (just toggle the op instead).
268 SkASSERT((unsigned)op <= SkRegion::kLastOp);
269 if (canRenderDirectToStencil && !fillInverted) {
270 GrUserStencilSettings const* const* directPass = gDirectDrawTable[op];
271 if (directPass[0]) {
272 *drawDirectToClip = true;
273 return directPass;
274 }
275 }
276 *drawDirectToClip = false;
277 return gUserToClipTable[fillInverted][op];
278}
279
280void draw_stencil_rect(skgpu::ganesh::SurfaceDrawContext* sdc,
281 const GrHardClip& clip,
282 const GrUserStencilSettings* ss,
283 const SkMatrix& matrix,
284 const SkRect& rect,
285 GrAA aa) {
287 paint.setXPFactory(GrDisableColorXPFactory::Get());
288 sdc->stencilRect(&clip, ss, std::move(paint), aa, matrix, rect);
289}
290
291void draw_path(GrRecordingContext* rContext,
294 const GrHardClip& clip,
295 const SkIRect& bounds,
296 const GrUserStencilSettings* ss,
297 const SkMatrix& matrix,
298 const GrStyledShape& shape,
299 GrAA aa) {
301 paint.setXPFactory(GrDisableColorXPFactory::Get());
302
303 // kMSAA is the only type of AA that's possible on a stencil buffer.
304 GrAAType pathAAType = aa == GrAA::kYes ? GrAAType::kMSAA : GrAAType::kNone;
305
307 std::move(paint),
308 ss,
309 sdc,
310 &clip,
311 &bounds,
312 &matrix,
313 &shape,
314 pathAAType,
315 false};
316 pr->drawPath(args);
317}
318
319void stencil_path(GrRecordingContext* rContext,
322 const GrFixedClip& clip,
323 const SkMatrix& matrix,
324 const GrStyledShape& shape,
325 GrAA aa) {
327 args.fContext = rContext;
328 args.fSurfaceDrawContext = sdc;
329 args.fClip = &clip;
330 args.fClipConservativeBounds = &clip.scissorRect();
331 args.fViewMatrix = &matrix;
332 args.fShape = &shape;
333 args.fDoStencilMSAA = aa;
334
335 pr->stencilPath(args);
336}
337
338GrAA supported_aa(skgpu::ganesh::SurfaceDrawContext* sdc, GrAA aa) {
339 return GrAA(sdc->numSamples() > 1 || sdc->canUseDynamicMSAA());
340}
341
342} // namespace
343
344namespace skgpu::ganesh {
345
348 : fContext(rContext)
349 , fSDC(sdc)
350 , fClip(sdc->dimensions()) {
351}
352
353bool StencilMaskHelper::init(const SkIRect& bounds, uint32_t genID,
354 const GrWindowRectangles& windowRects, int numFPs) {
355 if (!fSDC->mustRenderClip(genID, bounds, numFPs)) {
356 return false;
357 }
358
359 fClip.setStencilClip(genID);
360 // Should have caught bounds not intersecting the render target much earlier in clip application
361 SkAssertResult(fClip.fixedClip().setScissor(bounds));
362 if (!windowRects.empty()) {
365 }
366 fNumFPs = numFPs;
367 return true;
368}
369
371 const SkMatrix& matrix,
372 SkRegion::Op op,
373 GrAA aa) {
374 if (rect.isEmpty()) {
375 return;
376 }
377
378 bool drawDirectToClip;
379 auto passes = get_stencil_passes(op, PathRenderer::kNoRestriction_StencilSupport,
380 false, &drawDirectToClip);
381 aa = supported_aa(fSDC, aa);
382
383 if (!drawDirectToClip) {
384 // Draw to client stencil bits first
385 draw_stencil_rect(fSDC, fClip.fixedClip(), &gDrawToStencil, matrix, rect, aa);
386 }
387
388 // Now modify the clip bit (either by rendering directly), or by covering the bounding box
389 // of the clip
390 for (GrUserStencilSettings const* const* pass = passes; *pass; ++pass) {
391 if (drawDirectToClip) {
392 draw_stencil_rect(fSDC, fClip, *pass, matrix, rect, aa);
393 } else {
394 draw_stencil_rect(fSDC, fClip, *pass, SkMatrix::I(),
395 SkRect::Make(fClip.fixedClip().scissorRect()), aa);
396 }
397 }
398}
399
401 const SkMatrix& matrix,
402 SkRegion::Op op,
403 GrAA aa) {
404 if (path.isEmpty()) {
405 return true;
406 }
407
408 // drawPath follows a similar approach to drawRect(), where we either draw directly to the clip
409 // bit or first draw to client bits and then apply a cover pass. The complicating factor is that
410 // we rely on path rendering and how the chosen path renderer uses the stencil buffer.
411 aa = supported_aa(fSDC, aa);
412
413 GrAAType pathAAType = aa == GrAA::kYes ? GrAAType::kMSAA : GrAAType::kNone;
414
415 // This will be used to determine whether the clip shape can be rendered into the
416 // stencil with arbitrary stencil settings.
417 PathRenderer::StencilSupport stencilSupport;
418
419 // Make path canonical with regards to fill type (inverse handled by stencil settings).
420 bool fillInverted = path.isInverseFillType();
421 SkTCopyOnFirstWrite<SkPath> clipPath(path);
422 if (fillInverted) {
423 clipPath.writable()->toggleInverseFillType();
424 }
425
426 GrStyledShape shape(*clipPath, GrStyle::SimpleFill());
427 SkASSERT(!shape.inverseFilled());
428
430 canDrawArgs.fCaps = fContext->priv().caps();
431 canDrawArgs.fProxy = fSDC->asRenderTargetProxy();
432 canDrawArgs.fClipConservativeBounds = &fClip.fixedClip().scissorRect();
433 canDrawArgs.fViewMatrix = &matrix;
434 canDrawArgs.fShape = &shape;
435 canDrawArgs.fPaint = nullptr;
436 canDrawArgs.fSurfaceProps = &fSDC->surfaceProps();
437 canDrawArgs.fAAType = pathAAType;
438 canDrawArgs.fHasUserStencilSettings = false;
439
440 auto pr = fContext->priv().drawingManager()->getPathRenderer(
441 canDrawArgs, false, PathRendererChain::DrawType::kStencil, &stencilSupport);
442 if (!pr) {
443 return false;
444 }
445
446 bool drawDirectToClip;
447 auto passes = get_stencil_passes(op, stencilSupport, fillInverted, &drawDirectToClip);
448
449 // Write to client bits if necessary
450 if (!drawDirectToClip) {
451 if (stencilSupport == PathRenderer::kNoRestriction_StencilSupport) {
452 draw_path(fContext, fSDC, pr, fClip.fixedClip(), fClip.fixedClip().scissorRect(),
453 &gDrawToStencil, matrix, shape, aa);
454 } else {
455 stencil_path(fContext, fSDC, pr, fClip.fixedClip(), matrix, shape, aa);
456 }
457 }
458
459 // Now modify the clip bit (either by rendering directly), or by covering the bounding box
460 // of the clip
461 for (GrUserStencilSettings const* const* pass = passes; *pass; ++pass) {
462 if (drawDirectToClip) {
463 draw_path(fContext, fSDC, pr, fClip, fClip.fixedClip().scissorRect(),
464 *pass, matrix, shape, aa);
465 } else {
466 draw_stencil_rect(fSDC, fClip, *pass, SkMatrix::I(),
467 SkRect::Make(fClip.fixedClip().scissorRect()), aa);
468 }
469 }
470
471 return true;
472}
473
475 const SkMatrix& matrix,
476 SkRegion::Op op,
477 GrAA aa) {
478 if (shape.isRect() && !shape.inverted()) {
479 this->drawRect(shape.rect(), matrix, op, aa);
480 return true;
481 } else {
482 SkPath p;
483 shape.asPath(&p);
484 return this->drawPath(p, matrix, op, aa);
485 }
486}
487
488void StencilMaskHelper::clear(bool insideStencil) {
489 if (fClip.fixedClip().hasWindowRectangles()) {
490 // Use a draw to benefit from window rectangles when resetting the stencil buffer; for
491 // large buffers with MSAA this can be significant.
492 draw_stencil_rect(fSDC, fClip.fixedClip(),
495 } else {
496 fSDC->clearStencilClip(fClip.fixedClip().scissorRect(), insideStencil);
497 }
498}
499
501 fSDC->setLastClip(fClip.stencilStackID(), fClip.fixedClip().scissorRect(), fNumFPs);
502}
503
504} // namespace skgpu::ganesh
GrAAType
GrAA
#define SkAssertResult(cond)
Definition SkAssert.h:123
#define SkASSERT(cond)
Definition SkAssert.h:116
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition SkPath.cpp:3824
const Context & fContext
const GrCaps * caps() const
static const GrDisableColorXPFactory * Get()
PathRenderer * getPathRenderer(const PathRenderer::CanDrawPathArgs &, bool allowSW, PathRendererChain::DrawType, PathRenderer::StencilSupport *=nullptr)
const SkIRect & scissorRect() const
Definition GrFixedClip.h:29
bool hasWindowRectangles() const
Definition GrFixedClip.h:41
void setWindowRectangles(const GrWindowRectangles &windows, GrWindowRectsState::Mode mode)
Definition GrFixedClip.h:45
bool setScissor(const SkIRect &irect)
Definition GrFixedClip.h:33
GrDrawingManager * drawingManager()
GrRecordingContextPriv priv()
SkRect & rect()
Definition GrShape.h:134
bool isRect() const
Definition GrShape.h:86
bool inverted() const
Definition GrShape.h:99
void asPath(SkPath *out, bool simpleFill=true) const
Definition GrShape.cpp:421
static const GrUserStencilSettings * SetClipBitSettings(bool setToInside)
static const GrStyle & SimpleFill()
Definition GrStyle.h:30
bool inverseFilled() const
static const SkMatrix & I()
@ kReverseDifference_Op
operand minus target
Definition SkRegion.h:371
@ kUnion_Op
target unioned with operand
Definition SkRegion.h:369
@ kLastOp
last operator
Definition SkRegion.h:373
@ kReplace_Op
replace target with operand
Definition SkRegion.h:372
@ kIntersect_Op
target intersected with operand
Definition SkRegion.h:368
@ kDifference_Op
target minus operand
Definition SkRegion.h:367
@ kXOR_Op
target exclusive or with operand
Definition SkRegion.h:370
bool drawPath(const DrawPathArgs &args)
void stencilPath(const StencilPathArgs &args)
const GrFixedClip & fixedClip() const
Definition StencilClip.h:31
uint32_t stencilStackID() const
Definition StencilClip.h:34
void setStencilClip(uint32_t stencilStackID)
Definition StencilClip.h:36
StencilMaskHelper(GrRecordingContext *, SurfaceDrawContext *)
void drawRect(const SkRect &rect, const SkMatrix &matrix, SkRegion::Op, GrAA)
bool init(const SkIRect &maskBounds, uint32_t genID, const GrWindowRectangles &windowRects, int numFPs)
bool drawPath(const SkPath &path, const SkMatrix &matrix, SkRegion::Op, GrAA)
bool drawShape(const GrShape &shape, const SkMatrix &matrix, SkRegion::Op, GrAA)
GrRenderTargetProxy * asRenderTargetProxy()
void clearStencilClip(const SkIRect &scissor, bool insideStencilMask)
void setLastClip(uint32_t clipStackGenID, const SkIRect &devClipBounds, int numClipAnalyticElements)
bool mustRenderClip(uint32_t clipStackGenID, const SkIRect &devClipBounds, int numClipAnalyticElements)
void stencilRect(const GrClip *clip, const GrUserStencilSettings *ss, GrPaint &&paint, GrAA doStencilMSAA, const SkMatrix &viewMatrix, const SkRect &rect, const SkMatrix *localMatrix=nullptr)
const SkSurfaceProps & surfaceProps() const
const Paint & paint
static void draw_path(SkCanvas *canvas, const SkRect &r, sk_sp< SkImageFilter > imf)
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
unsigned useCenter Optional< SkMatrix > matrix
Definition SkRecords.h:258
Optional< SkRect > bounds
Definition SkRecords.h:189
static constexpr Init< Ref, Test, TestMask, PassOp, FailOp, WriteMask > StaticInit()
static SkRect Make(const SkISize &size)
Definition SkRect.h:669