blob: e6e25d256232a8b1a5fc105b2e19b47c706fbbc7 [file] [log] [blame]
Chris Dalton4da70192018-06-18 09:51:36 -06001/*
2 * Copyright 2018 Google Inc.
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 GrCCPathCache_DEFINED
9#define GrCCPathCache_DEFINED
10
11#include "SkExchange.h"
12#include "SkTHash.h"
13#include "SkTInternalLList.h"
14#include "ccpr/GrCCAtlas.h"
15#include "ccpr/GrCCPathProcessor.h"
16
17class GrCCPathCacheEntry;
18class GrShape;
19
20/**
21 * This class implements an LRU cache that maps from GrShape to GrCCPathCacheEntry objects. Shapes
22 * are only given one entry in the cache, so any time they are accessed with a different matrix, the
23 * old entry gets evicted.
24 */
25class GrCCPathCache {
26public:
27#ifdef SK_DEBUG
28 ~GrCCPathCache() {
29 // Ensure the hash table and LRU list are still coherent.
30 fHashTable.reset();
31 SkASSERT(fLRU.isEmpty());
32 }
33#endif
34
35 // Stores the components of a transformation that affect a path mask (i.e. everything but
36 // integer translation). During construction, any integer portions of the matrix's translate are
37 // shaved off and returned to the caller. The caller is responsible for those integer shifts.
38 struct MaskTransform {
39 MaskTransform(const SkMatrix& m, SkIVector* shift);
40 float fMatrix2x2[4];
41 float fSubpixelTranslate[2];
42 };
43
44 enum class CreateIfAbsent : bool {
45 kNo = false,
46 kYes = true
47 };
48
49 // Finds an entry in the cache. Shapes are only given one entry, so any time they are accessed
50 // with a different MaskTransform, the old entry gets evicted.
51 sk_sp<GrCCPathCacheEntry> find(const GrShape&, const MaskTransform&,
52 CreateIfAbsent = CreateIfAbsent::kNo);
53
54 void evict(const GrCCPathCacheEntry*);
55
56private:
57 // Wrapper around a raw GrShape key that has a specialized operator==. Used by the hash table.
58 struct HashKey {
59 const uint32_t* fData;
60 };
61 friend bool operator==(const HashKey&, const HashKey&);
62
63 // This is a special ref ptr for GrCCPathCacheEntry, used by the hash table. It can only be
64 // moved, which guarantees the hash table holds exactly one reference for each entry. When a
65 // HashNode goes out of scope, it therefore means the entry has been evicted from the cache.
66 class HashNode : SkNoncopyable {
67 public:
68 static HashKey GetKey(const HashNode& node) { return GetKey(node.fEntry); }
69 static HashKey GetKey(const GrCCPathCacheEntry*);
70 static uint32_t Hash(HashKey);
71
72 HashNode() = default;
73 HashNode(GrCCPathCache*, const MaskTransform&, const GrShape&);
74 HashNode(HashNode&& node) { fEntry = skstd::exchange(node.fEntry, nullptr); }
75 ~HashNode(); // Called when fEntry (if not null) has been evicted from the cache.
76
77 HashNode& operator=(HashNode&&);
78
79 GrCCPathCacheEntry* entry() const { return fEntry; }
80
81 private:
82 GrCCPathCacheEntry* fEntry = nullptr;
83 // The GrShape's unstyled key is stored as a variable-length footer to the 'fEntry'
84 // allocation. GetKey provides access to it.
85 };
86
87 SkTHashTable<HashNode, HashKey> fHashTable;
88 SkTInternalLList<GrCCPathCacheEntry> fLRU;
89};
90
91/**
92 * This class stores all the data necessary to draw a specific path from its corresponding cached
93 * atlas.
94 */
95class GrCCPathCacheEntry : public SkPathRef::GenIDChangeListener {
96public:
97 SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrCCPathCacheEntry);
98
99 // Does this entry reference a permanent, 8-bit atlas that resides in the resource cache?
100 // (i.e. not a temporarily-stashed, fp16 coverage count atlas.)
101 bool hasCachedAtlas() const { return SkToBool(fCachedAtlasInfo); }
102
103 const SkIRect& devIBounds() const { return fDevIBounds; }
104 int width() const { return fDevIBounds.width(); }
105 int height() const { return fDevIBounds.height(); }
106
107 // Called once our path has been rendered into the mainline CCPR (fp16, coverage count) atlas.
108 // The caller will stash this atlas texture away after drawing, and during the next flush,
109 // recover it and attempt to copy any paths that got reused into permanent 8-bit atlases.
110 void initAsStashedAtlas(const GrUniqueKey& atlasKey, const SkIVector& atlasOffset,
111 const SkRect& devBounds, const SkRect& devBounds45,
112 const SkIRect& devIBounds, const SkIVector& maskShift);
113
114 // Called once our path mask has been copied into a permanent, 8-bit atlas. This method points
115 // the entry at the new atlas and updates the CachedAtlasInfo data.
116 void updateToCachedAtlas(const GrUniqueKey& atlasKey, const SkIVector& newAtlasOffset,
117 sk_sp<GrCCAtlas::CachedAtlasInfo>);
118
119 const GrUniqueKey& atlasKey() const { return fAtlasKey; }
120
121 void resetAtlasKeyAndInfo() {
122 fAtlasKey.reset();
123 fCachedAtlasInfo.reset();
124 }
125
126 // This is a utility for the caller to detect when a path gets drawn more than once during the
127 // same flush, with compatible matrices. Before adding a path to an atlas, the caller may check
128 // here to see if they have already placed the path previously during the same flush. The caller
129 // is required to reset all currFlushAtlas references back to null before any subsequent flush.
130 void setCurrFlushAtlas(const GrCCAtlas* currFlushAtlas) {
131 // This should not get called more than once in a single flush. Once fCurrFlushAtlas is
132 // non-null, it can only be set back to null (once the flush is over).
133 SkASSERT(!fCurrFlushAtlas || !currFlushAtlas);
134 fCurrFlushAtlas = currFlushAtlas;
135 }
136 const GrCCAtlas* currFlushAtlas() const { return fCurrFlushAtlas; }
137
138private:
139 using MaskTransform = GrCCPathCache::MaskTransform;
140
141 GrCCPathCacheEntry(GrCCPathCache* cache, const MaskTransform& m)
142 : fCacheWeakPtr(cache), fMaskTransform(m) {}
143
144 // Called when our corresponding path is modified or deleted.
145 void onChange() override;
146
147 GrCCPathCache* fCacheWeakPtr; // Gets manually reset to null by the path cache upon eviction.
148 const MaskTransform fMaskTransform;
149
150 GrUniqueKey fAtlasKey;
151 SkIVector fAtlasOffset;
152
153 // If null, then we are referencing a "stashed" atlas (see initAsStashedAtlas()).
154 sk_sp<GrCCAtlas::CachedAtlasInfo> fCachedAtlasInfo;
155
156 SkRect fDevBounds;
157 SkRect fDevBounds45;
158 SkIRect fDevIBounds;
159
160 // This field is for when a path gets drawn more than once during the same flush.
161 const GrCCAtlas* fCurrFlushAtlas = nullptr;
162
163 friend class GrCCPathCache;
164 friend void GrCCPathProcessor::Instance::set(const GrCCPathCacheEntry&, const SkIVector&,
165 uint32_t, DoEvenOddFill); // To access data.
166};
167
168inline void GrCCPathProcessor::Instance::set(const GrCCPathCacheEntry& entry,
169 const SkIVector& shift, GrColor color,
170 DoEvenOddFill doEvenOddFill) {
171 float dx = (float)shift.fX, dy = (float)shift.fY;
172 this->set(entry.fDevBounds.makeOffset(dx, dy), MakeOffset45(entry.fDevBounds45, dx, dy),
173 entry.fAtlasOffset - shift, color, doEvenOddFill);
174}
175
176#endif