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