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