Flutter Engine
The Flutter Engine
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
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();
422 if (fillInverted) {
423 clipPath.writable()->toggleInverseFillType();
424 }
425
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
SkAssertResult(font.textToGlyphs("Hello", 5, SkTextEncoding::kUTF8, glyphs, std::size(glyphs))==count)
GrAAType
Definition: GrTypesPriv.h:200
GrAA
Definition: GrTypesPriv.h:173
#define SkASSERT(cond)
Definition: SkAssert.h:116
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition: SkPath.cpp:3892
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:423
static const GrUserStencilSettings * SetClipBitSettings(bool setToInside)
static const GrStyle & SimpleFill()
Definition: GrStyle.h:30
bool inverseFilled() const
static const SkMatrix & I()
Definition: SkMatrix.cpp:1544
Definition: SkPath.h:59
@ 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)
Definition: PathRenderer.h:167
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
Definition: color_source.cc:38
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
clipPath(r.path, r.opAA.op(), r.opAA.aa())) DRAW(ClipRRect
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
static constexpr Init< Ref, Test, TestMask, PassOp, FailOp, WriteMask > StaticInit()
Definition: SkRect.h:32
static SkRect Make(const SkISize &size)
Definition: SkRect.h:669
const GrRenderTargetProxy * fProxy
Definition: PathRenderer.h:85