/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 |