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