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 "GrContextFactory.h"
12#include "GrLayerCache.h"
13#include "SkPictureRecorder.h"
14#include "Test.h"
15
16class TestingAccess {
17public:
18    static unsigned NumLayers(GrLayerCache* cache) {
19        return cache->numLayers();
20    }
21    static void Purge(GrLayerCache* cache, uint32_t pictureID) {
22        cache->purge(pictureID);
23    }
24    static int Uses(GrCachedLayer* layer) {
25        return layer->uses();
26    }
27    static GrCachedLayer* Find(GrLayerCache* cache, uint32_t pictureID,
28                               const SkMatrix& initialMat,
29                               const unsigned* key, int keySize) {
30        return cache->findLayer(pictureID, initialMat, key, keySize);
31    }
32};
33
34// Add several layers to the cache
35static void create_layers(skiatest::Reporter* reporter,
36                          GrLayerCache* cache,
37                          const SkPicture& picture,
38                          unsigned numToAdd,
39                          unsigned idOffset) {
40
41    for (unsigned i = 0; i < numToAdd; ++i) {
42        unsigned indices[1] = { idOffset+i+1 };
43        GrCachedLayer* layer = cache->findLayerOrCreate(picture.uniqueID(),
44                                                        idOffset+i+1, idOffset+i+2,
45                                                        SkIRect::MakeEmpty(),
46                                                        SkIRect::MakeEmpty(),
47                                                        SkMatrix::I(),
48                                                        indices, 1,
49                                                        NULL);
50        REPORTER_ASSERT(reporter, layer);
51        GrCachedLayer* temp = TestingAccess::Find(cache, picture.uniqueID(), SkMatrix::I(),
52                                                  indices, 1);
53        REPORTER_ASSERT(reporter, temp == layer);
54
55        REPORTER_ASSERT(reporter, TestingAccess::NumLayers(cache) == idOffset + i + 1);
56
57        REPORTER_ASSERT(reporter, picture.uniqueID() == layer->pictureID());
58        REPORTER_ASSERT(reporter, layer->start() == idOffset + i + 1);
59        REPORTER_ASSERT(reporter, layer->stop() == idOffset + i + 2);
60        REPORTER_ASSERT(reporter, NULL == layer->texture());
61        REPORTER_ASSERT(reporter, NULL == layer->paint());
62        REPORTER_ASSERT(reporter, !layer->isAtlased());
63    }
64}
65
66static void lock_layer(skiatest::Reporter* reporter,
67                       GrLayerCache* cache,
68                       GrCachedLayer* layer) {
69    // Make the layer 512x512 (so it can be atlased)
70    GrSurfaceDesc desc;
71    desc.fWidth = 512;
72    desc.fHeight = 512;
73    desc.fConfig = kSkia8888_GrPixelConfig;
74
75    bool needsRerendering;
76    bool inAtlas = cache->tryToAtlas(layer, desc, &needsRerendering);
77    if (!inAtlas) {
78        cache->lock(layer, desc, &needsRerendering);
79    }
80    REPORTER_ASSERT(reporter, needsRerendering);
81
82    cache->lock(layer, desc, &needsRerendering);
83    REPORTER_ASSERT(reporter, !needsRerendering);
84
85    REPORTER_ASSERT(reporter, layer->texture());
86    REPORTER_ASSERT(reporter, layer->locked());
87
88    cache->addUse(layer);
89
90    REPORTER_ASSERT(reporter, 1 == TestingAccess::Uses(layer));
91}
92
93// This test case exercises the public API of the GrLayerCache class.
94// In particular it checks its interaction with the resource cache (w.r.t.
95// locking & unlocking textures).
96// TODO: need to add checks on VRAM usage!
97DEF_GPUTEST(GpuLayerCache, reporter, factory) {
98    static const unsigned kInitialNumLayers = 5;
99
100    for (int i= 0; i < GrContextFactory::kGLContextTypeCnt; ++i) {
101        GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i;
102
103        if (!GrContextFactory::IsRenderingGLContext(glCtxType)) {
104            continue;
105        }
106
107        GrContext* context = factory->get(glCtxType);
108
109        if (NULL == context) {
110            continue;
111        }
112
113        SkPictureRecorder recorder;
114        recorder.beginRecording(1, 1);
115        SkAutoTUnref<const SkPicture> picture(recorder.endRecording());
116
117        GrLayerCache cache(context);
118
119        create_layers(reporter, &cache, *picture, kInitialNumLayers, 0);
120
121        for (unsigned i = 0; i < kInitialNumLayers; ++i) {
122            unsigned indices[1] = { i + 1 };
123            GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(),
124                                                       indices, 1);
125            REPORTER_ASSERT(reporter, layer);
126
127            lock_layer(reporter, &cache, layer);
128
129            // The first 4 layers should be in the atlas (and thus have non-empty
130            // rects)
131            if (i < 4) {
132                REPORTER_ASSERT(reporter, layer->isAtlased());
133            } else {
134                // The 5th layer couldn't fit in the atlas
135                REPORTER_ASSERT(reporter, !layer->isAtlased());
136            }
137        }
138
139        // Unlock the textures
140        for (unsigned i = 0; i < kInitialNumLayers; ++i) {
141            unsigned indices[1] = { i+1 };
142
143            GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(),
144                                                       indices, 1);
145            REPORTER_ASSERT(reporter, layer);
146            cache.removeUse(layer);
147        }
148
149        for (unsigned i = 0; i < kInitialNumLayers; ++i) {
150            unsigned indices[1] = { i+1 };
151
152            GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(),
153                                                       indices, 1);
154            REPORTER_ASSERT(reporter, layer);
155
156            // All the layers should be unlocked
157            REPORTER_ASSERT(reporter, !layer->locked());
158
159            // When hoisted layers aren't cached they are aggressively removed
160            // from the atlas
161#if GR_CACHE_HOISTED_LAYERS
162            // The first 4 layers should still be in the atlas.
163            if (i < 4) {
164                REPORTER_ASSERT(reporter, layer->texture());
165                REPORTER_ASSERT(reporter, layer->isAtlased());
166            } else {
167#endif
168                // The final layer should not be atlased.
169                REPORTER_ASSERT(reporter, NULL == layer->texture());
170                REPORTER_ASSERT(reporter, !layer->isAtlased());
171#if GR_CACHE_HOISTED_LAYERS
172            }
173#endif
174        }
175
176        {
177            unsigned indices[1] = { kInitialNumLayers+1 };
178
179            // Add an additional layer. Since all the layers are unlocked this
180            // will force out the first atlased layer
181            create_layers(reporter, &cache, *picture, 1, kInitialNumLayers);
182            GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(),
183                                                       indices, 1);
184            REPORTER_ASSERT(reporter, layer);
185
186            lock_layer(reporter, &cache, layer);
187            cache.removeUse(layer);
188        }
189
190        for (unsigned i = 0; i < kInitialNumLayers+1; ++i) {
191            unsigned indices[1] = { i+1 };
192
193            GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(),
194                                                       indices, 1);
195#if GR_CACHE_HOISTED_LAYERS
196            // 3 old layers plus the new one should be in the atlas.
197            if (1 == i || 2 == i || 3 == i || 5 == i) {
198                REPORTER_ASSERT(reporter, layer);
199                REPORTER_ASSERT(reporter, !layer->locked());
200                REPORTER_ASSERT(reporter, layer->texture());
201                REPORTER_ASSERT(reporter, layer->isAtlased());
202            } else if (4 == i) {
203#endif
204                // The one that was never atlased should still be around
205                REPORTER_ASSERT(reporter, layer);
206
207                REPORTER_ASSERT(reporter, NULL == layer->texture());
208                REPORTER_ASSERT(reporter, !layer->isAtlased());
209#if GR_CACHE_HOISTED_LAYERS
210            } else {
211                // The one bumped out of the atlas (i.e., 0) should be gone
212                REPORTER_ASSERT(reporter, NULL == layer);
213            }
214#endif
215        }
216
217        //--------------------------------------------------------------------
218        // Free them all SkGpuDevice-style. This will not free up the
219        // atlas' texture but will eliminate all the layers.
220        TestingAccess::Purge(&cache, picture->uniqueID());
221
222        REPORTER_ASSERT(reporter, TestingAccess::NumLayers(&cache) == 0);
223        // TODO: add VRAM/resource cache check here
224
225        //--------------------------------------------------------------------
226        // Test out the GrContext-style purge. This should remove all the layers
227        // and the atlas.
228        // Re-create the layers
229        create_layers(reporter, &cache, *picture, kInitialNumLayers, 0);
230
231        // Free them again GrContext-style. This should free up everything.
232        cache.freeAll();
233
234        REPORTER_ASSERT(reporter, TestingAccess::NumLayers(&cache) == 0);
235        // TODO: add VRAM/resource cache check here
236
237        //--------------------------------------------------------------------
238        // Test out the MessageBus-style purge. This will not free the atlas
239        // but should eliminate the free-floating layers.
240        create_layers(reporter, &cache, *picture, kInitialNumLayers, 0);
241
242        picture.reset(NULL);
243        cache.processDeletedPictures();
244
245        REPORTER_ASSERT(reporter, TestingAccess::NumLayers(&cache) == 0);
246        // TODO: add VRAM/resource cache check here
247    }
248}
249
250#endif
251