Michael Ludwig | a195d10 | 2020-09-15 14:51:52 -0400 | [diff] [blame] | 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 | #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 | |
| 20 | class GrAppliedClip; |
| 21 | class GrProxyProvider; |
| 22 | class GrRecordingContext; |
Brian Salomon | eebe735 | 2020-12-09 16:37:04 -0500 | [diff] [blame] | 23 | class GrSurfaceDrawContext; |
Michael Ludwig | a195d10 | 2020-09-15 14:51:52 -0400 | [diff] [blame] | 24 | class GrSWMaskHelper; |
| 25 | |
| 26 | class GrClipStack final : public GrClip { |
| 27 | public: |
| 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 Salomon | eebe735 | 2020-12-09 16:37:04 -0500 | [diff] [blame] | 74 | GrClip::Effect apply(GrRecordingContext*, GrSurfaceDrawContext*, GrAAType aa, |
Derek Sollenberger | 396cd1d | 2021-04-02 15:44:36 +0000 | [diff] [blame^] | 75 | bool hasUserStencilSettings, |
| 76 | GrAppliedClip*, SkRect* bounds) const override; |
Michael Ludwig | a195d10 | 2020-09-15 14:51:52 -0400 | [diff] [blame] | 77 | GrClip::PreClipResult preApply(const SkRect& drawBounds, GrAA aa) const override; |
| 78 | SkIRect getConservativeBounds() const override; |
| 79 | |
Greg Daniel | a28ea67 | 2020-09-25 11:12:56 -0400 | [diff] [blame] | 80 | #if GR_TEST_UTILS |
Michael Ludwig | a195d10 | 2020-09-15 14:51:52 -0400 | [diff] [blame] | 81 | GrUniqueKey testingOnly_getLastSWMaskKey() const { |
| 82 | return fMasks.empty() ? GrUniqueKey() : fMasks.back().key(); |
| 83 | } |
| 84 | #endif |
| 85 | |
| 86 | private: |
| 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 |
| 323 | class GrClipStack::ElementIter { |
| 324 | public: |
| 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 | |
| 349 | GrClipStack::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 | |
| 359 | GrClipStack::ElementIter GrClipStack::end() const { |
| 360 | return ElementIter(fElements.ritems().end(), 0); |
| 361 | } |
| 362 | |
| 363 | #endif |