1/*
2 * Copyright (C) 2012 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1.  Redistributions of source code must retain the above copyright
8 *     notice, this list of conditions and the following disclaimer.
9 * 2.  Redistributions in binary form must reproduce the above copyright
10 *     notice, this list of conditions and the following disclaimer in the
11 *     documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "config.h"
26
27#include "platform/graphics/Canvas2DLayerManager.h"
28
29#include "SkDevice.h"
30#include "SkSurface.h"
31#include "platform/graphics/test/MockWebGraphicsContext3D.h"
32#include "public/platform/Platform.h"
33#include "public/platform/WebGraphicsContext3DProvider.h"
34#include "public/platform/WebThread.h"
35
36#include <gmock/gmock.h>
37#include <gtest/gtest.h>
38
39using namespace blink;
40using testing::InSequence;
41using testing::Return;
42using testing::Test;
43
44namespace {
45
46class MockWebGraphicsContext3DProvider : public WebGraphicsContext3DProvider {
47public:
48    MockWebGraphicsContext3DProvider(WebGraphicsContext3D* context3d)
49        : m_context3d(context3d) { }
50
51    WebGraphicsContext3D* context3d()
52    {
53        return m_context3d;
54    }
55
56    GrContext* grContext()
57    {
58        return 0;
59    }
60
61private:
62    WebGraphicsContext3D* m_context3d;
63};
64
65class FakeCanvas2DLayerBridge : public Canvas2DLayerBridge {
66public:
67    FakeCanvas2DLayerBridge(WebGraphicsContext3D* context, PassOwnPtr<SkDeferredCanvas> canvas, PassRefPtr<SkSurface> surface)
68        : Canvas2DLayerBridge(adoptPtr(new MockWebGraphicsContext3DProvider(context)), canvas, surface, 0, NonOpaque)
69        , m_freeableBytes(0)
70        , m_freeMemoryIfPossibleCount(0)
71        , m_flushCount(0)
72    {
73    }
74
75    virtual size_t storageAllocatedForRecording() OVERRIDE
76    {
77        // Because the fake layer has no canvas to query, just
78        // return status quo. Allocation changes that would normally be
79        // initiated by the canvas can be faked by invoking
80        // storageAllocatedForRecordingChanged directly from the test code.
81        return m_bytesAllocated;
82    }
83
84    void fakeFreeableBytes(size_t size)
85    {
86        m_freeableBytes = size;
87    }
88
89    virtual size_t freeMemoryIfPossible(size_t size) OVERRIDE
90    {
91        m_freeMemoryIfPossibleCount++;
92        size_t bytesFreed = size < m_freeableBytes ? size : m_freeableBytes;
93        m_freeableBytes -= bytesFreed;
94        if (bytesFreed)
95            storageAllocatedForRecordingChanged(m_bytesAllocated - bytesFreed);
96        return bytesFreed;
97    }
98
99    virtual void flush() OVERRIDE
100    {
101        flushedDrawCommands();
102        m_freeableBytes = bytesAllocated();
103        m_flushCount++;
104    }
105
106public:
107    size_t m_freeableBytes;
108    int m_freeMemoryIfPossibleCount;
109    int m_flushCount;
110};
111
112class FakeCanvas2DLayerBridgePtr {
113public:
114    FakeCanvas2DLayerBridgePtr(PassRefPtr<FakeCanvas2DLayerBridge> layerBridge)
115        : m_layerBridge(layerBridge) { }
116
117    ~FakeCanvas2DLayerBridgePtr()
118    {
119        m_layerBridge->beginDestruction();
120    }
121
122    FakeCanvas2DLayerBridge* operator->() { return m_layerBridge.get(); }
123    FakeCanvas2DLayerBridge* get() { return m_layerBridge.get(); }
124
125private:
126    RefPtr<FakeCanvas2DLayerBridge> m_layerBridge;
127};
128
129} // unnamed namespace
130
131class Canvas2DLayerManagerTest : public Test {
132protected:
133    void storageAllocationTrackingTest()
134    {
135        Canvas2DLayerManager& manager = Canvas2DLayerManager::get();
136        manager.init(10, 10);
137        {
138            OwnPtr<MockWebGraphicsContext3D> webContext = adoptPtr(new MockWebGraphicsContext3D);
139            RefPtr<SkSurface> surface1 = adoptRef(SkSurface::NewRasterPMColor(1, 1));
140            OwnPtr<SkDeferredCanvas> canvas1 = adoptPtr(SkDeferredCanvas::Create(surface1.get()));
141            FakeCanvas2DLayerBridgePtr layer1(adoptRef(new FakeCanvas2DLayerBridge(webContext.get(), canvas1.release(), surface1.release())));
142            EXPECT_EQ((size_t)0, manager.m_bytesAllocated);
143            layer1->storageAllocatedForRecordingChanged(1);
144            EXPECT_EQ((size_t)1, manager.m_bytesAllocated);
145            // Test allocation increase
146            layer1->storageAllocatedForRecordingChanged(2);
147            EXPECT_EQ((size_t)2, manager.m_bytesAllocated);
148            // Test allocation decrease
149            layer1->storageAllocatedForRecordingChanged(1);
150            EXPECT_EQ((size_t)1, manager.m_bytesAllocated);
151            {
152                RefPtr<SkSurface> surface2 = adoptRef(SkSurface::NewRasterPMColor(1, 1));
153                OwnPtr<SkDeferredCanvas> canvas2 = adoptPtr(SkDeferredCanvas::Create(surface2.get()));
154                FakeCanvas2DLayerBridgePtr layer2(adoptRef(new FakeCanvas2DLayerBridge(webContext.get(), canvas2.release(), surface2.release())));
155                EXPECT_EQ((size_t)1, manager.m_bytesAllocated);
156                // verify multi-layer allocation tracking
157                layer2->storageAllocatedForRecordingChanged(2);
158                EXPECT_EQ((size_t)3, manager.m_bytesAllocated);
159            }
160            // Verify tracking after destruction
161            EXPECT_EQ((size_t)1, manager.m_bytesAllocated);
162        }
163    }
164
165    void evictionTest()
166    {
167        OwnPtr<MockWebGraphicsContext3D> webContext = adoptPtr(new MockWebGraphicsContext3D);
168        Canvas2DLayerManager& manager = Canvas2DLayerManager::get();
169        manager.init(10, 5);
170        RefPtr<SkSurface> surface = adoptRef(SkSurface::NewRasterPMColor(1, 1));
171        OwnPtr<SkDeferredCanvas> canvas = adoptPtr(SkDeferredCanvas::Create(surface.get()));
172        FakeCanvas2DLayerBridgePtr layer(adoptRef(new FakeCanvas2DLayerBridge(webContext.get(), canvas.release(), surface.release())));
173        layer->fakeFreeableBytes(10);
174        layer->storageAllocatedForRecordingChanged(8); // under the max
175        EXPECT_EQ(0, layer->m_freeMemoryIfPossibleCount);
176        layer->storageAllocatedForRecordingChanged(12); // over the max
177        EXPECT_EQ(1, layer->m_freeMemoryIfPossibleCount);
178        EXPECT_EQ((size_t)3, layer->m_freeableBytes);
179        EXPECT_EQ(0, layer->m_flushCount); // eviction succeeded without triggering a flush
180        EXPECT_EQ((size_t)5, layer->bytesAllocated());
181    }
182
183    void hiddenCanvasTest()
184    {
185        OwnPtr<MockWebGraphicsContext3D> webContext = adoptPtr(new MockWebGraphicsContext3D);
186        Canvas2DLayerManager& manager = Canvas2DLayerManager::get();
187        manager.init(20, 5);
188        RefPtr<SkSurface> surface = adoptRef(SkSurface::NewRasterPMColor(1, 1));
189        OwnPtr<SkDeferredCanvas> canvas = adoptPtr(SkDeferredCanvas::Create(surface.get()));
190        FakeCanvas2DLayerBridgePtr layer(adoptRef(new FakeCanvas2DLayerBridge(webContext.get(), canvas.release(), surface.release())));
191        layer->fakeFreeableBytes(5);
192        layer->storageAllocatedForRecordingChanged(10);
193        EXPECT_EQ(0, layer->m_freeMemoryIfPossibleCount);
194        EXPECT_EQ(0, layer->m_flushCount);
195        EXPECT_EQ((size_t)10, layer->bytesAllocated());
196        layer->setIsHidden(true);
197        EXPECT_EQ(1, layer->m_freeMemoryIfPossibleCount);
198        EXPECT_EQ((size_t)0, layer->m_freeableBytes);
199        EXPECT_EQ((size_t)0, layer->bytesAllocated());
200        EXPECT_EQ(1, layer->m_flushCount);
201    }
202
203    void addRemoveLayerTest()
204    {
205        OwnPtr<MockWebGraphicsContext3D> webContext = adoptPtr(new MockWebGraphicsContext3D);
206        Canvas2DLayerManager& manager = Canvas2DLayerManager::get();
207        manager.init(10, 5);
208        RefPtr<SkSurface> surface = adoptRef(SkSurface::NewRasterPMColor(1, 1));
209        OwnPtr<SkDeferredCanvas> canvas = adoptPtr(SkDeferredCanvas::Create(surface.get()));
210        FakeCanvas2DLayerBridgePtr layer(adoptRef(new FakeCanvas2DLayerBridge(webContext.get(), canvas.release(), surface.release())));
211        EXPECT_FALSE(manager.isInList(layer.get()));
212        layer->storageAllocatedForRecordingChanged(5);
213        EXPECT_TRUE(manager.isInList(layer.get()));
214        layer->storageAllocatedForRecordingChanged(0);
215        EXPECT_FALSE(manager.isInList(layer.get()));
216    }
217
218    void flushEvictionTest()
219    {
220        OwnPtr<MockWebGraphicsContext3D> webContext = adoptPtr(new MockWebGraphicsContext3D);
221        Canvas2DLayerManager& manager = Canvas2DLayerManager::get();
222        manager.init(10, 5);
223        RefPtr<SkSurface> surface = adoptRef(SkSurface::NewRasterPMColor(1, 1));
224        OwnPtr<SkDeferredCanvas> canvas = adoptPtr(SkDeferredCanvas::Create(surface.get()));
225        FakeCanvas2DLayerBridgePtr layer(adoptRef(new FakeCanvas2DLayerBridge(webContext.get(), canvas.release(), surface.release())));
226        layer->fakeFreeableBytes(1); // Not enough freeable bytes, will cause aggressive eviction by flushing
227        layer->storageAllocatedForRecordingChanged(8); // under the max
228        EXPECT_EQ(0, layer->m_freeMemoryIfPossibleCount);
229        layer->storageAllocatedForRecordingChanged(12); // over the max
230        EXPECT_EQ(2, layer->m_freeMemoryIfPossibleCount); // Two tries, one before flush, one after flush
231        EXPECT_EQ((size_t)5, layer->m_freeableBytes);
232        EXPECT_EQ(1, layer->m_flushCount); // flush was attempted
233        EXPECT_EQ((size_t)5, layer->bytesAllocated());
234        EXPECT_TRUE(manager.isInList(layer.get()));
235    }
236
237    void doDeferredFrameTestTask(FakeCanvas2DLayerBridge* layer, bool skipCommands)
238    {
239        FloatRect invalidationRect(0, 0, 1, 1);
240        EXPECT_FALSE(Canvas2DLayerManager::get().m_taskObserverActive);
241        layer->finalizeFrame(invalidationRect);
242        layer->storageAllocatedForRecordingChanged(1);
243        EXPECT_TRUE(Canvas2DLayerManager::get().m_taskObserverActive);
244        if (skipCommands) {
245            layer->finalizeFrame(invalidationRect);
246            layer->skippedPendingDrawCommands();
247        }
248        Platform::current()->currentThread()->exitRunLoop();
249    }
250
251    class DeferredFrameTestTask : public WebThread::Task {
252    public:
253        DeferredFrameTestTask(Canvas2DLayerManagerTest* test, FakeCanvas2DLayerBridge* layer, bool skipCommands)
254        {
255            m_test = test;
256            m_layer = layer;
257            m_skipCommands = skipCommands;
258        }
259
260        virtual void run() OVERRIDE
261        {
262            m_test->doDeferredFrameTestTask(m_layer, m_skipCommands);
263        }
264    private:
265        Canvas2DLayerManagerTest* m_test;
266        FakeCanvas2DLayerBridge* m_layer;
267        bool m_skipCommands;
268    };
269
270    void deferredFrameTest()
271    {
272        OwnPtr<MockWebGraphicsContext3D> webContext = adoptPtr(new MockWebGraphicsContext3D);
273        Canvas2DLayerManager::get().init(10, 10);
274        RefPtr<SkSurface> surface = adoptRef(SkSurface::NewRasterPMColor(1, 1));
275        OwnPtr<SkDeferredCanvas> canvas = adoptPtr(SkDeferredCanvas::Create(surface.get()));
276        FakeCanvas2DLayerBridgePtr layer(adoptRef(new FakeCanvas2DLayerBridge(webContext.get(), canvas.release(), surface.release())));
277        Platform::current()->currentThread()->postTask(new DeferredFrameTestTask(this, layer.get(), true));
278        Platform::current()->currentThread()->enterRunLoop();
279        // Verify that didProcessTask was called upon completion
280        EXPECT_FALSE(Canvas2DLayerManager::get().m_taskObserverActive);
281        // Verify that no flush was performed because frame is fresh
282        EXPECT_EQ(0, layer->m_flushCount);
283
284        // Verify that no flushes are triggered as long as frame are fresh
285        Platform::current()->currentThread()->postTask(new DeferredFrameTestTask(this, layer.get(), true));
286        Platform::current()->currentThread()->enterRunLoop();
287        EXPECT_FALSE(Canvas2DLayerManager::get().m_taskObserverActive);
288        EXPECT_EQ(0, layer->m_flushCount);
289
290        Platform::current()->currentThread()->postTask(new DeferredFrameTestTask(this, layer.get(), true));
291        Platform::current()->currentThread()->enterRunLoop();
292        EXPECT_FALSE(Canvas2DLayerManager::get().m_taskObserverActive);
293        EXPECT_EQ(0, layer->m_flushCount);
294
295        // Verify that a flush is triggered when queue is accumulating a multi-frame backlog.
296        Platform::current()->currentThread()->postTask(new DeferredFrameTestTask(this, layer.get(), false));
297        Platform::current()->currentThread()->enterRunLoop();
298        EXPECT_FALSE(Canvas2DLayerManager::get().m_taskObserverActive);
299        EXPECT_EQ(1, layer->m_flushCount);
300
301        Platform::current()->currentThread()->postTask(new DeferredFrameTestTask(this, layer.get(), false));
302        Platform::current()->currentThread()->enterRunLoop();
303        EXPECT_FALSE(Canvas2DLayerManager::get().m_taskObserverActive);
304        EXPECT_EQ(2, layer->m_flushCount);
305    }
306};
307
308namespace {
309
310TEST_F(Canvas2DLayerManagerTest, testStorageAllocationTracking)
311{
312    storageAllocationTrackingTest();
313}
314
315TEST_F(Canvas2DLayerManagerTest, testEviction)
316{
317    evictionTest();
318}
319
320TEST_F(Canvas2DLayerManagerTest, testFlushEviction)
321{
322    flushEvictionTest();
323}
324
325TEST_F(Canvas2DLayerManagerTest, testDeferredFrame)
326{
327    deferredFrameTest();
328}
329
330TEST_F(Canvas2DLayerManagerTest, testHiddenCanvas)
331{
332    hiddenCanvasTest();
333}
334
335TEST_F(Canvas2DLayerManagerTest, testAddRemoveLayer)
336{
337    addRemoveLayerTest();
338}
339
340} // unnamed namespace
341
342