DeferredCanvasTest.cpp revision 3de7acc180e37c7c513f2b3425bd4616ea47fa57
1
2/*
3 * Copyright 2012 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8#include "Test.h"
9#include "SkBitmap.h"
10#include "SkDeferredCanvas.h"
11#include "SkShader.h"
12
13static const int gWidth = 2;
14static const int gHeight = 2;
15
16static void create(SkBitmap* bm, SkBitmap::Config config, SkColor color) {
17    bm->setConfig(config, gWidth, gHeight);
18    bm->allocPixels();
19    bm->eraseColor(color);
20}
21
22static void TestDeferredCanvasBitmapAccess(skiatest::Reporter* reporter) {
23    SkBitmap store;
24
25    create(&store, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
26    SkDevice device(store);
27    SkDeferredCanvas canvas(&device);
28
29    canvas.clear(0x00000000);
30
31    SkAutoLockPixels alp(store);
32    REPORTER_ASSERT(reporter, store.getColor(0,0) == 0xFFFFFFFF); //verify that clear was deferred
33    SkBitmap accessed = canvas.getDevice()->accessBitmap(false);
34    REPORTER_ASSERT(reporter, store.getColor(0,0) == 0x00000000); //verify that clear was executed
35    REPORTER_ASSERT(reporter, accessed.pixelRef() == store.pixelRef());
36}
37
38static void TestDeferredCanvasFlush(skiatest::Reporter* reporter) {
39    SkBitmap store;
40
41    create(&store, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
42    SkDevice device(store);
43    SkDeferredCanvas canvas(&device);
44
45    canvas.clear(0x00000000);
46
47    SkAutoLockPixels alp(store);
48    REPORTER_ASSERT(reporter, store.getColor(0,0) == 0xFFFFFFFF); //verify that clear was deferred
49    canvas.flush();
50    REPORTER_ASSERT(reporter, store.getColor(0,0) == 0x00000000); //verify that clear was executed
51}
52
53static void TestDeferredCanvasFreshFrame(skiatest::Reporter* reporter) {
54    SkBitmap store;
55    SkRect fullRect;
56    fullRect.setXYWH(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(gWidth),
57        SkIntToScalar(gHeight));
58    SkRect partialRect;
59    partialRect.setXYWH(SkIntToScalar(0), SkIntToScalar(0),
60        SkIntToScalar(1), SkIntToScalar(1));
61    create(&store, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
62    SkDevice device(store);
63    SkDeferredCanvas canvas(&device);
64
65    // verify that frame is intially fresh
66    REPORTER_ASSERT(reporter, canvas.getDeferredDevice()->isFreshFrame());
67    // no clearing op since last call to isFreshFrame -> not fresh
68    REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame());
69
70    // Verify that clear triggers a fresh frame
71    canvas.clear(0x00000000);
72    REPORTER_ASSERT(reporter, canvas.getDeferredDevice()->isFreshFrame());
73
74    // Verify that clear with saved state triggers a fresh frame
75    canvas.save(SkCanvas::kMatrixClip_SaveFlag);
76    canvas.clear(0x00000000);
77    canvas.restore();
78    REPORTER_ASSERT(reporter, canvas.getDeferredDevice()->isFreshFrame());
79
80    // Verify that clear within a layer does NOT trigger a fresh frame
81    canvas.saveLayer(NULL, NULL, SkCanvas::kARGB_ClipLayer_SaveFlag);
82    canvas.clear(0x00000000);
83    canvas.restore();
84    REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame());
85
86    // Verify that a clear with clipping triggers a fresh frame
87    // (clear is not affected by clipping)
88    canvas.save(SkCanvas::kMatrixClip_SaveFlag);
89    canvas.clipRect(partialRect, SkRegion::kIntersect_Op, false);
90    canvas.clear(0x00000000);
91    canvas.restore();
92    REPORTER_ASSERT(reporter, canvas.getDeferredDevice()->isFreshFrame());
93
94    // Verify that full frame rects with different forms of opaque paint
95    // trigger frames to be marked as fresh
96    {
97        SkPaint paint;
98        paint.setStyle( SkPaint::kFill_Style );
99        paint.setAlpha( 255 );
100        canvas.drawRect(fullRect, paint);
101        REPORTER_ASSERT(reporter, canvas.getDeferredDevice()->isFreshFrame());
102    }
103    {
104        SkPaint paint;
105        paint.setStyle( SkPaint::kFill_Style );
106        SkBitmap bmp;
107        create(&bmp, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
108        bmp.setIsOpaque(true);
109        SkShader* shader = SkShader::CreateBitmapShader(bmp,
110            SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
111        paint.setShader(shader)->unref();
112        canvas.drawRect(fullRect, paint);
113        REPORTER_ASSERT(reporter, canvas.getDeferredDevice()->isFreshFrame());
114    }
115
116    // Verify that full frame rects with different forms of non-opaque paint
117    // do not trigger frames to be marked as fresh
118    {
119        SkPaint paint;
120        paint.setStyle( SkPaint::kFill_Style );
121        paint.setAlpha( 254 );
122        canvas.drawRect(fullRect, paint);
123        REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame());
124    }
125    {
126        SkPaint paint;
127        paint.setStyle( SkPaint::kFill_Style );
128        SkBitmap bmp;
129        create(&bmp, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
130        bmp.setIsOpaque(false);
131        SkShader* shader = SkShader::CreateBitmapShader(bmp,
132            SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
133        paint.setShader(shader)->unref();
134        canvas.drawRect(fullRect, paint);
135        REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame());
136    }
137
138    // Verify that incomplete coverage does not trigger a fresh frame
139    {
140        SkPaint paint;
141        paint.setStyle(SkPaint::kFill_Style);
142        paint.setAlpha(255);
143        canvas.drawRect(partialRect, paint);
144        REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame());
145    }
146
147    // Verify that incomplete coverage due to clipping does not trigger a fresh
148    // frame
149    {
150        canvas.save(SkCanvas::kMatrixClip_SaveFlag);
151        canvas.clipRect(partialRect, SkRegion::kIntersect_Op, false);
152        SkPaint paint;
153        paint.setStyle(SkPaint::kFill_Style);
154        paint.setAlpha(255);
155        canvas.drawRect(fullRect, paint);
156        REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame());
157    }
158
159    // Verify that stroked rect does not trigger a fresh frame
160    {
161        SkPaint paint;
162        paint.setStyle( SkPaint::kStroke_Style );
163        paint.setAlpha( 255 );
164        canvas.drawRect(fullRect, paint);
165        REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame());
166    }
167
168    // Verify kSrcMode triggers a fresh frame even with transparent color
169    {
170        SkPaint paint;
171        paint.setStyle( SkPaint::kFill_Style );
172        paint.setAlpha( 100 );
173        paint.setXfermodeMode(SkXfermode::kSrc_Mode);
174        canvas.drawRect(fullRect, paint);
175        REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame());
176    }
177}
178
179class MockDevice : public SkDevice {
180public:
181    MockDevice(const SkBitmap& bm) : SkDevice(bm) {
182        fDrawBitmapCallCount = 0;
183    }
184    virtual void drawBitmap(const SkDraw&, const SkBitmap&,
185                            const SkIRect*,
186                            const SkMatrix&, const SkPaint&) {
187        fDrawBitmapCallCount++;
188    }
189
190    int fDrawBitmapCallCount;
191};
192
193// Verifies that the deferred canvas triggers a flush when its memory
194// limit is exceeded
195static void TestDeferredCanvasMemoryLimit(skiatest::Reporter* reporter) {
196    SkBitmap store;
197    store.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
198    store.allocPixels();
199    MockDevice mockDevice(store);
200    SkDeferredCanvas canvas(&mockDevice);
201    canvas.setMaxRecordingStorage(160000);
202
203    SkBitmap sourceImage;
204    // 100 by 100 image, takes 40,000 bytes in memory
205    sourceImage.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
206    sourceImage.allocPixels();
207
208    for (int i = 0; i < 5; i++) {
209        sourceImage.notifyPixelsChanged(); // to force re-serialization
210        canvas.drawBitmap(sourceImage, 0, 0, NULL);
211    }
212
213    REPORTER_ASSERT(reporter, mockDevice.fDrawBitmapCallCount == 4);
214}
215
216static void TestDeferredCanvasBitmapCaching(skiatest::Reporter* reporter) {
217    SkBitmap store;
218    store.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
219    store.allocPixels();
220    SkDevice device(store);
221    SkDeferredCanvas canvas(&device);
222
223    const int imageCount = 2;
224    SkBitmap sourceImages[imageCount];
225    for (int i = 0; i < imageCount; i++)
226    {
227        sourceImages[i].setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
228        sourceImages[i].allocPixels();
229    }
230
231    size_t bitmapSize = sourceImages[0].getSize();
232
233    canvas.drawBitmap(sourceImages[0], 0, 0, NULL);
234    // stored bitmap + drawBitmap command
235    REPORTER_ASSERT(reporter, canvas.storageAllocatedForRecording() > bitmapSize);
236
237    // verify that nothing can be freed at this point
238    REPORTER_ASSERT(reporter, 0 == canvas.freeMemoryIfPossible(~0));
239
240    // verify that flush leaves image in cache
241    canvas.flush();
242    REPORTER_ASSERT(reporter, canvas.storageAllocatedForRecording() >= bitmapSize);
243
244    // verify that after a flush, cached image can be freed
245    REPORTER_ASSERT(reporter, canvas.freeMemoryIfPossible(~0) >= bitmapSize);
246
247    // Verify that caching works for avoiding multiple copies of the same bitmap
248    canvas.drawBitmap(sourceImages[0], 0, 0, NULL);
249    canvas.drawBitmap(sourceImages[0], 0, 0, NULL);
250    REPORTER_ASSERT(reporter, canvas.storageAllocatedForRecording() < 2 * bitmapSize);
251
252    // Verify partial eviction based on bytesToFree
253    canvas.drawBitmap(sourceImages[1], 0, 0, NULL);
254    canvas.flush();
255    REPORTER_ASSERT(reporter, canvas.storageAllocatedForRecording() > 2 * bitmapSize);
256    size_t bytesFreed = canvas.freeMemoryIfPossible(1);
257    REPORTER_ASSERT(reporter,  bytesFreed >= bitmapSize);
258    REPORTER_ASSERT(reporter,  bytesFreed < 2*bitmapSize);
259
260    // Verifiy that partial purge works, image zero is in cache but not reffed by
261    // a pending draw, while image 1 is locked-in.
262    canvas.freeMemoryIfPossible(~0);
263    canvas.drawBitmap(sourceImages[0], 0, 0, NULL);
264    canvas.flush();
265    canvas.drawBitmap(sourceImages[1], 0, 0, NULL);
266    bytesFreed = canvas.freeMemoryIfPossible(~0);
267    // only one bitmap should have been freed.
268    REPORTER_ASSERT(reporter,  bytesFreed >= bitmapSize);
269    REPORTER_ASSERT(reporter,  bytesFreed < 2*bitmapSize);
270    // Clear for next test
271    canvas.flush();
272    canvas.freeMemoryIfPossible(~0);
273    REPORTER_ASSERT(reporter, canvas.storageAllocatedForRecording() < bitmapSize);
274
275    // Verify the image cache is sensitive to genID bumps
276    canvas.drawBitmap(sourceImages[1], 0, 0, NULL);
277    sourceImages[1].notifyPixelsChanged();
278    canvas.drawBitmap(sourceImages[1], 0, 0, NULL);
279    REPORTER_ASSERT(reporter, canvas.storageAllocatedForRecording() > 2*bitmapSize);
280}
281
282static void TestDeferredCanvas(skiatest::Reporter* reporter) {
283    TestDeferredCanvasBitmapAccess(reporter);
284    TestDeferredCanvasFlush(reporter);
285    TestDeferredCanvasFreshFrame(reporter);
286    TestDeferredCanvasMemoryLimit(reporter);
287    TestDeferredCanvasBitmapCaching(reporter);
288}
289
290#include "TestClassDef.h"
291DEFINE_TESTCLASS("DeferredCanvas", TestDeferredCanvasClass, TestDeferredCanvas)
292