blob: ff26750df764cc1b1a33bcee0fee81096a42adc4 [file] [log] [blame]
robertphillips@google.come930a072014-04-03 00:34:27 +00001/*
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#include "GrAtlas.h"
9#include "GrGpu.h"
10#include "GrLayerCache.h"
11
12/**
robertphillips21048b52014-07-15 19:46:35 -070013 * PictureLayerKey just wraps a saveLayer's id in a picture for GrTHashTable.
robertphillips@google.come930a072014-04-03 00:34:27 +000014 */
15class GrLayerCache::PictureLayerKey {
16public:
skia.committer@gmail.coma9157722014-04-03 03:04:26 +000017 PictureLayerKey(uint32_t pictureID, int layerID)
robertphillips@google.come930a072014-04-03 00:34:27 +000018 : fPictureID(pictureID)
19 , fLayerID(layerID) {
20 }
21
22 uint32_t pictureID() const { return fPictureID; }
23 int layerID() const { return fLayerID; }
24
25 uint32_t getHash() const { return (fPictureID << 16) | fLayerID; }
26
commit-bot@chromium.org365cd312014-04-11 15:53:47 +000027 static bool LessThan(const GrCachedLayer& layer, const PictureLayerKey& key) {
robertphillips@google.come930a072014-04-03 00:34:27 +000028 if (layer.pictureID() == key.pictureID()) {
29 return layer.layerID() < key.layerID();
30 }
31
32 return layer.pictureID() < key.pictureID();
33 }
34
commit-bot@chromium.org365cd312014-04-11 15:53:47 +000035 static bool Equals(const GrCachedLayer& layer, const PictureLayerKey& key) {
robertphillips@google.come930a072014-04-03 00:34:27 +000036 return layer.pictureID() == key.pictureID() && layer.layerID() == key.layerID();
37 }
38
39private:
40 uint32_t fPictureID;
41 int fLayerID;
42};
43
robertphillips261b8a92014-07-17 08:26:44 -070044/**
45 * PictureKey just wraps a picture's unique ID for GrTHashTable. It is used to
46 * look up a picture's GrPictureInfo (i.e., its GrPlot usage).
47 */
48class GrLayerCache::PictureKey {
49public:
50 PictureKey(uint32_t pictureID) : fPictureID(pictureID) { }
51
52 uint32_t pictureID() const { return fPictureID; }
53
54 uint32_t getHash() const { return fPictureID; }
55
56 static bool LessThan(const GrPictureInfo& pictInfo, const PictureKey& key) {
57 return pictInfo.fPictureID < key.pictureID();
58 }
59
60 static bool Equals(const GrPictureInfo& pictInfo, const PictureKey& key) {
61 return pictInfo.fPictureID == key.pictureID();
62
63 }
64
65private:
66 uint32_t fPictureID;
67};
68
robertphillips21048b52014-07-15 19:46:35 -070069#ifdef SK_DEBUG
robertphillips261b8a92014-07-17 08:26:44 -070070void GrCachedLayer::validate(const GrTexture* backingTexture) const {
robertphillips21048b52014-07-15 19:46:35 -070071 SkASSERT(SK_InvalidGenID != fPictureID);
72 SkASSERT(-1 != fLayerID);
73
74 if (NULL != fTexture) {
75 // If the layer is in some texture then it must occupy some rectangle
76 SkASSERT(!fRect.isEmpty());
77 if (!this->isAtlased()) {
78 // If it isn't atlased then the rectangle should start at the origin
79 SkASSERT(0.0f == fRect.fLeft && 0.0f == fRect.fTop);
80 }
81 } else {
82 SkASSERT(fRect.isEmpty());
robertphillips261b8a92014-07-17 08:26:44 -070083 SkASSERT(NULL == fPlot);
84 }
85
86 if (NULL != fPlot) {
87 // If a layer has a plot (i.e., is atlased) then it must point to
88 // the backing texture. Additionally, its rect should be non-empty.
89 SkASSERT(NULL != fTexture && backingTexture == fTexture);
90 SkASSERT(!fRect.isEmpty());
robertphillips21048b52014-07-15 19:46:35 -070091 }
92}
93
94class GrAutoValidateLayer : ::SkNoncopyable {
95public:
96 GrAutoValidateLayer(GrTexture* backingTexture, const GrCachedLayer* layer)
97 : fBackingTexture(backingTexture)
98 , fLayer(layer) {
99 if (NULL != fLayer) {
100 fLayer->validate(backingTexture);
101 }
102 }
103 ~GrAutoValidateLayer() {
104 if (NULL != fLayer) {
105 fLayer->validate(fBackingTexture);
106 }
107 }
robertphillips261b8a92014-07-17 08:26:44 -0700108 void setBackingTexture(GrTexture* backingTexture) {
109 SkASSERT(NULL == fBackingTexture || fBackingTexture == backingTexture);
110 fBackingTexture = backingTexture;
111 }
robertphillips21048b52014-07-15 19:46:35 -0700112
113private:
robertphillips261b8a92014-07-17 08:26:44 -0700114 const GrTexture* fBackingTexture;
robertphillips21048b52014-07-15 19:46:35 -0700115 const GrCachedLayer* fLayer;
116};
117#endif
118
robertphillips4ec84da2014-06-24 13:10:43 -0700119GrLayerCache::GrLayerCache(GrContext* context)
robertphillips952841b2014-06-30 08:26:50 -0700120 : fContext(context) {
121 this->initAtlas();
robertphillips@google.come930a072014-04-03 00:34:27 +0000122}
123
124GrLayerCache::~GrLayerCache() {
robertphillips952841b2014-06-30 08:26:50 -0700125 SkTDArray<GrCachedLayer*>& layerArray = fLayerHash.getArray();
126 for (int i = 0; i < fLayerHash.count(); ++i) {
127 this->unlock(layerArray[i]);
128 }
129
130 fLayerHash.deleteAll();
131
132 // The atlas only lets go of its texture when the atlas is deleted.
133 fAtlas.free();
robertphillips@google.come930a072014-04-03 00:34:27 +0000134}
135
robertphillips952841b2014-06-30 08:26:50 -0700136void GrLayerCache::initAtlas() {
robertphillips@google.come930a072014-04-03 00:34:27 +0000137 static const int kAtlasTextureWidth = 1024;
138 static const int kAtlasTextureHeight = 1024;
139
robertphillips1d86ee82014-06-24 15:08:49 -0700140 SkASSERT(NULL == fAtlas.get());
robertphillips@google.come930a072014-04-03 00:34:27 +0000141
142 // The layer cache only gets 1 plot
143 SkISize textureSize = SkISize::Make(kAtlasTextureWidth, kAtlasTextureHeight);
robertphillips1d86ee82014-06-24 15:08:49 -0700144 fAtlas.reset(SkNEW_ARGS(GrAtlas, (fContext->getGpu(), kSkia8888_GrPixelConfig,
robertphillips952841b2014-06-30 08:26:50 -0700145 kRenderTarget_GrTextureFlagBit,
robertphillips261b8a92014-07-17 08:26:44 -0700146 textureSize, kNumPlotsX, kNumPlotsY, false)));
robertphillips@google.come930a072014-04-03 00:34:27 +0000147}
148
149void GrLayerCache::freeAll() {
robertphillips952841b2014-06-30 08:26:50 -0700150 SkTDArray<GrCachedLayer*>& layerArray = fLayerHash.getArray();
151 for (int i = 0; i < fLayerHash.count(); ++i) {
152 this->unlock(layerArray[i]);
153 }
154
robertphillips@google.come930a072014-04-03 00:34:27 +0000155 fLayerHash.deleteAll();
robertphillips952841b2014-06-30 08:26:50 -0700156
157 // The atlas only lets go of its texture when the atlas is deleted.
robertphillips1d86ee82014-06-24 15:08:49 -0700158 fAtlas.free();
robertphillips952841b2014-06-30 08:26:50 -0700159 // GrLayerCache always assumes an atlas exists so recreate it. The atlas
160 // lazily allocates a replacement texture so reallocating a new
161 // atlas here won't disrupt a GrContext::contextDestroyed or freeGpuResources.
162 // TODO: Make GrLayerCache lazily allocate the atlas manager?
163 this->initAtlas();
robertphillips@google.come930a072014-04-03 00:34:27 +0000164}
165
robertphillips9b14f262014-06-04 05:40:44 -0700166GrCachedLayer* GrLayerCache::createLayer(const SkPicture* picture, int layerID) {
robertphillipse462f2b2014-06-29 17:16:27 -0700167 SkASSERT(picture->uniqueID() != SK_InvalidGenID);
robertphillips952841b2014-06-30 08:26:50 -0700168
169 GrCachedLayer* layer = SkNEW_ARGS(GrCachedLayer, (picture->uniqueID(), layerID));
commit-bot@chromium.org2b4e3702014-04-07 18:26:22 +0000170 fLayerHash.insert(PictureLayerKey(picture->uniqueID(), layerID), layer);
robertphillips@google.come930a072014-04-03 00:34:27 +0000171 return layer;
172}
173
robertphillips4ec84da2014-06-24 13:10:43 -0700174GrCachedLayer* GrLayerCache::findLayer(const SkPicture* picture, int layerID) {
175 SkASSERT(picture->uniqueID() != SK_InvalidGenID);
176 return fLayerHash.find(PictureLayerKey(picture->uniqueID(), layerID));
177}
robertphillips@google.come930a072014-04-03 00:34:27 +0000178
robertphillips9b14f262014-06-04 05:40:44 -0700179GrCachedLayer* GrLayerCache::findLayerOrCreate(const SkPicture* picture, int layerID) {
commit-bot@chromium.org2b4e3702014-04-07 18:26:22 +0000180 SkASSERT(picture->uniqueID() != SK_InvalidGenID);
commit-bot@chromium.org365cd312014-04-11 15:53:47 +0000181 GrCachedLayer* layer = fLayerHash.find(PictureLayerKey(picture->uniqueID(), layerID));
robertphillips@google.come930a072014-04-03 00:34:27 +0000182 if (NULL == layer) {
183 layer = this->createLayer(picture, layerID);
184 }
robertphillips4ec84da2014-06-24 13:10:43 -0700185
robertphillips@google.come930a072014-04-03 00:34:27 +0000186 return layer;
187}
robertphillips4ec84da2014-06-24 13:10:43 -0700188
189bool GrLayerCache::lock(GrCachedLayer* layer, const GrTextureDesc& desc) {
robertphillips21048b52014-07-15 19:46:35 -0700190 SkDEBUGCODE(GrAutoValidateLayer avl(fAtlas->getTexture(), layer);)
robertphillips4ec84da2014-06-24 13:10:43 -0700191
robertphillips952841b2014-06-30 08:26:50 -0700192 if (NULL != layer->texture()) {
193 // This layer is already locked
194#ifdef SK_DEBUG
robertphillips21048b52014-07-15 19:46:35 -0700195 if (layer->isAtlased()) {
robertphillips952841b2014-06-30 08:26:50 -0700196 // It claims to be atlased
197 SkASSERT(layer->rect().width() == desc.fWidth);
198 SkASSERT(layer->rect().height() == desc.fHeight);
199 }
200#endif
201 return true;
202 }
203
204#if USE_ATLAS
robertphillips261b8a92014-07-17 08:26:44 -0700205 {
206 GrPictureInfo* pictInfo = fPictureHash.find(PictureKey(layer->pictureID()));
207 if (NULL == pictInfo) {
208 pictInfo = SkNEW_ARGS(GrPictureInfo, (layer->pictureID()));
209 fPictureHash.insert(PictureKey(layer->pictureID()), pictInfo);
210 }
211
212 SkIPoint16 loc;
213 GrPlot* plot = fAtlas->addToAtlas(&pictInfo->fPlotUsage,
214 desc.fWidth, desc.fHeight,
215 NULL, &loc);
216 // addToAtlas can allocate the backing texture
217 SkDEBUGCODE(avl.setBackingTexture(fAtlas->getTexture()));
218 if (NULL != plot) {
219 GrIRect16 bounds = GrIRect16::MakeXYWH(loc.fX, loc.fY,
220 SkToS16(desc.fWidth), SkToS16(desc.fHeight));
221 layer->setTexture(fAtlas->getTexture(), bounds);
222 layer->setPlot(plot);
223 return false;
224 }
robertphillips952841b2014-06-30 08:26:50 -0700225 }
226#endif
227
robertphillips21048b52014-07-15 19:46:35 -0700228 // The texture wouldn't fit in the cache - give it it's own texture.
robertphillips952841b2014-06-30 08:26:50 -0700229 // This path always uses a new scratch texture and (thus) doesn't cache anything.
robertphillips4ec84da2014-06-24 13:10:43 -0700230 // This can yield a lot of re-rendering
robertphillips952841b2014-06-30 08:26:50 -0700231 layer->setTexture(fContext->lockAndRefScratchTexture(desc, GrContext::kApprox_ScratchTexMatch),
robertphillips21048b52014-07-15 19:46:35 -0700232 GrIRect16::MakeWH(SkToS16(desc.fWidth), SkToS16(desc.fHeight)));
robertphillips4ec84da2014-06-24 13:10:43 -0700233 return false;
234}
235
236void GrLayerCache::unlock(GrCachedLayer* layer) {
robertphillips21048b52014-07-15 19:46:35 -0700237 SkDEBUGCODE(GrAutoValidateLayer avl(fAtlas->getTexture(), layer);)
238
robertphillips952841b2014-06-30 08:26:50 -0700239 if (NULL == layer || NULL == layer->texture()) {
robertphillips4ec84da2014-06-24 13:10:43 -0700240 return;
241 }
242
robertphillips21048b52014-07-15 19:46:35 -0700243 if (layer->isAtlased()) {
robertphillips261b8a92014-07-17 08:26:44 -0700244 SkASSERT(layer->texture() == fAtlas->getTexture());
245
246 GrPictureInfo* pictInfo = fPictureHash.find(PictureKey(layer->pictureID()));
247 SkASSERT(NULL != pictInfo);
248 pictInfo->fPlotUsage.isEmpty(); // just to silence compiler warnings for the time being
249
250 // TODO: purging from atlas goes here
robertphillips21048b52014-07-15 19:46:35 -0700251 } else {
robertphillips952841b2014-06-30 08:26:50 -0700252 fContext->unlockScratchTexture(layer->texture());
253 layer->setTexture(NULL, GrIRect16::MakeEmpty());
254 }
255}
256
robertphillips21048b52014-07-15 19:46:35 -0700257#ifdef SK_DEBUG
258void GrLayerCache::validate() const {
259 const SkTDArray<GrCachedLayer*>& layerArray = fLayerHash.getArray();
260 for (int i = 0; i < fLayerHash.count(); ++i) {
261 layerArray[i]->validate(fAtlas->getTexture());
262 }
263}
264
265class GrAutoValidateCache : ::SkNoncopyable {
266public:
267 explicit GrAutoValidateCache(GrLayerCache* cache)
268 : fCache(cache) {
269 fCache->validate();
270 }
271 ~GrAutoValidateCache() {
272 fCache->validate();
273 }
274private:
275 GrLayerCache* fCache;
276};
277#endif
278
robertphillips952841b2014-06-30 08:26:50 -0700279void GrLayerCache::purge(const SkPicture* picture) {
robertphillips21048b52014-07-15 19:46:35 -0700280 SkDEBUGCODE(GrAutoValidateCache avc(this);)
281
robertphillips952841b2014-06-30 08:26:50 -0700282 // This is somewhat of an abuse of GrTHashTable. We need to find all the
283 // layers associated with 'picture' but the usual hash calls only look for
284 // exact key matches. This code peeks into the hash table's innards to
285 // find all the 'picture'-related layers.
286 // TODO: use a different data structure for the layer hash?
287 SkTDArray<GrCachedLayer*> toBeRemoved;
288
289 const SkTDArray<GrCachedLayer*>& layerArray = fLayerHash.getArray();
290 for (int i = 0; i < fLayerHash.count(); ++i) {
291 if (picture->uniqueID() == layerArray[i]->pictureID()) {
292 *toBeRemoved.append() = layerArray[i];
293 }
294 }
295
296 for (int i = 0; i < toBeRemoved.count(); ++i) {
297 this->unlock(toBeRemoved[i]);
298
299 PictureLayerKey key(picture->uniqueID(), toBeRemoved[i]->layerID());
300 fLayerHash.remove(key, toBeRemoved[i]);
301 SkDELETE(toBeRemoved[i]);
302 }
robertphillips261b8a92014-07-17 08:26:44 -0700303
304 GrPictureInfo* pictInfo = fPictureHash.find(PictureKey(picture->uniqueID()));
305 if (NULL != pictInfo) {
306 fPictureHash.remove(PictureKey(picture->uniqueID()), pictInfo);
307 SkDELETE(pictInfo);
308 }
robertphillips4ec84da2014-06-24 13:10:43 -0700309}