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