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 #if SK_SUPPORT_GPU
9
10 #include "GrContext.h"
11 #include "GrLayerCache.h"
12 #include "GrResourceCache.h"
13 #include "SkPictureRecorder.h"
14 #include "Test.h"
15
16 class TestingAccess {
17 public:
NumPlots()18 static int NumPlots() {
19 return GrLayerCache::kNumPlotsX * GrLayerCache::kNumPlotsY;
20 }
PlotSize()21 static SkISize PlotSize() {
22 return SkISize::Make(GrLayerCache::kAtlasTextureWidth / GrLayerCache::kNumPlotsX,
23 GrLayerCache::kAtlasTextureHeight / GrLayerCache::kNumPlotsY);
24 }
25
GetBackingTexture(GrLayerCache * cache)26 static GrTexture* GetBackingTexture(GrLayerCache* cache) {
27 return cache->fAtlas->getTextureOrNull();
28 }
29
NumLayers(GrLayerCache * cache)30 static int NumLayers(GrLayerCache* cache) {
31 return cache->numLayers();
32 }
Purge(GrLayerCache * cache,uint32_t pictureID)33 static void Purge(GrLayerCache* cache, uint32_t pictureID) {
34 cache->purge(pictureID);
35 }
Uses(GrCachedLayer * layer)36 static int Uses(GrCachedLayer* layer) {
37 return layer->uses();
38 }
Find(GrLayerCache * cache,uint32_t pictureID,const SkMatrix & initialMat,const int * key,int keySize)39 static GrCachedLayer* Find(GrLayerCache* cache, uint32_t pictureID,
40 const SkMatrix& initialMat,
41 const int* key, int keySize) {
42 return cache->findLayer(pictureID, initialMat, key, keySize);
43 }
44 };
45
46 // Add several layers to the cache
create_layers(skiatest::Reporter * reporter,GrLayerCache * cache,const SkPicture & picture,int numToAdd,int idOffset)47 static void create_layers(skiatest::Reporter* reporter,
48 GrLayerCache* cache,
49 const SkPicture& picture,
50 int numToAdd,
51 int idOffset) {
52
53 for (int i = 0; i < numToAdd; ++i) {
54 int key[1] = { idOffset+i+1 };
55 GrCachedLayer* layer = cache->findLayerOrCreate(picture.uniqueID(),
56 idOffset+i+1, idOffset+i+2,
57 SkIRect::MakeEmpty(),
58 SkIRect::MakeEmpty(),
59 SkMatrix::I(),
60 key, 1,
61 nullptr);
62 REPORTER_ASSERT(reporter, layer);
63 GrCachedLayer* temp = TestingAccess::Find(cache, picture.uniqueID(), SkMatrix::I(),
64 key, 1);
65 REPORTER_ASSERT(reporter, temp == layer);
66
67 REPORTER_ASSERT(reporter, TestingAccess::NumLayers(cache) == idOffset + i + 1);
68
69 REPORTER_ASSERT(reporter, picture.uniqueID() == layer->pictureID());
70 REPORTER_ASSERT(reporter, layer->start() == idOffset + i + 1);
71 REPORTER_ASSERT(reporter, layer->stop() == idOffset + i + 2);
72 REPORTER_ASSERT(reporter, nullptr == layer->texture());
73 REPORTER_ASSERT(reporter, nullptr == layer->paint());
74 REPORTER_ASSERT(reporter, !layer->isAtlased());
75 }
76 }
77
lock_layer(skiatest::Reporter * reporter,GrLayerCache * cache,GrCachedLayer * layer)78 static void lock_layer(skiatest::Reporter* reporter,
79 GrLayerCache* cache,
80 GrCachedLayer* layer) {
81 // Make each layer big enough to consume one whole plot in the atlas
82 GrSurfaceDesc desc;
83 desc.fFlags = kRenderTarget_GrSurfaceFlag;
84 desc.fWidth = TestingAccess::PlotSize().fWidth;
85 desc.fHeight = TestingAccess::PlotSize().fHeight;
86 desc.fConfig = kSkia8888_GrPixelConfig;
87
88 bool needsRerendering;
89 bool inAtlas = cache->tryToAtlas(layer, desc, &needsRerendering);
90 if (!inAtlas) {
91 cache->lock(layer, desc, &needsRerendering);
92 }
93 REPORTER_ASSERT(reporter, needsRerendering);
94
95 cache->lock(layer, desc, &needsRerendering);
96 REPORTER_ASSERT(reporter, !needsRerendering);
97
98 REPORTER_ASSERT(reporter, layer->texture());
99 REPORTER_ASSERT(reporter, layer->locked());
100
101 cache->addUse(layer);
102
103 REPORTER_ASSERT(reporter, 1 == TestingAccess::Uses(layer));
104 }
105
106 // This test case exercises the public API of the GrLayerCache class.
107 // In particular it checks its interaction with the resource cache (w.r.t.
108 // locking & unlocking textures).
109 // TODO: need to add checks on VRAM usage!
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GpuLayerCache,reporter,context)110 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GpuLayerCache, reporter, context) {
111 // Add one more layer than can fit in the atlas
112 static const int kInitialNumLayers = TestingAccess::NumPlots() + 1;
113
114 #if GR_CACHE_STATS
115 GrResourceCache::Stats stats;
116 #endif
117
118 SkAutoTUnref<const SkPicture> picture;
119
120 {
121 SkPictureRecorder recorder;
122 SkCanvas* c = recorder.beginRecording(1, 1);
123 // Draw something, anything, to prevent an empty-picture optimization,
124 // which is a singleton and never purged.
125 c->drawRect(SkRect::MakeWH(1,1), SkPaint());
126 picture.reset(recorder.endRecording());
127 }
128
129 GrResourceCache* resourceCache = context->getResourceCache();
130
131 GrLayerCache cache(context);
132
133 create_layers(reporter, &cache, *picture, kInitialNumLayers, 0);
134
135 for (int i = 0; i < kInitialNumLayers; ++i) {
136 int key[1] = { i + 1 };
137 GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(),
138 key, 1);
139 REPORTER_ASSERT(reporter, layer);
140
141 lock_layer(reporter, &cache, layer);
142
143 #if GR_CACHE_STATS
144 resourceCache->getStats(&stats);
145 #endif
146
147 // The first 4 layers should be in the atlas (and thus have non-empty rects)
148 if (i < TestingAccess::NumPlots()) {
149 REPORTER_ASSERT(reporter, layer->isAtlased());
150 #if GR_CACHE_STATS
151 REPORTER_ASSERT(reporter, 1 == stats.fTotal);
152 #endif
153 } else {
154 // The 5th layer couldn't fit in the atlas
155 REPORTER_ASSERT(reporter, !layer->isAtlased());
156 #if GR_CACHE_STATS
157 REPORTER_ASSERT(reporter, 2 == stats.fTotal);
158 #endif
159 }
160 }
161
162 // Unlock the textures
163 for (int i = 0; i < kInitialNumLayers; ++i) {
164 int key[1] = { i+1 };
165
166 GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(),
167 key, 1);
168 REPORTER_ASSERT(reporter, layer);
169 cache.removeUse(layer);
170 }
171
172 #if GR_CACHE_STATS
173 resourceCache->getStats(&stats);
174 REPORTER_ASSERT(reporter, 2 == stats.fTotal);
175 // The floating layer is purgeable the cache is not
176 REPORTER_ASSERT(reporter, 1 == stats.fNumPurgeable);
177 REPORTER_ASSERT(reporter, 1 == stats.fNumNonPurgeable);
178 #endif
179
180 for (int i = 0; i < kInitialNumLayers; ++i) {
181 int key[1] = { i+1 };
182
183 GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(),
184 key, 1);
185 REPORTER_ASSERT(reporter, layer);
186
187 // All the layers should be unlocked
188 REPORTER_ASSERT(reporter, !layer->locked());
189
190 // When hoisted layers aren't cached they are aggressively removed
191 // from the atlas
192 #if GR_CACHE_HOISTED_LAYERS
193 // The first 4 layers should still be in the atlas.
194 if (i < 4) {
195 REPORTER_ASSERT(reporter, layer->texture());
196 REPORTER_ASSERT(reporter, layer->isAtlased());
197 } else {
198 #endif
199 // The final layer should not be atlased.
200 REPORTER_ASSERT(reporter, nullptr == layer->texture());
201 REPORTER_ASSERT(reporter, !layer->isAtlased());
202 #if GR_CACHE_HOISTED_LAYERS
203 }
204 #endif
205 }
206
207 // Let go of the backing texture
208 cache.end();
209 REPORTER_ASSERT(reporter, nullptr == TestingAccess::GetBackingTexture(&cache));
210
211 #if GR_CACHE_STATS
212 resourceCache->getStats(&stats);
213 REPORTER_ASSERT(reporter, 2 == stats.fTotal);
214 // Now both the floater and the atlas are purgeable
215 REPORTER_ASSERT(reporter, 2 == stats.fNumPurgeable);
216 #endif
217
218 // re-attach to the backing texture
219 cache.begin();
220 REPORTER_ASSERT(reporter, TestingAccess::GetBackingTexture(&cache));
221
222 #if GR_CACHE_STATS
223 resourceCache->getStats(&stats);
224 REPORTER_ASSERT(reporter, 2 == stats.fTotal);
225 // The atlas is restored to being non-purgeable
226 REPORTER_ASSERT(reporter, 1 == stats.fNumPurgeable);
227 REPORTER_ASSERT(reporter, 1 == stats.fNumNonPurgeable);
228 #endif
229
230 {
231 int key[1] = { kInitialNumLayers+1 };
232
233 // Add an additional layer. Since all the layers are unlocked this
234 // will force out the first atlased layer
235 create_layers(reporter, &cache, *picture, 1, kInitialNumLayers);
236 GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(),
237 key, 1);
238 REPORTER_ASSERT(reporter, layer);
239
240 lock_layer(reporter, &cache, layer);
241 cache.removeUse(layer);
242 }
243
244 for (int i = 0; i < kInitialNumLayers+1; ++i) {
245 int key[1] = { i+1 };
246
247 GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(),
248 key, 1);
249 #if GR_CACHE_HOISTED_LAYERS
250 // 3 old layers plus the new one should be in the atlas.
251 if (1 == i || 2 == i || 3 == i || 5 == i) {
252 REPORTER_ASSERT(reporter, layer);
253 REPORTER_ASSERT(reporter, !layer->locked());
254 REPORTER_ASSERT(reporter, layer->texture());
255 REPORTER_ASSERT(reporter, layer->isAtlased());
256 } else if (4 == i) {
257 #endif
258 // The one that was never atlased should still be around
259 REPORTER_ASSERT(reporter, layer);
260
261 REPORTER_ASSERT(reporter, nullptr == layer->texture());
262 REPORTER_ASSERT(reporter, !layer->isAtlased());
263 #if GR_CACHE_HOISTED_LAYERS
264 } else {
265 // The one bumped out of the atlas (i.e., 0) should be gone
266 REPORTER_ASSERT(reporter, nullptr == layer);
267 }
268 #endif
269 }
270
271 //--------------------------------------------------------------------
272 // Free them all SkGpuDevice-style. This will not free up the
273 // atlas' texture but will eliminate all the layers.
274 TestingAccess::Purge(&cache, picture->uniqueID());
275
276 REPORTER_ASSERT(reporter, TestingAccess::NumLayers(&cache) == 0);
277
278 #if GR_CACHE_STATS
279 resourceCache->getStats(&stats);
280 REPORTER_ASSERT(reporter, 2 == stats.fTotal);
281 // Atlas isn't purgeable
282 REPORTER_ASSERT(reporter, 1 == stats.fNumPurgeable);
283 REPORTER_ASSERT(reporter, 1 == stats.fNumNonPurgeable);
284 #endif
285
286 //--------------------------------------------------------------------
287 // Test out the GrContext-style purge. This should remove all the layers
288 // and the atlas.
289 // Re-create the layers
290 create_layers(reporter, &cache, *picture, kInitialNumLayers, 0);
291
292 // Free them again GrContext-style. This should free up everything.
293 cache.freeAll();
294
295 REPORTER_ASSERT(reporter, TestingAccess::NumLayers(&cache) == 0);
296
297 REPORTER_ASSERT(reporter, nullptr == TestingAccess::GetBackingTexture(&cache));
298
299 #if GR_CACHE_STATS
300 resourceCache->getStats(&stats);
301 REPORTER_ASSERT(reporter, 2 == stats.fTotal);
302 REPORTER_ASSERT(reporter, 2 == stats.fNumPurgeable);
303 #endif
304
305 // Purge the resource cache ...
306 resourceCache->purgeAllUnlocked();
307
308 #if GR_CACHE_STATS
309 resourceCache->getStats(&stats);
310 REPORTER_ASSERT(reporter, 0 == stats.fTotal);
311 #endif
312
313 // and try to re-attach to the backing texture. This should fail
314 cache.begin();
315 REPORTER_ASSERT(reporter, nullptr == TestingAccess::GetBackingTexture(&cache));
316
317 //--------------------------------------------------------------------
318 // Test out the MessageBus-style purge. This will not free the atlas
319 // but should eliminate the free-floating layers.
320 create_layers(reporter, &cache, *picture, kInitialNumLayers, 0);
321
322 // Allocate/use the layers
323 for (int i = 0; i < kInitialNumLayers; ++i) {
324 int key[1] = { i + 1 };
325 GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(),
326 key, 1);
327 REPORTER_ASSERT(reporter, layer);
328
329 lock_layer(reporter, &cache, layer);
330 }
331
332 #if GR_CACHE_STATS
333 resourceCache->getStats(&stats);
334 REPORTER_ASSERT(reporter, 2 == stats.fTotal);
335 REPORTER_ASSERT(reporter, 2 == stats.fNumNonPurgeable);
336 #endif
337
338 // Unlock the textures
339 for (int i = 0; i < kInitialNumLayers; ++i) {
340 int key[1] = { i+1 };
341
342 GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(),
343 key, 1);
344 REPORTER_ASSERT(reporter, layer);
345 cache.removeUse(layer);
346 }
347
348 picture.reset(nullptr);
349 cache.processDeletedPictures();
350
351 REPORTER_ASSERT(reporter, TestingAccess::NumLayers(&cache) == 0);
352
353 #if GR_CACHE_STATS
354 resourceCache->getStats(&stats);
355 REPORTER_ASSERT(reporter, 2 == stats.fTotal);
356 REPORTER_ASSERT(reporter, 1 == stats.fNumPurgeable);
357 REPORTER_ASSERT(reporter, 1 == stats.fNumNonPurgeable);
358 #endif
359
360 cache.end();
361
362 #if GR_CACHE_STATS
363 resourceCache->getStats(&stats);
364 REPORTER_ASSERT(reporter, 2 == stats.fTotal);
365 REPORTER_ASSERT(reporter, 2 == stats.fNumPurgeable);
366 #endif
367 }
368
369 #endif
370