Flutter Engine
The Flutter Engine
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 {
36 kEmpty, kWideOpen, kDeviceRect, kDeviceRRect, kComplex
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);
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
Definition: FontMgrTest.cpp:50
std::tuple< bool, std::unique_ptr< GrFragmentProcessor > > GrFPResult
GrAAType
Definition: GrTypesPriv.h:200
GrAA
Definition: GrTypesPriv.h:173
const SkRect fBounds
#define SkASSERT(cond)
Definition: SkAssert.h:116
SkClipOp
Definition: SkClipOp.h:13
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition: SkPath.cpp:3892
SkDEBUGCODE(SK_SPI) SkThreadID SkGetThreadID()
GrStyledShape fShape
static EdgeMap simplify(const EdgeMap &edges, SkPathFillType fillType)
Item begin() const
Definition: SkTBlockList.h:450
Item end() const
Definition: SkTBlockList.h:451
Definition: GrClip.h:29
Effect
Definition: GrClip.h:31
Definition: SkPath.h:59
RIter ritems()
Definition: SkTBlockList.h:301
int count() const
Definition: SkTBlockList.h:167
bool empty() const
Definition: SkTBlockList.h:185
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
Definition: ClipStack.cpp:1267
ClipStack & operator=(const ClipStack &)=delete
GrClip::PreClipResult preApply(const SkRect &drawBounds, GrAA aa) const override
Definition: ClipStack.cpp:1213
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)
Definition: ClipStack.cpp:1531
ElementIter end() const
Definition: ClipStack.h:371
ClipStack(const SkIRect &deviceBounds, const SkMatrix *ctm, bool forceAA)
Definition: ClipStack.cpp:1147
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
Definition: ClipStack.cpp:1195
void replaceClip(const SkIRect &rect)
Definition: ClipStack.cpp:1542
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
uint8_t value
Definition: dart.idl:42
Optional< SkRect > bounds
Definition: SkRecords.h:189
SkRRect rrect
Definition: SkRecords.h:232
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
Definition: GpuTools.h:21
constexpr bool contains(std::string_view str, std::string_view needle)
Definition: SkStringView.h:41
Definition: SkRect.h:32
std::true_type sk_is_trivially_relocatable
Definition: ClipStack.h:51