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