1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2022 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #ifndef skgpu_graphite_ClipStack_DEFINED
9*c8dee2aaSAndroid Build Coastguard Worker #define skgpu_graphite_ClipStack_DEFINED
10*c8dee2aaSAndroid Build Coastguard Worker
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkClipOp.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTArray.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkTBlockList.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/DrawOrder.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/DrawParams.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/geom/Shape.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/geom/Transform_graphite.h"
18*c8dee2aaSAndroid Build Coastguard Worker
19*c8dee2aaSAndroid Build Coastguard Worker class SkShader;
20*c8dee2aaSAndroid Build Coastguard Worker class SkStrokeRec;
21*c8dee2aaSAndroid Build Coastguard Worker
22*c8dee2aaSAndroid Build Coastguard Worker namespace skgpu::graphite {
23*c8dee2aaSAndroid Build Coastguard Worker
24*c8dee2aaSAndroid Build Coastguard Worker class BoundsManager;
25*c8dee2aaSAndroid Build Coastguard Worker class Device;
26*c8dee2aaSAndroid Build Coastguard Worker class Geometry;
27*c8dee2aaSAndroid Build Coastguard Worker
28*c8dee2aaSAndroid Build Coastguard Worker // TODO: Port over many of the unit tests for skgpu/v1/ClipStack defined in GrClipStackTest since
29*c8dee2aaSAndroid Build Coastguard Worker // those tests do a thorough job of enumerating the different element combinations.
30*c8dee2aaSAndroid Build Coastguard Worker class ClipStack {
31*c8dee2aaSAndroid Build Coastguard Worker public:
32*c8dee2aaSAndroid Build Coastguard Worker // TODO: Some of these states reflect what SkDevice requires. Others are based on what Ganesh
33*c8dee2aaSAndroid Build Coastguard Worker // could handle analytically. They will likely change as graphite's clips are sorted out
34*c8dee2aaSAndroid Build Coastguard Worker enum class ClipState : uint8_t {
35*c8dee2aaSAndroid Build Coastguard Worker kEmpty, kWideOpen, kDeviceRect, kDeviceRRect, kComplex
36*c8dee2aaSAndroid Build Coastguard Worker };
37*c8dee2aaSAndroid Build Coastguard Worker
38*c8dee2aaSAndroid Build Coastguard Worker // All data describing a geometric modification to the clip
39*c8dee2aaSAndroid Build Coastguard Worker struct Element {
40*c8dee2aaSAndroid Build Coastguard Worker Shape fShape;
41*c8dee2aaSAndroid Build Coastguard Worker Transform fLocalToDevice; // TODO: reference a cached Transform like DrawList?
42*c8dee2aaSAndroid Build Coastguard Worker SkClipOp fOp;
43*c8dee2aaSAndroid Build Coastguard Worker };
44*c8dee2aaSAndroid Build Coastguard Worker
45*c8dee2aaSAndroid Build Coastguard Worker // 'owningDevice' must outlive the clip stack.
46*c8dee2aaSAndroid Build Coastguard Worker ClipStack(Device* owningDevice);
47*c8dee2aaSAndroid Build Coastguard Worker
48*c8dee2aaSAndroid Build Coastguard Worker ~ClipStack();
49*c8dee2aaSAndroid Build Coastguard Worker
50*c8dee2aaSAndroid Build Coastguard Worker ClipStack(const ClipStack&) = delete;
51*c8dee2aaSAndroid Build Coastguard Worker ClipStack& operator=(const ClipStack&) = delete;
52*c8dee2aaSAndroid Build Coastguard Worker
clipState()53*c8dee2aaSAndroid Build Coastguard Worker ClipState clipState() const { return this->currentSaveRecord().state(); }
maxDeferredClipDraws()54*c8dee2aaSAndroid Build Coastguard Worker int maxDeferredClipDraws() const { return fElements.count(); }
55*c8dee2aaSAndroid Build Coastguard Worker Rect conservativeBounds() const;
56*c8dee2aaSAndroid Build Coastguard Worker
57*c8dee2aaSAndroid Build Coastguard Worker class ElementIter;
58*c8dee2aaSAndroid Build Coastguard Worker // Provides for-range over active, valid clip elements from most recent to oldest.
59*c8dee2aaSAndroid Build Coastguard Worker // The iterator provides items as "const Element&".
60*c8dee2aaSAndroid Build Coastguard Worker inline ElementIter begin() const;
61*c8dee2aaSAndroid Build Coastguard Worker inline ElementIter end() const;
62*c8dee2aaSAndroid Build Coastguard Worker
63*c8dee2aaSAndroid Build Coastguard Worker // Clip stack manipulation
64*c8dee2aaSAndroid Build Coastguard Worker void save();
65*c8dee2aaSAndroid Build Coastguard Worker void restore();
66*c8dee2aaSAndroid Build Coastguard Worker
67*c8dee2aaSAndroid Build Coastguard Worker // The clip stack does not have a notion of AA vs. non-AA. However, if PixelSnapping::kYes is
68*c8dee2aaSAndroid Build Coastguard Worker // used and the right conditions are met, it can adjust the clip geometry to align with the
69*c8dee2aaSAndroid Build Coastguard Worker // pixel grid and emulate some aspects of non-AA behavior.
70*c8dee2aaSAndroid Build Coastguard Worker enum class PixelSnapping : bool {
71*c8dee2aaSAndroid Build Coastguard Worker kNo = false,
72*c8dee2aaSAndroid Build Coastguard Worker kYes = true
73*c8dee2aaSAndroid Build Coastguard Worker };
74*c8dee2aaSAndroid Build Coastguard Worker void clipShape(const Transform& localToDevice, const Shape& shape, SkClipOp op,
75*c8dee2aaSAndroid Build Coastguard Worker PixelSnapping = PixelSnapping::kNo);
76*c8dee2aaSAndroid Build Coastguard Worker void clipShader(sk_sp<SkShader> shader);
77*c8dee2aaSAndroid Build Coastguard Worker
78*c8dee2aaSAndroid Build Coastguard Worker // Compute the bounds and the effective elements of the clip stack when applied to the draw
79*c8dee2aaSAndroid Build Coastguard Worker // described by the provided transform, shape, and stroke.
80*c8dee2aaSAndroid Build Coastguard Worker //
81*c8dee2aaSAndroid Build Coastguard Worker // Applying clips to a draw is a mostly lazy operation except for what is returned:
82*c8dee2aaSAndroid Build Coastguard Worker // - The Clip's scissor is set to 'conservativeBounds()'.
83*c8dee2aaSAndroid Build Coastguard Worker // - The Clip stores the draw's clipped bounds, taking into account its transform, styling, and
84*c8dee2aaSAndroid Build Coastguard Worker // the above scissor.
85*c8dee2aaSAndroid Build Coastguard Worker // - The Clip also stores the draw's fill-style invariant clipped bounds which is used in atlas
86*c8dee2aaSAndroid Build Coastguard Worker // draws and may differ from the draw bounds.
87*c8dee2aaSAndroid Build Coastguard Worker //
88*c8dee2aaSAndroid Build Coastguard Worker // All clip elements that affect the draw will be returned in `outEffectiveElements` alongside
89*c8dee2aaSAndroid Build Coastguard Worker // the bounds. This method does not have any side-effects and the per-clip element state has to
90*c8dee2aaSAndroid Build Coastguard Worker // be explicitly updated by calling `updateClipStateForDraw()` which prepares the clip stack for
91*c8dee2aaSAndroid Build Coastguard Worker // later rendering.
92*c8dee2aaSAndroid Build Coastguard Worker //
93*c8dee2aaSAndroid Build Coastguard Worker // The returned clip element list will be empty if the shape is clipped out or if the draw is
94*c8dee2aaSAndroid Build Coastguard Worker // unaffected by any of the clip elements.
95*c8dee2aaSAndroid Build Coastguard Worker using ElementList = skia_private::STArray<4, const Element*>;
96*c8dee2aaSAndroid Build Coastguard Worker Clip visitClipStackForDraw(const Transform&,
97*c8dee2aaSAndroid Build Coastguard Worker const Geometry&,
98*c8dee2aaSAndroid Build Coastguard Worker const SkStrokeRec&,
99*c8dee2aaSAndroid Build Coastguard Worker bool outsetBoundsForAA,
100*c8dee2aaSAndroid Build Coastguard Worker ElementList* outEffectiveElements) const;
101*c8dee2aaSAndroid Build Coastguard Worker
102*c8dee2aaSAndroid Build Coastguard Worker // Update the per-clip element state for later rendering using pre-computed clip state data for
103*c8dee2aaSAndroid Build Coastguard Worker // a particular draw. The provided 'z' value is the depth value that the draw will use if it's
104*c8dee2aaSAndroid Build Coastguard Worker // not clipped out entirely.
105*c8dee2aaSAndroid Build Coastguard Worker //
106*c8dee2aaSAndroid Build Coastguard Worker // The returned CompressedPaintersOrder is the largest order that will be used by any of the
107*c8dee2aaSAndroid Build Coastguard Worker // clip elements that affect the draw.
108*c8dee2aaSAndroid Build Coastguard Worker //
109*c8dee2aaSAndroid Build Coastguard Worker // If the provided `clipState` indicates that the draw will be clipped out, then this method has
110*c8dee2aaSAndroid Build Coastguard Worker // no effect and returns DrawOrder::kNoIntersection.
111*c8dee2aaSAndroid Build Coastguard Worker CompressedPaintersOrder updateClipStateForDraw(const Clip& clip,
112*c8dee2aaSAndroid Build Coastguard Worker const ElementList& effectiveElements,
113*c8dee2aaSAndroid Build Coastguard Worker const BoundsManager*,
114*c8dee2aaSAndroid Build Coastguard Worker PaintersDepth z);
115*c8dee2aaSAndroid Build Coastguard Worker
116*c8dee2aaSAndroid Build Coastguard Worker void recordDeferredClipDraws();
117*c8dee2aaSAndroid Build Coastguard Worker
118*c8dee2aaSAndroid Build Coastguard Worker private:
119*c8dee2aaSAndroid Build Coastguard Worker // SaveRecords and Elements are stored in two parallel stacks. The top-most SaveRecord is the
120*c8dee2aaSAndroid Build Coastguard Worker // active record, older records represent earlier save points and aren't modified until they
121*c8dee2aaSAndroid Build Coastguard Worker // become active again. Elements may be owned by the active SaveRecord, in which case they are
122*c8dee2aaSAndroid Build Coastguard Worker // fully mutable, or they may be owned by a prior SaveRecord. However, Elements from both the
123*c8dee2aaSAndroid Build Coastguard Worker // active SaveRecord and older records can be valid and affect draw operations. Elements are
124*c8dee2aaSAndroid Build Coastguard Worker // marked inactive when new elements are determined to supersede their effect completely.
125*c8dee2aaSAndroid Build Coastguard Worker // Inactive elements of the active SaveRecord can be deleted immediately; inactive elements of
126*c8dee2aaSAndroid Build Coastguard Worker // older SaveRecords may become active again as the save stack is popped back.
127*c8dee2aaSAndroid Build Coastguard Worker //
128*c8dee2aaSAndroid Build Coastguard Worker // See go/grclipstack-2.0 for additional details and visualization of the data structures.
129*c8dee2aaSAndroid Build Coastguard Worker class SaveRecord;
130*c8dee2aaSAndroid Build Coastguard Worker
131*c8dee2aaSAndroid Build Coastguard Worker // Internally, a lot of clip reasoning is based on an op, outer bounds, and whether a shape
132*c8dee2aaSAndroid Build Coastguard Worker // contains another (possibly just conservatively based on inner/outer device-space bounds).
133*c8dee2aaSAndroid Build Coastguard Worker // Element and SaveRecord store this information directly. A draw is equivalent to a clip
134*c8dee2aaSAndroid Build Coastguard Worker // element with the intersection op. TransformedShape is a lightweight wrapper that can convert
135*c8dee2aaSAndroid Build Coastguard Worker // these different types into a common type that Simplify() can reason about.
136*c8dee2aaSAndroid Build Coastguard Worker struct TransformedShape;
137*c8dee2aaSAndroid Build Coastguard Worker // This captures which of the two elements in (A op B) would be required when they are combined,
138*c8dee2aaSAndroid Build Coastguard Worker // where op is intersect or difference.
139*c8dee2aaSAndroid Build Coastguard Worker enum class SimplifyResult {
140*c8dee2aaSAndroid Build Coastguard Worker kEmpty,
141*c8dee2aaSAndroid Build Coastguard Worker kAOnly,
142*c8dee2aaSAndroid Build Coastguard Worker kBOnly,
143*c8dee2aaSAndroid Build Coastguard Worker kBoth
144*c8dee2aaSAndroid Build Coastguard Worker };
145*c8dee2aaSAndroid Build Coastguard Worker static SimplifyResult Simplify(const TransformedShape& a, const TransformedShape& b);
146*c8dee2aaSAndroid Build Coastguard Worker
147*c8dee2aaSAndroid Build Coastguard Worker // Wraps the geometric Element data with logic for containment and bounds testing.
148*c8dee2aaSAndroid Build Coastguard Worker class RawElement : public Element {
149*c8dee2aaSAndroid Build Coastguard Worker public:
150*c8dee2aaSAndroid Build Coastguard Worker using Stack = SkTBlockList<RawElement, 1>;
151*c8dee2aaSAndroid Build Coastguard Worker
152*c8dee2aaSAndroid Build Coastguard Worker RawElement(const Rect& deviceBounds,
153*c8dee2aaSAndroid Build Coastguard Worker const Transform& localToDevice,
154*c8dee2aaSAndroid Build Coastguard Worker const Shape& shape,
155*c8dee2aaSAndroid Build Coastguard Worker SkClipOp op,
156*c8dee2aaSAndroid Build Coastguard Worker PixelSnapping);
157*c8dee2aaSAndroid Build Coastguard Worker
~RawElement()158*c8dee2aaSAndroid Build Coastguard Worker ~RawElement() {
159*c8dee2aaSAndroid Build Coastguard Worker // A pending draw means the element affects something already recorded, so its own
160*c8dee2aaSAndroid Build Coastguard Worker // shape needs to be recorded as a draw. Since recording requires the Device (and
161*c8dee2aaSAndroid Build Coastguard Worker // DrawContext), it must happen before we destroy the element itself.
162*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!this->hasPendingDraw());
163*c8dee2aaSAndroid Build Coastguard Worker }
164*c8dee2aaSAndroid Build Coastguard Worker
165*c8dee2aaSAndroid Build Coastguard Worker // Silence warnings about implicit copy ctor/assignment because we're declaring a dtor
166*c8dee2aaSAndroid Build Coastguard Worker RawElement(const RawElement&) = default;
167*c8dee2aaSAndroid Build Coastguard Worker RawElement& operator=(const RawElement&) = default;
168*c8dee2aaSAndroid Build Coastguard Worker
169*c8dee2aaSAndroid Build Coastguard Worker operator TransformedShape() const;
170*c8dee2aaSAndroid Build Coastguard Worker
hasPendingDraw()171*c8dee2aaSAndroid Build Coastguard Worker bool hasPendingDraw() const { return fOrder != DrawOrder::kNoIntersection; }
shape()172*c8dee2aaSAndroid Build Coastguard Worker const Shape& shape() const { return fShape; }
localToDevice()173*c8dee2aaSAndroid Build Coastguard Worker const Transform& localToDevice() const { return fLocalToDevice; }
outerBounds()174*c8dee2aaSAndroid Build Coastguard Worker const Rect& outerBounds() const { return fOuterBounds; }
innerBounds()175*c8dee2aaSAndroid Build Coastguard Worker const Rect& innerBounds() const { return fInnerBounds; }
op()176*c8dee2aaSAndroid Build Coastguard Worker SkClipOp op() const { return fOp; }
177*c8dee2aaSAndroid Build Coastguard Worker ClipState clipType() const;
178*c8dee2aaSAndroid Build Coastguard Worker
179*c8dee2aaSAndroid Build Coastguard Worker // As new elements are pushed on to the stack, they may make older elements redundant.
180*c8dee2aaSAndroid Build Coastguard Worker // The old elements are marked invalid so they are skipped during clip application, but may
181*c8dee2aaSAndroid Build Coastguard Worker // become active again when a save record is restored.
isInvalid()182*c8dee2aaSAndroid Build Coastguard Worker bool isInvalid() const { return fInvalidatedByIndex >= 0; }
183*c8dee2aaSAndroid Build Coastguard Worker void markInvalid(const SaveRecord& current);
184*c8dee2aaSAndroid Build Coastguard Worker void restoreValid(const SaveRecord& current);
185*c8dee2aaSAndroid Build Coastguard Worker
186*c8dee2aaSAndroid Build Coastguard Worker // 'added' represents a new op added to the element stack. Its combination with this element
187*c8dee2aaSAndroid Build Coastguard Worker // can result in a number of possibilities:
188*c8dee2aaSAndroid Build Coastguard Worker // 1. The entire clip is empty (signaled by both this and 'added' being invalidated).
189*c8dee2aaSAndroid Build Coastguard Worker // 2. The 'added' op supercedes this element (this element is invalidated).
190*c8dee2aaSAndroid Build Coastguard Worker // 3. This op supercedes the 'added' element (the added element is marked invalidated).
191*c8dee2aaSAndroid Build Coastguard Worker // 4. Their combination can be represented by a single new op (in which case this
192*c8dee2aaSAndroid Build Coastguard Worker // element should be invalidated, and the combined shape stored in 'added').
193*c8dee2aaSAndroid Build Coastguard Worker // 5. Or both elements remain needed to describe the clip (both are valid and unchanged).
194*c8dee2aaSAndroid Build Coastguard Worker //
195*c8dee2aaSAndroid Build Coastguard Worker // The calling element will only modify its invalidation index since it could belong
196*c8dee2aaSAndroid Build Coastguard Worker // to part of the inactive stack (that might be restored later). All merged state/geometry
197*c8dee2aaSAndroid Build Coastguard Worker // is handled by modifying 'added'.
198*c8dee2aaSAndroid Build Coastguard Worker void updateForElement(RawElement* added, const SaveRecord& current);
199*c8dee2aaSAndroid Build Coastguard Worker
200*c8dee2aaSAndroid Build Coastguard Worker // Returns how this element affects the draw after more detailed analysis.
201*c8dee2aaSAndroid Build Coastguard Worker enum class DrawInfluence {
202*c8dee2aaSAndroid Build Coastguard Worker kNone, // The element does not affect the draw
203*c8dee2aaSAndroid Build Coastguard Worker kClipOut, // The element causes the draw shape to be entirely clipped out
204*c8dee2aaSAndroid Build Coastguard Worker kIntersect, // The element intersects the draw shape in a complex way
205*c8dee2aaSAndroid Build Coastguard Worker };
206*c8dee2aaSAndroid Build Coastguard Worker DrawInfluence testForDraw(const TransformedShape& draw) const;
207*c8dee2aaSAndroid Build Coastguard Worker
208*c8dee2aaSAndroid Build Coastguard Worker // Updates usage tracking to incorporate the bounds and Z value for the new draw call.
209*c8dee2aaSAndroid Build Coastguard Worker // If this element hasn't affected any prior draws, it will use the bounds manager to
210*c8dee2aaSAndroid Build Coastguard Worker // assign itself a compressed painters order for later rendering.
211*c8dee2aaSAndroid Build Coastguard Worker //
212*c8dee2aaSAndroid Build Coastguard Worker // This method assumes that this element affects the draw in a complex way, such that
213*c8dee2aaSAndroid Build Coastguard Worker // calling `testForDraw()` on the same draw would return `DrawInfluence::kIntersect`. It is
214*c8dee2aaSAndroid Build Coastguard Worker // assumed that `testForDraw()` was called beforehand to ensure that this is the case.
215*c8dee2aaSAndroid Build Coastguard Worker //
216*c8dee2aaSAndroid Build Coastguard Worker // Assuming that this element does not clip out the draw, returns the painters order the
217*c8dee2aaSAndroid Build Coastguard Worker // draw must sort after.
218*c8dee2aaSAndroid Build Coastguard Worker CompressedPaintersOrder updateForDraw(const BoundsManager* boundsManager,
219*c8dee2aaSAndroid Build Coastguard Worker const Rect& drawBounds,
220*c8dee2aaSAndroid Build Coastguard Worker PaintersDepth drawZ);
221*c8dee2aaSAndroid Build Coastguard Worker
222*c8dee2aaSAndroid Build Coastguard Worker // Record a depth-only draw to the given device, restricted to the portion of the clip that
223*c8dee2aaSAndroid Build Coastguard Worker // is actually required based on prior recorded draws. Resets usage tracking for subsequent
224*c8dee2aaSAndroid Build Coastguard Worker // passes.
225*c8dee2aaSAndroid Build Coastguard Worker void drawClip(Device*);
226*c8dee2aaSAndroid Build Coastguard Worker
227*c8dee2aaSAndroid Build Coastguard Worker void validate() const;
228*c8dee2aaSAndroid Build Coastguard Worker
229*c8dee2aaSAndroid Build Coastguard Worker private:
230*c8dee2aaSAndroid Build Coastguard Worker // TODO: Should only combine elements within the same save record, that don't have pending
231*c8dee2aaSAndroid Build Coastguard Worker // draws already. Otherwise, we're changing the geometry that will be rasterized and it
232*c8dee2aaSAndroid Build Coastguard Worker // could lead to gaps even if in a perfect the world the analytically intersected shape was
233*c8dee2aaSAndroid Build Coastguard Worker // equivalent. Can't combine with other save records, since they *might* become pending
234*c8dee2aaSAndroid Build Coastguard Worker // later on.
235*c8dee2aaSAndroid Build Coastguard Worker bool combine(const RawElement& other, const SaveRecord& current);
236*c8dee2aaSAndroid Build Coastguard Worker
237*c8dee2aaSAndroid Build Coastguard Worker // Device space bounds. These bounds are not snapped to pixels with the assumption that if
238*c8dee2aaSAndroid Build Coastguard Worker // a relation (intersects, contains, etc.) is true for the bounds it will be true for the
239*c8dee2aaSAndroid Build Coastguard Worker // rasterization of the coordinates that produced those bounds.
240*c8dee2aaSAndroid Build Coastguard Worker Rect fInnerBounds;
241*c8dee2aaSAndroid Build Coastguard Worker Rect fOuterBounds;
242*c8dee2aaSAndroid Build Coastguard Worker // TODO: Convert fOuterBounds to a ComplementRect to make intersection tests faster?
243*c8dee2aaSAndroid Build Coastguard Worker // Would need to store both original and complement, since the intersection test is
244*c8dee2aaSAndroid Build Coastguard Worker // Rect + ComplementRect and Element/SaveRecord could be on either side of operation.
245*c8dee2aaSAndroid Build Coastguard Worker
246*c8dee2aaSAndroid Build Coastguard Worker // State tracking how this clip element needs to be recorded into the draw context. As the
247*c8dee2aaSAndroid Build Coastguard Worker // clip stack is applied to additional draws, the clip's Z and usage bounds grow to account
248*c8dee2aaSAndroid Build Coastguard Worker // for it; its compressed painter's order is selected the first time a draw is affected.
249*c8dee2aaSAndroid Build Coastguard Worker Rect fUsageBounds;
250*c8dee2aaSAndroid Build Coastguard Worker CompressedPaintersOrder fOrder;
251*c8dee2aaSAndroid Build Coastguard Worker PaintersDepth fMaxZ;
252*c8dee2aaSAndroid Build Coastguard Worker
253*c8dee2aaSAndroid Build Coastguard Worker // Elements are invalidated by SaveRecords as the record is updated with new elements that
254*c8dee2aaSAndroid Build Coastguard Worker // override old geometry. An invalidated element stores the index of the first element of
255*c8dee2aaSAndroid Build Coastguard Worker // the save record that invalidated it. This makes it easy to undo when the save record is
256*c8dee2aaSAndroid Build Coastguard Worker // popped from the stack, and is stable as the current save record is modified.
257*c8dee2aaSAndroid Build Coastguard Worker int fInvalidatedByIndex;
258*c8dee2aaSAndroid Build Coastguard Worker };
259*c8dee2aaSAndroid Build Coastguard Worker
260*c8dee2aaSAndroid Build Coastguard Worker // Represents a saved point in the clip stack, and manages the life time of elements added to
261*c8dee2aaSAndroid Build Coastguard Worker // stack within the record's life time. Also provides the logic for determining active elements
262*c8dee2aaSAndroid Build Coastguard Worker // given a draw query.
263*c8dee2aaSAndroid Build Coastguard Worker class SaveRecord {
264*c8dee2aaSAndroid Build Coastguard Worker public:
265*c8dee2aaSAndroid Build Coastguard Worker using Stack = SkTBlockList<SaveRecord, 2>;
266*c8dee2aaSAndroid Build Coastguard Worker
267*c8dee2aaSAndroid Build Coastguard Worker explicit SaveRecord(const Rect& deviceBounds);
268*c8dee2aaSAndroid Build Coastguard Worker
269*c8dee2aaSAndroid Build Coastguard Worker SaveRecord(const SaveRecord& prior, int startingElementIndex);
270*c8dee2aaSAndroid Build Coastguard Worker
shader()271*c8dee2aaSAndroid Build Coastguard Worker const SkShader* shader() const { return fShader.get(); }
outerBounds()272*c8dee2aaSAndroid Build Coastguard Worker const Rect& outerBounds() const { return fOuterBounds; }
innerBounds()273*c8dee2aaSAndroid Build Coastguard Worker const Rect& innerBounds() const { return fInnerBounds; }
op()274*c8dee2aaSAndroid Build Coastguard Worker SkClipOp op() const { return fStackOp; }
275*c8dee2aaSAndroid Build Coastguard Worker ClipState state() const;
276*c8dee2aaSAndroid Build Coastguard Worker
firstActiveElementIndex()277*c8dee2aaSAndroid Build Coastguard Worker int firstActiveElementIndex() const { return fStartingElementIndex; }
oldestElementIndex()278*c8dee2aaSAndroid Build Coastguard Worker int oldestElementIndex() const { return fOldestValidIndex; }
canBeUpdated()279*c8dee2aaSAndroid Build Coastguard Worker bool canBeUpdated() const { return (fDeferredSaveCount == 0); }
280*c8dee2aaSAndroid Build Coastguard Worker
281*c8dee2aaSAndroid Build Coastguard Worker Rect scissor(const Rect& deviceBounds, const Rect& drawBounds) const;
282*c8dee2aaSAndroid Build Coastguard Worker
283*c8dee2aaSAndroid Build Coastguard Worker // Deferred save manipulation
pushSave()284*c8dee2aaSAndroid Build Coastguard Worker void pushSave() {
285*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fDeferredSaveCount >= 0);
286*c8dee2aaSAndroid Build Coastguard Worker fDeferredSaveCount++;
287*c8dee2aaSAndroid Build Coastguard Worker }
288*c8dee2aaSAndroid Build Coastguard Worker // Returns true if the record should stay alive. False means the ClipStack must delete it
popSave()289*c8dee2aaSAndroid Build Coastguard Worker bool popSave() {
290*c8dee2aaSAndroid Build Coastguard Worker fDeferredSaveCount--;
291*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fDeferredSaveCount >= -1);
292*c8dee2aaSAndroid Build Coastguard Worker return fDeferredSaveCount >= 0;
293*c8dee2aaSAndroid Build Coastguard Worker }
294*c8dee2aaSAndroid Build Coastguard Worker
295*c8dee2aaSAndroid Build Coastguard Worker // Return true if the element was added to 'elements', or otherwise affected the save record
296*c8dee2aaSAndroid Build Coastguard Worker // (e.g. turned it empty).
297*c8dee2aaSAndroid Build Coastguard Worker bool addElement(RawElement&& toAdd, RawElement::Stack* elements, Device*);
298*c8dee2aaSAndroid Build Coastguard Worker
299*c8dee2aaSAndroid Build Coastguard Worker void addShader(sk_sp<SkShader> shader);
300*c8dee2aaSAndroid Build Coastguard Worker
301*c8dee2aaSAndroid Build Coastguard Worker // Remove the elements owned by this save record, which must happen before the save record
302*c8dee2aaSAndroid Build Coastguard Worker // itself is removed from the clip stack. Records draws for any removed elements that have
303*c8dee2aaSAndroid Build Coastguard Worker // draw usages.
304*c8dee2aaSAndroid Build Coastguard Worker void removeElements(RawElement::Stack* elements, Device*);
305*c8dee2aaSAndroid Build Coastguard Worker
306*c8dee2aaSAndroid Build Coastguard Worker // Restore element validity now that this record is the new top of the stack.
307*c8dee2aaSAndroid Build Coastguard Worker void restoreElements(RawElement::Stack* elements);
308*c8dee2aaSAndroid Build Coastguard Worker
309*c8dee2aaSAndroid Build Coastguard Worker private:
310*c8dee2aaSAndroid Build Coastguard Worker // These functions modify 'elements' and element-dependent state of the record
311*c8dee2aaSAndroid Build Coastguard Worker // (such as valid index and fState). Records draws for any clips that have deferred usages
312*c8dee2aaSAndroid Build Coastguard Worker // that are inactivated and cannot be restored (i.e. part of the active save record).
313*c8dee2aaSAndroid Build Coastguard Worker bool appendElement(RawElement&& toAdd, RawElement::Stack* elements, Device*);
314*c8dee2aaSAndroid Build Coastguard Worker void replaceWithElement(RawElement&& toAdd, RawElement::Stack* elements, Device*);
315*c8dee2aaSAndroid Build Coastguard Worker
316*c8dee2aaSAndroid Build Coastguard Worker // Inner bounds is always contained in outer bounds, or it is empty. All bounds will be
317*c8dee2aaSAndroid Build Coastguard Worker // contained in the device bounds.
318*c8dee2aaSAndroid Build Coastguard Worker Rect fInnerBounds; // Inside is full coverage (stack op == intersect) or 0 cov (diff)
319*c8dee2aaSAndroid Build Coastguard Worker Rect fOuterBounds; // Outside is 0 coverage (op == intersect) or full cov (diff)
320*c8dee2aaSAndroid Build Coastguard Worker
321*c8dee2aaSAndroid Build Coastguard Worker // A save record can have up to one shader, multiple shaders are automatically blended
322*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkShader> fShader;
323*c8dee2aaSAndroid Build Coastguard Worker
324*c8dee2aaSAndroid Build Coastguard Worker const int fStartingElementIndex; // First element owned by this save record
325*c8dee2aaSAndroid Build Coastguard Worker int fOldestValidIndex; // Index of oldest element that's valid for this record
326*c8dee2aaSAndroid Build Coastguard Worker int fDeferredSaveCount; // Number of save() calls without modifications (yet)
327*c8dee2aaSAndroid Build Coastguard Worker
328*c8dee2aaSAndroid Build Coastguard Worker // Will be kIntersect unless every valid element is kDifference, which is significant
329*c8dee2aaSAndroid Build Coastguard Worker // because if kDifference then there is an implicit extra outer bounds at the device edges.
330*c8dee2aaSAndroid Build Coastguard Worker SkClipOp fStackOp;
331*c8dee2aaSAndroid Build Coastguard Worker ClipState fState;
332*c8dee2aaSAndroid Build Coastguard Worker };
333*c8dee2aaSAndroid Build Coastguard Worker
334*c8dee2aaSAndroid Build Coastguard Worker Rect deviceBounds() const;
335*c8dee2aaSAndroid Build Coastguard Worker
currentSaveRecord()336*c8dee2aaSAndroid Build Coastguard Worker const SaveRecord& currentSaveRecord() const {
337*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!fSaves.empty());
338*c8dee2aaSAndroid Build Coastguard Worker return fSaves.back();
339*c8dee2aaSAndroid Build Coastguard Worker }
340*c8dee2aaSAndroid Build Coastguard Worker
341*c8dee2aaSAndroid Build Coastguard Worker // Will return the current save record, properly updating deferred saves
342*c8dee2aaSAndroid Build Coastguard Worker // and initializing a first record if it were empty.
343*c8dee2aaSAndroid Build Coastguard Worker SaveRecord& writableSaveRecord(bool* wasDeferred);
344*c8dee2aaSAndroid Build Coastguard Worker
345*c8dee2aaSAndroid Build Coastguard Worker RawElement::Stack fElements;
346*c8dee2aaSAndroid Build Coastguard Worker SaveRecord::Stack fSaves; // always has one wide open record at the top
347*c8dee2aaSAndroid Build Coastguard Worker
348*c8dee2aaSAndroid Build Coastguard Worker Device* fDevice; // the device this clip stack is coupled with
349*c8dee2aaSAndroid Build Coastguard Worker };
350*c8dee2aaSAndroid Build Coastguard Worker
351*c8dee2aaSAndroid Build Coastguard Worker // Clip element iteration
352*c8dee2aaSAndroid Build Coastguard Worker class ClipStack::ElementIter {
353*c8dee2aaSAndroid Build Coastguard Worker public:
354*c8dee2aaSAndroid Build Coastguard Worker bool operator!=(const ElementIter& o) const {
355*c8dee2aaSAndroid Build Coastguard Worker return o.fItem != fItem && o.fRemaining != fRemaining;
356*c8dee2aaSAndroid Build Coastguard Worker }
357*c8dee2aaSAndroid Build Coastguard Worker
358*c8dee2aaSAndroid Build Coastguard Worker const Element& operator*() const { return *fItem; }
359*c8dee2aaSAndroid Build Coastguard Worker
360*c8dee2aaSAndroid Build Coastguard Worker ElementIter& operator++() {
361*c8dee2aaSAndroid Build Coastguard Worker // Skip over invalidated elements
362*c8dee2aaSAndroid Build Coastguard Worker do {
363*c8dee2aaSAndroid Build Coastguard Worker fRemaining--;
364*c8dee2aaSAndroid Build Coastguard Worker ++fItem;
365*c8dee2aaSAndroid Build Coastguard Worker } while(fRemaining > 0 && (*fItem).isInvalid());
366*c8dee2aaSAndroid Build Coastguard Worker
367*c8dee2aaSAndroid Build Coastguard Worker return *this;
368*c8dee2aaSAndroid Build Coastguard Worker }
369*c8dee2aaSAndroid Build Coastguard Worker
ElementIter(RawElement::Stack::CRIter::Item item,int r)370*c8dee2aaSAndroid Build Coastguard Worker ElementIter(RawElement::Stack::CRIter::Item item, int r) : fItem(item), fRemaining(r) {}
371*c8dee2aaSAndroid Build Coastguard Worker
372*c8dee2aaSAndroid Build Coastguard Worker RawElement::Stack::CRIter::Item fItem;
373*c8dee2aaSAndroid Build Coastguard Worker int fRemaining;
374*c8dee2aaSAndroid Build Coastguard Worker
375*c8dee2aaSAndroid Build Coastguard Worker friend class ClipStack;
376*c8dee2aaSAndroid Build Coastguard Worker };
377*c8dee2aaSAndroid Build Coastguard Worker
begin()378*c8dee2aaSAndroid Build Coastguard Worker ClipStack::ElementIter ClipStack::begin() const {
379*c8dee2aaSAndroid Build Coastguard Worker if (this->currentSaveRecord().state() == ClipState::kEmpty ||
380*c8dee2aaSAndroid Build Coastguard Worker this->currentSaveRecord().state() == ClipState::kWideOpen) {
381*c8dee2aaSAndroid Build Coastguard Worker // No visible clip elements when empty or wide open
382*c8dee2aaSAndroid Build Coastguard Worker return this->end();
383*c8dee2aaSAndroid Build Coastguard Worker }
384*c8dee2aaSAndroid Build Coastguard Worker int count = fElements.count() - this->currentSaveRecord().oldestElementIndex();
385*c8dee2aaSAndroid Build Coastguard Worker return ElementIter(fElements.ritems().begin(), count);
386*c8dee2aaSAndroid Build Coastguard Worker }
387*c8dee2aaSAndroid Build Coastguard Worker
end()388*c8dee2aaSAndroid Build Coastguard Worker ClipStack::ElementIter ClipStack::end() const {
389*c8dee2aaSAndroid Build Coastguard Worker return ElementIter(fElements.ritems().end(), 0);
390*c8dee2aaSAndroid Build Coastguard Worker }
391*c8dee2aaSAndroid Build Coastguard Worker
392*c8dee2aaSAndroid Build Coastguard Worker } // namespace skgpu::graphite
393*c8dee2aaSAndroid Build Coastguard Worker
394*c8dee2aaSAndroid Build Coastguard Worker #endif // skgpu_graphite_ClipStack_DEFINED
395