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