1/*
2 * Copyright 2014 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 GrLayerCache_DEFINED
9#define GrLayerCache_DEFINED
10
11#include "GrAtlas.h"
12#include "GrPictureUtils.h"
13#include "GrRect.h"
14#include "SkChecksum.h"
15#include "SkTDynamicHash.h"
16#include "SkMessageBus.h"
17
18class SkPicture;
19
20// The layer cache listens for these messages to purge picture-related resources.
21struct GrPictureDeletedMessage {
22    uint32_t pictureID;
23};
24
25// GrPictureInfo stores the atlas plots used by a single picture. A single
26// plot may be used to store layers from multiple pictures.
27struct GrPictureInfo {
28public:
29    // for SkTDynamicHash - just use the pictureID as the hash key
30    static const uint32_t& GetKey(const GrPictureInfo& pictInfo) { return pictInfo.fPictureID; }
31    static uint32_t Hash(const uint32_t& key) { return SkChecksum::Mix(key); }
32
33    // GrPictureInfo proper
34    GrPictureInfo(uint32_t pictureID) : fPictureID(pictureID) { }
35
36    const uint32_t fPictureID;
37
38    GrAtlas::ClientPlotUsage  fPlotUsage;
39};
40
41// GrCachedLayer encapsulates the caching information for a single saveLayer.
42//
43// Atlased layers get a ref to the backing GrTexture while non-atlased layers
44// get a ref to the GrTexture in which they reside. In both cases 'fRect'
45// contains the layer's extent in its texture.
46// Atlased layers also get a pointer to the plot in which they reside.
47// For non-atlased layers, the lock field just corresponds to locking in
48// the resource cache. For atlased layers, it implements an additional level
49// of locking to allow atlased layers to be reused multiple times.
50struct GrCachedLayer {
51public:
52    // For SkTDynamicHash
53    struct Key {
54        Key(uint32_t pictureID, int start, int stop, const SkIPoint& offset, const SkMatrix& ctm)
55        : fPictureID(pictureID)
56        , fStart(start)
57        , fStop(stop)
58        , fOffset(offset)
59        , fCTM(ctm) {
60            fCTM.getType(); // force initialization of type so hashes match
61
62            // Key needs to be tightly packed.
63            GR_STATIC_ASSERT(sizeof(Key) == sizeof(uint32_t) + 2 * sizeof(int) +
64                                            2 * sizeof(int32_t) +
65                                            9 * sizeof(SkScalar) + sizeof(uint32_t));
66        }
67
68        bool operator==(const Key& other) const {
69            return fPictureID == other.fPictureID &&
70                   fStart == other.fStart &&
71                   fStop == other.fStop &&
72                   fOffset == other.fOffset &&
73                   fCTM.cheapEqualTo(other.fCTM);
74        }
75
76        uint32_t pictureID() const { return fPictureID; }
77        int start() const { return fStart; }
78        int stop() const { return fStop; }
79        const SkIPoint& offset() const { return fOffset; }
80        const SkMatrix& ctm() const { return fCTM; }
81
82    private:
83        // ID of the picture of which this layer is a part
84        const uint32_t fPictureID;
85        // The range of commands in the picture this layer represents
86        const int      fStart;
87        const int      fStop;
88        // The offset of the layer in device space
89        const SkIPoint fOffset;
90        // The CTM applied to this layer in the picture
91        SkMatrix       fCTM;
92    };
93
94    static const Key& GetKey(const GrCachedLayer& layer) { return layer.fKey; }
95    static uint32_t Hash(const Key& key) {
96        return SkChecksum::Murmur3(reinterpret_cast<const uint32_t*>(&key), sizeof(Key));
97    }
98
99    // GrCachedLayer proper
100    GrCachedLayer(uint32_t pictureID, int start, int stop,
101                  const SkIPoint& offset, const SkMatrix& ctm,
102                  const SkPaint* paint)
103        : fKey(pictureID, start, stop, offset, ctm)
104        , fPaint(paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL)
105        , fTexture(NULL)
106        , fRect(GrIRect16::MakeEmpty())
107        , fPlot(NULL)
108        , fLocked(false) {
109        SkASSERT(SK_InvalidGenID != pictureID && start >= 0 && stop >= 0);
110    }
111
112    ~GrCachedLayer() {
113        SkSafeUnref(fTexture);
114        SkDELETE(fPaint);
115    }
116
117    uint32_t pictureID() const { return fKey.pictureID(); }
118    int start() const { return fKey.start(); }
119    int stop() const { return fKey.stop(); }
120    const SkIPoint& offset() const { return fKey.offset(); }
121    const SkMatrix& ctm() const { return fKey.ctm(); }
122
123    void setTexture(GrTexture* texture, const GrIRect16& rect) {
124        SkRefCnt_SafeAssign(fTexture, texture);
125        fRect = rect;
126    }
127    GrTexture* texture() { return fTexture; }
128    const SkPaint* paint() const { return fPaint; }
129    const GrIRect16& rect() const { return fRect; }
130
131    void setPlot(GrPlot* plot) {
132        SkASSERT(NULL == plot || NULL == fPlot);
133        fPlot = plot;
134    }
135    GrPlot* plot() { return fPlot; }
136
137    bool isAtlased() const { return SkToBool(fPlot); }
138
139    void setLocked(bool locked) { fLocked = locked; }
140    bool locked() const { return fLocked; }
141
142    SkDEBUGCODE(const GrPlot* plot() const { return fPlot; })
143    SkDEBUGCODE(void validate(const GrTexture* backingTexture) const;)
144
145private:
146    const Key       fKey;
147
148    // The paint used when dropping the layer down into the owning canvas.
149    // Can be NULL. This class makes a copy for itself.
150    const SkPaint*  fPaint;
151
152    // fTexture is a ref on the atlasing texture for atlased layers and a
153    // ref on a GrTexture for non-atlased textures.
154    GrTexture*      fTexture;
155
156    // For both atlased and non-atlased layers 'fRect' contains the  bound of
157    // the layer in whichever texture it resides. It is empty when 'fTexture'
158    // is NULL.
159    GrIRect16       fRect;
160
161    // For atlased layers, fPlot stores the atlas plot in which the layer rests.
162    // It is always NULL for non-atlased layers.
163    GrPlot*         fPlot;
164
165    // For non-atlased layers 'fLocked' should always match "fTexture".
166    // (i.e., if there is a texture it is locked).
167    // For atlased layers, 'fLocked' is true if the layer is in a plot and
168    // actively required for rendering. If the layer is in a plot but not
169    // actively required for rendering, then 'fLocked' is false. If the
170    // layer isn't in a plot then is can never be locked.
171    bool            fLocked;
172};
173
174// The GrLayerCache caches pre-computed saveLayers for later rendering.
175// Non-atlased layers are stored in their own GrTexture while the atlased
176// layers share a single GrTexture.
177// Unlike the GrFontCache, the GrTexture atlas only has one GrAtlas (for 8888)
178// and one GrPlot (for the entire atlas). As such, the GrLayerCache
179// roughly combines the functionality of the GrFontCache and GrTextStrike
180// classes.
181class GrLayerCache {
182public:
183    GrLayerCache(GrContext*);
184    ~GrLayerCache();
185
186    // As a cache, the GrLayerCache can be ordered to free up all its cached
187    // elements by the GrContext
188    void freeAll();
189
190    GrCachedLayer* findLayer(uint32_t pictureID, int start, int stop,
191                             const SkIPoint& offset, const SkMatrix& ctm);
192    GrCachedLayer* findLayerOrCreate(uint32_t pictureID,
193                                     int start, int stop,
194                                     const SkIPoint& offset,
195                                     const SkMatrix& ctm,
196                                     const SkPaint* paint);
197
198    // Inform the cache that layer's cached image is now required.
199    // Return true if the layer must be re-rendered. Return false if the
200    // layer was found in the cache and can be reused.
201    bool lock(GrCachedLayer* layer, const GrTextureDesc& desc, bool dontAtlas);
202
203    // Inform the cache that layer's cached image is not currently required
204    void unlock(GrCachedLayer* layer);
205
206    // Setup to be notified when 'picture' is deleted
207    void trackPicture(const SkPicture* picture);
208
209    // Cleanup after any SkPicture deletions
210    void processDeletedPictures();
211
212    SkDEBUGCODE(void validate() const;)
213
214private:
215    static const int kAtlasTextureWidth = 1024;
216    static const int kAtlasTextureHeight = 1024;
217
218    static const int kNumPlotsX = 2;
219    static const int kNumPlotsY = 2;
220
221    static const int kPlotWidth = kAtlasTextureWidth / kNumPlotsX;
222    static const int kPlotHeight = kAtlasTextureHeight / kNumPlotsY;
223
224    GrContext*                fContext;  // pointer back to owning context
225    SkAutoTDelete<GrAtlas>    fAtlas;    // TODO: could lazily allocate
226
227    // We cache this information here (rather then, say, on the owning picture)
228    // because we want to be able to clean it up as needed (e.g., if a picture
229    // is leaked and never cleans itself up we still want to be able to
230    // remove the GrPictureInfo once its layers are purged from all the atlas
231    // plots).
232    SkTDynamicHash<GrPictureInfo, uint32_t> fPictureHash;
233
234    SkTDynamicHash<GrCachedLayer, GrCachedLayer::Key> fLayerHash;
235
236    SkMessageBus<GrPictureDeletedMessage>::Inbox fPictDeletionInbox;
237
238    SkAutoTUnref<SkPicture::DeletionListener> fDeletionListener;
239
240    // This implements a plot-centric locking mechanism (since the atlas
241    // backing texture is always locked). Each layer that is locked (i.e.,
242    // needed for the current rendering) in a plot increments the plot lock
243    // count for that plot. Similarly, once a rendering is complete all the
244    // layers used in it decrement the lock count for the used plots.
245    // Plots with a 0 lock count are open for recycling/purging.
246    int fPlotLocks[kNumPlotsX * kNumPlotsY];
247
248    void initAtlas();
249    GrCachedLayer* createLayer(uint32_t pictureID, int start, int stop,
250                               const SkIPoint& offset, const SkMatrix& ctm,
251                               const SkPaint* paint);
252
253    void purgeAll();
254
255    // Remove all the layers (and unlock any resources) associated with 'pictureID'
256    void purge(uint32_t pictureID);
257
258    static bool PlausiblyAtlasable(int width, int height) {
259        return width <= kPlotWidth && height <= kPlotHeight;
260    }
261
262    void purgePlot(GrPlot* plot);
263
264    // Try to find a purgeable plot and clear it out. Return true if a plot
265    // was purged; false otherwise.
266    bool purgePlot();
267
268    // for testing
269    friend class TestingAccess;
270    int numLayers() const { return fLayerHash.count(); }
271};
272
273#endif
274