• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
clipState()62     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 
clipRect(const SkMatrix & ctm,const SkRect & rect,GrAA aa,SkClipOp op)74     void clipRect(const SkMatrix& ctm, const SkRect& rect, GrAA aa, SkClipOp op) {
75         this->clip({ctm, GrShape(rect), aa, op});
76     }
clipRRect(const SkMatrix & ctm,const SkRRect & rrect,GrAA aa,SkClipOp op)77     void clipRRect(const SkMatrix& ctm, const SkRRect& rrect, GrAA aa, SkClipOp op) {
78         this->clip({ctm, GrShape(rrect), aa, op});
79     }
clipPath(const SkMatrix & ctm,const SkPath & path,GrAA aa,SkClipOp op)80     void clipPath(const SkMatrix& ctm, const SkPath& path, GrAA aa, SkClipOp op) {
81         this->clip({ctm, GrShape(path), aa, op});
82     }
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)
testingOnly_getLastSWMaskKey()98     UniqueKey testingOnly_getLastSWMaskKey() const {
99         return fMasks.empty() ? UniqueKey() : fMasks.back().key();
100     }
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
op()123         SkClipOp        op() const { return fOp; }
outerBounds()124         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
asElement()130         const Element&  asElement() const { return *this; }
131 
shape()132         const GrShape&  shape() const { return fShape; }
localToDevice()133         const SkMatrix& localToDevice() const { return fLocalToDevice; }
innerBounds()134         const SkIRect&  innerBounds() const { return fInnerBounds; }
aa()135         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.
isInvalid()142         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 
~Mask()189         ~Mask() {
190             // The key should have been released by the clip stack before hand
191             SkASSERT(!fKey.isValid());
192         }
193 
key()194         const UniqueKey& key() const { return fKey; }
bounds()195         const SkIRect&   bounds() const { return fBounds; }
genID()196         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
op()226         SkClipOp        op() const { return fStackOp; }
outerBounds()227         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
shader()232         const SkShader* shader() const { return fShader.get(); }
innerBounds()233         const SkIRect&  innerBounds() const { return fInnerBounds; }
firstActiveElementIndex()234         int             firstActiveElementIndex() const { return fStartingElementIndex; }
oldestElementIndex()235         int             oldestElementIndex() const { return fOldestValidIndex; }
canBeUpdated()236         bool            canBeUpdated() const { return (fDeferredSaveCount == 0); }
237 
238         ClipState       state() const;
239         uint32_t        genID() const;
240 
241         // Deferred save manipulation
pushSave()242         void pushSave() {
243             SkASSERT(fDeferredSaveCount >= 0);
244             fDeferredSaveCount++;
245         }
246         // Returns true if the record should stay alive. False means the ClipStack must delete it
popSave()247         bool popSave() {
248             fDeferredSaveCount--;
249             SkASSERT(fDeferredSaveCount >= -1);
250             return fDeferredSaveCount >= 0;
251         }
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 
currentSaveRecord()300     const SaveRecord& currentSaveRecord() const {
301         SkASSERT(!fSaves.empty());
302         return fSaves.back();
303     }
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     bool operator!=(const ElementIter& o) const {
338         return o.fItem != fItem && o.fRemaining != fRemaining;
339     }
340 
341     const Element& operator*() const { return (*fItem).asElement(); }
342 
343     ElementIter& operator++() {
344         // Skip over invalidated elements
345         do {
346             fRemaining--;
347             ++fItem;
348         } while(fRemaining > 0 && (*fItem).isInvalid());
349 
350         return *this;
351     }
352 
ElementIter(RawElement::Stack::CRIter::Item item,int r)353     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 
begin()361 ClipStack::ElementIter ClipStack::begin() const {
362     if (this->currentSaveRecord().state() == ClipState::kEmpty ||
363         this->currentSaveRecord().state() == ClipState::kWideOpen) {
364         // No visible clip elements when empty or wide open
365         return this->end();
366     }
367     int count = fElements.count() - this->currentSaveRecord().oldestElementIndex();
368     return ElementIter(fElements.ritems().begin(), count);
369 }
370 
end()371 ClipStack::ElementIter ClipStack::end() const {
372     return ElementIter(fElements.ritems().end(), 0);
373 }
374 
375 }  // namespace skgpu::ganesh
376 
377 #endif // ClipStack_DEFINED
378