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/GraphicsContext3D.h" 32 #include "public/platform/Platform.h" 33 #include "public/platform/WebThread.h" 34 #include "web/tests/MockWebGraphicsContext3D.h" 35 36 #include <gmock/gmock.h> 37 #include <gtest/gtest.h> 38 39 using namespace WebCore; 40 using testing::InSequence; 41 using testing::Return; 42 using testing::Test; 43 44 45 class FakeCanvas2DLayerBridge : public Canvas2DLayerBridge { 46 public: 47 FakeCanvas2DLayerBridge(PassRefPtr<GraphicsContext3D> context, PassOwnPtr<SkDeferredCanvas> canvas) 48 : Canvas2DLayerBridge(context, canvas, 0, NonOpaque) 49 , m_freeableBytes(0) 50 , m_freeMemoryIfPossibleCount(0) 51 , m_flushCount(0) 52 { 53 } 54 55 virtual size_t storageAllocatedForRecording() OVERRIDE 56 { 57 // Because the fake layer has no canvas to query, just 58 // return status quo. Allocation changes that would normally be 59 // initiated by the canvas can be faked by invoking 60 // storageAllocatedForRecordingChanged directly from the test code. 61 return m_bytesAllocated; 62 } 63 64 void fakeFreeableBytes(size_t size) 65 { 66 m_freeableBytes = size; 67 } 68 69 virtual size_t freeMemoryIfPossible(size_t size) OVERRIDE 70 { 71 m_freeMemoryIfPossibleCount++; 72 size_t bytesFreed = size < m_freeableBytes ? size : m_freeableBytes; 73 m_freeableBytes -= bytesFreed; 74 if (bytesFreed) 75 Canvas2DLayerManager::get().layerAllocatedStorageChanged(this, -((intptr_t)bytesFreed)); 76 m_bytesAllocated -= bytesFreed; 77 return bytesFreed; 78 } 79 80 virtual void flush() OVERRIDE 81 { 82 flushedDrawCommands(); 83 m_flushCount++; 84 } 85 86 public: 87 size_t m_freeableBytes; 88 int m_freeMemoryIfPossibleCount; 89 int m_flushCount; 90 }; 91 92 class FakeCanvas2DLayerBridgePtr { 93 public: 94 FakeCanvas2DLayerBridgePtr(PassRefPtr<FakeCanvas2DLayerBridge> layerBridge) 95 : m_layerBridge(layerBridge) { } 96 97 ~FakeCanvas2DLayerBridgePtr() 98 { 99 m_layerBridge->beginDestruction(); 100 } 101 102 FakeCanvas2DLayerBridge* operator->() { return m_layerBridge.get(); } 103 FakeCanvas2DLayerBridge* get() { return m_layerBridge.get(); } 104 105 private: 106 RefPtr<FakeCanvas2DLayerBridge> m_layerBridge; 107 }; 108 109 static PassOwnPtr<SkDeferredCanvas> createCanvas(GraphicsContext3D* context) 110 { 111 return adoptPtr(SkDeferredCanvas::Create(SkSurface::NewRasterPMColor(1, 1))); 112 } 113 114 class Canvas2DLayerManagerTest : public Test { 115 protected: 116 void storageAllocationTrackingTest() 117 { 118 Canvas2DLayerManager& manager = Canvas2DLayerManager::get(); 119 manager.init(10, 10); 120 { 121 RefPtr<GraphicsContext3D> context = GraphicsContext3D::createGraphicsContextFromWebContext(adoptPtr(new blink::MockWebGraphicsContext3D)); 122 OwnPtr<SkDeferredCanvas> canvas1 = createCanvas(context.get()); 123 FakeCanvas2DLayerBridgePtr layer1(adoptRef(new FakeCanvas2DLayerBridge(context, canvas1.release()))); 124 EXPECT_EQ((size_t)0, manager.m_bytesAllocated); 125 layer1->storageAllocatedForRecordingChanged(1); 126 EXPECT_EQ((size_t)1, manager.m_bytesAllocated); 127 // Test allocation increase 128 layer1->storageAllocatedForRecordingChanged(2); 129 EXPECT_EQ((size_t)2, manager.m_bytesAllocated); 130 // Test allocation decrease 131 layer1->storageAllocatedForRecordingChanged(1); 132 EXPECT_EQ((size_t)1, manager.m_bytesAllocated); 133 { 134 OwnPtr<SkDeferredCanvas> canvas2 = createCanvas(context.get()); 135 FakeCanvas2DLayerBridgePtr layer2(adoptRef(new FakeCanvas2DLayerBridge(context, canvas2.release()))); 136 EXPECT_EQ((size_t)1, manager.m_bytesAllocated); 137 // verify multi-layer allocation tracking 138 layer2->storageAllocatedForRecordingChanged(2); 139 EXPECT_EQ((size_t)3, manager.m_bytesAllocated); 140 } 141 // Verify tracking after destruction 142 EXPECT_EQ((size_t)1, manager.m_bytesAllocated); 143 } 144 } 145 146 void evictionTest() 147 { 148 RefPtr<GraphicsContext3D> context = GraphicsContext3D::createGraphicsContextFromWebContext(adoptPtr(new blink::MockWebGraphicsContext3D)); 149 Canvas2DLayerManager& manager = Canvas2DLayerManager::get(); 150 manager.init(10, 5); 151 OwnPtr<SkDeferredCanvas> canvas = createCanvas(context.get()); 152 FakeCanvas2DLayerBridgePtr layer(adoptRef(new FakeCanvas2DLayerBridge(context, canvas.release()))); 153 layer->fakeFreeableBytes(10); 154 layer->storageAllocatedForRecordingChanged(8); // under the max 155 EXPECT_EQ(0, layer->m_freeMemoryIfPossibleCount); 156 layer->storageAllocatedForRecordingChanged(12); // over the max 157 EXPECT_EQ(1, layer->m_freeMemoryIfPossibleCount); 158 EXPECT_EQ((size_t)3, layer->m_freeableBytes); 159 EXPECT_EQ(0, layer->m_flushCount); // eviction succeeded without triggering a flush 160 EXPECT_EQ((size_t)5, layer->bytesAllocated()); 161 } 162 163 void flushEvictionTest() 164 { 165 RefPtr<GraphicsContext3D> context = GraphicsContext3D::createGraphicsContextFromWebContext(adoptPtr(new blink::MockWebGraphicsContext3D)); 166 Canvas2DLayerManager& manager = Canvas2DLayerManager::get(); 167 manager.init(10, 5); 168 OwnPtr<SkDeferredCanvas> canvas = createCanvas(context.get()); 169 FakeCanvas2DLayerBridgePtr layer(adoptRef(new FakeCanvas2DLayerBridge(context, canvas.release()))); 170 layer->fakeFreeableBytes(1); // Not enough freeable bytes, will cause aggressive eviction by flushing 171 layer->storageAllocatedForRecordingChanged(8); // under the max 172 EXPECT_EQ(0, layer->m_freeMemoryIfPossibleCount); 173 layer->storageAllocatedForRecordingChanged(12); // over the max 174 EXPECT_EQ(2, layer->m_freeMemoryIfPossibleCount); // Two tries, one before flush, one after flush 175 EXPECT_EQ((size_t)0, layer->m_freeableBytes); 176 EXPECT_EQ(1, layer->m_flushCount); // flush was attempted 177 EXPECT_EQ((size_t)11, layer->bytesAllocated()); // flush drops the layer from manager's tracking list 178 EXPECT_FALSE(manager.isInList(layer.get())); 179 } 180 181 void doDeferredFrameTestTask(FakeCanvas2DLayerBridge* layer, bool skipCommands) 182 { 183 EXPECT_FALSE(Canvas2DLayerManager::get().m_taskObserverActive); 184 layer->willUse(); 185 layer->storageAllocatedForRecordingChanged(1); 186 EXPECT_TRUE(Canvas2DLayerManager::get().m_taskObserverActive); 187 if (skipCommands) { 188 layer->willUse(); 189 layer->storageAllocatedForRecordingChanged(0); 190 layer->skippedPendingDrawCommands(); 191 } 192 blink::Platform::current()->currentThread()->exitRunLoop(); 193 } 194 195 class DeferredFrameTestTask : public blink::WebThread::Task { 196 public: 197 DeferredFrameTestTask(Canvas2DLayerManagerTest* test, FakeCanvas2DLayerBridge* layer, bool skipCommands) 198 { 199 m_test = test; 200 m_layer = layer; 201 m_skipCommands = skipCommands; 202 } 203 204 virtual void run() OVERRIDE 205 { 206 m_test->doDeferredFrameTestTask(m_layer, m_skipCommands); 207 } 208 private: 209 Canvas2DLayerManagerTest* m_test; 210 FakeCanvas2DLayerBridge* m_layer; 211 bool m_skipCommands; 212 }; 213 214 void deferredFrameTest() 215 { 216 RefPtr<GraphicsContext3D> context = GraphicsContext3D::createGraphicsContextFromWebContext(adoptPtr(new blink::MockWebGraphicsContext3D)); 217 Canvas2DLayerManager::get().init(10, 10); 218 OwnPtr<SkDeferredCanvas> canvas = createCanvas(context.get()); 219 FakeCanvas2DLayerBridgePtr layer(adoptRef(new FakeCanvas2DLayerBridge(context, canvas.release()))); 220 blink::Platform::current()->currentThread()->postTask(new DeferredFrameTestTask(this, layer.get(), true)); 221 blink::Platform::current()->currentThread()->enterRunLoop(); 222 // Verify that didProcessTask was called upon completion 223 EXPECT_FALSE(Canvas2DLayerManager::get().m_taskObserverActive); 224 // Verify that no flush was performed because frame is fresh 225 EXPECT_EQ(0, layer->m_flushCount); 226 227 // Verify that no flushes are triggered as long as frame are fresh 228 blink::Platform::current()->currentThread()->postTask(new DeferredFrameTestTask(this, layer.get(), true)); 229 blink::Platform::current()->currentThread()->enterRunLoop(); 230 EXPECT_FALSE(Canvas2DLayerManager::get().m_taskObserverActive); 231 EXPECT_EQ(0, layer->m_flushCount); 232 233 blink::Platform::current()->currentThread()->postTask(new DeferredFrameTestTask(this, layer.get(), true)); 234 blink::Platform::current()->currentThread()->enterRunLoop(); 235 EXPECT_FALSE(Canvas2DLayerManager::get().m_taskObserverActive); 236 EXPECT_EQ(0, layer->m_flushCount); 237 238 // Verify that a flush is triggered when queue is accumulating a multi-frame backlog. 239 blink::Platform::current()->currentThread()->postTask(new DeferredFrameTestTask(this, layer.get(), false)); 240 blink::Platform::current()->currentThread()->enterRunLoop(); 241 EXPECT_FALSE(Canvas2DLayerManager::get().m_taskObserverActive); 242 EXPECT_EQ(1, layer->m_flushCount); 243 244 blink::Platform::current()->currentThread()->postTask(new DeferredFrameTestTask(this, layer.get(), false)); 245 blink::Platform::current()->currentThread()->enterRunLoop(); 246 EXPECT_FALSE(Canvas2DLayerManager::get().m_taskObserverActive); 247 EXPECT_EQ(2, layer->m_flushCount); 248 } 249 }; 250 251 namespace { 252 253 TEST_F(Canvas2DLayerManagerTest, testStorageAllocationTracking) 254 { 255 storageAllocationTrackingTest(); 256 } 257 258 TEST_F(Canvas2DLayerManagerTest, testEviction) 259 { 260 evictionTest(); 261 } 262 263 TEST_F(Canvas2DLayerManagerTest, testFlushEviction) 264 { 265 flushEvictionTest(); 266 } 267 268 TEST_F(Canvas2DLayerManagerTest, testDeferredFrame) 269 { 270 deferredFrameTest(); 271 } 272 273 } // namespace 274 275