Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
ClipStack.h
Go to the documentation of this file.
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
16#include "src/gpu/ResourceKey.h"
20
21class GrAppliedClip;
22class GrProxyProvider;
24namespace skgpu {
25namespace ganesh {
27}
28} // namespace skgpu
29class GrSWMaskHelper;
30
31namespace skgpu::ganesh {
32
33class ClipStack final : public GrClip {
34public:
35 enum class ClipState : uint8_t {
37 };
38
39 // All data describing a geometric modification to the clip
40 struct Element {
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 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 void clipRect(const SkMatrix& ctm, const SkRect& rect, GrAA aa, SkClipOp op) {
75 this->clip({ctm, GrShape(rect), aa, op});
76 }
77 void clipRRect(const SkMatrix& ctm, const SkRRect& rrect, GrAA aa, SkClipOp op) {
78 this->clip({ctm, GrShape(rrect), aa, op});
79 }
80 void clipPath(const SkMatrix& ctm, const SkPath& path, GrAA aa, SkClipOp op) {
81 this->clip({ctm, GrShape(path), aa, op});
82 }
83 void clipShader(sk_sp<SkShader> shader);
84
85 void replaceClip(const SkIRect& rect);
86
87 // GrClip implementation
90 GrDrawOp*,
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 UniqueKey testingOnly_getLastSWMaskKey() const {
99 return fMasks.empty() ? UniqueKey() : fMasks.back().key();
100 }
101#endif
102
103private:
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 SkClipOp op() const { return fOp; }
124 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 const Element& asElement() const { return *this; }
131
132 const GrShape& shape() const { return fShape; }
133 const SkMatrix& localToDevice() const { return fLocalToDevice; }
134 const SkIRect& innerBounds() const { return fInnerBounds; }
135 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 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 ~Mask() {
190 // The key should have been released by the clip stack before hand
191 SkASSERT(!fKey.isValid());
192 }
193
194 const UniqueKey& key() const { return fKey; }
195 const SkIRect& bounds() const { return fBounds; }
196 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.
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 SkClipOp op() const { return fStackOp; }
227 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 const SkShader* shader() const { return fShader.get(); }
233 const SkIRect& innerBounds() const { return fInnerBounds; }
234 int firstActiveElementIndex() const { return fStartingElementIndex; }
235 int oldestElementIndex() const { return fOldestValidIndex; }
236 bool canBeUpdated() const { return (fDeferredSaveCount == 0); }
237
238 ClipState state() const;
239 uint32_t genID() const;
240
241 // Deferred save manipulation
242 void pushSave() {
243 SkASSERT(fDeferredSaveCount >= 0);
244 fDeferredSaveCount++;
245 }
246 // Returns true if the record should stay alive. False means the ClipStack must delete it
247 bool popSave() {
248 fDeferredSaveCount--;
249 SkASSERT(fDeferredSaveCount >= -1);
250 return fDeferredSaveCount >= 0;
251 }
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 const SaveRecord& currentSaveRecord() const {
301 SkASSERT(!fSaves.empty());
302 return fSaves.back();
303 }
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
336public:
337 bool operator!=(const ElementIter& o) const {
338 return o.fItem != fItem && o.fRemaining != fRemaining;
339 }
340
341 const Element& operator*() const { return (*fItem).asElement(); }
342
344 // Skip over invalidated elements
345 do {
346 fRemaining--;
347 ++fItem;
348 } while(fRemaining > 0 && (*fItem).isInvalid());
349
350 return *this;
351 }
352
353 ElementIter(RawElement::Stack::CRIter::Item item, int r) : fItem(item), fRemaining(r) {}
354
355 RawElement::Stack::CRIter::Item fItem;
357
358 friend class ClipStack;
359};
360
362 if (this->currentSaveRecord().state() == ClipState::kEmpty ||
363 this->currentSaveRecord().state() == ClipState::kWideOpen) {
364 // No visible clip elements when empty or wide open
365 return this->end();
366 }
367 int count = fElements.count() - this->currentSaveRecord().oldestElementIndex();
368 return ElementIter(fElements.ritems().begin(), count);
369}
370
372 return ElementIter(fElements.ritems().end(), 0);
373}
374
375} // namespace skgpu::ganesh
376
377#endif // ClipStack_DEFINED
m reset()
int count
std::tuple< bool, std::unique_ptr< GrFragmentProcessor > > GrFPResult
GrAAType
GrAA
const SkRect fBounds
#define SkASSERT(cond)
Definition SkAssert.h:116
SkClipOp
Definition SkClipOp.h:13
#define SkDEBUGCODE(...)
Definition SkDebug.h:23
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition SkPath.cpp:3824
Item begin() const
Effect
Definition GrClip.h:31
int count() const
bool empty() const
ElementIter(RawElement::Stack::CRIter::Item item, int r)
Definition ClipStack.h:353
bool operator!=(const ElementIter &o) const
Definition ClipStack.h:337
const Element & operator*() const
Definition ClipStack.h:341
RawElement::Stack::CRIter::Item fItem
Definition ClipStack.h:355
ClipStack(const ClipStack &)=delete
GrClip::Effect apply(GrRecordingContext *, skgpu::ganesh::SurfaceDrawContext *, GrDrawOp *, GrAAType, GrAppliedClip *, SkRect *bounds) const override
ClipStack & operator=(const ClipStack &)=delete
GrClip::PreClipResult preApply(const SkRect &drawBounds, GrAA aa) const override
ElementIter begin() const
Definition ClipStack.h:361
void clipRRect(const SkMatrix &ctm, const SkRRect &rrect, GrAA aa, SkClipOp op)
Definition ClipStack.h:77
void clipPath(const SkMatrix &ctm, const SkPath &path, GrAA aa, SkClipOp op)
Definition ClipStack.h:80
void clipShader(sk_sp< SkShader > shader)
ElementIter end() const
Definition ClipStack.h:371
void clipRect(const SkMatrix &ctm, const SkRect &rect, GrAA aa, SkClipOp op)
Definition ClipStack.h:74
ClipState clipState() const
Definition ClipStack.h:62
SkIRect getConservativeBounds() const override
void replaceClip(const SkIRect &rect)
static void Draw(SkCanvas *canvas, const SkRect &rect)
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition main.cc:19
struct MyStruct s
AtkStateType state
Optional< SkRect > bounds
Definition SkRecords.h:189
constexpr bool contains(std::string_view str, std::string_view needle)
std::true_type sk_is_trivially_relocatable
Definition ClipStack.h:51