blob: 9688cac650d094fbc4ebc169a24e70d8e3fd577e [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
robertphillips21048b52014-07-15 19:46:35 -070044#ifdef SK_DEBUG
45void GrCachedLayer::validate(GrTexture* backingTexture) const {
46 SkASSERT(SK_InvalidGenID != fPictureID);
47 SkASSERT(-1 != fLayerID);
48
49 if (NULL != fTexture) {
50 // If the layer is in some texture then it must occupy some rectangle
51 SkASSERT(!fRect.isEmpty());
52 if (!this->isAtlased()) {
53 // If it isn't atlased then the rectangle should start at the origin
54 SkASSERT(0.0f == fRect.fLeft && 0.0f == fRect.fTop);
55 }
56 } else {
57 SkASSERT(fRect.isEmpty());
58 }
59}
60
61class GrAutoValidateLayer : ::SkNoncopyable {
62public:
63 GrAutoValidateLayer(GrTexture* backingTexture, const GrCachedLayer* layer)
64 : fBackingTexture(backingTexture)
65 , fLayer(layer) {
66 if (NULL != fLayer) {
67 fLayer->validate(backingTexture);
68 }
69 }
70 ~GrAutoValidateLayer() {
71 if (NULL != fLayer) {
72 fLayer->validate(fBackingTexture);
73 }
74 }
75
76private:
77 GrTexture* fBackingTexture;
78 const GrCachedLayer* fLayer;
79};
80#endif
81
robertphillips4ec84da2014-06-24 13:10:43 -070082GrLayerCache::GrLayerCache(GrContext* context)
robertphillips952841b2014-06-30 08:26:50 -070083 : fContext(context) {
84 this->initAtlas();
robertphillips@google.come930a072014-04-03 00:34:27 +000085}
86
87GrLayerCache::~GrLayerCache() {
robertphillips952841b2014-06-30 08:26:50 -070088 SkTDArray<GrCachedLayer*>& layerArray = fLayerHash.getArray();
89 for (int i = 0; i < fLayerHash.count(); ++i) {
90 this->unlock(layerArray[i]);
91 }
92
93 fLayerHash.deleteAll();
94
95 // The atlas only lets go of its texture when the atlas is deleted.
96 fAtlas.free();
robertphillips@google.come930a072014-04-03 00:34:27 +000097}
98
robertphillips952841b2014-06-30 08:26:50 -070099void GrLayerCache::initAtlas() {
robertphillips@google.come930a072014-04-03 00:34:27 +0000100 static const int kAtlasTextureWidth = 1024;
101 static const int kAtlasTextureHeight = 1024;
102
robertphillips1d86ee82014-06-24 15:08:49 -0700103 SkASSERT(NULL == fAtlas.get());
robertphillips@google.come930a072014-04-03 00:34:27 +0000104
105 // The layer cache only gets 1 plot
106 SkISize textureSize = SkISize::Make(kAtlasTextureWidth, kAtlasTextureHeight);
robertphillips1d86ee82014-06-24 15:08:49 -0700107 fAtlas.reset(SkNEW_ARGS(GrAtlas, (fContext->getGpu(), kSkia8888_GrPixelConfig,
robertphillips952841b2014-06-30 08:26:50 -0700108 kRenderTarget_GrTextureFlagBit,
robertphillips1d86ee82014-06-24 15:08:49 -0700109 textureSize, 1, 1, false)));
robertphillips@google.come930a072014-04-03 00:34:27 +0000110}
111
112void GrLayerCache::freeAll() {
robertphillips952841b2014-06-30 08:26:50 -0700113 SkTDArray<GrCachedLayer*>& layerArray = fLayerHash.getArray();
114 for (int i = 0; i < fLayerHash.count(); ++i) {
115 this->unlock(layerArray[i]);
116 }
117
robertphillips@google.come930a072014-04-03 00:34:27 +0000118 fLayerHash.deleteAll();
robertphillips952841b2014-06-30 08:26:50 -0700119
120 // The atlas only lets go of its texture when the atlas is deleted.
robertphillips1d86ee82014-06-24 15:08:49 -0700121 fAtlas.free();
robertphillips952841b2014-06-30 08:26:50 -0700122 // GrLayerCache always assumes an atlas exists so recreate it. The atlas
123 // lazily allocates a replacement texture so reallocating a new
124 // atlas here won't disrupt a GrContext::contextDestroyed or freeGpuResources.
125 // TODO: Make GrLayerCache lazily allocate the atlas manager?
126 this->initAtlas();
robertphillips@google.come930a072014-04-03 00:34:27 +0000127}
128
robertphillips9b14f262014-06-04 05:40:44 -0700129GrCachedLayer* GrLayerCache::createLayer(const SkPicture* picture, int layerID) {
robertphillipse462f2b2014-06-29 17:16:27 -0700130 SkASSERT(picture->uniqueID() != SK_InvalidGenID);
robertphillips952841b2014-06-30 08:26:50 -0700131
132 GrCachedLayer* layer = SkNEW_ARGS(GrCachedLayer, (picture->uniqueID(), layerID));
commit-bot@chromium.org2b4e3702014-04-07 18:26:22 +0000133 fLayerHash.insert(PictureLayerKey(picture->uniqueID(), layerID), layer);
robertphillips@google.come930a072014-04-03 00:34:27 +0000134 return layer;
135}
136
robertphillips4ec84da2014-06-24 13:10:43 -0700137GrCachedLayer* GrLayerCache::findLayer(const SkPicture* picture, int layerID) {
138 SkASSERT(picture->uniqueID() != SK_InvalidGenID);
139 return fLayerHash.find(PictureLayerKey(picture->uniqueID(), layerID));
140}
robertphillips@google.come930a072014-04-03 00:34:27 +0000141
robertphillips9b14f262014-06-04 05:40:44 -0700142GrCachedLayer* GrLayerCache::findLayerOrCreate(const SkPicture* picture, int layerID) {
commit-bot@chromium.org2b4e3702014-04-07 18:26:22 +0000143 SkASSERT(picture->uniqueID() != SK_InvalidGenID);
commit-bot@chromium.org365cd312014-04-11 15:53:47 +0000144 GrCachedLayer* layer = fLayerHash.find(PictureLayerKey(picture->uniqueID(), layerID));
robertphillips@google.come930a072014-04-03 00:34:27 +0000145 if (NULL == layer) {
146 layer = this->createLayer(picture, layerID);
147 }
robertphillips4ec84da2014-06-24 13:10:43 -0700148
robertphillips@google.come930a072014-04-03 00:34:27 +0000149 return layer;
150}
robertphillips4ec84da2014-06-24 13:10:43 -0700151
152bool GrLayerCache::lock(GrCachedLayer* layer, const GrTextureDesc& desc) {
robertphillips21048b52014-07-15 19:46:35 -0700153 SkDEBUGCODE(GrAutoValidateLayer avl(fAtlas->getTexture(), layer);)
robertphillips4ec84da2014-06-24 13:10:43 -0700154
robertphillips952841b2014-06-30 08:26:50 -0700155 if (NULL != layer->texture()) {
156 // This layer is already locked
157#ifdef SK_DEBUG
robertphillips21048b52014-07-15 19:46:35 -0700158 if (layer->isAtlased()) {
robertphillips952841b2014-06-30 08:26:50 -0700159 // It claims to be atlased
160 SkASSERT(layer->rect().width() == desc.fWidth);
161 SkASSERT(layer->rect().height() == desc.fHeight);
162 }
163#endif
164 return true;
165 }
166
167#if USE_ATLAS
168 SkIPoint16 loc;
169 GrPlot* plot = fAtlas->addToAtlas(&fPlotUsage, desc.fWidth, desc.fHeight, NULL, &loc);
170 if (NULL != plot) {
171 GrIRect16 bounds = GrIRect16::MakeXYWH(loc.fX, loc.fY,
172 SkToS16(desc.fWidth), SkToS16(desc.fHeight));
173 layer->setTexture(fAtlas->getTexture(), bounds);
robertphillips21048b52014-07-15 19:46:35 -0700174 layer->setAtlased(true);
robertphillips952841b2014-06-30 08:26:50 -0700175 return false;
176 }
177#endif
178
robertphillips21048b52014-07-15 19:46:35 -0700179 // The texture wouldn't fit in the cache - give it it's own texture.
robertphillips952841b2014-06-30 08:26:50 -0700180 // This path always uses a new scratch texture and (thus) doesn't cache anything.
robertphillips4ec84da2014-06-24 13:10:43 -0700181 // This can yield a lot of re-rendering
robertphillips952841b2014-06-30 08:26:50 -0700182 layer->setTexture(fContext->lockAndRefScratchTexture(desc, GrContext::kApprox_ScratchTexMatch),
robertphillips21048b52014-07-15 19:46:35 -0700183 GrIRect16::MakeWH(SkToS16(desc.fWidth), SkToS16(desc.fHeight)));
robertphillips4ec84da2014-06-24 13:10:43 -0700184 return false;
185}
186
187void GrLayerCache::unlock(GrCachedLayer* layer) {
robertphillips21048b52014-07-15 19:46:35 -0700188 SkDEBUGCODE(GrAutoValidateLayer avl(fAtlas->getTexture(), layer);)
189
robertphillips952841b2014-06-30 08:26:50 -0700190 if (NULL == layer || NULL == layer->texture()) {
robertphillips4ec84da2014-06-24 13:10:43 -0700191 return;
192 }
193
robertphillips21048b52014-07-15 19:46:35 -0700194 if (layer->isAtlased()) {
195 // The atlas doesn't currently use a scratch texture (and we would have
196 // to free up space differently anyways)
197 // TODO: unlock atlas space when a recycling rectanizer is available
198 } else {
robertphillips952841b2014-06-30 08:26:50 -0700199 fContext->unlockScratchTexture(layer->texture());
200 layer->setTexture(NULL, GrIRect16::MakeEmpty());
201 }
202}
203
robertphillips21048b52014-07-15 19:46:35 -0700204#ifdef SK_DEBUG
205void GrLayerCache::validate() const {
206 const SkTDArray<GrCachedLayer*>& layerArray = fLayerHash.getArray();
207 for (int i = 0; i < fLayerHash.count(); ++i) {
208 layerArray[i]->validate(fAtlas->getTexture());
209 }
210}
211
212class GrAutoValidateCache : ::SkNoncopyable {
213public:
214 explicit GrAutoValidateCache(GrLayerCache* cache)
215 : fCache(cache) {
216 fCache->validate();
217 }
218 ~GrAutoValidateCache() {
219 fCache->validate();
220 }
221private:
222 GrLayerCache* fCache;
223};
224#endif
225
robertphillips952841b2014-06-30 08:26:50 -0700226void GrLayerCache::purge(const SkPicture* picture) {
robertphillips21048b52014-07-15 19:46:35 -0700227 SkDEBUGCODE(GrAutoValidateCache avc(this);)
228
robertphillips952841b2014-06-30 08:26:50 -0700229 // This is somewhat of an abuse of GrTHashTable. We need to find all the
230 // layers associated with 'picture' but the usual hash calls only look for
231 // exact key matches. This code peeks into the hash table's innards to
232 // find all the 'picture'-related layers.
233 // TODO: use a different data structure for the layer hash?
234 SkTDArray<GrCachedLayer*> toBeRemoved;
235
236 const SkTDArray<GrCachedLayer*>& layerArray = fLayerHash.getArray();
237 for (int i = 0; i < fLayerHash.count(); ++i) {
238 if (picture->uniqueID() == layerArray[i]->pictureID()) {
239 *toBeRemoved.append() = layerArray[i];
240 }
241 }
242
243 for (int i = 0; i < toBeRemoved.count(); ++i) {
244 this->unlock(toBeRemoved[i]);
245
246 PictureLayerKey key(picture->uniqueID(), toBeRemoved[i]->layerID());
247 fLayerHash.remove(key, toBeRemoved[i]);
248 SkDELETE(toBeRemoved[i]);
249 }
robertphillips4ec84da2014-06-24 13:10:43 -0700250}