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