Coverage Report

Created: 2024-05-20 07:14

/src/skia/src/gpu/ganesh/ClipStack.h
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
#ifndef ClipStack_DEFINED
9
#define ClipStack_DEFINED
10
11
#include "include/core/SkClipOp.h"
12
#include "include/core/SkMatrix.h"
13
#include "include/core/SkShader.h"
14
#include "include/private/base/SkTypeTraits.h"
15
#include "src/base/SkTBlockList.h"
16
#include "src/gpu/ResourceKey.h"
17
#include "src/gpu/ganesh/GrClip.h"
18
#include "src/gpu/ganesh/GrSurfaceProxyView.h"
19
#include "src/gpu/ganesh/geometry/GrShape.h"
20
21
class GrAppliedClip;
22
class GrProxyProvider;
23
class GrRecordingContext;
24
namespace skgpu {
25
namespace ganesh {
26
class SurfaceDrawContext;
27
}
28
}  // namespace skgpu
29
class GrSWMaskHelper;
30
31
namespace skgpu::ganesh {
32
33
class ClipStack final : public GrClip {
34
public:
35
    enum class ClipState : uint8_t {
36
        kEmpty, kWideOpen, kDeviceRect, kDeviceRRect, kComplex
37
    };
38
39
    // All data describing a geometric modification to the clip
40
    struct Element {
41
        GrShape  fShape;
42
        SkMatrix fLocalToDevice;
43
        SkClipOp fOp;
44
        GrAA     fAA;
45
46
        static_assert(::sk_is_trivially_relocatable<decltype(fShape)>::value);
47
        static_assert(::sk_is_trivially_relocatable<decltype(fLocalToDevice)>::value);
48
        static_assert(::sk_is_trivially_relocatable<decltype(fOp)>::value);
49
        static_assert(::sk_is_trivially_relocatable<decltype(fAA)>::value);
50
51
        using sk_is_trivially_relocatable = std::true_type;
52
    };
53
54
    // The ctm must outlive the ClipStack.
55
    ClipStack(const SkIRect& deviceBounds, const SkMatrix* ctm, bool forceAA);
56
57
    ~ClipStack() override;
58
59
    ClipStack(const ClipStack&) = delete;
60
    ClipStack& operator=(const ClipStack&) = delete;
61
62
133k
    ClipState clipState() const { return this->currentSaveRecord().state(); }
63
64
    class ElementIter;
65
    // Provides for-range over active, valid clip elements from most recent to oldest.
66
    // The iterator provides items as "const Element&".
67
    inline ElementIter begin() const;
68
    inline ElementIter end() const;
69
70
    // Clip stack manipulation
71
    void save();
72
    void restore();
73
74
41.6k
    void clipRect(const SkMatrix& ctm, const SkRect& rect, GrAA aa, SkClipOp op) {
75
41.6k
        this->clip({ctm, GrShape(rect), aa, op});
76
41.6k
    }
77
629
    void clipRRect(const SkMatrix& ctm, const SkRRect& rrect, GrAA aa, SkClipOp op) {
78
629
        this->clip({ctm, GrShape(rrect), aa, op});
79
629
    }
80
539
    void clipPath(const SkMatrix& ctm, const SkPath& path, GrAA aa, SkClipOp op) {
81
539
        this->clip({ctm, GrShape(path), aa, op});
82
539
    }
83
    void clipShader(sk_sp<SkShader> shader);
84
85
    void replaceClip(const SkIRect& rect);
86
87
    // GrClip implementation
88
    GrClip::Effect apply(GrRecordingContext*,
89
                         skgpu::ganesh::SurfaceDrawContext*,
90
                         GrDrawOp*,
91
                         GrAAType,
92
                         GrAppliedClip*,
93
                         SkRect* bounds) const override;
94
    GrClip::PreClipResult preApply(const SkRect& drawBounds, GrAA aa) const override;
95
    SkIRect getConservativeBounds() const override;
96
97
#if defined(GR_TEST_UTILS)
98
0
    UniqueKey testingOnly_getLastSWMaskKey() const {
99
0
        return fMasks.empty() ? UniqueKey() : fMasks.back().key();
100
0
    }
101
#endif
102
103
private:
104
    class SaveRecord;
105
    class Mask;
106
107
    // Internally, a lot of clip reasoning is based on an op, outer bounds, and whether a shape
108
    // contains another (possibly just conservatively based on inner/outer device-space bounds).
109
    //
110
    // Element and SaveRecord store this information directly, but a draw fits the same definition
111
    // with an implicit intersect op and empty inner bounds. The OpDraw and RRectDraw types provide
112
    // the same interface as Element and SaveRecord for internal clip reasoning templates.
113
    class Draw;
114
115
    // Wraps the geometric Element data with logic for containment and bounds testing.
116
    class RawElement : private Element {
117
    public:
118
        using Stack = SkTBlockList<RawElement, 1>;
119
120
        RawElement(const SkMatrix& localToDevice, const GrShape& shape, GrAA aa, SkClipOp op);
121
122
        // Common clip type interface
123
107k
        SkClipOp        op() const { return fOp; }
124
102k
        const SkIRect&  outerBounds() const { return fOuterBounds; }
125
        bool            contains(const SaveRecord& s) const;
126
        bool            contains(const Draw& d) const;
127
        bool            contains(const RawElement& e) const;
128
129
        // Additional element-specific data
130
14.7k
        const Element&  asElement() const { return *this; }
131
132
123k
        const GrShape&  shape() const { return fShape; }
133
14.2k
        const SkMatrix& localToDevice() const { return fLocalToDevice; }
134
44.3k
        const SkIRect&  innerBounds() const { return fInnerBounds; }
135
61.7k
        GrAA            aa() const { return fAA; }
136
137
        ClipState       clipType() const;
138
139
        // As new elements are pushed on to the stack, they may make older elements redundant.
140
        // The old elements are marked invalid so they are skipped during clip application, but may
141
        // become active again when a save record is restored.
142
22.2k
        bool isInvalid() const { return fInvalidatedByIndex >= 0; }
143
        void markInvalid(const SaveRecord& current);
144
        void restoreValid(const SaveRecord& current);
145
146
        // 'added' represents a new op added to the element stack. Its combination with this element
147
        // can result in a number of possibilities:
148
        //  1. The entire clip is empty (signaled by both this and 'added' being invalidated).
149
        //  2. The 'added' op supercedes this element (this element is invalidated).
150
        //  3. This op supercedes the 'added' element (the added element is marked invalidated).
151
        //  4. Their combination can be represented by a single new op (in which case this
152
        //     element should be invalidated, and the combined shape stored in 'added').
153
        //  5. Or both elements remain needed to describe the clip (both are valid and unchanged).
154
        //
155
        // The calling element will only modify its invalidation index since it could belong
156
        // to part of the inactive stack (that might be restored later). All merged state/geometry
157
        // is handled by modifying 'added'.
158
        void updateForElement(RawElement* added, const SaveRecord& current);
159
160
        void simplify(const SkIRect& deviceBounds, bool forceAA);
161
162
    private:
163
        bool combine(const RawElement& other, const SaveRecord& current);
164
165
        SkMatrix fDeviceToLocal; // cached inverse of fLocalToDevice for contains() optimization
166
167
        // Device space bounds, rounded in or out to pixel boundaries and accounting for any
168
        // uncertainty around anti-aliasing and rasterization snapping.
169
        SkIRect  fInnerBounds;
170
        SkIRect  fOuterBounds;
171
172
        // Elements are invalidated by SaveRecords as the record is updated with new elements that
173
        // override old geometry. An invalidated element stores the index of the first element of
174
        // the save record that invalidated it. This makes it easy to undo when the save record is
175
        // popped from the stack, and is stable as the current save record is modified.
176
        int fInvalidatedByIndex;
177
    };
178
179
    // Represents an alpha mask with the rasterized coverage from elements in a draw query that
180
    // could not be converted to analytic coverage FPs.
181
    // TODO: This is only required for SW masks. Stencil masks and atlas masks don't have resources
182
    // owned by the ClipStack. Once SW masks are no longer needed, this can go away.
183
    class Mask {
184
    public:
185
        using Stack = SkTBlockList<Mask, 1>;
186
187
        Mask(const SaveRecord& current, const SkIRect& bounds);
188
189
192
        ~Mask() {
190
            // The key should have been released by the clip stack before hand
191
192
            SkASSERT(!fKey.isValid());
192
192
        }
193
194
1.00k
        const UniqueKey& key() const { return fKey; }
195
808
        const SkIRect&   bounds() const { return fBounds; }
196
1.54k
        uint32_t         genID() const { return fGenID; }
197
198
        bool appliesToDraw(const SaveRecord& current, const SkIRect& drawBounds) const;
199
        void invalidate(GrProxyProvider* proxyProvider);
200
201
        SkDEBUGCODE(const SaveRecord* owner() const { return fOwner; })
202
    private:
203
        UniqueKey fKey;
204
        // The gen ID of the save record and the query bounds uniquely define the set of elements
205
        // that would go into a mask. If the save record adds new elements, its gen ID would change.
206
        // If the draw had different bounds it would select a different set of masked elements.
207
        // Repeatedly querying an unmodified save record with the same bounds is idempotent.
208
        SkIRect     fBounds;
209
        uint32_t    fGenID;
210
211
        SkDEBUGCODE(const SaveRecord* fOwner;)
212
    };
213
214
    // Represents a saved point in the clip stack, and manages the life time of elements added to
215
    // stack within the record's life time. Also provides the logic for determining active elements
216
    // given a draw query.
217
    class SaveRecord {
218
    public:
219
        using Stack = SkTBlockList<SaveRecord, 2>;
220
221
        explicit SaveRecord(const SkIRect& deviceBounds);
222
223
        SaveRecord(const SaveRecord& prior, int startingMaskIndex, int startingElementIndex);
224
225
        // The common clip type interface
226
190k
        SkClipOp        op() const { return fStackOp; }
227
225k
        const SkIRect&  outerBounds() const { return fOuterBounds; }
228
        bool            contains(const Draw& d) const;
229
        bool            contains(const RawElement& e) const;
230
231
        // Additional save record-specific data/functionality
232
26.8k
        const SkShader* shader() const { return fShader.get(); }
233
7.33k
        const SkIRect&  innerBounds() const { return fInnerBounds; }
234
1.20k
        int             firstActiveElementIndex() const { return fStartingElementIndex; }
235
15.5k
        int             oldestElementIndex() const { return fOldestValidIndex; }
236
33.0k
        bool            canBeUpdated() const { return (fDeferredSaveCount == 0); }
237
238
        ClipState       state() const;
239
        uint32_t        genID() const;
240
241
        // Deferred save manipulation
242
29.5k
        void pushSave() {
243
29.5k
            SkASSERT(fDeferredSaveCount >= 0);
244
29.5k
            fDeferredSaveCount++;
245
29.5k
        }
246
        // Returns true if the record should stay alive. False means the ClipStack must delete it
247
31.6k
        bool popSave() {
248
31.6k
            fDeferredSaveCount--;
249
31.6k
            SkASSERT(fDeferredSaveCount >= -1);
250
31.6k
            return fDeferredSaveCount >= 0;
251
31.6k
        }
252
253
        // Return true if the element was added to 'elements', or otherwise affected the save record
254
        // (e.g. turned it empty).
255
        bool addElement(RawElement&& toAdd, RawElement::Stack* elements);
256
257
        void addShader(sk_sp<SkShader> shader);
258
        void reset(const SkIRect& bounds);
259
260
        // Remove the elements owned by this save record, which must happen before the save record
261
        // itself is removed from the clip stack.
262
        void removeElements(RawElement::Stack* elements);
263
264
        // Restore element validity now that this record is the new top of the stack.
265
        void restoreElements(RawElement::Stack* elements);
266
267
        void invalidateMasks(GrProxyProvider* proxyProvider, Mask::Stack* masks);
268
269
    private:
270
        // These functions modify 'elements' and element-dependent state of the record
271
        // (such as valid index and fState).
272
        bool appendElement(RawElement&& toAdd, RawElement::Stack* elements);
273
        void replaceWithElement(RawElement&& toAdd, RawElement::Stack* elements);
274
275
        // Inner bounds is always contained in outer bounds, or it is empty. All bounds will be
276
        // contained in the device bounds.
277
        SkIRect   fInnerBounds; // Inside is full coverage (stack op == intersect) or 0 cov (diff)
278
        SkIRect   fOuterBounds; // Outside is 0 coverage (op == intersect) or full cov (diff)
279
280
        // A save record can have up to one shader, multiple shaders are automatically blended
281
        sk_sp<SkShader> fShader;
282
283
        const int fStartingMaskIndex; // First mask owned by this save record
284
        const int fStartingElementIndex;  // First element owned by this save record
285
        int       fOldestValidIndex; // Index of oldest element that remains valid for this record
286
287
        int       fDeferredSaveCount; // Number of save() calls without modifications (yet)
288
289
        // Will be kIntersect unless every valid element is kDifference, which is significant
290
        // because if kDifference then there is an implicit extra outer bounds at the device edges.
291
        SkClipOp  fStackOp;
292
        ClipState fState;
293
        uint32_t  fGenID;
294
    };
295
296
    // Adds the element to the clip, handling allocating a new save record on the stack if
297
    // there is a deferred save.
298
    void clip(RawElement&& element);
299
300
488k
    const SaveRecord& currentSaveRecord() const {
301
488k
        SkASSERT(!fSaves.empty());
302
488k
        return fSaves.back();
303
488k
    }
304
305
    // Will return the current save record, properly updating deferred saves
306
    // and initializing a first record if it were empty.
307
    SaveRecord& writableSaveRecord(bool* wasDeferred);
308
309
    // Generate or find a cached SW coverage mask and return an FP that samples it.
310
    // 'elements' is an array of pointers to elements in the stack.
311
    static GrFPResult GetSWMaskFP(GrRecordingContext* context, Mask::Stack* masks,
312
                                  const SaveRecord& current, const SkIRect& bounds,
313
                                  const Element** elements, int count,
314
                                  std::unique_ptr<GrFragmentProcessor> clipFP);
315
316
    RawElement::Stack        fElements;
317
    SaveRecord::Stack        fSaves; // always has one wide open record at the top
318
319
    // The masks are recorded during apply() calls so we can cache them; they are not modifications
320
    // of the actual clip stack.
321
    // NOTE: These fields can go away once a context has a dedicated clip atlas
322
    mutable Mask::Stack      fMasks;
323
    mutable GrProxyProvider* fProxyProvider;
324
325
    const SkIRect            fDeviceBounds;
326
    const SkMatrix*          fCTM;
327
328
    // When there's MSAA, clip elements are applied using the stencil buffer. If a backend cannot
329
    // disable MSAA per draw, then all elements are effectively AA'ed. Tracking them as such makes
330
    // keeps the entire stack as simple as possible.
331
    bool                     fForceAA;
332
};
333
334
// Clip element iteration
335
class ClipStack::ElementIter {
336
public:
337
0
    bool operator!=(const ElementIter& o) const {
338
0
        return o.fItem != fItem && o.fRemaining != fRemaining;
339
0
    }
340
341
0
    const Element& operator*() const { return (*fItem).asElement(); }
342
343
0
    ElementIter& operator++() {
344
        // Skip over invalidated elements
345
0
        do {
346
0
            fRemaining--;
347
0
            ++fItem;
348
0
        } while(fRemaining > 0 && (*fItem).isInvalid());
349
350
0
        return *this;
351
0
    }
352
353
0
    ElementIter(RawElement::Stack::CRIter::Item item, int r) : fItem(item), fRemaining(r) {}
354
355
    RawElement::Stack::CRIter::Item fItem;
356
    int fRemaining;
357
358
    friend class ClipStack;
359
};
360
361
0
ClipStack::ElementIter ClipStack::begin() const {
362
0
    if (this->currentSaveRecord().state() == ClipState::kEmpty ||
363
0
        this->currentSaveRecord().state() == ClipState::kWideOpen) {
364
        // No visible clip elements when empty or wide open
365
0
        return this->end();
366
0
    }
367
0
    int count = fElements.count() - this->currentSaveRecord().oldestElementIndex();
368
0
    return ElementIter(fElements.ritems().begin(), count);
369
0
}
370
371
0
ClipStack::ElementIter ClipStack::end() const {
372
0
    return ElementIter(fElements.ritems().end(), 0);
373
0
}
374
375
}  // namespace skgpu::ganesh
376
377
#endif // ClipStack_DEFINED