Coverage Report

Created: 2024-05-20 07:14

/src/skia/src/gpu/ganesh/StencilMaskHelper.cpp
Line
Count
Source (jump to first uncovered line)
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
8
#include "src/gpu/ganesh/StencilMaskHelper.h"
9
10
#include "include/core/SkMatrix.h"
11
#include "include/core/SkPath.h"
12
#include "include/core/SkRegion.h"
13
#include "src/gpu/ganesh/GrRecordingContextPriv.h"
14
#include "src/gpu/ganesh/GrStencilSettings.h"
15
#include "src/gpu/ganesh/SurfaceDrawContext.h"
16
#include "src/gpu/ganesh/effects/GrDisableColorXP.h"
17
#include "src/gpu/ganesh/geometry/GrShape.h"
18
#include "src/gpu/ganesh/geometry/GrStyledShape.h"
19
20
namespace {
21
22
////////////////////////////////////////////////////////////////////////////////
23
// Stencil Rules for Merging user stencil space into clip
24
//
25
26
///////
27
// Replace
28
static constexpr GrUserStencilSettings gUserToClipReplace(
29
    GrUserStencilSettings::StaticInit<
30
        0x0000,
31
        GrUserStencilTest::kNotEqual,
32
        0xffff,
33
        GrUserStencilOp::kSetClipAndReplaceUserBits,
34
        GrUserStencilOp::kZeroClipAndUserBits,
35
        0xffff>()
36
);
37
38
static constexpr GrUserStencilSettings gInvUserToClipReplace(
39
    GrUserStencilSettings::StaticInit<
40
        0x0000,
41
        GrUserStencilTest::kEqual,
42
        0xffff,
43
        GrUserStencilOp::kSetClipAndReplaceUserBits,
44
        GrUserStencilOp::kZeroClipAndUserBits,
45
        0xffff>()
46
);
47
48
///////
49
// Intersect
50
static constexpr GrUserStencilSettings gUserToClipIsect(
51
    GrUserStencilSettings::StaticInit<
52
        0x0000,
53
        GrUserStencilTest::kLessIfInClip, // "0 < userBits" is equivalent to "0 != userBits".
54
        0xffff,
55
        GrUserStencilOp::kSetClipAndReplaceUserBits,
56
        GrUserStencilOp::kZeroClipAndUserBits,
57
        0xffff>()
58
);
59
60
///////
61
// Difference
62
static constexpr GrUserStencilSettings gUserToClipDiff(
63
    GrUserStencilSettings::StaticInit<
64
        0x0000,
65
        GrUserStencilTest::kEqualIfInClip,
66
        0xffff,
67
        GrUserStencilOp::kSetClipAndReplaceUserBits,
68
        GrUserStencilOp::kZeroClipAndUserBits,
69
        0xffff>()
70
);
71
72
///////
73
// Union
74
static constexpr GrUserStencilSettings gUserToClipUnion(
75
    GrUserStencilSettings::StaticInit<
76
        0x0000,
77
        GrUserStencilTest::kNotEqual,
78
        0xffff,
79
        GrUserStencilOp::kSetClipAndReplaceUserBits,
80
        GrUserStencilOp::kKeep,
81
        0xffff>()
82
);
83
84
static constexpr GrUserStencilSettings gInvUserToClipUnionPass0( // Does not zero user bits.
85
    GrUserStencilSettings::StaticInit<
86
        0x0000,
87
        GrUserStencilTest::kEqual,
88
        0xffff,
89
        GrUserStencilOp::kSetClipBit,
90
        GrUserStencilOp::kKeep,
91
        0x0000>()
92
);
93
94
///////
95
// Xor
96
static constexpr GrUserStencilSettings gUserToClipXorPass0( // Does not zero user bits.
97
    GrUserStencilSettings::StaticInit<
98
        0x0000,
99
        GrUserStencilTest::kNotEqual,
100
        0xffff,
101
        GrUserStencilOp::kInvertClipBit,
102
        GrUserStencilOp::kKeep,
103
        0x0000>()
104
);
105
106
static constexpr GrUserStencilSettings gInvUserToClipXorPass0( // Does not zero user bits.
107
    GrUserStencilSettings::StaticInit<
108
        0x0000,
109
        GrUserStencilTest::kEqual,
110
        0xffff,
111
        GrUserStencilOp::kInvertClipBit,
112
        GrUserStencilOp::kKeep,
113
        0x0000>()
114
);
115
116
///////
117
// Reverse Diff
118
static constexpr GrUserStencilSettings gUserToClipRDiffPass0( // Does not zero user bits.
119
    GrUserStencilSettings::StaticInit<
120
        0x0000,
121
        GrUserStencilTest::kNotEqual,
122
        0xffff,
123
        GrUserStencilOp::kInvertClipBit,
124
        GrUserStencilOp::kZeroClipBit,
125
        0x0000>()
126
);
127
128
static constexpr GrUserStencilSettings gInvUserToClipRDiffPass0( // Does not zero user bits.
129
    GrUserStencilSettings::StaticInit<
130
        0x0000,
131
        GrUserStencilTest::kEqual,
132
        0xffff,
133
        GrUserStencilOp::kInvertClipBit,
134
        GrUserStencilOp::kZeroClipBit,
135
        0x0000>()
136
);
137
138
///////
139
// Second pass to clear user bits (only needed sometimes)
140
static constexpr GrUserStencilSettings gZeroUserBits(
141
    GrUserStencilSettings::StaticInit<
142
        0x0000,
143
        GrUserStencilTest::kNotEqual,
144
        0xffff,
145
        GrUserStencilOp::kZero,
146
        GrUserStencilOp::kKeep,
147
        0xffff>()
148
);
149
150
static 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.
178
static constexpr GrUserStencilSettings gReplaceClip(
179
    GrUserStencilSettings::StaticInit<
180
        0x0000,
181
        GrUserStencilTest::kAlways,
182
        0xffff,
183
        GrUserStencilOp::kSetClipBit,
184
        GrUserStencilOp::kSetClipBit,
185
        0x0000>()
186
);
187
188
static constexpr GrUserStencilSettings gUnionClip(
189
    GrUserStencilSettings::StaticInit<
190
        0x0000,
191
        GrUserStencilTest::kAlwaysIfInClip,
192
        0xffff,
193
        GrUserStencilOp::kKeep,
194
        GrUserStencilOp::kSetClipBit,
195
        0x0000>()
196
);
197
198
static constexpr GrUserStencilSettings gXorClip(
199
    GrUserStencilSettings::StaticInit<
200
        0x0000,
201
        GrUserStencilTest::kAlways,
202
        0xffff,
203
        GrUserStencilOp::kInvertClipBit,
204
        GrUserStencilOp::kInvertClipBit,
205
        0x0000>()
206
);
207
208
static constexpr GrUserStencilSettings gDiffClip(
209
    GrUserStencilSettings::StaticInit<
210
        0x0000,
211
        GrUserStencilTest::kAlwaysIfInClip,
212
        0xffff,
213
        GrUserStencilOp::kZeroClipBit,
214
        GrUserStencilOp::kKeep,
215
        0x0000>()
216
);
217
218
static 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
227
static_assert(0 == SkRegion::kDifference_Op);
228
static_assert(1 == SkRegion::kIntersect_Op);
229
static_assert(2 == SkRegion::kUnion_Op);
230
static_assert(3 == SkRegion::kXOR_Op);
231
static_assert(4 == SkRegion::kReverseDifference_Op);
232
static_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.
236
static constexpr GrUserStencilSettings gDrawToStencil(
237
    GrUserStencilSettings::StaticInit<
238
        0x0000,
239
        GrUserStencilTest::kAlways,
240
        0xffff,
241
        GrUserStencilOp::kIncMaybeClamp,
242
        GrUserStencilOp::kIncMaybeClamp,
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*
257
GrUserStencilSettings const* const* get_stencil_passes(
258
        SkRegion::Op op,
259
        skgpu::ganesh::PathRenderer::StencilSupport stencilSupport,
260
        bool fillInverted,
261
1.67k
        bool* drawDirectToClip) {
262
1.67k
    bool canRenderDirectToStencil =
263
1.67k
            skgpu::ganesh::PathRenderer::kNoRestriction_StencilSupport == stencilSupport;
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
1.67k
    SkASSERT((unsigned)op <= SkRegion::kLastOp);
269
1.67k
    if (canRenderDirectToStencil && !fillInverted) {
270
1.45k
        GrUserStencilSettings const* const* directPass = gDirectDrawTable[op];
271
1.45k
        if (directPass[0]) {
272
1.39k
            *drawDirectToClip = true;
273
1.39k
            return directPass;
274
1.39k
        }
275
1.45k
    }
276
280
    *drawDirectToClip = false;
277
280
    return gUserToClipTable[fillInverted][op];
278
1.67k
}
279
280
void draw_stencil_rect(skgpu::ganesh::SurfaceDrawContext* sdc,
281
                       const GrHardClip& clip,
282
                       const GrUserStencilSettings* ss,
283
                       const SkMatrix& matrix,
284
                       const SkRect& rect,
285
579
                       GrAA aa) {
286
579
    GrPaint paint;
287
579
    paint.setXPFactory(GrDisableColorXPFactory::Get());
288
579
    sdc->stencilRect(&clip, ss, std::move(paint), aa, matrix, rect);
289
579
}
290
291
void draw_path(GrRecordingContext* rContext,
292
               skgpu::ganesh::SurfaceDrawContext* sdc,
293
               skgpu::ganesh::PathRenderer* pr,
294
               const GrHardClip& clip,
295
               const SkIRect& bounds,
296
               const GrUserStencilSettings* ss,
297
               const SkMatrix& matrix,
298
               const GrStyledShape& shape,
299
1.16k
               GrAA aa) {
300
1.16k
    GrPaint paint;
301
1.16k
    paint.setXPFactory(GrDisableColorXPFactory::Get());
302
303
    // kMSAA is the only type of AA that's possible on a stencil buffer.
304
1.16k
    GrAAType pathAAType = aa == GrAA::kYes ? GrAAType::kMSAA : GrAAType::kNone;
305
306
1.16k
    skgpu::ganesh::PathRenderer::DrawPathArgs args{rContext,
307
1.16k
                                                   std::move(paint),
308
1.16k
                                                   ss,
309
1.16k
                                                   sdc,
310
1.16k
                                                   &clip,
311
1.16k
                                                   &bounds,
312
1.16k
                                                   &matrix,
313
1.16k
                                                   &shape,
314
1.16k
                                                   pathAAType,
315
1.16k
                                                   false};
316
1.16k
    pr->drawPath(args);
317
1.16k
}
318
319
void stencil_path(GrRecordingContext* rContext,
320
                  skgpu::ganesh::SurfaceDrawContext* sdc,
321
                  skgpu::ganesh::PathRenderer* pr,
322
                  const GrFixedClip& clip,
323
                  const SkMatrix& matrix,
324
                  const GrStyledShape& shape,
325
216
                  GrAA aa) {
326
216
    skgpu::ganesh::PathRenderer::StencilPathArgs args;
327
216
    args.fContext = rContext;
328
216
    args.fSurfaceDrawContext = sdc;
329
216
    args.fClip = &clip;
330
216
    args.fClipConservativeBounds = &clip.scissorRect();
331
216
    args.fViewMatrix = &matrix;
332
216
    args.fShape = &shape;
333
216
    args.fDoStencilMSAA = aa;
334
335
216
    pr->stencilPath(args);
336
216
}
337
338
1.67k
GrAA supported_aa(skgpu::ganesh::SurfaceDrawContext* sdc, GrAA aa) {
339
1.67k
    return GrAA(sdc->numSamples() > 1 || sdc->canUseDynamicMSAA());
340
1.67k
}
341
342
}  // namespace
343
344
namespace skgpu::ganesh {
345
346
StencilMaskHelper::StencilMaskHelper(GrRecordingContext* rContext,
347
                                     SurfaceDrawContext* sdc)
348
        : fContext(rContext)
349
        , fSDC(sdc)
350
2.47k
        , fClip(sdc->dimensions()) {
351
2.47k
}
352
353
bool StencilMaskHelper::init(const SkIRect& bounds, uint32_t genID,
354
2.47k
                             const GrWindowRectangles& windowRects, int numFPs) {
355
2.47k
    if (!fSDC->mustRenderClip(genID, bounds, numFPs)) {
356
900
        return false;
357
900
    }
358
359
1.57k
    fClip.setStencilClip(genID);
360
    // Should have caught bounds not intersecting the render target much earlier in clip application
361
1.57k
    SkAssertResult(fClip.fixedClip().setScissor(bounds));
362
1.57k
    if (!windowRects.empty()) {
363
0
        fClip.fixedClip().setWindowRectangles(
364
0
                windowRects, GrWindowRectsState::Mode::kExclusive);
365
0
    }
366
1.57k
    fNumFPs = numFPs;
367
1.57k
    return true;
368
2.47k
}
skgpu::ganesh::StencilMaskHelper::init(SkIRect const&, unsigned int, GrWindowRectangles const&, int)
Line
Count
Source
354
2.47k
                             const GrWindowRectangles& windowRects, int numFPs) {
355
2.47k
    if (!fSDC->mustRenderClip(genID, bounds, numFPs)) {
356
900
        return false;
357
900
    }
358
359
1.57k
    fClip.setStencilClip(genID);
360
    // Should have caught bounds not intersecting the render target much earlier in clip application
361
1.57k
    SkAssertResult(fClip.fixedClip().setScissor(bounds));
362
1.57k
    if (!windowRects.empty()) {
363
0
        fClip.fixedClip().setWindowRectangles(
364
0
                windowRects, GrWindowRectsState::Mode::kExclusive);
365
0
    }
366
1.57k
    fNumFPs = numFPs;
367
1.57k
    return true;
368
2.47k
}
Unexecuted instantiation: skgpu::ganesh::StencilMaskHelper::init(SkIRect const&, unsigned int, GrWindowRectangles const&, int)
369
370
void StencilMaskHelper::drawRect(const SkRect& rect,
371
                                 const SkMatrix& matrix,
372
                                 SkRegion::Op op,
373
299
                                 GrAA aa) {
374
299
    if (rect.isEmpty()) {
375
0
        return;
376
0
    }
377
378
299
    bool drawDirectToClip;
379
299
    auto passes = get_stencil_passes(op, PathRenderer::kNoRestriction_StencilSupport,
380
299
                                     false, &drawDirectToClip);
381
299
    aa = supported_aa(fSDC, aa);
382
383
299
    if (!drawDirectToClip) {
384
        // Draw to client stencil bits first
385
38
        draw_stencil_rect(fSDC, fClip.fixedClip(), &gDrawToStencil, matrix, rect, aa);
386
38
    }
387
388
    // Now modify the clip bit (either by rendering directly), or by covering the bounding box
389
    // of the clip
390
598
    for (GrUserStencilSettings const* const* pass = passes; *pass; ++pass) {
391
299
        if (drawDirectToClip) {
392
261
            draw_stencil_rect(fSDC, fClip, *pass, matrix, rect, aa);
393
261
        } else {
394
38
            draw_stencil_rect(fSDC, fClip, *pass, SkMatrix::I(),
395
38
                              SkRect::Make(fClip.fixedClip().scissorRect()), aa);
396
38
        }
397
299
    }
398
299
}
399
400
bool StencilMaskHelper::drawPath(const SkPath& path,
401
                                 const SkMatrix& matrix,
402
                                 SkRegion::Op op,
403
1.37k
                                 GrAA aa) {
404
1.37k
    if (path.isEmpty()) {
405
0
        return true;
406
0
    }
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
1.37k
    aa = supported_aa(fSDC, aa);
412
413
1.37k
    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
1.37k
    PathRenderer::StencilSupport stencilSupport;
418
419
    // Make path canonical with regards to fill type (inverse handled by stencil settings).
420
1.37k
    bool fillInverted = path.isInverseFillType();
421
1.37k
    SkTCopyOnFirstWrite<SkPath> clipPath(path);
422
1.37k
    if (fillInverted) {
423
0
        clipPath.writable()->toggleInverseFillType();
424
0
    }
425
426
1.37k
    GrStyledShape shape(*clipPath, GrStyle::SimpleFill());
427
1.37k
    SkASSERT(!shape.inverseFilled());
428
429
1.37k
    PathRenderer::CanDrawPathArgs canDrawArgs;
430
1.37k
    canDrawArgs.fCaps = fContext->priv().caps();
431
1.37k
    canDrawArgs.fProxy = fSDC->asRenderTargetProxy();
432
1.37k
    canDrawArgs.fClipConservativeBounds = &fClip.fixedClip().scissorRect();
433
1.37k
    canDrawArgs.fViewMatrix = &matrix;
434
1.37k
    canDrawArgs.fShape = &shape;
435
1.37k
    canDrawArgs.fPaint = nullptr;
436
1.37k
    canDrawArgs.fSurfaceProps = &fSDC->surfaceProps();
437
1.37k
    canDrawArgs.fAAType = pathAAType;
438
1.37k
    canDrawArgs.fHasUserStencilSettings = false;
439
440
1.37k
    auto pr =  fContext->priv().drawingManager()->getPathRenderer(
441
1.37k
            canDrawArgs, false, PathRendererChain::DrawType::kStencil, &stencilSupport);
442
1.37k
    if (!pr) {
443
0
        return false;
444
0
    }
445
446
1.37k
    bool drawDirectToClip;
447
1.37k
    auto passes = get_stencil_passes(op, stencilSupport, fillInverted, &drawDirectToClip);
448
449
    // Write to client bits if necessary
450
1.37k
    if (!drawDirectToClip) {
451
242
        if (stencilSupport == PathRenderer::kNoRestriction_StencilSupport) {
452
26
            draw_path(fContext, fSDC, pr, fClip.fixedClip(), fClip.fixedClip().scissorRect(),
453
26
                      &gDrawToStencil, matrix, shape, aa);
454
216
        } else {
455
216
            stencil_path(fContext, fSDC, pr, fClip.fixedClip(), matrix, shape, aa);
456
216
        }
457
242
    }
458
459
    // Now modify the clip bit (either by rendering directly), or by covering the bounding box
460
    // of the clip
461
2.75k
    for (GrUserStencilSettings const* const* pass = passes; *pass; ++pass) {
462
1.37k
        if (drawDirectToClip) {
463
1.13k
            draw_path(fContext, fSDC, pr, fClip, fClip.fixedClip().scissorRect(),
464
1.13k
                      *pass, matrix, shape, aa);
465
1.13k
        } else {
466
242
            draw_stencil_rect(fSDC, fClip, *pass, SkMatrix::I(),
467
242
                              SkRect::Make(fClip.fixedClip().scissorRect()), aa);
468
242
        }
469
1.37k
    }
470
471
1.37k
    return true;
472
1.37k
}
473
474
bool StencilMaskHelper::drawShape(const GrShape& shape,
475
                                  const SkMatrix& matrix,
476
                                  SkRegion::Op op,
477
1.67k
                                  GrAA aa) {
478
1.67k
    if (shape.isRect() && !shape.inverted()) {
479
299
        this->drawRect(shape.rect(), matrix, op, aa);
480
299
        return true;
481
1.37k
    } else {
482
1.37k
        SkPath p;
483
1.37k
        shape.asPath(&p);
484
1.37k
        return this->drawPath(p, matrix, op, aa);
485
1.37k
    }
486
1.67k
}
487
488
1.57k
void StencilMaskHelper::clear(bool insideStencil) {
489
1.57k
    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
0
        draw_stencil_rect(fSDC, fClip.fixedClip(),
493
0
                          GrStencilSettings::SetClipBitSettings(insideStencil), SkMatrix::I(),
494
0
                          SkRect::Make(fClip.fixedClip().scissorRect()), GrAA::kNo);
495
1.57k
    } else {
496
1.57k
        fSDC->clearStencilClip(fClip.fixedClip().scissorRect(), insideStencil);
497
1.57k
    }
498
1.57k
}
499
500
1.57k
void StencilMaskHelper::finish() {
501
1.57k
    fSDC->setLastClip(fClip.stencilStackID(), fClip.fixedClip().scissorRect(), fNumFPs);
502
1.57k
}
503
504
}  // namespace skgpu::ganesh