blob: 9abb00e5fd5e935a6450fdb93d93c5324095ffac [file] [log] [blame]
Michael Ludwiga195d102020-09-15 14:51:52 -04001/*
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
20class GrAppliedClip;
21class GrProxyProvider;
22class GrRecordingContext;
Brian Salomoneebe7352020-12-09 16:37:04 -050023class GrSurfaceDrawContext;
Michael Ludwiga195d102020-09-15 14:51:52 -040024class GrSWMaskHelper;
25
26class GrClipStack final : public GrClip {
27public:
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
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
60 void clipRect(const SkMatrix& ctm, const SkRect& rect, GrAA aa, SkClipOp op) {
61 this->clip({ctm, GrShape(rect), aa, op});
62 }
63 void clipRRect(const SkMatrix& ctm, const SkRRect& rrect, GrAA aa, SkClipOp op) {
64 this->clip({ctm, GrShape(rrect), aa, op});
65 }
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
Brian Salomoneebe7352020-12-09 16:37:04 -050074 GrClip::Effect apply(GrRecordingContext*, GrSurfaceDrawContext*, GrAAType aa,
Derek Sollenberger396cd1d2021-04-02 15:44:36 +000075 bool hasUserStencilSettings,
76 GrAppliedClip*, SkRect* bounds) const override;
Michael Ludwiga195d102020-09-15 14:51:52 -040077 GrClip::PreClipResult preApply(const SkRect& drawBounds, GrAA aa) const override;
78 SkIRect getConservativeBounds() const override;
79
Greg Daniela28ea672020-09-25 11:12:56 -040080#if GR_TEST_UTILS
Michael Ludwiga195d102020-09-15 14:51:52 -040081 GrUniqueKey testingOnly_getLastSWMaskKey() const {
82 return fMasks.empty() ? GrUniqueKey() : fMasks.back().key();
83 }
84#endif
85
86private:
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
106 SkClipOp op() const { return fOp; }
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
113 const Element& asElement() const { return *this; }
114
115 const GrShape& shape() const { return fShape; }
116 const SkMatrix& localToDevice() const { return fLocalToDevice; }
117 const SkIRect& innerBounds() const { return fInnerBounds; }
118 GrAA aa() const { return fAA; }
119
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.
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
177 ~Mask() {
178 // The key should have been released by the clip stack before hand
179 SkASSERT(!fKey.isValid());
180 }
181
182 const GrUniqueKey& key() const { return fKey; }
183 const SkIRect& bounds() const { return fBounds; }
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
214 SkClipOp op() const { return fStackOp; }
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
220 const SkShader* shader() const { return fShader.get(); }
221 const SkIRect& innerBounds() const { return fInnerBounds; }
222 int firstActiveElementIndex() const { return fStartingElementIndex; }
223 int oldestElementIndex() const { return fOldestValidIndex; }
224 bool canBeUpdated() const { return (fDeferredSaveCount == 0); }
225
226 ClipState state() const;
227 uint32_t genID() const;
228
229 // Deferred save manipulation
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
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
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
323class GrClipStack::ElementIter {
324public:
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
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
349GrClipStack::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
359GrClipStack::ElementIter GrClipStack::end() const {
360 return ElementIter(fElements.ritems().end(), 0);
361}
362
363#endif